Compare commits
15 Commits
8ce0c1bf9e
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| d4b962769f | |||
| d9c362419b | |||
| bfd8ce841f | |||
| a41e5b79a3 | |||
| 75578b6126 | |||
| 9afc4d1d9c | |||
| d36724f3e7 | |||
| bcd525d787 | |||
| 8289042af4 | |||
| 0246a3fb19 | |||
| 617c7f161f | |||
| 67f16571f1 | |||
| be5e0c9ae6 | |||
| 51c02b3d55 | |||
| c19cdf6f41 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -6,3 +6,4 @@ testbed
|
|||||||
icons-not-used
|
icons-not-used
|
||||||
venv*
|
venv*
|
||||||
*.spec
|
*.spec
|
||||||
|
/version.txt
|
||||||
|
|||||||
1
.idea/misc.xml
generated
1
.idea/misc.xml
generated
@@ -1,6 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="Black">
|
<component name="Black">
|
||||||
|
<option name="enabledOnSave" value="true" />
|
||||||
<option name="sdkName" value="Python 3.12 (krowlog)" />
|
<option name="sdkName" value="Python 3.12 (krowlog)" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.12 (krowlog)" project-jdk-type="Python SDK" />
|
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.12 (krowlog)" project-jdk-type="Python SDK" />
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ KrowLog is a viewer for text files of arbitrary size.
|
|||||||
* Select arbitrary strings (not just full lines).
|
* Select arbitrary strings (not just full lines).
|
||||||
* Double click selects word.
|
* Double click selects word.
|
||||||
* Triple click selects line.
|
* Triple click selects line.
|
||||||
* Copy protection: Users is warned before creating a clipboard more than 5 MB in size. They can choose to copy the
|
* Copy protection: Users are warned before creating a clipboard more than 5 MB in size. They can choose to copy the
|
||||||
selection into a new file instead.
|
selection into a new file instead.
|
||||||
* Optionally open a new tab when saving selection as file.
|
* Optionally open a new tab when saving selection as file.
|
||||||
* Panel for temporary notes
|
* Panel for temporary notes
|
||||||
|
|||||||
16
changelog.txt
Normal file
16
changelog.txt
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
|
||||||
|
Next Version
|
||||||
|
* Feature: Add changelog.
|
||||||
|
* Fix: When the range sliders are overlapping the end slider cannot be moved.
|
||||||
|
* Feature: Get version number from git tags.
|
||||||
|
* Feature: You can now "follow" a file. When enabled the file is automatically reloaded
|
||||||
|
and scrolled to the end. Any manual scroll action disables "follow" mode.
|
||||||
|
* Feature: Better support for fonts and characters with non-uniform width.
|
||||||
|
* Fix: Cannot scroll to arbitrary positions in a file if the file is larger than 2GB
|
||||||
|
* Feature: File type specific highlighters.
|
||||||
|
|
||||||
|
0.2.1
|
||||||
|
* Feature: Show how many bytes are selected.
|
||||||
|
* Feature: Highlighters can be disabled.
|
||||||
|
* Feature: If a regex contains a group, then only the group is highlighted.
|
||||||
|
Using a filter expression like '(\d+)ms' will only hightlight the number.
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
krow_icon = "icons" + os.sep + "krowlog.svg"
|
krow_icon = "icons" + os.sep + "krowlog.svg"
|
||||||
|
qt_icon = "icons" + os.sep + "qt-logo.png"
|
||||||
license_file = os.path.dirname(os.path.realpath(__file__)) + os.sep + "LICENSE"
|
license_file = os.path.dirname(os.path.realpath(__file__)) + os.sep + "LICENSE"
|
||||||
|
changelog_file = os.path.dirname(os.path.realpath(__file__)) + os.sep + "changelog.txt"
|
||||||
|
|
||||||
tab_width = 4
|
tab_width = 4
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ Just run `make_installer.py` with the following command. The distribution can be
|
|||||||
to run this on all target platforms.
|
to run this on all target platforms.
|
||||||
|
|
||||||
```
|
```
|
||||||
venv311/bin/python make_installer.py
|
venv312/bin/python make_installer.py
|
||||||
```
|
```
|
||||||
|
|
||||||
## Update Python
|
## Update Python
|
||||||
|
|
||||||
1. install the latest python version. We need the dev version, because PyInstaller requires it.
|
1. install the latest python version. We need the dev version, because PyInstaller requires it.
|
||||||
`sudo apt install python-3.11-dev python-3.11-venv`
|
`sudo apt install python-3.12-dev python-3.12-venv`
|
||||||
2. create new virtual environment with `python3.11 -m venv /path/to/venv`
|
2. create new virtual environment with `python3.12 -m venv /path/to/venv`
|
||||||
3. select the venv in PyCharm
|
3. select the venv in PyCharm
|
||||||
31
krowlog.py
31
krowlog.py
@@ -1,21 +1,26 @@
|
|||||||
import argparse
|
import argparse
|
||||||
|
import gettext
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
import signal
|
import signal
|
||||||
from PySide6 import QtCore
|
|
||||||
from PySide6.QtWidgets import QApplication
|
|
||||||
from PySide6.QtCore import QTimer
|
|
||||||
import sys
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from PySide6 import QtCore
|
||||||
|
from PySide6.QtCore import QTimer
|
||||||
|
from PySide6.QtWidgets import QApplication
|
||||||
|
|
||||||
import constants
|
import constants
|
||||||
from src import install
|
from src import install
|
||||||
from src.pluginregistry import PluginRegistry
|
from src.pluginregistry import PluginRegistry
|
||||||
import gettext
|
|
||||||
from src.ui.icon import Icon
|
from src.ui.icon import Icon
|
||||||
from pathlib import Path
|
|
||||||
import os
|
|
||||||
|
|
||||||
__version__ = Path(os.path.dirname(os.path.realpath(__file__)) + os.sep + "version.txt").read_text()
|
version_file = Path(
|
||||||
|
os.path.dirname(os.path.realpath(__file__)) + os.sep + "version.txt"
|
||||||
|
)
|
||||||
|
__version__ = version_file.read_text() if version_file.is_file() else "0.0.0"
|
||||||
|
|
||||||
gettext.install('krowlog', 'locale')
|
gettext.install("krowlog", "locale")
|
||||||
|
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
log = logging.getLogger("main")
|
log = logging.getLogger("main")
|
||||||
@@ -27,7 +32,7 @@ def register_signal_handler():
|
|||||||
|
|
||||||
|
|
||||||
def stop_signal(signum, _stackframe):
|
def stop_signal(signum, _stackframe):
|
||||||
""" Handle terminate signal """
|
"""Handle terminate signal"""
|
||||||
try:
|
try:
|
||||||
log.info("Terminate signal received. %s", signum)
|
log.info("Terminate signal received. %s", signum)
|
||||||
QtCore.QCoreApplication.quit()
|
QtCore.QCoreApplication.quit()
|
||||||
@@ -67,7 +72,9 @@ class CmdArgs:
|
|||||||
|
|
||||||
def parse_command_line_parameters() -> CmdArgs:
|
def parse_command_line_parameters() -> CmdArgs:
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument('files', metavar='F', type=str, nargs='*', help='file(s) to open')
|
parser.add_argument(
|
||||||
|
"files", metavar="F", type=str, nargs="*", help="file(s) to open"
|
||||||
|
)
|
||||||
namespace = parser.parse_args()
|
namespace = parser.parse_args()
|
||||||
return CmdArgs(files=namespace.files)
|
return CmdArgs(files=namespace.files)
|
||||||
|
|
||||||
@@ -76,7 +83,9 @@ if __name__ == "__main__":
|
|||||||
cmd_args = parse_command_line_parameters()
|
cmd_args = parse_command_line_parameters()
|
||||||
|
|
||||||
app = QApplication(sys.argv)
|
app = QApplication(sys.argv)
|
||||||
app.setWindowIcon(Icon(constants.krow_icon)) # works only for Linux (but only X11, not Wayland)
|
app.setWindowIcon(
|
||||||
|
Icon(constants.krow_icon)
|
||||||
|
) # works only for Linux (but only X11, not Wayland)
|
||||||
|
|
||||||
# install stuff, e.g. a desktop file, set icon on Windows
|
# install stuff, e.g. a desktop file, set icon on Windows
|
||||||
install.install()
|
install.install()
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import PyInstaller.__main__
|
import PyInstaller.__main__
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
os.system("git -C . describe --match \"*.*.*\" --tags > version.txt")
|
os.system("git -C . describe --match \"*.*.*\" --tags > version.txt")
|
||||||
|
|
||||||
PyInstaller.__main__.run([
|
arguments = [
|
||||||
'krowlog.py',
|
'krowlog.py',
|
||||||
# '--onefile',
|
# '--onefile',
|
||||||
'--noconfirm',
|
'--noconfirm',
|
||||||
@@ -14,10 +15,14 @@ PyInstaller.__main__.run([
|
|||||||
'--add-binary', 'icons' + os.pathsep + 'icons',
|
'--add-binary', 'icons' + os.pathsep + 'icons',
|
||||||
'--add-binary', 'locales' + os.pathsep + 'locales',
|
'--add-binary', 'locales' + os.pathsep + 'locales',
|
||||||
'--add-binary', 'LICENSE' + os.pathsep + '.',
|
'--add-binary', 'LICENSE' + os.pathsep + '.',
|
||||||
|
'--add-binary', 'changelog.txt' + os.pathsep + '.',
|
||||||
'--add-binary', 'version.txt' + os.pathsep + '.',
|
'--add-binary', 'version.txt' + os.pathsep + '.',
|
||||||
'--hidden-import=krowlog',
|
'--hidden-import=krowlog',
|
||||||
'--hidden-import=watchdog',
|
'--hidden-import=__future__',
|
||||||
'--hidden-import=watchdog.observers',
|
|
||||||
'--hidden-import=watchdog.version',
|
|
||||||
'--hidden-import=configparser'
|
'--hidden-import=configparser'
|
||||||
])
|
]
|
||||||
|
|
||||||
|
if sys.platform == 'win32' or sys.platform == 'cygwin':
|
||||||
|
arguments.append('--version-file=version.py')
|
||||||
|
|
||||||
|
PyInstaller.__main__.run(arguments)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
pip==25.0.1
|
pip==25.0.1
|
||||||
PySide6_Essentials==6.8.2.1
|
PySide6_Essentials==6.8.2.1
|
||||||
setuptools==77.0.3
|
setuptools==77.0.3
|
||||||
watchdog==6.0.0
|
|
||||||
pyinstaller==6.12.0
|
pyinstaller==6.12.0
|
||||||
|
isort==6.0.1
|
||||||
|
black==25.1.0
|
||||||
|
|||||||
79
src/plugins/krowlog/about_qt_dialog.py
Normal file
79
src/plugins/krowlog/about_qt_dialog.py
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import textwrap
|
||||||
|
|
||||||
|
import PySide6
|
||||||
|
from PySide6.QtCore import Qt
|
||||||
|
from PySide6.QtGui import QFont, QPalette
|
||||||
|
from PySide6.QtWidgets import *
|
||||||
|
|
||||||
|
import constants
|
||||||
|
|
||||||
|
import krowlog
|
||||||
|
from src.ui.icon import Icon
|
||||||
|
from src.ui.label import Label
|
||||||
|
from src.ui.vbox import VBox
|
||||||
|
from src.i18n import _
|
||||||
|
|
||||||
|
|
||||||
|
class AboutQTDialog(QDialog):
|
||||||
|
"""Dialog for showing info about KrowLog"""
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super(AboutQTDialog, self).__init__(parent)
|
||||||
|
self.setWindowTitle(_("About QT"))
|
||||||
|
self.setModal(True)
|
||||||
|
# self.setMinimumWidth(850)
|
||||||
|
# self.setFixedHeight(400)
|
||||||
|
|
||||||
|
self.layout = QVBoxLayout(self)
|
||||||
|
|
||||||
|
text = f"""
|
||||||
|
<b>About QT</b>
|
||||||
|
<p>This program uses QT version {PySide6.QtCore.__version__}.</p>
|
||||||
|
|
||||||
|
<p>QT is a C++ toolkit for cross-platform application development.</p>
|
||||||
|
|
||||||
|
<p>Qt provides single-source portability across all major desktop
|
||||||
|
operating systems. It is also available for embedded Linux and other
|
||||||
|
embedded and mobile operating systems.</p>
|
||||||
|
|
||||||
|
<p>Qt is available under multiple licensing options designed to accommodate
|
||||||
|
the needs of our various users.</p>
|
||||||
|
|
||||||
|
<p>Qt licensed under our commercial license agreement is appropriate for
|
||||||
|
development of proprietary/commercial software where you do not want to
|
||||||
|
share any source code with third parties or otherwise cannot comply with
|
||||||
|
the terms of GNU (L)GPL.</p>
|
||||||
|
|
||||||
|
<p>Qt licensed under GNU (L)GPL is appropriate for the development of Qt
|
||||||
|
applications provided you can comply with the terms and conditions of the
|
||||||
|
respective licenses.</p>
|
||||||
|
|
||||||
|
Please see <a href="http://qt.io/licensing">qt.io/licensing</a> for an<
|
||||||
|
overview of Qt licensing.
|
||||||
|
|
||||||
|
<p>Copyright (C) 2025 The Qt Company Ltd and other contributors.</p>
|
||||||
|
|
||||||
|
<p>Qt and the Qt logo are trademarks of The Qt Company Ltd.</p>
|
||||||
|
|
||||||
|
<p>Qt is The Qt Company Ltd product developed as an open source project.
|
||||||
|
See <a href="http://qt.io">qt.io</a> for more information.</p>
|
||||||
|
"""
|
||||||
|
label = Label(text)
|
||||||
|
label.setWordWrap(True)
|
||||||
|
|
||||||
|
app_icon = QLabel()
|
||||||
|
app_icon.setPixmap(Icon(constants.qt_icon).pixmap(64, 64))
|
||||||
|
heading = QWidget(self)
|
||||||
|
hbox = QHBoxLayout(heading)
|
||||||
|
hbox.addWidget(app_icon)
|
||||||
|
hbox.addWidget(label)
|
||||||
|
hbox.addSpacerItem(QSpacerItem(1, 1, hData=QSizePolicy.Policy.Expanding))
|
||||||
|
|
||||||
|
heading.layout = hbox
|
||||||
|
self.layout.addWidget(heading)
|
||||||
|
|
||||||
|
buttons = QDialogButtonBox(self)
|
||||||
|
buttons.setStandardButtons(QDialogButtonBox.StandardButton.Close)
|
||||||
|
buttons.rejected.connect(self.close)
|
||||||
|
self.layout.addWidget(buttons)
|
||||||
|
self.setSizePolicy(QSizePolicy.Policy.MinimumExpanding, QSizePolicy.Policy.MinimumExpanding)
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import textwrap
|
import textwrap
|
||||||
|
|
||||||
import PySide6
|
import PySide6
|
||||||
from watchdog import version as watchdog_version
|
|
||||||
from PySide6.QtCore import Qt
|
from PySide6.QtCore import Qt
|
||||||
from PySide6.QtGui import QFont, QPalette
|
from PySide6.QtGui import QFont, QPalette
|
||||||
from PySide6.QtWidgets import *
|
from PySide6.QtWidgets import *
|
||||||
@@ -22,8 +21,8 @@ class AboutDialog(QDialog):
|
|||||||
super(AboutDialog, self).__init__(parent)
|
super(AboutDialog, self).__init__(parent)
|
||||||
self.setWindowTitle(_("About KrowLog"))
|
self.setWindowTitle(_("About KrowLog"))
|
||||||
self.setModal(True)
|
self.setModal(True)
|
||||||
self.setMinimumWidth(650)
|
self.setMinimumWidth(850)
|
||||||
self.setFixedHeight(300)
|
self.setFixedHeight(400)
|
||||||
|
|
||||||
self.layout = QVBoxLayout(self)
|
self.layout = QVBoxLayout(self)
|
||||||
|
|
||||||
@@ -50,6 +49,7 @@ class AboutDialog(QDialog):
|
|||||||
tabs.addTab(self._about(), _("About"))
|
tabs.addTab(self._about(), _("About"))
|
||||||
tabs.addTab(self._libraries(), _("Libraries"))
|
tabs.addTab(self._libraries(), _("Libraries"))
|
||||||
tabs.addTab(self._license(), _("License"))
|
tabs.addTab(self._license(), _("License"))
|
||||||
|
tabs.addTab(self._changelog(), _("Changelog"))
|
||||||
|
|
||||||
self.layout.addWidget(tabs)
|
self.layout.addWidget(tabs)
|
||||||
|
|
||||||
@@ -74,11 +74,9 @@ class AboutDialog(QDialog):
|
|||||||
<ul>
|
<ul>
|
||||||
<li>PySide6-Essentials {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>watchdog {watchdog} (Apache 2.0) - <a href="https://github.com/gorakhargosh/watchdog">https://github.com/gorakhargosh/watchdog</a></li>
|
|
||||||
</ul>""".format(
|
</ul>""".format(
|
||||||
pyside=PySide6.__version__,
|
pyside=PySide6.__version__,
|
||||||
qt=PySide6.QtCore.__version__,
|
qt=PySide6.QtCore.__version__)
|
||||||
watchdog=watchdog_version.VERSION_STRING)
|
|
||||||
label = textwrap.dedent(dependencies)
|
label = textwrap.dedent(dependencies)
|
||||||
|
|
||||||
result = QWidget()
|
result = QWidget()
|
||||||
@@ -106,3 +104,25 @@ class AboutDialog(QDialog):
|
|||||||
panel.setBackgroundRole(QPalette.ColorRole.Light)
|
panel.setBackgroundRole(QPalette.ColorRole.Light)
|
||||||
result.layout.addWidget(panel)
|
result.layout.addWidget(panel)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def _changelog(self) -> QWidget:
|
||||||
|
with open(constants.changelog_file, 'r') as file:
|
||||||
|
text = file.read()
|
||||||
|
|
||||||
|
result = QWidget()
|
||||||
|
result.layout = QVBoxLayout(result)
|
||||||
|
result.layout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
result.setSizePolicy(QSizePolicy.Policy.MinimumExpanding, QSizePolicy.Policy.MinimumExpanding)
|
||||||
|
|
||||||
|
label = Label(text)
|
||||||
|
label.setFont(QFont("Monospace"))
|
||||||
|
label.setSizePolicy(QSizePolicy.Policy.MinimumExpanding, QSizePolicy.Policy.MinimumExpanding)
|
||||||
|
|
||||||
|
panel = QScrollArea(result)
|
||||||
|
panel.setContentsMargins(0, 0, 0, 0)
|
||||||
|
panel.setViewportMargins(0, 0, 0, 0)
|
||||||
|
panel.setSizePolicy(QSizePolicy.Policy.MinimumExpanding, QSizePolicy.Policy.MinimumExpanding)
|
||||||
|
panel.setWidget(label)
|
||||||
|
panel.setBackgroundRole(QPalette.ColorRole.Light)
|
||||||
|
result.layout.addWidget(panel)
|
||||||
|
return result
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ from PySide6.QtCore import Qt
|
|||||||
from PySide6.QtWidgets import QDockWidget, QMessageBox
|
from PySide6.QtWidgets import QDockWidget, QMessageBox
|
||||||
|
|
||||||
import constants
|
import constants
|
||||||
|
from src.plugins.krowlog.about_qt_dialog import AboutQTDialog
|
||||||
from src.plugins.krowlog.aboutdialog import AboutDialog
|
from src.plugins.krowlog.aboutdialog import AboutDialog
|
||||||
from src.mainwindow import MainWindow
|
from src.mainwindow import MainWindow
|
||||||
from src.pluginbase import PluginBase
|
from src.pluginbase import PluginBase
|
||||||
@@ -36,6 +37,7 @@ class KrowLogPlugin(PluginBase):
|
|||||||
return [
|
return [
|
||||||
MenuContribution("file", action=self._action_close(), action_id="close application", after="<last>"),
|
MenuContribution("file", action=self._action_close(), action_id="close application", after="<last>"),
|
||||||
MenuContribution("help", action=self._action_about(), action_id="open about dialog", after="<last>"),
|
MenuContribution("help", action=self._action_about(), action_id="open about dialog", after="<last>"),
|
||||||
|
MenuContribution("help", action=self._action_about_qt(), action_id="open about QT dialog", after="<last>"),
|
||||||
MenuContribution("settings", menu=self._sub_menu_languages(), action_id="recent files menu"),
|
MenuContribution("settings", menu=self._sub_menu_languages(), action_id="recent files menu"),
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -108,6 +110,14 @@ class KrowLogPlugin(PluginBase):
|
|||||||
)
|
)
|
||||||
return about_action
|
return about_action
|
||||||
|
|
||||||
|
def _action_about_qt(self) -> RAction:
|
||||||
|
action = RAction(
|
||||||
|
_("&About QT"),
|
||||||
|
action=lambda: AboutQTDialog().exec(),
|
||||||
|
icon_file=constants.qt_icon
|
||||||
|
)
|
||||||
|
return action
|
||||||
|
|
||||||
def _action_close(self) -> RAction:
|
def _action_close(self) -> RAction:
|
||||||
icon = "close" if sys.platform == 'win32' or sys.platform == 'cygwin' else "exit"
|
icon = "close" if sys.platform == 'win32' or sys.platform == 'cygwin' else "exit"
|
||||||
close_action = RAction(_("E&xit"), action=lambda: self.main_window.destruct(), shortcut='Ctrl+X',
|
close_action = RAction(_("E&xit"), action=lambda: self.main_window.destruct(), shortcut='Ctrl+X',
|
||||||
|
|||||||
@@ -279,6 +279,7 @@ class FilterWidget(QWidget):
|
|||||||
|
|
||||||
def destruct(self):
|
def destruct(self):
|
||||||
self._cancel_search()
|
self._cancel_search()
|
||||||
|
self.hits_view.destruct()
|
||||||
os.remove(self.tmp_filename)
|
os.remove(self.tmp_filename)
|
||||||
|
|
||||||
def _cancel_search(self):
|
def _cancel_search(self):
|
||||||
|
|||||||
@@ -23,55 +23,31 @@ from src.ui.icon import Icon
|
|||||||
from src.ui.rangeslider import RangeSlider
|
from src.ui.rangeslider import RangeSlider
|
||||||
from src.util.conversion import humanbytes
|
from src.util.conversion import humanbytes
|
||||||
from src.pluginregistry import PluginRegistry
|
from src.pluginregistry import PluginRegistry
|
||||||
|
from threading import Event
|
||||||
|
|
||||||
from src.settings.settings import Settings
|
|
||||||
from watchdog.observers import Observer
|
|
||||||
from watchdog.events import FileSystemEventHandler
|
|
||||||
from src.i18n import _
|
from src.i18n import _
|
||||||
|
|
||||||
log = logging.getLogger("bigtext")
|
log = logging.getLogger("bigtext")
|
||||||
|
|
||||||
|
|
||||||
class FileObserver(FileSystemEventHandler):
|
|
||||||
|
|
||||||
def __init__(self, big_text):
|
|
||||||
super(FileObserver, self).__init__()
|
|
||||||
self.big_text = big_text
|
|
||||||
self._last_mtime = -1
|
|
||||||
|
|
||||||
def on_modified(self, event):
|
|
||||||
# slow down the updates. This is needed, because the file is modified
|
|
||||||
# constantly, which would lead to constant re-rendering, which would
|
|
||||||
# block the UI thread and make the UI unresponsive.
|
|
||||||
# Note: we don't miss events, because they are queued and de-duplicated
|
|
||||||
if not event.is_directory:
|
|
||||||
try:
|
|
||||||
mtime = os.stat(event.src_path).st_mtime
|
|
||||||
if mtime != self._last_mtime:
|
|
||||||
self._last_mtime = mtime
|
|
||||||
time.sleep(0.5)
|
|
||||||
self.big_text.trigger_update.emit()
|
|
||||||
except FileNotFoundError:
|
|
||||||
# ignore: happens when closing the application, because tmp files are deleted,
|
|
||||||
# which triggers a modification event
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class FileWatchdogThread(QRunnable):
|
class FileWatchdogThread(QRunnable):
|
||||||
|
|
||||||
def __init__(self, big_text, file: str):
|
def __init__(self, big_text, file: str):
|
||||||
super(FileWatchdogThread, self).__init__()
|
super(FileWatchdogThread, self).__init__()
|
||||||
self.file = file
|
self.file = file
|
||||||
self.big_text = big_text
|
self.big_text = big_text
|
||||||
self.observer = Observer()
|
self.stop = Event()
|
||||||
|
|
||||||
def run(self) -> None:
|
def run(self) -> None:
|
||||||
self.observer.schedule(FileObserver(self.big_text), self.file)
|
_last_mtime = None
|
||||||
self.observer.start()
|
while not self.stop.is_set():
|
||||||
|
mtime = os.stat(self.file).st_mtime
|
||||||
|
if mtime != _last_mtime:
|
||||||
|
_last_mtime = mtime
|
||||||
|
self.big_text.trigger_update.emit()
|
||||||
|
self.stop.wait(0.5)
|
||||||
|
|
||||||
def destruct(self):
|
def destruct(self):
|
||||||
self.observer.stop()
|
self.stop.set()
|
||||||
# self.observer.join(1)
|
|
||||||
|
|
||||||
|
|
||||||
class BigText(QWidget):
|
class BigText(QWidget):
|
||||||
@@ -503,7 +479,7 @@ class InnerBigText(QWidget):
|
|||||||
# # print("%s + %s = %s" % (line.byte_offset(), char_in_line, current_byte))
|
# # print("%s + %s = %s" % (line.byte_offset(), char_in_line, current_byte))
|
||||||
else:
|
else:
|
||||||
current_byte = self.model.byte_count()
|
current_byte = self.model.byte_count()
|
||||||
return current_byte
|
return SelectionPos(current_byte, True, 1)
|
||||||
|
|
||||||
def elided_text(self, text: str, width: int):
|
def elided_text(self, text: str, width: int):
|
||||||
w = width + self.font_metric.horizontalAdvance("…")
|
w = width + self.font_metric.horizontalAdvance("…")
|
||||||
@@ -581,7 +557,6 @@ class InnerBigText(QWidget):
|
|||||||
|
|
||||||
def _toggle_follow(self):
|
def _toggle_follow(self):
|
||||||
self._follow = not self._follow
|
self._follow = not self._follow
|
||||||
print(f"follow={self._follow}")
|
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def _update_highlight_selected_text(self):
|
def _update_highlight_selected_text(self):
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ class Line:
|
|||||||
self._cache_char_to_column()
|
self._cache_char_to_column()
|
||||||
|
|
||||||
def get_width_in_px(self, font_metric: QFontMetrics):
|
def get_width_in_px(self, font_metric: QFontMetrics):
|
||||||
return font_metric.horizontalAdvance(self._line)
|
return font_metric.horizontalAdvance(self.line_prepared_for_display())
|
||||||
|
|
||||||
def byte_offset(self) -> int:
|
def byte_offset(self) -> int:
|
||||||
return self._byte_offset
|
return self._byte_offset
|
||||||
@@ -43,7 +43,8 @@ class Line:
|
|||||||
return len(prefix_chars)
|
return len(prefix_chars)
|
||||||
|
|
||||||
def line_prepared_for_display(self) -> str:
|
def line_prepared_for_display(self) -> str:
|
||||||
line = self._line_tabs_replaced()
|
# line = self._line_tabs_replaced()
|
||||||
|
line = self._line
|
||||||
line = self._replace_control_chars_with_pictures(line)
|
line = self._replace_control_chars_with_pictures(line)
|
||||||
return line
|
return line
|
||||||
|
|
||||||
@@ -108,7 +109,7 @@ class Line:
|
|||||||
if not result in self._column_to_char_cache:
|
if not result in self._column_to_char_cache:
|
||||||
self._column_to_char_cache[result] = i
|
self._column_to_char_cache[result] = i
|
||||||
current_char = self._line[i]
|
current_char = self._line[i]
|
||||||
if current_char == "\t":
|
if False and current_char == "\t":
|
||||||
result = result + constants.tab_width - result % constants.tab_width
|
result = result + constants.tab_width - result % constants.tab_width
|
||||||
else:
|
else:
|
||||||
result = result + 1
|
result = result + 1
|
||||||
|
|||||||
11
src/ui/bigtext/selectionPos.py
Normal file
11
src/ui/bigtext/selectionPos.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
class SelectionPos:
|
||||||
|
def __init__(self, index: int, is_in_left_half: bool, num_bytes_of_char: int):
|
||||||
|
self.index = index
|
||||||
|
self.is_in_left_half = is_in_left_half
|
||||||
|
self.num_bytes_of_char = num_bytes_of_char
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"{self.index}{'🞀' if self.is_in_left_half else '🞂'}({self.num_bytes_of_char})"
|
||||||
|
|
||||||
|
def pos(self):
|
||||||
|
return self.index + (0 if self.is_in_left_half else self.num_bytes_of_char)
|
||||||
@@ -78,20 +78,6 @@ class MyTestCase(unittest.TestCase):
|
|||||||
self.assertEqual(2, line.char_to_column(4)) # z̈
|
self.assertEqual(2, line.char_to_column(4)) # z̈
|
||||||
self.assertEqual(2, line.char_to_column(5)) # z̈
|
self.assertEqual(2, line.char_to_column(5)) # z̈
|
||||||
|
|
||||||
def test_line_tabs_replaced(self):
|
|
||||||
byte_offset = 123
|
|
||||||
text = "\ta\tb" # will be rendered as: ....abc where . represents a whitespace column
|
|
||||||
expected = " a b"
|
|
||||||
line = Line(byte_offset=byte_offset, byte_end=byte_offset + len(text.encode("utf8")), line=text)
|
|
||||||
self.assertEqual(expected, line.line_prepared_for_display())
|
|
||||||
|
|
||||||
def test_line_tabs_replaced_performance(self):
|
|
||||||
byte_offset = 123
|
|
||||||
text = "a\t" * 10000
|
|
||||||
expected = "a " * 10000
|
|
||||||
line = Line(byte_offset=byte_offset, byte_end=byte_offset + len(text.encode("utf8")), line=text)
|
|
||||||
self.assertEqual(expected, line.line_prepared_for_display())
|
|
||||||
|
|
||||||
def test_byte_index_to_char_index(self):
|
def test_byte_index_to_char_index(self):
|
||||||
byte_offset = 123
|
byte_offset = 123
|
||||||
text = "x\u0308y\u0308z\u0308\t\u0308a"
|
text = "x\u0308y\u0308z\u0308\t\u0308a"
|
||||||
|
|||||||
@@ -177,3 +177,7 @@ ä---------ä----------ä---------ä----------ä---------ä----------ä--
|
|||||||
17
|
17
|
||||||
18
|
18
|
||||||
19
|
19
|
||||||
|
|
||||||
|
|
||||||
|
アンドレアス
|
||||||
|
アンドレアス
|
||||||
28
version.py
Normal file
28
version.py
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
VSVersionInfo(
|
||||||
|
ffi=FixedFileInfo(
|
||||||
|
filevers=(0, 2, 1, 0),
|
||||||
|
prodvers=(0, 2, 1, 0),
|
||||||
|
mask=0x3f,
|
||||||
|
flags=0x0,
|
||||||
|
OS=0x40004,
|
||||||
|
fileType=0x1,
|
||||||
|
subtype=0x0,
|
||||||
|
date=(0, 0)
|
||||||
|
),
|
||||||
|
kids=[
|
||||||
|
StringFileInfo(
|
||||||
|
[
|
||||||
|
StringTable(
|
||||||
|
u'040904B0',
|
||||||
|
[StringStruct(u'CompanyName', u''),
|
||||||
|
StringStruct(u'FileDescription', u'KrowLog is a viewer for log files of arbitrary size.'),
|
||||||
|
StringStruct(u'FileVersion', u'0.2.1' ),
|
||||||
|
StringStruct(u'InternalName', u'krowlog'),
|
||||||
|
StringStruct(u'LegalCopyright', u'\xa9 Andreas Huber'),
|
||||||
|
StringStruct(u'OriginalFilename', u'krowlog.exe'),
|
||||||
|
StringStruct(u'ProductName', u'KrowLog'),
|
||||||
|
StringStruct(u'ProductVersion', u'0.2.1-dev')])
|
||||||
|
]),
|
||||||
|
VarFileInfo([VarStruct(u'Translation', [1033, 1200])])
|
||||||
|
]
|
||||||
|
)
|
||||||
@@ -1 +0,0 @@
|
|||||||
0.2.1-36-g9902be0
|
|
||||||
Reference in New Issue
Block a user