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.
Files changed (44) hide show
  1. rbeesoft-2.0.1/PKG-INFO +13 -0
  2. rbeesoft-2.0.1/README.md +2 -0
  3. rbeesoft-2.0.1/pyproject.toml +22 -0
  4. rbeesoft-2.0.1/src/rbeesoft/app/core/processes/dummyprocess.py +18 -0
  5. rbeesoft-2.0.1/src/rbeesoft/app/core/processes/process.py +64 -0
  6. rbeesoft-2.0.1/src/rbeesoft/app/core/processes/processrunner.py +34 -0
  7. rbeesoft-2.0.1/src/rbeesoft/app/main.py +86 -0
  8. rbeesoft-2.0.1/src/rbeesoft/app/ui/rbeesoftmainwindow.py +77 -0
  9. rbeesoft-2.0.1/src/rbeesoft/app/ui/settings.py +61 -0
  10. rbeesoft-2.0.1/src/rbeesoft/app/ui/widgets/centraldockwidget.py +51 -0
  11. rbeesoft-1.0.9/rbeesoft/ui/panels/logpanel.py → rbeesoft-2.0.1/src/rbeesoft/app/ui/widgets/logdockwidget.py +18 -11
  12. rbeesoft-2.0.1/src/rbeesoft/app/ui/widgets/pages/__init__.py +0 -0
  13. rbeesoft-2.0.1/src/rbeesoft/app/ui/widgets/pages/page.py +24 -0
  14. rbeesoft-2.0.1/src/rbeesoft/app/ui/widgets/pages/pagerouter.py +48 -0
  15. rbeesoft-2.0.1/src/rbeesoft/common/__init__.py +0 -0
  16. {rbeesoft-1.0.9/rbeesoft/core/utils → rbeesoft-2.0.1/src/rbeesoft/common}/logmanager.py +12 -2
  17. rbeesoft-2.0.1/src/rbeesoft/webapp/__init__.py +0 -0
  18. rbeesoft-1.0.9/LICENSE +0 -674
  19. rbeesoft-1.0.9/PKG-INFO +0 -18
  20. rbeesoft-1.0.9/README.md +0 -2
  21. rbeesoft-1.0.9/pyproject.toml +0 -26
  22. rbeesoft-1.0.9/rbeesoft/app.py +0 -23
  23. rbeesoft-1.0.9/rbeesoft/core/utils/functions.py +0 -10
  24. rbeesoft-1.0.9/rbeesoft/resources/VERSION +0 -1
  25. rbeesoft-1.0.9/rbeesoft/resources/icons/rbeesoft.icns +0 -0
  26. rbeesoft-1.0.9/rbeesoft/resources/icons/rbeesoft.ico +0 -0
  27. rbeesoft-1.0.9/rbeesoft/resources/icons/spinner.gif +0 -0
  28. rbeesoft-1.0.9/rbeesoft/ui/components/splashscreen.py +0 -121
  29. rbeesoft-1.0.9/rbeesoft/ui/constants.py +0 -45
  30. rbeesoft-1.0.9/rbeesoft/ui/mainwindow.py +0 -100
  31. rbeesoft-1.0.9/rbeesoft/ui/panels/defaultpanel.py +0 -13
  32. rbeesoft-1.0.9/rbeesoft/ui/panels/mainpanel.py +0 -81
  33. rbeesoft-1.0.9/rbeesoft/ui/panels/settingspanel.py +0 -62
  34. rbeesoft-1.0.9/rbeesoft/ui/panels/stackedpanel.py +0 -22
  35. rbeesoft-1.0.9/rbeesoft/ui/settings.py +0 -32
  36. rbeesoft-1.0.9/rbeesoft/ui/utils.py +0 -32
  37. {rbeesoft-1.0.9 → rbeesoft-2.0.1/src}/rbeesoft/__init__.py +0 -0
  38. {rbeesoft-1.0.9/rbeesoft/core → rbeesoft-2.0.1/src/rbeesoft/app}/__init__.py +0 -0
  39. {rbeesoft-1.0.9/rbeesoft/ui → rbeesoft-2.0.1/src/rbeesoft/app/core}/__init__.py +0 -0
  40. {rbeesoft-1.0.9/rbeesoft/ui/components → rbeesoft-2.0.1/src/rbeesoft/app/core/processes}/__init__.py +0 -0
  41. {rbeesoft-1.0.9/rbeesoft/ui/panels → rbeesoft-2.0.1/src/rbeesoft/app/ui}/__init__.py +0 -0
  42. /rbeesoft-1.0.9/rbeesoft/core/utils/___init__.py → /rbeesoft-2.0.1/src/rbeesoft/app/ui/widgets/__init__.py +0 -0
  43. {rbeesoft-1.0.9/rbeesoft/core/utils → rbeesoft-2.0.1/src/rbeesoft/common}/logmanagerlistener.py +0 -0
  44. {rbeesoft-1.0.9/rbeesoft/core → rbeesoft-2.0.1/src/rbeesoft/common}/singleton.py +0 -0
@@ -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
@@ -0,0 +1,2 @@
1
+ # rbeesoft
2
+ Python package with base classes for developing apps and webapps
@@ -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
- from rbeesoft.core.utils.logmanagerlistener import LogManagerListener
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(Constants.RBEESOFT_LOG_PANEL_TITLE)
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(Constants.RBEESOFT_LOG_PANEL_CLEAR_LOGS_BUTTON)
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(Constants.RBEESOFT_LOG_PANEL_NAME)
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()
@@ -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.core.singleton import singleton
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