17 Commits
0.1.0 ... 0.2.0

Author SHA1 Message Date
32028a54b9 make version accessible without having to read a file
This works when krowlog is bundled into a single executable
2022-08-28 08:44:45 +02:00
b6aa3083d2 find icons if cwd is not in the program root 2022-08-27 17:59:37 +02:00
0c55fdc44b Update 'README.md' 2022-08-27 09:16:31 +00:00
750456642c use krowlog svg as icon 2022-08-27 11:08:56 +02:00
27dbc9085d cleanup 2022-08-26 19:24:16 +02:00
7b0d7f8807 use PySide6-Essentials instead of full install
This reduces the size to 'just' 220mb.
2022-08-26 19:00:37 +02:00
bbedaf73de highlight other matches of selected text 2022-08-26 15:31:45 +02:00
6040b1633d cleanup 2022-08-26 14:31:51 +02:00
1ddd589cc2 support Windows file share locations in drag&drop 2022-08-26 14:09:34 +02:00
5e81d90c1f update urllib3 2022-08-25 19:54:28 +02:00
46a49f1b90 get more library versions programmatically 2022-08-25 19:38:18 +02:00
3772e696ce make version label selectable 2022-08-25 19:27:05 +02:00
f9d10d37ec translate the number of matched lines text 2022-08-25 19:23:46 +02:00
4a082ab8ee fix a few warnings 2022-08-25 19:19:30 +02:00
fcc570d75f show how many lines match 2022-08-25 19:15:30 +02:00
7732d95626 remove abstract method annotation from copy method
The method was not abstract.
2022-08-25 19:13:59 +02:00
fc0922c661 rename folder filesbrowserplugin is in 2022-08-25 19:12:16 +02:00
22 changed files with 240 additions and 153 deletions

View File

@@ -1,4 +1,4 @@
# ![Logo for KrowLog](icons/icon.png "Logo for KrowLog") KrowLog # ![Logo for KrowLog](icons/krowlog.svg "Logo for KrowLog") KrowLog
KrowLog is a viewer for text files of arbitrary size. KrowLog is a viewer for text files of arbitrary size.

View File

@@ -1 +0,0 @@
0.2-alpha

View File

@@ -1,3 +1,3 @@
krow_icon = "icons/icon.png" krow_icon = "icons/krowlog.svg"
tab_width = 4 tab_width = 4

46
icons/krowlog.svg Normal file
View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="64" height="64" viewBox="0 0 128 128" version="1.1" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="bg1" x1="0%" y1="100%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:#9f8700"/>
<stop offset="100%" style="stop-color:#ffdb00"/>
</linearGradient>
<linearGradient id="fg1" x1="0%" y1="100%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:#8caec7"/>
<stop offset="100%" style="stop-color:#6289a3"/>
</linearGradient>
<radialGradient id="rg1">
<stop offset="0%" stop-color="white"/>
<stop offset="100%" stop-color="black"/>
</radialGradient>
</defs>
<rect x="8" y="8" width="112" height="112" style="fill:url(#fg1)" rx="10"/>
<path style="fill:black"
d="M76,113
L15,113
L15,70
C15,70 25,55 45,36
C53,29 60,26 78,32
C85,34 90,32 90,32
C95,30 108,35 110,40
L110,40
C83,48 80,60 76,71
C74,80 76,113 76,113
z"/>
<rect x="8" y="8" width="112" height="112"
style="fill:none; stroke:url(#bg1); stroke-width:14" rx="10"/>
<circle cx="65" cy="40" r="6" fill="url(#rg1)"/>
<circle cx="65" cy="40" r="3" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -1,16 +1,15 @@
import logging import logging
import signal import signal
from PySide6 import QtCore from PySide6 import QtCore
from PySide6.QtWidgets import QApplication from PySide6.QtWidgets import QApplication
from PySide6.QtCore import QTimer from PySide6.QtCore import QTimer
from PySide6.QtGui import QIcon
import sys import sys
import constants import constants
from src.pluginregistry import PluginRegistry from src.pluginregistry import PluginRegistry
import gettext import gettext
from src.ui.icon import Icon
__version__ = '0.2.0'
gettext.install('krowlog', 'locale') gettext.install('krowlog', 'locale')
@@ -31,7 +30,7 @@ def stop_signal(signum, _stackframe):
if __name__ == "__main__": if __name__ == "__main__":
app = QApplication(sys.argv) app = QApplication(sys.argv)
app.setWindowIcon(QIcon(constants.krow_icon)) # works only for Linux app.setWindowIcon(Icon(constants.krow_icon)) # works only for Linux
# make icon appear in Windows # make icon appear in Windows
# see https://stackoverflow.com/questions/1551605/how-to-set-applications-taskbar-icon-in-windows-7/1552105#1552105 # see https://stackoverflow.com/questions/1551605/how-to-set-applications-taskbar-icon-in-windows-7/1552105#1552105

View File

@@ -5,8 +5,8 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: RavenLog\n" "Project-Id-Version: RavenLog\n"
"POT-Creation-Date: 2022-08-23 20:08+0200\n" "POT-Creation-Date: 2022-08-25 19:21+0200\n"
"PO-Revision-Date: 2022-08-23 20:09+0200\n" "PO-Revision-Date: 2022-08-25 19:23+0200\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: \n" "Language-Team: \n"
"Language: de\n" "Language: de\n"
@@ -54,30 +54,30 @@ msgstr "&Suchtreffer Hervorheben"
msgid "Open Tab on Save As File" msgid "Open Tab on Save As File"
msgstr "Öffne neues Tab wenn Selektion als neue Datei gespeichert wird" msgstr "Öffne neues Tab wenn Selektion als neue Datei gespeichert wird"
#: src/plugins/filesbrowserplugin.py:35 #: src/plugins/filesbrowser/filesbrowserwidget.py:37
msgid "&Files Browser"
msgstr "&Dateibrowser"
#: src/plugins/filesbrowserplugin.py:41
msgid "Files Browser"
msgstr "Dateibrowser"
#: src/plugins/findInFiles/filesbrowserwidget.py:37
msgid "Focus on current file" msgid "Focus on current file"
msgstr "Auf aktuelle Datei fokussieren" msgstr "Auf aktuelle Datei fokussieren"
#: src/plugins/findInFiles/filesbrowserwidget.py:40 #: src/plugins/filesbrowser/filesbrowserwidget.py:40
msgid "Folder:" msgid "Folder:"
msgstr "Ordner:" msgstr "Ordner:"
#: src/plugins/findInFiles/filesbrowserwidget.py:45 #: src/plugins/filesbrowser/filesbrowserwidget.py:45
msgid "Filter:" msgid "Filter:"
msgstr "Filter:" msgstr "Filter:"
#: src/plugins/findInFiles/filesbrowserwidget.py:73 #: src/plugins/filesbrowser/filesbrowserwidget.py:73
msgid "Open Directory" msgid "Open Directory"
msgstr "Ordner öffnen" msgstr "Ordner öffnen"
#: src/plugins/filesbrowserplugin.py:30
msgid "&Files Browser"
msgstr "&Dateibrowser"
#: src/plugins/filesbrowserplugin.py:36
msgid "Files Browser"
msgstr "Dateibrowser"
#: src/plugins/krowlog/aboutdialog.py:19 #: src/plugins/krowlog/aboutdialog.py:19
msgid "About KrowLog" msgid "About KrowLog"
msgstr "Über KrowLog" msgstr "Über KrowLog"
@@ -146,22 +146,26 @@ msgstr "&Über KrowLog"
msgid "E&xit" msgid "E&xit"
msgstr "&Beenden" msgstr "&Beenden"
#: src/plugins/logfile/filterwidget.py:137 #: src/plugins/logfile/filterwidget.py:149
msgid "Cancel" msgid "Cancel"
msgstr "Abbrechen" msgstr "Abbrechen"
#: src/plugins/logfile/filterwidget.py:143 #: src/plugins/logfile/filterwidget.py:155
msgid "save query" msgid "save query"
msgstr "suche speichern" msgstr "suche speichern"
#: src/plugins/logfile/filterwidget.py:148 #: src/plugins/logfile/filterwidget.py:160
msgid "ignore case" msgid "ignore case"
msgstr "Groß-/Kleinschreibung ignorieren" msgstr "Groß-/Kleinschreibung ignorieren"
#: src/plugins/logfile/filterwidget.py:152 #: src/plugins/logfile/filterwidget.py:164
msgid "regex" msgid "regex"
msgstr "RegExp" msgstr "RegExp"
#: src/plugins/logfile/filterwidget.py:251
msgid "({hits} lines)"
msgstr "({hits} Zeilen)"
#: src/plugins/logfileplugin.py:26 #: src/plugins/logfileplugin.py:26
msgid "File not found" msgid "File not found"
msgstr "Datei nicht gefunden" msgstr "Datei nicht gefunden"
@@ -312,11 +316,11 @@ msgstr "Echtes Blau"
#: src/ui/colorbutton.py:33 #: src/ui/colorbutton.py:33
msgid "Fairy Topia" msgid "Fairy Topia"
msgstr "" msgstr "Fairy Topia"
#: src/ui/colorbutton.py:34 #: src/ui/colorbutton.py:34
msgid "Magenta Bachiego" msgid "Magenta Bachiego"
msgstr "" msgstr "Magenta Bachiego"
#: src/ui/colorbutton.py:36 #: src/ui/colorbutton.py:36
msgid "Breeze of Mist" msgid "Breeze of Mist"

View File

@@ -5,7 +5,7 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2022-08-23 20:08+0200\n" "POT-Creation-Date: 2022-08-25 19:21+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -52,30 +52,30 @@ msgstr ""
msgid "Open Tab on Save As File" msgid "Open Tab on Save As File"
msgstr "" msgstr ""
#: src/plugins/filesbrowserplugin.py:35 #: src/plugins/filesbrowser/filesbrowserwidget.py:37
msgid "&Files Browser"
msgstr ""
#: src/plugins/filesbrowserplugin.py:41
msgid "Files Browser"
msgstr ""
#: src/plugins/findInFiles/filesbrowserwidget.py:37
msgid "Focus on current file" msgid "Focus on current file"
msgstr "" msgstr ""
#: src/plugins/findInFiles/filesbrowserwidget.py:40 #: src/plugins/filesbrowser/filesbrowserwidget.py:40
msgid "Folder:" msgid "Folder:"
msgstr "" msgstr ""
#: src/plugins/findInFiles/filesbrowserwidget.py:45 #: src/plugins/filesbrowser/filesbrowserwidget.py:45
msgid "Filter:" msgid "Filter:"
msgstr "" msgstr ""
#: src/plugins/findInFiles/filesbrowserwidget.py:73 #: src/plugins/filesbrowser/filesbrowserwidget.py:73
msgid "Open Directory" msgid "Open Directory"
msgstr "" msgstr ""
#: src/plugins/filesbrowserplugin.py:30
msgid "&Files Browser"
msgstr ""
#: src/plugins/filesbrowserplugin.py:36
msgid "Files Browser"
msgstr ""
#: src/plugins/krowlog/aboutdialog.py:19 #: src/plugins/krowlog/aboutdialog.py:19
msgid "About KrowLog" msgid "About KrowLog"
msgstr "" msgstr ""
@@ -140,22 +140,26 @@ msgstr ""
msgid "E&xit" msgid "E&xit"
msgstr "" msgstr ""
#: src/plugins/logfile/filterwidget.py:137 #: src/plugins/logfile/filterwidget.py:149
msgid "Cancel" msgid "Cancel"
msgstr "" msgstr ""
#: src/plugins/logfile/filterwidget.py:143 #: src/plugins/logfile/filterwidget.py:155
msgid "save query" msgid "save query"
msgstr "" msgstr ""
#: src/plugins/logfile/filterwidget.py:148 #: src/plugins/logfile/filterwidget.py:160
msgid "ignore case" msgid "ignore case"
msgstr "" msgstr ""
#: src/plugins/logfile/filterwidget.py:152 #: src/plugins/logfile/filterwidget.py:164
msgid "regex" msgid "regex"
msgstr "" msgstr ""
#: src/plugins/logfile/filterwidget.py:251
msgid "({hits} lines)"
msgstr ""
#: src/plugins/logfileplugin.py:26 #: src/plugins/logfileplugin.py:26
msgid "File not found" msgid "File not found"
msgstr "" msgstr ""

View File

@@ -1,5 +1,5 @@
pip==22.2.2 pip==22.2.2
PySide6==6.3.1 PySide6-Essentials==6.3.1
setuptools==62.1.0 setuptools==65.3.0
urllib3==1.26.11 urllib3==1.26.12
watchdog==2.1.9 watchdog==2.1.9

View File

@@ -1,11 +1,7 @@
from abc import abstractmethod
class PluginBase(): class PluginBase():
def __init__(self): def __init__(self):
pass pass
@abstractmethod
def copy(self): def copy(self):
""" """
Subclasses that use state must implement this method and return a new instance of themselves. Subclasses that use state must implement this method and return a new instance of themselves.

View File

@@ -3,6 +3,8 @@ from typing import Callable
from PySide6.QtGui import QAction, QIcon from PySide6.QtGui import QAction, QIcon
from PySide6.QtWidgets import QMenu, QPushButton, QWidget from PySide6.QtWidgets import QMenu, QPushButton, QWidget
from src.ui.icon import Icon
class RAction(): class RAction():
@@ -65,9 +67,9 @@ class RAction():
def _update_check_state(self): def _update_check_state(self):
if self._action: if self._action:
if self.checked: if self.checked:
self._action.setIcon(QIcon("icons/ionicons/checkbox-outline.svg")) self._action.setIcon(Icon("icons/ionicons/checkbox-outline.svg"))
else: else:
self._action.setIcon(QIcon("icons/ionicons/square-outline.svg")) self._action.setIcon(Icon("icons/ionicons/square-outline.svg"))
def set_label(self, label: str): def set_label(self, label: str):
if self._action: if self._action:
@@ -77,9 +79,9 @@ class RAction():
action = QAction(self.label, qmenu) action = QAction(self.label, qmenu)
self._action = action self._action = action
if self.icon_from_theme: if self.icon_from_theme:
action.setIcon(QIcon.fromTheme(self.icon_from_theme)) action.setIcon(Icon.fromTheme(self.icon_from_theme))
if self.icon_file: if self.icon_file:
action.setIcon(QIcon(self.icon_file)) action.setIcon(Icon(self.icon_file))
if self.shortcut: if self.shortcut:
action.setShortcut(self.shortcut) action.setShortcut(self.shortcut)
if self.action: if self.action:
@@ -95,9 +97,9 @@ class RAction():
if self.label: if self.label:
button.setText(self.label) button.setText(self.label)
if self.icon_from_theme: if self.icon_from_theme:
button.setIcon(QIcon.fromTheme(self.icon_from_theme)) button.setIcon(Icon.fromTheme(self.icon_from_theme))
if self.icon_file: if self.icon_file:
button.setIcon(QIcon(self.icon_file)) button.setIcon(Icon(self.icon_file))
if self.shortcut: if self.shortcut:
button.setShortcut(self.shortcut) button.setShortcut(self.shortcut)
if self.action: if self.action:

View File

@@ -1,12 +1,10 @@
from abc import ABC
from PySide6.QtCore import Qt from PySide6.QtCore import Qt
from src.pluginbase import PluginBase from src.pluginbase import PluginBase
from src.pluginregistry import PluginRegistry from src.pluginregistry import PluginRegistry
from src.plugins.domain.menucontribution import MenuContribution from src.plugins.domain.menucontribution import MenuContribution
from src.plugins.domain.raction import RAction from src.plugins.domain.raction import RAction
from src.plugins.findInFiles.filesbrowserwidget import FilesBrowserWidget from src.plugins.filesbrowser.filesbrowserwidget import FilesBrowserWidget
from src.i18n import _ from src.i18n import _
from src.settings.settings import Settings from src.settings.settings import Settings
@@ -17,9 +15,6 @@ class FilesBrowserPlugin(PluginBase):
super(FilesBrowserPlugin, self).__init__() super(FilesBrowserPlugin, self).__init__()
self.settings = None self.settings = None
def copy(self):
return self
def set_settings(self, settings: Settings): def set_settings(self, settings: Settings):
self.settings = settings self.settings = settings
if not self.settings.session.has_section("filesBrowser"): if not self.settings.session.has_section("filesBrowser"):

View File

@@ -1,11 +1,16 @@
import textwrap import textwrap
import PySide6 import PySide6
import urllib3
from watchdog import version as watchdog_version
from PySide6.QtCore import Qt from PySide6.QtCore import Qt
from PySide6.QtGui import QFont, QPixmap from PySide6.QtGui import QFont
from PySide6.QtWidgets import * from PySide6.QtWidgets import *
import constants import constants
import krowlog
from src.ui.icon import Icon
from src.ui.label import Label from src.ui.label import Label
from src.ui.vbox import VBox from src.ui.vbox import VBox
from src.i18n import _ from src.i18n import _
@@ -21,16 +26,16 @@ class AboutDialog(QDialog):
self.layout = QVBoxLayout(self) self.layout = QVBoxLayout(self)
heading_app_name = QLabel(_("KrowLog")) heading_app_name = Label(_("KrowLog"))
heading_app_name.setAlignment(Qt.AlignmentFlag.AlignLeft) heading_app_name.setAlignment(Qt.AlignmentFlag.AlignLeft)
heading_app_name.setFont(QFont("default", 25)) heading_app_name.setFont(QFont("default", 25))
heading_app_name.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse) # heading_app_name.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse)
version = QLabel(_("Version: {0}").format(self._version())) version = Label(_("Version: {0}").format(krowlog.__version__))
version.setAlignment(Qt.AlignmentFlag.AlignLeft) version.setAlignment(Qt.AlignmentFlag.AlignLeft)
app_icon = QLabel() app_icon = QLabel()
app_icon.setPixmap(QPixmap(constants.krow_icon)) app_icon.setPixmap(Icon(constants.krow_icon).pixmap(64, 64))
heading = QWidget(self) heading = QWidget(self)
hbox = QHBoxLayout(heading) hbox = QHBoxLayout(heading)
hbox.addWidget(app_icon) hbox.addWidget(app_icon)
@@ -66,21 +71,18 @@ class AboutDialog(QDialog):
dependencies = """ dependencies = """
<ul> <ul>
<li>Ionicons (MIT) - <a href="https://github.com/ionic-team/ionicons">https://github.com/ionic-team/ionicons</a></li> <li>Ionicons (MIT) - <a href="https://github.com/ionic-team/ionicons">https://github.com/ionic-team/ionicons</a></li>
<li>PySide6 {pyside} (LGPL v3) - <a href="https://doc.qt.io/qtforpython-6/">https://doc.qt.io/qtforpython-6/</a></li> <li>PySide6-Essentials {pyside} (LGPL v3) - <a href="https://doc.qt.io/qtforpython-6/">https://doc.qt.io/qtforpython-6/</a></li>
<li>Qt6 {qt} (LGPL v3) - <a href="https://code.qt.io/cgit/qt/qtbase.git/">https://code.qt.io/cgit/qt/qtbase.git/</a></li> <li>Qt6 {qt} (LGPL v3) - <a href="https://code.qt.io/cgit/qt/qtbase.git/">https://code.qt.io/cgit/qt/qtbase.git/</a></li>
<li>urllib3 (MIT) - <a href="https://github.com/urllib3/urllib3">https://github.com/urllib3/urllib3</a></li> <li>urllib3 {urllib3} (MIT) - <a href="https://github.com/urllib3/urllib3">https://github.com/urllib3/urllib3</a></li>
<li>watchdog 2.16 (Apache 2.0) - <a href="https://github.com/gorakhargosh/watchdog">https://github.com/gorakhargosh/watchdog</a></li> <li>watchdog {watchdog} (Apache 2.0) - <a href="https://github.com/gorakhargosh/watchdog">https://github.com/gorakhargosh/watchdog</a></li>
</ul>""".format(pyside=PySide6.__version__, qt=PySide6.QtCore.__version__) </ul>""".format(
pyside=PySide6.__version__,
qt=PySide6.QtCore.__version__,
urllib3=urllib3.__version__,
watchdog=watchdog_version.VERSION_STRING)
label = textwrap.dedent(dependencies) label = textwrap.dedent(dependencies)
result = QWidget() result = QWidget()
result.layout = QVBoxLayout(result) result.layout = QVBoxLayout(result)
result.layout.addWidget(Label(label)) result.layout.addWidget(Label(label))
return result return result
def _version(self):
with open('VERSION.info', "rt") as f:
line = f.readline()
version = line.strip()
return version

View File

@@ -8,7 +8,7 @@ from typing import Optional, Callable
from PySide6.QtCore import QRunnable, QThreadPool, Signal from PySide6.QtCore import QRunnable, QThreadPool, Signal
from PySide6.QtGui import QIcon from PySide6.QtGui import QIcon
from PySide6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QCheckBox, QPushButton, QComboBox, \ from PySide6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QCheckBox, QPushButton, QComboBox, \
QSizePolicy, QProgressBar, QMenu, QMenuBar QSizePolicy, QProgressBar
from src.plugins.domain.raction import RAction from src.plugins.domain.raction import RAction
from src.plugins.logfile.preprocesslineshook import PreProcessLinesHook from src.plugins.logfile.preprocesslineshook import PreProcessLinesHook
@@ -18,6 +18,8 @@ from src.ui.bigtext.logFileModel import LogFileModel
from src.i18n import _ from src.i18n import _
from src.pluginregistry import PluginRegistry from src.pluginregistry import PluginRegistry
from src.ui.hbox import HBox from src.ui.hbox import HBox
from src.ui.icon import Icon
from src.ui.label import Label
from src.zonedpluginregistry import ZonedPluginRegistry from src.zonedpluginregistry import ZonedPluginRegistry
@@ -33,6 +35,7 @@ class FilterTask(QRunnable):
filter_match_found_listeners: Callable[[int], None], filter_match_found_listeners: Callable[[int], None],
pre_process_lines_hooks: [PreProcessLinesHook], pre_process_lines_hooks: [PreProcessLinesHook],
progress_handler: Callable[[float], None], progress_handler: Callable[[float], None],
update_hits_handler: Callable[[int], None],
on_before: Callable[[], None], on_before: Callable[[], None],
on_finish: Callable[[], None] on_finish: Callable[[], None]
): ):
@@ -41,6 +44,7 @@ class FilterTask(QRunnable):
self.filter_model = filter_model self.filter_model = filter_model
self.regex = regex self.regex = regex
self.progress_handler = progress_handler self.progress_handler = progress_handler
self.update_hits_handler = update_hits_handler
self.pre_process_lines_hooks = pre_process_lines_hooks self.pre_process_lines_hooks = pre_process_lines_hooks
self.on_before = on_before self.on_before = on_before
self.on_finish = on_finish self.on_finish = on_finish
@@ -61,19 +65,20 @@ class FilterTask(QRunnable):
for listener in self.filter_match_found_listeners: for listener in self.filter_match_found_listeners:
listener(-1, -1) # notify listeners that a new search started listener(-1, -1) # notify listeners that a new search started
hits_count = 0
last_progress_report = time.time() last_progress_report = time.time()
try: try:
with open(self.source_model.get_file(), "rb") as source: with open(self.source_model.get_file(), "rb") as source:
with open(self.filter_model.get_file(), "w+b") as target: with open(self.filter_model.get_file(), "w+b") as target:
line_count = 0 line_count = 0
lines_written = 0 lines_written = 0
while l := source.readline(): while line_encoded := source.readline():
line_count = line_count + 1 line_count = line_count + 1
line = l.decode("utf8", errors="ignore") line = line_encoded.decode("utf8", errors="ignore")
if self.regex.findall(line): if self.regex.findall(line):
lines_written = lines_written + 1 lines_written = lines_written + 1
source_line_offset = source.tell() - len(l) source_line_offset = source.tell() - len(line_encoded)
target_line_offset = target.tell() target_line_offset = target.tell()
for listener in self.filter_match_found_listeners: for listener in self.filter_match_found_listeners:
listener(target_line_offset, source_line_offset) listener(target_line_offset, source_line_offset)
@@ -83,6 +88,7 @@ class FilterTask(QRunnable):
line = h.pre_process_line(line, target) line = h.pre_process_line(line, target)
target.write(line.encode("utf8")) target.write(line.encode("utf8"))
hits_count = hits_count + 1
# sometime buffering can hide results for a while # sometime buffering can hide results for a while
# We force a flush periodically. # We force a flush periodically.
@@ -95,12 +101,15 @@ class FilterTask(QRunnable):
if now - last_progress_report > 0.2: if now - last_progress_report > 0.2:
progress = source.tell() / os.stat(self.source_model.get_file()).st_size progress = source.tell() / os.stat(self.source_model.get_file()).st_size
self.progress_handler(progress) self.progress_handler(progress)
self.update_hits_handler(hits_count)
last_progress_report = now last_progress_report = now
if self.aborted: if self.aborted:
self.update_hits_handler(hits_count)
# print("aborted ", time.time()) # print("aborted ", time.time())
break break
finally: finally:
self.update_hits_handler(hits_count)
self.on_finish() self.on_finish()
# print("dome thread ", threading.currentThread()) # print("dome thread ", threading.currentThread())
@@ -110,6 +119,7 @@ class FilterWidget(QWidget):
filter_task: Optional[FilterTask] = None filter_task: Optional[FilterTask] = None
search_is_running = Signal(bool) search_is_running = Signal(bool)
signal_update_progress = Signal(float) signal_update_progress = Signal(float)
signal_update_hits = Signal(int)
def __init__(self, source_model: LogFileModel, zoned_plugin_registry: ZonedPluginRegistry): def __init__(self, source_model: LogFileModel, zoned_plugin_registry: ZonedPluginRegistry):
super(FilterWidget, self).__init__() super(FilterWidget, self).__init__()
@@ -129,6 +139,9 @@ class FilterWidget(QWidget):
self.query_field.lineEdit().returnPressed.connect(self.filter_changed) self.query_field.lineEdit().returnPressed.connect(self.filter_changed)
self.query_field.setInsertPolicy(QComboBox.NoInsert) self.query_field.setInsertPolicy(QComboBox.NoInsert)
self.hits_field = Label("")
self.signal_update_hits.connect(self._update_hits)
self.progress_bar = QProgressBar() self.progress_bar = QProgressBar()
self.progress_bar.setVisible(False) self.progress_bar.setVisible(False)
self.progress_bar.setMaximumWidth(50) self.progress_bar.setMaximumWidth(50)
@@ -139,7 +152,7 @@ class FilterWidget(QWidget):
self.btn_cancel_search.pressed.connect(self._cancel_search) self.btn_cancel_search.pressed.connect(self._cancel_search)
self.search_is_running.connect(self.search_running_status_changed) self.search_is_running.connect(self.search_running_status_changed)
self.btn_bookmark = QPushButton(QIcon("icons/ionicons/star.svg"), "") self.btn_bookmark = QPushButton(Icon("icons/ionicons/star.svg"), "")
self.btn_bookmark.setToolTip(_("save query")) self.btn_bookmark.setToolTip(_("save query"))
self.btn_bookmark.pressed.connect(self._save_query) self.btn_bookmark.pressed.connect(self._save_query)
@@ -157,16 +170,17 @@ class FilterWidget(QWidget):
filter_bar.layout = QHBoxLayout(filter_bar) filter_bar.layout = QHBoxLayout(filter_bar)
filter_bar.layout.setContentsMargins(0, 0, 0, 0) filter_bar.layout.setContentsMargins(0, 0, 0, 0)
filter_bar.layout.addWidget(self.query_field) filter_bar.layout.addWidget(self.query_field)
filter_bar.layout.addWidget(self.btn_bookmark)
filter_bar.layout.addWidget(self.hits_field)
filter_bar.layout.addWidget(self.progress_bar) filter_bar.layout.addWidget(self.progress_bar)
filter_bar.layout.addWidget(self.btn_cancel_search) filter_bar.layout.addWidget(self.btn_cancel_search)
filter_bar.layout.addWidget(self.btn_bookmark)
filter_bar.layout.addWidget(self.menu) filter_bar.layout.addWidget(self.menu)
filter_bar.layout.addWidget(self.ignore_case) filter_bar.layout.addWidget(self.ignore_case)
filter_bar.layout.addWidget(self.is_regex) filter_bar.layout.addWidget(self.is_regex)
(handle, self.tmpfilename) = tempfile.mkstemp() (handle, self.tmp_filename) = tempfile.mkstemp()
os.close(handle) os.close(handle)
self.filter_model = LogFileModel(self.tmpfilename, self.source_model.settings) self.filter_model = LogFileModel(self.tmp_filename, self.source_model.settings)
self.hits_view = BigText(self.filter_model) self.hits_view = BigText(self.filter_model)
self.layout.addWidget(filter_bar) self.layout.addWidget(filter_bar)
@@ -208,9 +222,8 @@ class FilterWidget(QWidget):
self._reload_save_queries() self._reload_save_queries()
def destruct(self): def destruct(self):
# print("cleanup: ", self.tmpfilename)
self._cancel_search() self._cancel_search()
os.remove(self.tmpfilename) os.remove(self.tmp_filename)
def _cancel_search(self): def _cancel_search(self):
if self.filter_task: if self.filter_task:
@@ -224,6 +237,7 @@ class FilterWidget(QWidget):
self.filter_model.truncate() self.filter_model.truncate()
self.source_model.clear_query_highlight() self.source_model.clear_query_highlight()
self.filter_model.clear_query_highlight() self.filter_model.clear_query_highlight()
self._update_hits(-1)
PluginRegistry.execute("update_ui") PluginRegistry.execute("update_ui")
def search_running_status_changed(self, is_running: bool): def search_running_status_changed(self, is_running: bool):
@@ -233,9 +247,18 @@ class FilterWidget(QWidget):
def update_progress(self, progress: float): def update_progress(self, progress: float):
self.progress_bar.setValue(progress * 100) self.progress_bar.setValue(progress * 100)
def _update_hits(self, hits: int):
if hits >= 0:
self.hits_field.setText(_("({hits} lines)").format(hits=hits))
else:
self.hits_field.setText("")
def progress_handler(self, progress: float): def progress_handler(self, progress: float):
self.signal_update_progress.emit(progress) self.signal_update_progress.emit(progress)
def update_hits_handler(self, hits: int):
self.signal_update_hits.emit(hits)
def filter_changed(self): def filter_changed(self):
query = self.query_field.currentText() query = self.query_field.currentText()
ignore_case = self.ignore_case.isChecked() ignore_case = self.ignore_case.isChecked()
@@ -260,6 +283,7 @@ class FilterWidget(QWidget):
return return
self.progress_bar.setValue(0) self.progress_bar.setValue(0)
self._update_hits(-1)
self.source_model.set_query_highlight(query, ignore_case, is_regex) self.source_model.set_query_highlight(query, ignore_case, is_regex)
self.filter_model.set_query_highlight(query, ignore_case, is_regex) self.filter_model.set_query_highlight(query, ignore_case, is_regex)
@@ -274,6 +298,7 @@ class FilterWidget(QWidget):
self.filter_match_found_listeners, self.filter_match_found_listeners,
pre_process_lines_hooks, pre_process_lines_hooks,
self.progress_handler, self.progress_handler,
self.update_hits_handler,
lambda: self.search_is_running.emit(True), lambda: self.search_is_running.emit(True),
lambda: self.search_is_running.emit(False) lambda: self.search_is_running.emit(False)
) )

View File

@@ -1,5 +1,3 @@
import sys
import math import math
import os import os
import time import time
@@ -12,6 +10,7 @@ from PySide6.QtGui import QMouseEvent
from PySide6.QtWidgets import * from PySide6.QtWidgets import *
from src.ui.ScaledScrollBar import ScaledScrollBar from src.ui.ScaledScrollBar import ScaledScrollBar
from src.ui.bigtext.highlight_regex import HighlightRegex
from src.ui.bigtext.highlight_selection import HighlightSelection from src.ui.bigtext.highlight_selection import HighlightSelection
from src.ui.bigtext.highlighted_range import HighlightedRange from src.ui.bigtext.highlighted_range import HighlightedRange
from src.ui.bigtext.highlightingdialog import HighlightingDialog from src.ui.bigtext.highlightingdialog import HighlightingDialog
@@ -97,7 +96,6 @@ class BigText(QWidget):
def add_line_click_listener(self, listener: Callable[[int], None]): def add_line_click_listener(self, listener: Callable[[int], None]):
""" """
:param listener: a callable, the parameter is the byte offset of the clicked line :param listener: a callable, the parameter is the byte offset of the clicked line
:return: :return:
""" """
@@ -111,6 +109,7 @@ class BigText(QWidget):
pass pass
# noinspection PyArgumentList,PyTypeChecker
class InnerBigText(QWidget): class InnerBigText(QWidget):
_byte_offset = 0 _byte_offset = 0
_left_offset = 0 _left_offset = 0
@@ -119,6 +118,8 @@ class InnerBigText(QWidget):
def __init__(self, parent: BigText, model: LogFileModel): def __init__(self, parent: BigText, model: LogFileModel):
super(InnerBigText, self).__init__() super(InnerBigText, self).__init__()
self.char_height = None
self.char_width = None
self.model = model self.model = model
self.parent = parent self.parent = parent
self.setFocusPolicy(Qt.FocusPolicy.StrongFocus) self.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
@@ -132,6 +133,12 @@ class InnerBigText(QWidget):
self._last_double_click_time = 0 self._last_double_click_time = 0
self._last_double_click_line_number = -1 self._last_double_click_line_number = -1
self.highlight_selected_text = HighlightRegex(
"",
is_regex=False,
ignore_case=True,
hit_background_color="d7efffc0") # same blue as the selection hightlight, but with lower saturation
self.line_click_listeners: [Callable[[int], None]] = [] self.line_click_listeners: [Callable[[int], None]] = []
def keyPressEvent(self, e: QKeyEvent) -> None: def keyPressEvent(self, e: QKeyEvent) -> None:
@@ -203,10 +210,12 @@ class InnerBigText(QWidget):
self.update() self.update()
self.parent.v_scroll_bar.setValue(self._byte_offset) self.parent.v_scroll_bar.setValue(self._byte_offset)
# noinspection PyTypeChecker
def mousePressEvent(self, e: QtGui.QMouseEvent) -> None: def mousePressEvent(self, e: QtGui.QMouseEvent) -> None:
if e.buttons() == Qt.MouseButton.LeftButton and e.modifiers() == Qt.KeyboardModifier.ShiftModifier: if e.buttons() == Qt.MouseButton.LeftButton and e.modifiers() == Qt.KeyboardModifier.ShiftModifier:
offset = self.to_byte_offset(e) offset = self.to_byte_offset(e)
self.selection_highlight.set_end_byte(offset) self.selection_highlight.set_end_byte(offset)
self._update_highlight_selected_text()
self.update() self.update()
return return
@@ -217,6 +226,7 @@ class InnerBigText(QWidget):
line: Line = self.lines[line_number] line: Line = self.lines[line_number]
self.selection_highlight.set_start(line.byte_offset()) self.selection_highlight.set_start(line.byte_offset())
self.selection_highlight.set_end_byte(line.byte_end()) self.selection_highlight.set_end_byte(line.byte_end())
self._update_highlight_selected_text()
self.update() self.update()
return return
@@ -224,6 +234,7 @@ class InnerBigText(QWidget):
offset = self.to_byte_offset(e) offset = self.to_byte_offset(e)
self.selection_highlight.set_start(offset) self.selection_highlight.set_start(offset)
self.selection_highlight.set_end_byte(offset) self.selection_highlight.set_end_byte(offset)
self._update_highlight_selected_text()
self.update() self.update()
line_number = self.y_pos_to_line(e.pos().y()) line_number = self.y_pos_to_line(e.pos().y())
@@ -245,6 +256,8 @@ class InnerBigText(QWidget):
else: else:
self.selection_highlight.set_start(offset) self.selection_highlight.set_start(offset)
self.selection_highlight.set_end_byte(offset) self.selection_highlight.set_end_byte(offset)
self._update_highlight_selected_text()
self.update() self.update()
def mouseMoveEvent(self, e: QMouseEvent): def mouseMoveEvent(self, e: QMouseEvent):
@@ -256,8 +269,10 @@ class InnerBigText(QWidget):
if self.selection_highlight.end_byte != current_byte: if self.selection_highlight.end_byte != current_byte:
self.selection_highlight.set_end_byte(current_byte) self.selection_highlight.set_end_byte(current_byte)
self._update_highlight_selected_text()
self.update() self.update()
# print("-> %s,%s" %(self._selection_start_byte, self._selection_end_byte)) # print("-> %s,%s" %(self._selection_start_byte, self._selection_end_byte))
line_number = self.y_pos_to_line(e.pos().y()) line_number = self.y_pos_to_line(e.pos().y())
column_in_line = self.x_pos_to_column(e.pos().x()) column_in_line = self.x_pos_to_column(e.pos().x())
if line_number < 0: if line_number < 0:
@@ -310,7 +325,7 @@ class InnerBigText(QWidget):
column_in_line = self.x_pos_to_column(e.pos().x()) + self._left_offset column_in_line = self.x_pos_to_column(e.pos().x()) + self._left_offset
column_in_line = min(column_in_line, line.length_in_columns()) # x was behind the last column of this line column_in_line = min(column_in_line, line.length_in_columns()) # x was behind the last column of this line
char_in_line = line.column_to_char(column_in_line) char_in_line = line.column_to_char(column_in_line)
# print("%s in line %s lcolumn_in_line=%s" % (char_in_line, line_number, column_in_line)) # print("%s in line %s column_in_line=%s" % (char_in_line, line_number, column_in_line))
byte_in_line = line.char_index_to_byte(char_in_line) byte_in_line = line.char_index_to_byte(char_in_line)
current_byte = line.byte_offset() + byte_in_line current_byte = line.byte_offset() + byte_in_line
# print("%s + %s = %s" % (line.byte_offset(), char_in_line, current_byte)) # print("%s + %s = %s" % (line.byte_offset(), char_in_line, current_byte))
@@ -332,6 +347,7 @@ class InnerBigText(QWidget):
_("data selection"), _("data selection"),
_( _(
"You have selected <b>{0}</b> of data.").format(bytes_human_readable)) "You have selected <b>{0}</b> of data.").format(bytes_human_readable))
# noinspection PyTypeChecker
you_sure.setStandardButtons(QMessageBox.Cancel) you_sure.setStandardButtons(QMessageBox.Cancel)
copy_btn = you_sure.addButton(_("Copy {0} to Clipboard").format(bytes_human_readable), copy_btn = you_sure.addButton(_("Copy {0} to Clipboard").format(bytes_human_readable),
QMessageBox.ActionRole) QMessageBox.ActionRole)
@@ -357,6 +373,7 @@ class InnerBigText(QWidget):
end = max(self.selection_highlight.start_byte, self.selection_highlight.end_byte) end = max(self.selection_highlight.start_byte, self.selection_highlight.end_byte)
dialog = QFileDialog(self) dialog = QFileDialog(self)
(selected_file, _filter) = dialog.getSaveFileName( (selected_file, _filter) = dialog.getSaveFileName(
parent=self,
caption=_("Save File"), caption=_("Save File"),
dir=os.path.dirname(self.model.get_file()) dir=os.path.dirname(self.model.get_file())
) )
@@ -369,19 +386,28 @@ class InnerBigText(QWidget):
def _select_all(self): def _select_all(self):
self.selection_highlight.start_byte = 0 self.selection_highlight.start_byte = 0
self.selection_highlight.end_byte = self.model.byte_count() self.selection_highlight.end_byte = self.model.byte_count()
self._update_highlight_selected_text()
self.update() self.update()
def _update_highlight_selected_text(self):
start_byte=self.selection_highlight.start_byte
end_byte=self.selection_highlight.end_byte
if abs(start_byte - end_byte) < 1024:
query = self.model.read_range(start_byte, end_byte)
if query.find("\n") < 0:
self.highlight_selected_text.set_query(query)
return
self.highlight_selected_text.set_query("")
def paintEvent(self, event: QPaintEvent) -> None: def paintEvent(self, event: QPaintEvent) -> None:
# print("paintEvent %s" % (self.model.get_file()))
painter = QPainter(self) painter = QPainter(self)
# painter.setFont(self.model.settings.font()) # font = "Courier New" if sys.platform == 'win32' or sys.platform == 'cygwin' else "Monospace"
# Courier New, DejaVu Sans Mono, Monospace, Liberation Mono, Noto Mono, Nimbus Mono L, Tlwg Mono, Ubuntu Mono, FreeMono, Mitra Mono
font = "Courier New" if sys.platform == 'win32' or sys.platform == 'cygwin' else "Monospace"
painter.setFont(QFont("Courier New", self.model.settings.getint_session('general', "font_size"))) painter.setFont(QFont("Courier New", self.model.settings.getint_session('general', "font_size")))
painter.setPen(QColor(0, 0, 0)) painter.setPen(QColor(0, 0, 0))
self.update_font_metrics(painter) self.update_font_metrics(painter)
lines_to_show = self.lines_shown() lines_to_show = math.ceil(self.lines_shown())
# print("%s / %s = %s" %(self.height(), float(self.char_height), lines_to_show)) # print("%s / %s = %s" %(self.height(), float(self.char_height), lines_to_show))
self.lines = self.model.data(self._byte_offset, self.scroll_lines, lines_to_show) self.lines = self.model.data(self._byte_offset, self.scroll_lines, lines_to_show)
@@ -392,36 +418,33 @@ class InnerBigText(QWidget):
# document length == maximum + pageStep + aFewBytesSoThatTheLastLineIsShown # document length == maximum + pageStep + aFewBytesSoThatTheLastLineIsShown
self.parent.v_scroll_bar.setMaximum(self.model.byte_count() - 1) self.parent.v_scroll_bar.setMaximum(self.model.byte_count() - 1)
for l in self.lines: for line in self.lines:
self.update_longest_line(len(l.line())) self.update_longest_line(len(line.line()))
highlighters = self.model.highlighters() highlighters = self.model.highlighters()
if self.model.get_query_highlight(): if self.model.get_query_highlight():
highlighters = highlighters + [self.model.get_query_highlight()] highlighters = highlighters + [self.model.get_query_highlight()]
highlighters = highlighters + [self.highlight_selected_text]
highlighters = highlighters + [self.selection_highlight] # selection highlight should be last highlighters = highlighters + [self.selection_highlight] # selection highlight should be last
# draw hightlights first - some characters may overlap to the next line # draw highlights first - some characters may overlap to the next line
# by drawing the background hightlights first we prevent that the hightlight # by drawing the background highlights first we prevent that the highlight
# draws over a character # draws over a character
start = time.time() y_line_offset = self.char_height
y_line_offset = self.char_height; for line in self.lines:
for l in self.lines:
highlight_ranges = [] highlight_ranges = []
for h in highlighters: for h in highlighters:
optional_highlight_range = h.compute_highlight(l) optional_highlight_range = h.compute_highlight(line)
if optional_highlight_range: if optional_highlight_range:
highlight_ranges = highlight_ranges + optional_highlight_range highlight_ranges = highlight_ranges + optional_highlight_range
self.draw_highlights(highlight_ranges, painter, y_line_offset) self.draw_highlights(highlight_ranges, painter, y_line_offset)
y_line_offset = y_line_offset + self.char_height y_line_offset = y_line_offset + self.char_height
end = time.time()
# print("highlight duration: %.3f" %((end-start)*1000))
left_offset = int(-1 * self._left_offset * self.char_width) left_offset = int(-1 * self._left_offset * self.char_width)
y_line_offset = self.char_height; y_line_offset = self.char_height
for l in self.lines: for line in self.lines:
text = l.line_prepared_for_display() text = line.line_prepared_for_display()
painter.drawText(left_offset, y_line_offset, text) painter.drawText(left_offset, y_line_offset, text)
y_line_offset = y_line_offset + self.char_height y_line_offset = y_line_offset + self.char_height
@@ -461,6 +484,6 @@ class InnerBigText(QWidget):
fm: QFontMetrics = painter.fontMetrics() fm: QFontMetrics = painter.fontMetrics()
self.char_height = fm.height() self.char_height = fm.height()
self.char_width = fm.averageCharWidth() # all chars have same width for monospace font self.char_width = fm.averageCharWidth() # all chars have same width for monospace font
text = "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" text = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012"
self.char_width = fm.horizontalAdvance(text) / float(len(text)) self.char_width = fm.horizontalAdvance(text) / float(len(text))
# print("font width=%s height=%s" % (self.char_width, self.char_height)) # print("font width=%s height=%s" % (self.char_width, self.char_height))

View File

@@ -29,6 +29,10 @@ class HighlightRegex(Highlight):
else: else:
return re.compile(re.escape(self.query), flags=flags) return re.compile(re.escape(self.query), flags=flags)
def set_query(self, query: str) -> None:
self.query = query
self.regex = self._get_regex()
def compute_highlight(self, line: Line) -> Optional[List[HighlightedRange]]: def compute_highlight(self, line: Line) -> Optional[List[HighlightedRange]]:
result = [] result = []
# print("execute regex: %s in %s" % (self.regex, line.line())) # print("execute regex: %s in %s" % (self.regex, line.line()))
@@ -56,9 +60,15 @@ class HighlightRegex(Highlight):
@staticmethod @staticmethod
def brush(color: str) -> QBrush: def brush(color: str) -> QBrush:
if re.match("[0-9a-f]{6}", color, flags=re.IGNORECASE): if re.match("^[0-9a-f]{6}$", color, flags=re.IGNORECASE):
red = int(color[0:2], 16) red = int(color[0:2], 16)
green = int(color[2:4], 16) green = int(color[2:4], 16)
blue = int(color[4:6], 16) blue = int(color[4:6], 16)
return QBrush(QColor(red, green, blue)) return QBrush(QColor(red, green, blue))
if re.match("^[0-9a-f]{8}$", color, flags=re.IGNORECASE):
red = int(color[0:2], 16)
green = int(color[2:4], 16)
blue = int(color[4:6], 16)
alpha = int(color[6:8], 16)
return QBrush(QColor(red, green, blue, alpha))
return QBrush() return QBrush()

View File

@@ -1,4 +1,3 @@
from PySide6.QtGui import QIcon
from PySide6.QtWidgets import QDialog, QLineEdit, QLabel, QGridLayout, QCheckBox, QListWidget, QListWidgetItem, \ from PySide6.QtWidgets import QDialog, QLineEdit, QLabel, QGridLayout, QCheckBox, QListWidget, QListWidgetItem, \
QPushButton, QDialogButtonBox, QMessageBox, QSizePolicy QPushButton, QDialogButtonBox, QMessageBox, QSizePolicy
@@ -9,6 +8,7 @@ from src.ui.hbox import HBox
from src.settings.settings import Settings from src.settings.settings import Settings
from src.i18n import _ from src.i18n import _
from src.ui.icon import Icon
class PayloadItem(QListWidgetItem): class PayloadItem(QListWidgetItem):
@@ -32,23 +32,23 @@ class HighlightingDialog(QDialog):
form_grid.addWidget(self.list, row, 0, 1, 2) form_grid.addWidget(self.list, row, 0, 1, 2)
row = row + 1 row = row + 1
self.btn_add = QPushButton(QIcon.fromTheme("list-add"), _("Add")) self.btn_add = QPushButton(Icon.fromTheme("list-add"), _("Add"))
self.btn_add.setSizePolicy(QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)) self.btn_add.setSizePolicy(QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed))
self.btn_add.pressed.connect(self._add) self.btn_add.pressed.connect(self._add)
self.btn_update = QPushButton(QIcon.fromTheme("stock_edit"), _("Update")) self.btn_update = QPushButton(Icon.fromTheme("stock_edit"), _("Update"))
self.btn_update.setSizePolicy(QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)) self.btn_update.setSizePolicy(QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed))
self.btn_update.pressed.connect(self._update) self.btn_update.pressed.connect(self._update)
self.btn_delete = QPushButton(QIcon.fromTheme("list-remove"), _("Remove")) self.btn_delete = QPushButton(Icon.fromTheme("list-remove"), _("Remove"))
self.btn_delete.setSizePolicy(QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)) self.btn_delete.setSizePolicy(QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed))
self.btn_delete.pressed.connect(self._delete) self.btn_delete.pressed.connect(self._delete)
self.btn_move_up = QPushButton(QIcon.fromTheme("go-up"), _("Up")) self.btn_move_up = QPushButton(Icon.fromTheme("go-up"), _("Up"))
self.btn_move_up.setSizePolicy(QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)) self.btn_move_up.setSizePolicy(QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed))
self.btn_move_up.pressed.connect(self._move_up) self.btn_move_up.pressed.connect(self._move_up)
self.btn_move_down = QPushButton(QIcon.fromTheme("go-down"), _("Down")) self.btn_move_down = QPushButton(Icon.fromTheme("go-down"), _("Down"))
self.btn_move_down.setSizePolicy(QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)) self.btn_move_down.setSizePolicy(QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed))
self.btn_move_down.pressed.connect(self._move_down) self.btn_move_down.pressed.connect(self._move_down)
button_box = HBox(self.btn_update, self.btn_add, self.btn_delete, self.btn_move_up, self.btn_move_down) button_box = HBox(self.btn_update, self.btn_add, self.btn_delete, self.btn_move_up, self.btn_move_down)

11
src/ui/icon.py Normal file
View File

@@ -0,0 +1,11 @@
from PySide6.QtGui import QIcon
from pathlib import Path
class Icon(QIcon):
def __init__(self, file_name: str):
super(Icon, self).__init__("%s" % Path(__file__).parent.parent.parent.joinpath(file_name).absolute())
print("%s -> %s" % (file_name, Path(__file__).parent.parent.parent.joinpath(file_name).absolute()))
def fromTheme(icon_from_theme: str) -> QIcon:
return QIcon.fromTheme(icon_from_theme)

View File

@@ -11,9 +11,12 @@ def urls_to_path(urls: str) -> [str]:
result.append(path) result.append(path)
return result return result
def url_to_path(url: str) -> str: def url_to_path(url: str) -> str:
p = urlparse(url) p = urlparse(url)
if sys.platform == 'win32' or sys.platform == 'cygwin': if sys.platform == 'win32' or sys.platform == 'cygwin':
if p.netloc:
return f"//{p.netloc}{p.path}"
return os.path.abspath(p.path[1:]) return os.path.abspath(p.path[1:])
return os.path.abspath(os.path.join(p.netloc, p.path)) return os.path.abspath(os.path.join(p.netloc, p.path))

View File

@@ -1,32 +0,0 @@
import sys
from PyQt6.QtWidgets import QApplication, QMainWindow, QDockWidget, QLabel, QTextEdit
from PyQt6.QtCore import Qt
class DockWindow(QMainWindow):
def __init__(self, *args, **kwargs):
super(DockWindow, self).__init__(*args, **kwargs)
self.dock_1 = QDockWidget("Dock1", self)
self.dock_1.layout().addWidget(QLabel("dock1"))
self.dock_2 = QDockWidget("Dock2", self)
self.dock_2.layout().addWidget(QLabel("dock2"))
self.dock_3 = QDockWidget("Dock3", self)
self.dock_3.layout().addWidget(QLabel("dock3"))
self.setCentralWidget(QTextEdit())
self.addDockWidget(Qt.DockWidgetArea.RightDockWidgetArea, self.dock_1)
self.addDockWidget(Qt.DockWidgetArea.LeftDockWidgetArea, self.dock_2)
self.addDockWidget(Qt.DockWidgetArea.LeftDockWidgetArea, self.dock_3)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = DockWindow()
window.show()
app.exec()