rbeesoft 1.0.9__tar.gz → 2.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rbeesoft-2.0.1/PKG-INFO +13 -0
- rbeesoft-2.0.1/README.md +2 -0
- rbeesoft-2.0.1/pyproject.toml +22 -0
- rbeesoft-2.0.1/src/rbeesoft/app/core/processes/dummyprocess.py +18 -0
- rbeesoft-2.0.1/src/rbeesoft/app/core/processes/process.py +64 -0
- rbeesoft-2.0.1/src/rbeesoft/app/core/processes/processrunner.py +34 -0
- rbeesoft-2.0.1/src/rbeesoft/app/main.py +86 -0
- rbeesoft-2.0.1/src/rbeesoft/app/ui/rbeesoftmainwindow.py +77 -0
- rbeesoft-2.0.1/src/rbeesoft/app/ui/settings.py +61 -0
- rbeesoft-2.0.1/src/rbeesoft/app/ui/widgets/centraldockwidget.py +51 -0
- rbeesoft-1.0.9/rbeesoft/ui/panels/logpanel.py → rbeesoft-2.0.1/src/rbeesoft/app/ui/widgets/logdockwidget.py +18 -11
- rbeesoft-2.0.1/src/rbeesoft/app/ui/widgets/pages/__init__.py +0 -0
- rbeesoft-2.0.1/src/rbeesoft/app/ui/widgets/pages/page.py +24 -0
- rbeesoft-2.0.1/src/rbeesoft/app/ui/widgets/pages/pagerouter.py +48 -0
- rbeesoft-2.0.1/src/rbeesoft/common/__init__.py +0 -0
- {rbeesoft-1.0.9/rbeesoft/core/utils → rbeesoft-2.0.1/src/rbeesoft/common}/logmanager.py +12 -2
- rbeesoft-2.0.1/src/rbeesoft/webapp/__init__.py +0 -0
- rbeesoft-1.0.9/LICENSE +0 -674
- rbeesoft-1.0.9/PKG-INFO +0 -18
- rbeesoft-1.0.9/README.md +0 -2
- rbeesoft-1.0.9/pyproject.toml +0 -26
- rbeesoft-1.0.9/rbeesoft/app.py +0 -23
- rbeesoft-1.0.9/rbeesoft/core/utils/functions.py +0 -10
- rbeesoft-1.0.9/rbeesoft/resources/VERSION +0 -1
- rbeesoft-1.0.9/rbeesoft/resources/icons/rbeesoft.icns +0 -0
- rbeesoft-1.0.9/rbeesoft/resources/icons/rbeesoft.ico +0 -0
- rbeesoft-1.0.9/rbeesoft/resources/icons/spinner.gif +0 -0
- rbeesoft-1.0.9/rbeesoft/ui/components/splashscreen.py +0 -121
- rbeesoft-1.0.9/rbeesoft/ui/constants.py +0 -45
- rbeesoft-1.0.9/rbeesoft/ui/mainwindow.py +0 -100
- rbeesoft-1.0.9/rbeesoft/ui/panels/defaultpanel.py +0 -13
- rbeesoft-1.0.9/rbeesoft/ui/panels/mainpanel.py +0 -81
- rbeesoft-1.0.9/rbeesoft/ui/panels/settingspanel.py +0 -62
- rbeesoft-1.0.9/rbeesoft/ui/panels/stackedpanel.py +0 -22
- rbeesoft-1.0.9/rbeesoft/ui/settings.py +0 -32
- rbeesoft-1.0.9/rbeesoft/ui/utils.py +0 -32
- {rbeesoft-1.0.9 → rbeesoft-2.0.1/src}/rbeesoft/__init__.py +0 -0
- {rbeesoft-1.0.9/rbeesoft/core → rbeesoft-2.0.1/src/rbeesoft/app}/__init__.py +0 -0
- {rbeesoft-1.0.9/rbeesoft/ui → rbeesoft-2.0.1/src/rbeesoft/app/core}/__init__.py +0 -0
- {rbeesoft-1.0.9/rbeesoft/ui/components → rbeesoft-2.0.1/src/rbeesoft/app/core/processes}/__init__.py +0 -0
- {rbeesoft-1.0.9/rbeesoft/ui/panels → rbeesoft-2.0.1/src/rbeesoft/app/ui}/__init__.py +0 -0
- /rbeesoft-1.0.9/rbeesoft/core/utils/___init__.py → /rbeesoft-2.0.1/src/rbeesoft/app/ui/widgets/__init__.py +0 -0
- {rbeesoft-1.0.9/rbeesoft/core/utils → rbeesoft-2.0.1/src/rbeesoft/common}/logmanagerlistener.py +0 -0
- {rbeesoft-1.0.9/rbeesoft/core → rbeesoft-2.0.1/src/rbeesoft/common}/singleton.py +0 -0
rbeesoft-2.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: rbeesoft
|
|
3
|
+
Version: 2.0.1
|
|
4
|
+
Summary:
|
|
5
|
+
Author: Brecheisen
|
|
6
|
+
Author-email: r.brecheisen@maastrichtuniversity.nl
|
|
7
|
+
Requires-Python: >=3.11,<3.12
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
12
|
+
# rbeesoft
|
|
13
|
+
Python package with base classes for developing apps and webapps
|
rbeesoft-2.0.1/README.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "rbeesoft"
|
|
3
|
+
version = "2.0.1"
|
|
4
|
+
description = ""
|
|
5
|
+
authors = [
|
|
6
|
+
{name = "Brecheisen", email = "r.brecheisen@maastrichtuniversity.nl"}
|
|
7
|
+
]
|
|
8
|
+
readme = "README.md"
|
|
9
|
+
requires-python = ">=3.11,<3.12"
|
|
10
|
+
dependencies = [
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
[tool.poetry]
|
|
14
|
+
packages = [{include = "rbeesoft", from = "src"}]
|
|
15
|
+
|
|
16
|
+
[tool.poetry.group.dev.dependencies]
|
|
17
|
+
pytest = "^8.4.1"
|
|
18
|
+
pyinstaller = "^6.17.0"
|
|
19
|
+
|
|
20
|
+
[build-system]
|
|
21
|
+
requires = ["poetry-core>=2.0.0,<3.0.0"]
|
|
22
|
+
build-backend = "poetry.core.masonry.api"
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import time
|
|
2
|
+
from rbeesoft.app.core.processes.process import Process
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class DummyProcess(Process):
|
|
6
|
+
def __init__(self):
|
|
7
|
+
super(DummyProcess, self).__init__()
|
|
8
|
+
self._n = 10
|
|
9
|
+
|
|
10
|
+
def execute(self):
|
|
11
|
+
out = []
|
|
12
|
+
for i in range(self._n):
|
|
13
|
+
if self.is_canceled():
|
|
14
|
+
return out
|
|
15
|
+
time.sleep(0.25)
|
|
16
|
+
out.append(i)
|
|
17
|
+
self.progress.emit(int((i+1)/self._n*100))
|
|
18
|
+
return out
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
from PySide6.QtCore import (
|
|
2
|
+
QObject,
|
|
3
|
+
QThread,
|
|
4
|
+
Signal,
|
|
5
|
+
Slot,
|
|
6
|
+
QCoreApplication,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Process(QObject):
|
|
11
|
+
progress = Signal(int)
|
|
12
|
+
finished = Signal(object)
|
|
13
|
+
failed = Signal(Exception)
|
|
14
|
+
started = Signal()
|
|
15
|
+
canceled = Signal()
|
|
16
|
+
|
|
17
|
+
def __init__(self, parent=None):
|
|
18
|
+
super().__init__(parent)
|
|
19
|
+
self._cancel = False
|
|
20
|
+
self._thread = None
|
|
21
|
+
self._main_thread = None
|
|
22
|
+
|
|
23
|
+
def name(self):
|
|
24
|
+
return self._name
|
|
25
|
+
|
|
26
|
+
def cancel(self):
|
|
27
|
+
self._cancel = True
|
|
28
|
+
|
|
29
|
+
def is_canceled(self) -> bool:
|
|
30
|
+
return self._cancel
|
|
31
|
+
|
|
32
|
+
def execute(self):
|
|
33
|
+
raise NotImplementedError
|
|
34
|
+
|
|
35
|
+
def start(self):
|
|
36
|
+
if self._thread is not None:
|
|
37
|
+
raise RuntimeError("Process already started")
|
|
38
|
+
self._main_thread = QCoreApplication.instance().thread()
|
|
39
|
+
self._thread = QThread()
|
|
40
|
+
self.moveToThread(self._thread)
|
|
41
|
+
self._thread.started.connect(self._run_internal)
|
|
42
|
+
self.finished.connect(self._thread.quit)
|
|
43
|
+
self.failed.connect(self._thread.quit)
|
|
44
|
+
self._thread.finished.connect(self._cleanup)
|
|
45
|
+
self._thread.start()
|
|
46
|
+
|
|
47
|
+
@Slot()
|
|
48
|
+
def _run_internal(self):
|
|
49
|
+
self.started.emit()
|
|
50
|
+
try:
|
|
51
|
+
if self._cancel:
|
|
52
|
+
self.canceled.emit()
|
|
53
|
+
self.finished.emit(None)
|
|
54
|
+
return
|
|
55
|
+
result = self.execute()
|
|
56
|
+
self.finished.emit(result)
|
|
57
|
+
except Exception as e:
|
|
58
|
+
self.failed.emit(e)
|
|
59
|
+
|
|
60
|
+
@Slot()
|
|
61
|
+
def _cleanup(self):
|
|
62
|
+
self.moveToThread(self._main_thread)
|
|
63
|
+
self._thread.deleteLater()
|
|
64
|
+
self._thread = None
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from rbeesoft.common.singleton import singleton
|
|
2
|
+
from rbeesoft.common.logmanager import LogManager
|
|
3
|
+
|
|
4
|
+
LOG = LogManager()
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@singleton
|
|
8
|
+
class ProcessRunner:
|
|
9
|
+
current_process = {}
|
|
10
|
+
|
|
11
|
+
def start(self, process, callback_progress=None, callback_finished=None, callback_failed=None):
|
|
12
|
+
if callback_progress:
|
|
13
|
+
process.progress.connect(callback_progress)
|
|
14
|
+
else:
|
|
15
|
+
process.progress.connect(self.handle_process_progress)
|
|
16
|
+
if callback_finished:
|
|
17
|
+
process.finished.connect(callback_finished)
|
|
18
|
+
else:
|
|
19
|
+
process.finished.connect(self.handle_process_finished)
|
|
20
|
+
if callback_failed:
|
|
21
|
+
process.failed.connect(callback_failed)
|
|
22
|
+
else:
|
|
23
|
+
process.failed.connect(self.handle_process_failed)
|
|
24
|
+
self.current_process = process
|
|
25
|
+
self.current_process.start()
|
|
26
|
+
|
|
27
|
+
def handle_process_progress(self, progress):
|
|
28
|
+
LOG.info(f'Progress: {progress}')
|
|
29
|
+
|
|
30
|
+
def handle_process_finished(self, result):
|
|
31
|
+
LOG.info(f'Process finished successfully: {result}')
|
|
32
|
+
|
|
33
|
+
def handle_process_failed(self, error):
|
|
34
|
+
LOG.error(f'Process failed ({error})')
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from PySide6 import QtWidgets, QtCore
|
|
3
|
+
from rbeesoft.app.ui.rbeesoftmainwindow import RbeesoftMainWindow
|
|
4
|
+
from rbeesoft.app.ui.widgets.pages.page import Page
|
|
5
|
+
from rbeesoft.app.core.processes.process import Process
|
|
6
|
+
from rbeesoft.app.core.processes.processrunner import ProcessRunner
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class MainWindow(RbeesoftMainWindow):
|
|
10
|
+
def __init__(self, app_icon):
|
|
11
|
+
super(MainWindow, self).__init__(
|
|
12
|
+
bundle_identifier='rbeesoft.nl',
|
|
13
|
+
app_name='example',
|
|
14
|
+
app_title='Rbeesoft App Example',
|
|
15
|
+
width=800,
|
|
16
|
+
height=600,
|
|
17
|
+
app_icon=app_icon,
|
|
18
|
+
)
|
|
19
|
+
self.add_page(HomePage(self.settings()), home_page=True)
|
|
20
|
+
self.add_page(NextPage(self.settings()))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class HomePage(Page):
|
|
24
|
+
def __init__(self, settings):
|
|
25
|
+
super(HomePage, self).__init__('home', 'HomePage', settings)
|
|
26
|
+
button = QtWidgets.QPushButton('Go to next page')
|
|
27
|
+
button.clicked.connect(self.handle_button)
|
|
28
|
+
process_button = QtWidgets.QPushButton('Run process')
|
|
29
|
+
process_button.clicked.connect(self.handle_process_button)
|
|
30
|
+
self._process_runner = ProcessRunner()
|
|
31
|
+
layout = QtWidgets.QVBoxLayout()
|
|
32
|
+
layout.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop)
|
|
33
|
+
layout.addWidget(QtWidgets.QLabel(self.title()))
|
|
34
|
+
layout.addWidget(button)
|
|
35
|
+
layout.addWidget(process_button)
|
|
36
|
+
self.setLayout(layout)
|
|
37
|
+
|
|
38
|
+
def handle_button(self):
|
|
39
|
+
self.switch_to_page('next')
|
|
40
|
+
|
|
41
|
+
def handle_process_button(self):
|
|
42
|
+
self._process_runner.start(ExampleProcess())
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class NextPage(Page):
|
|
46
|
+
def __init__(self, settings):
|
|
47
|
+
super(NextPage, self).__init__('next', 'NextPage', settings)
|
|
48
|
+
button = QtWidgets.QPushButton('Go to home page')
|
|
49
|
+
button.clicked.connect(self.handle_button)
|
|
50
|
+
layout = QtWidgets.QVBoxLayout()
|
|
51
|
+
layout.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop)
|
|
52
|
+
layout.addWidget(QtWidgets.QLabel(self.title()))
|
|
53
|
+
layout.addWidget(button)
|
|
54
|
+
self.setLayout(layout)
|
|
55
|
+
|
|
56
|
+
def handle_button(self):
|
|
57
|
+
self.switch_to_page('home')
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class ExampleProcess(Process):
|
|
61
|
+
def __init__(self):
|
|
62
|
+
super(ExampleProcess, self).__init__()
|
|
63
|
+
self._n = 10
|
|
64
|
+
|
|
65
|
+
def execute(self):
|
|
66
|
+
import time
|
|
67
|
+
out = []
|
|
68
|
+
for i in range(self._n):
|
|
69
|
+
if self.is_canceled():
|
|
70
|
+
return out
|
|
71
|
+
time.sleep(0.25)
|
|
72
|
+
out.append(i)
|
|
73
|
+
self.progress.emit(int((i+1)/self._n*100))
|
|
74
|
+
return out
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def main():
|
|
78
|
+
QtWidgets.QApplication.setApplicationName('example')
|
|
79
|
+
app = QtWidgets.QApplication(sys.argv)
|
|
80
|
+
window = MainWindow(app.style().standardIcon(QtWidgets.QStyle.StandardPixmap.SP_ArrowForward))
|
|
81
|
+
window.show()
|
|
82
|
+
sys.exit(app.exec())
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
if __name__ == '__main__':
|
|
86
|
+
main()
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
from PySide6.QtCore import Qt, QByteArray
|
|
2
|
+
from PySide6.QtWidgets import QMainWindow, QStyle
|
|
3
|
+
from PySide6.QtGui import QGuiApplication, QAction
|
|
4
|
+
from rbeesoft.app.ui.settings import Settings
|
|
5
|
+
from rbeesoft.app.ui.widgets.centraldockwidget import CentralDockWidget
|
|
6
|
+
from rbeesoft.app.ui.widgets.logdockwidget import LogDockWidget
|
|
7
|
+
from rbeesoft.common.logmanager import LogManager
|
|
8
|
+
|
|
9
|
+
LOG = LogManager()
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class RbeesoftMainWindow(QMainWindow):
|
|
13
|
+
def __init__(self, bundle_identifier, app_name, app_title, width=1024, height=1024, app_icon=None):
|
|
14
|
+
super(RbeesoftMainWindow, self).__init__()
|
|
15
|
+
self._settings = Settings(bundle_identifier, app_name)
|
|
16
|
+
self._app_title = app_title
|
|
17
|
+
self._width = width
|
|
18
|
+
self._height = height
|
|
19
|
+
self._app_icon = app_icon
|
|
20
|
+
self._central_dockwidget = CentralDockWidget(self, self._settings)
|
|
21
|
+
self._log_dockwidget = LogDockWidget(self)
|
|
22
|
+
self.init()
|
|
23
|
+
|
|
24
|
+
# INITIALIZATION
|
|
25
|
+
|
|
26
|
+
def init(self):
|
|
27
|
+
self.setWindowTitle(self._app_title)
|
|
28
|
+
self.addDockWidget(Qt.DockWidgetArea.TopDockWidgetArea, self._central_dockwidget)
|
|
29
|
+
self.addDockWidget(Qt.DockWidgetArea.BottomDockWidgetArea, self._log_dockwidget)
|
|
30
|
+
if self._app_icon:
|
|
31
|
+
self.setWindowIcon(self._app_icon)
|
|
32
|
+
self.load_geometry_and_state()
|
|
33
|
+
self.init_default_menus()
|
|
34
|
+
self.statusBar().showMessage('Ready')
|
|
35
|
+
LOG.info(f'Settings path: {self.settings().fileName()}')
|
|
36
|
+
|
|
37
|
+
def init_default_menus(self):
|
|
38
|
+
exit_action_icon = self.style().standardIcon(QStyle.StandardPixmap.SP_DialogCloseButton)
|
|
39
|
+
exit_action = QAction(exit_action_icon, 'E&xit', self)
|
|
40
|
+
exit_action.triggered.connect(self.close)
|
|
41
|
+
application_menu = self.menuBar().addMenu('Application')
|
|
42
|
+
application_menu.addAction(exit_action)
|
|
43
|
+
|
|
44
|
+
# GETTERS
|
|
45
|
+
|
|
46
|
+
def settings(self):
|
|
47
|
+
return self._settings
|
|
48
|
+
|
|
49
|
+
# EVENT HANDLERS
|
|
50
|
+
|
|
51
|
+
def closeEvent(self, _):
|
|
52
|
+
self.save_geometry_and_state()
|
|
53
|
+
|
|
54
|
+
# HELPERS
|
|
55
|
+
|
|
56
|
+
def add_page(self, page, home_page=False):
|
|
57
|
+
self._central_dockwidget.add_page(page, home_page)
|
|
58
|
+
|
|
59
|
+
def load_geometry_and_state(self):
|
|
60
|
+
geometry = self.settings().get('mainwindow/geometry')
|
|
61
|
+
state = self.settings().get('mainwindow/state')
|
|
62
|
+
if isinstance(geometry, QByteArray) and self.restoreGeometry(geometry):
|
|
63
|
+
if isinstance(state, QByteArray):
|
|
64
|
+
self.restoreState(state)
|
|
65
|
+
return
|
|
66
|
+
self.resize(self._width, self._height)
|
|
67
|
+
self.center_window()
|
|
68
|
+
|
|
69
|
+
def save_geometry_and_state(self):
|
|
70
|
+
self.settings().set('mainwindow/geometry', self.saveGeometry())
|
|
71
|
+
self.settings().set('mainwindow/state', self.saveState())
|
|
72
|
+
|
|
73
|
+
def center_window(self):
|
|
74
|
+
screen = QGuiApplication.primaryScreen().geometry()
|
|
75
|
+
x = (screen.width() - self.geometry().width()) / 2
|
|
76
|
+
y = (screen.height() - self.geometry().height()) / 2
|
|
77
|
+
self.move(int(x), int(y))
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
from PySide6.QtCore import QSettings
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Settings(QSettings):
|
|
5
|
+
def __init__(self, bundle_identifier: str, app_name: str):
|
|
6
|
+
super(Settings, self).__init__(
|
|
7
|
+
QSettings.IniFormat,
|
|
8
|
+
QSettings.UserScope,
|
|
9
|
+
bundle_identifier,
|
|
10
|
+
app_name,
|
|
11
|
+
)
|
|
12
|
+
self._bundle_identifier = bundle_identifier
|
|
13
|
+
self._app_name = app_name
|
|
14
|
+
|
|
15
|
+
def _prepend_bundle_identifier_and_name(self, name):
|
|
16
|
+
return '{}.{}.{}'.format(self._bundle_identifier, self._app_name, name)
|
|
17
|
+
|
|
18
|
+
def get(self, name, default=None):
|
|
19
|
+
if not name.startswith(self._bundle_identifier):
|
|
20
|
+
name = self._prepend_bundle_identifier_and_name(name)
|
|
21
|
+
value = self.value(name)
|
|
22
|
+
if value is None or value == '':
|
|
23
|
+
return default
|
|
24
|
+
return value
|
|
25
|
+
|
|
26
|
+
def get_int(self, name, default=None):
|
|
27
|
+
try:
|
|
28
|
+
return int(self.get(name, default))
|
|
29
|
+
except ValueError as e:
|
|
30
|
+
return default
|
|
31
|
+
|
|
32
|
+
def get_float(self, name, default=None):
|
|
33
|
+
try:
|
|
34
|
+
return float(self.get(name, default))
|
|
35
|
+
except ValueError as e:
|
|
36
|
+
return default
|
|
37
|
+
|
|
38
|
+
def get_bool(self, name, default=None):
|
|
39
|
+
try:
|
|
40
|
+
value = self.get(name, default)
|
|
41
|
+
if value and isinstance(value, str):
|
|
42
|
+
if value == '0' or value.lower() == 'false':
|
|
43
|
+
return False
|
|
44
|
+
elif value == '1' or value.lower() == 'true':
|
|
45
|
+
return True
|
|
46
|
+
else:
|
|
47
|
+
return default
|
|
48
|
+
if value and isinstance(value, bool):
|
|
49
|
+
return value
|
|
50
|
+
except ValueError as e:
|
|
51
|
+
return default
|
|
52
|
+
|
|
53
|
+
def set(self, name, value):
|
|
54
|
+
name = self._prepend_bundle_identifier_and_name(name)
|
|
55
|
+
self.setValue(name, value)
|
|
56
|
+
|
|
57
|
+
def to_string(self):
|
|
58
|
+
text = f'Settings path: {self.fileName()}'
|
|
59
|
+
for key in self.allKeys():
|
|
60
|
+
text += f'\nSettings: {key}'
|
|
61
|
+
return text
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from PySide6.QtWidgets import (
|
|
2
|
+
QWidget,
|
|
3
|
+
QVBoxLayout,
|
|
4
|
+
QDockWidget,
|
|
5
|
+
)
|
|
6
|
+
from rbeesoft.common.logmanager import LogManager
|
|
7
|
+
from rbeesoft.app.ui.widgets.pages.pagerouter import PageRouter
|
|
8
|
+
|
|
9
|
+
LOG = LogManager()
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class CentralDockWidget(QDockWidget):
|
|
13
|
+
def __init__(self, parent, settings):
|
|
14
|
+
super(CentralDockWidget, self).__init__(parent)
|
|
15
|
+
self._settings = settings
|
|
16
|
+
self._page_router = None
|
|
17
|
+
self.init()
|
|
18
|
+
|
|
19
|
+
# INITIALIZATION
|
|
20
|
+
|
|
21
|
+
def init(self):
|
|
22
|
+
layout = QVBoxLayout()
|
|
23
|
+
layout.addWidget(self.page_router())
|
|
24
|
+
container = QWidget()
|
|
25
|
+
container.setLayout(layout)
|
|
26
|
+
self.setObjectName('centraldockwidget') # Needed for saving geometry/state
|
|
27
|
+
self.setWidget(container)
|
|
28
|
+
|
|
29
|
+
# GETTERS/SETTERS
|
|
30
|
+
|
|
31
|
+
def settings(self):
|
|
32
|
+
return self._settings
|
|
33
|
+
|
|
34
|
+
def page_router(self):
|
|
35
|
+
if not self._page_router:
|
|
36
|
+
self._page_router = PageRouter()
|
|
37
|
+
return self._page_router
|
|
38
|
+
|
|
39
|
+
# HELPERS
|
|
40
|
+
|
|
41
|
+
def add_page(self, page, home_page=False):
|
|
42
|
+
page.page_changed.connect(self.handle_page_changed)
|
|
43
|
+
self.page_router().add_page(page, home_page)
|
|
44
|
+
|
|
45
|
+
def switch_to_page(self, name):
|
|
46
|
+
self.page_router().switch_to_page(name)
|
|
47
|
+
|
|
48
|
+
# EVENT HANDLERS
|
|
49
|
+
|
|
50
|
+
def handle_page_changed(self, name):
|
|
51
|
+
self.switch_to_page(name)
|
|
@@ -6,22 +6,19 @@ from PySide6.QtWidgets import (
|
|
|
6
6
|
QVBoxLayout,
|
|
7
7
|
QLabel,
|
|
8
8
|
)
|
|
9
|
+
from rbeesoft.common.logmanagerlistener import LogManagerListener
|
|
9
10
|
|
|
10
|
-
from rbeesoft.ui.constants import Constants
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class LogPanel(QDockWidget, LogManagerListener):
|
|
16
|
-
def __init__(self):
|
|
17
|
-
super(LogPanel, self).__init__()
|
|
12
|
+
class LogDockWidget(QDockWidget, LogManagerListener):
|
|
13
|
+
def __init__(self, parent):
|
|
14
|
+
super(LogDockWidget, self).__init__(parent)
|
|
18
15
|
self._title_label = None
|
|
19
16
|
self._text_edit = None
|
|
20
17
|
self.init_layout()
|
|
21
18
|
|
|
22
19
|
def title_label(self):
|
|
23
20
|
if not self._title_label:
|
|
24
|
-
self._title_label = QLabel(
|
|
21
|
+
self._title_label = QLabel('Output log')
|
|
25
22
|
return self._title_label
|
|
26
23
|
|
|
27
24
|
def text_edit(self):
|
|
@@ -30,20 +27,30 @@ class LogPanel(QDockWidget, LogManagerListener):
|
|
|
30
27
|
return self._text_edit
|
|
31
28
|
|
|
32
29
|
def init_layout(self):
|
|
33
|
-
clear_logs_button = QPushButton(
|
|
30
|
+
clear_logs_button = QPushButton('Clear log')
|
|
34
31
|
clear_logs_button.clicked.connect(self.handle_clear_logs_button)
|
|
35
32
|
layout = QVBoxLayout()
|
|
36
|
-
# layout.addWidget(self.title_label())
|
|
37
33
|
layout.addWidget(self.text_edit())
|
|
38
34
|
layout.addWidget(clear_logs_button)
|
|
39
35
|
container = QWidget()
|
|
40
36
|
container.setLayout(layout)
|
|
41
|
-
self.setObjectName(
|
|
37
|
+
self.setObjectName('logdockwidget') # Needed for save geometry/state
|
|
42
38
|
self.setWindowTitle(self.title_label().text())
|
|
43
39
|
self.setWidget(container)
|
|
44
40
|
|
|
41
|
+
# HELPERS
|
|
42
|
+
|
|
45
43
|
def add_line(self, line):
|
|
46
44
|
self.text_edit().insertPlainText(line + '\n')
|
|
45
|
+
self.move_to_end()
|
|
46
|
+
|
|
47
|
+
def move_to_end(self):
|
|
48
|
+
cursor = self.text_edit().textCursor()
|
|
49
|
+
cursor.movePosition(cursor.MoveOperation.End)
|
|
50
|
+
self.text_edit().setTextCursor(cursor)
|
|
51
|
+
self.text_edit().ensureCursorVisible()
|
|
52
|
+
|
|
53
|
+
# EVENTS
|
|
47
54
|
|
|
48
55
|
def handle_clear_logs_button(self):
|
|
49
56
|
self.text_edit().clear()
|
|
File without changes
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from PySide6.QtCore import Signal
|
|
2
|
+
from PySide6.QtWidgets import QWidget
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Page(QWidget):
|
|
6
|
+
page_changed = Signal(str)
|
|
7
|
+
|
|
8
|
+
def __init__(self, name, title, settings):
|
|
9
|
+
super(Page, self).__init__()
|
|
10
|
+
self._name = name
|
|
11
|
+
self._title = title
|
|
12
|
+
self._settings = settings
|
|
13
|
+
|
|
14
|
+
def name(self):
|
|
15
|
+
return self._name
|
|
16
|
+
|
|
17
|
+
def title(self):
|
|
18
|
+
return self._title
|
|
19
|
+
|
|
20
|
+
def settings(self):
|
|
21
|
+
return self._settings
|
|
22
|
+
|
|
23
|
+
def switch_to_page(self, name):
|
|
24
|
+
self.page_changed.emit(name)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
from PySide6.QtWidgets import QStackedWidget
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class PageRouter(QStackedWidget):
|
|
5
|
+
def __init__(self):
|
|
6
|
+
super(PageRouter, self).__init__()
|
|
7
|
+
self._pages = {}
|
|
8
|
+
self._home_page = None
|
|
9
|
+
self._current_page = None
|
|
10
|
+
self._previous_page = None
|
|
11
|
+
|
|
12
|
+
def pages(self):
|
|
13
|
+
return self._pages
|
|
14
|
+
|
|
15
|
+
def page(self, name):
|
|
16
|
+
if name in self.pages().keys():
|
|
17
|
+
return self.pages()[name]
|
|
18
|
+
return None
|
|
19
|
+
|
|
20
|
+
def home_page(self):
|
|
21
|
+
return self._home_page
|
|
22
|
+
|
|
23
|
+
def current_page(self):
|
|
24
|
+
return self._current_page
|
|
25
|
+
|
|
26
|
+
def previous_page(self):
|
|
27
|
+
return self._previous_page
|
|
28
|
+
|
|
29
|
+
def add_page(self, page, home_page=False):
|
|
30
|
+
if page.name() not in self.pages().keys():
|
|
31
|
+
self.pages()[page.name()] = page
|
|
32
|
+
if home_page:
|
|
33
|
+
self._home_page = page
|
|
34
|
+
self.addWidget(page)
|
|
35
|
+
|
|
36
|
+
def switch_to_page(self, name):
|
|
37
|
+
if self._current_page and name == self._current_page.name():
|
|
38
|
+
return
|
|
39
|
+
if self._current_page:
|
|
40
|
+
self._previous_page = self._current_page
|
|
41
|
+
self._current_page = self.page(name)
|
|
42
|
+
self.setCurrentWidget(self._current_page)
|
|
43
|
+
|
|
44
|
+
def switch_to_home(self):
|
|
45
|
+
if self._current_page:
|
|
46
|
+
self._previous_page = self._current_page
|
|
47
|
+
self._current_page = self._home_page
|
|
48
|
+
self.setCurrentWidget(self._current_page)
|
|
File without changes
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import atexit
|
|
1
3
|
import datetime
|
|
2
|
-
|
|
3
|
-
from rbeesoft.
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from rbeesoft.common.singleton import singleton
|
|
4
6
|
|
|
5
7
|
|
|
6
8
|
@singleton
|
|
@@ -8,12 +10,16 @@ class LogManager:
|
|
|
8
10
|
def __init__(self, suppress_print=False):
|
|
9
11
|
self._suppress_print = suppress_print
|
|
10
12
|
self._listeners = []
|
|
13
|
+
file_path = os.path.join(Path.home(), 'mosamatic2.log')
|
|
14
|
+
self._file_handle = open(file_path, 'w', buffering=1)
|
|
15
|
+
atexit.register(self.close_file)
|
|
11
16
|
|
|
12
17
|
def _log(self, level, message):
|
|
13
18
|
timestamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
|
14
19
|
message = f'[{timestamp}] {level} : {message}'
|
|
15
20
|
if not self._suppress_print:
|
|
16
21
|
print(message)
|
|
22
|
+
self._file_handle.write(message + '\n')
|
|
17
23
|
self.notify_listeners(message)
|
|
18
24
|
return message
|
|
19
25
|
|
|
@@ -33,3 +39,7 @@ class LogManager:
|
|
|
33
39
|
def notify_listeners(self, message):
|
|
34
40
|
for listener in self._listeners:
|
|
35
41
|
listener.new_message(message)
|
|
42
|
+
|
|
43
|
+
def close_file(self):
|
|
44
|
+
if self._file_handle:
|
|
45
|
+
self._file_handle.close()
|
|
File without changes
|