Index: .gitignore
===================================================================
diff -u
--- .gitignore (revision 0)
+++ .gitignore (revision d711fdaf5fdcd83ec84ec7a05965768429483c29)
@@ -0,0 +1,13 @@
+__pycache__
+*.pyc
+paths.ini*
+tags
+tags.*
+*.whl
+*.log
+*.*~
+venv*
+.idea/
+.vscode/
+build/
+dist/
Index: dialin
===================================================================
diff -u
--- dialin (revision 0)
+++ dialin (revision d711fdaf5fdcd83ec84ec7a05965768429483c29)
@@ -0,0 +1 @@
+../dialin/leahi_dialin
\ No newline at end of file
Index: engine/diality-logo.jpg
===================================================================
diff -u
Binary files differ
Index: engine/dynamicloader.py
===================================================================
diff -u
--- engine/dynamicloader.py (revision 0)
+++ engine/dynamicloader.py (revision d711fdaf5fdcd83ec84ec7a05965768429483c29)
@@ -0,0 +1,203 @@
+"""
+ find and loads the ui objects
+"""
+import os
+from PySide2.QtUiTools import QUiLoader
+from PySide2.QtCore import QFile, QObject
+from PySide2 import QtWidgets
+
+
+class DynamicLoader(QObject):
+ """
+ the parent class of all the run time loadable widgets
+ """
+
+ canMaximize = False
+ isVisible = False
+ menu_name = 'Dynamic Loader'
+
+ loader: QUiLoader
+ window: QtWidgets.QWidget
+
+ def __init__(self, location: str):
+ super().__init__(None)
+ self.location = location
+ self.loader = QUiLoader()
+ self.__load_ui()
+ self._init_loader()
+ self._init_widgets()
+ self._init_connections()
+
+ def _init_loader(self):
+ """
+ Virtual method to be implemented in the child class and should be called in order
+ Main purpose is to find/create the widgets objects in this method
+ :return: None
+ """
+ raise NotImplementedError()
+
+ def _init_widgets(self):
+ """
+ Virtual method to be implemented in the child class and should be called in order
+ Main purpose is to setup/initialize widgets properties in this method
+ :return: None
+ """
+ raise NotImplementedError()
+
+ def _init_connections(self):
+ """
+ Virtual method to be implemented in the child class and should be called in order
+ Main purpose is to initial the widgets signal/slots connections in this method
+ :return: None
+ """
+ raise NotImplementedError()
+
+ def __load_ui(self):
+ """
+ loads the ui file of the GUI at run time
+ :return: none
+ """
+ ui_file = QFile(self.location + "/interface.ui")
+ ui_file.open(QFile.ReadOnly)
+ self.window = self.loader.load(ui_file)
+ # assert if the ui file can't be loaded
+ error = self.loader.errorString()
+ assert len(error) == 0, error
+ ui_file.close()
+
+ def show(self):
+ """
+ shows the main container window
+ :return: none
+ """
+ self.window.show()
+
+ def find_widget(self, child_type, child_name: str) -> QtWidgets.QWidget:
+ """
+ finds a child in the loaded ui and returns the reference to it if found
+ otherwise quits the application
+ :param child_type: (var) type of the child
+ :param child_name: (str) name of the child
+ :return: (QtWidgets.QWidget) reference to the child
+ """
+ child: QtWidgets.QWidget = self.window.findChild(child_type, child_name)
+ assert child is not None, "child name '{}' with type '{}' can't be found.".format(child_name, child_type)
+ return child
+
+ def find_action(self, name: str) -> QtWidgets.QAction:
+ """
+ convenient method of find_widget for QAction
+ :param name: (str) name of the QLabel Object
+ :return: (QAction) reference to the QAction
+ """
+ child = self.find_widget(QtWidgets.QAction, name)
+ return child
+
+ def find_label(self, name: str) -> QtWidgets.QLabel:
+ """
+ convenient method of find_widget for QLabel
+ :param name: (str) name of the QLabel Object
+ :return: (QLabel) reference to the QLabel
+ """
+ child = self.find_widget(QtWidgets.QLabel, name)
+ return child
+
+ def find_line_edit(self, name: str) -> QtWidgets.QLineEdit:
+ """
+ convenient method of find_widget for QLineEdit
+ :param name: (str) name of the QLabel Object
+ :return: (QLabel) reference to the QLineEdit
+ """
+ child = self.find_widget(QtWidgets.QLineEdit, name)
+ return child
+
+ def find_button(self, name: str) -> QtWidgets.QPushButton:
+ """
+ convenient method of find_widget for QPushButton
+ :param name: (str) name of the QPushButton Object
+ :return: (QPushButton) reference to the QPushButton
+ """
+ child = self.find_widget(QtWidgets.QPushButton, name)
+ return child
+
+ def find_combobox(self, name: str) -> QtWidgets.QComboBox:
+ """
+ convenient method of find_widget for QComboBox
+ :param name: (str) name of the QComboBox Object
+ :return: (QComboBox) reference to the QComboBox
+ """
+ child = self.find_widget(QtWidgets.QComboBox, name)
+ return child
+
+ def find_checkbox(self, name: str) -> QtWidgets.QCheckBox:
+ """
+ convenient method of find_widget for QCheckBox
+ :param name: (str) name of the QCheckBox Object
+ :return: (QCheckBox) reference to the QComboBox
+ """
+ child = self.find_widget(QtWidgets.QCheckBox, name)
+ return child
+
+ def find_spinbox(self, name: str) -> QtWidgets.QSpinBox:
+ """
+ convenient method of find_widget for QSpinBox
+ :param name: (str) name of the QSpinBox Object
+ :return: (QSpinBox) reference to the QSpinBox
+ """
+ child = self.find_widget(QtWidgets.QSpinBox, name)
+ return child
+
+ def find_double_spinbox(self, name: str) -> QtWidgets.QDoubleSpinBox:
+ """
+ convenient method of find_widget for QDoubleSpinBox
+ :param name: (str) name of the QDoubleSpinBox Object
+ :return: (QDoubleSpinBox) reference to the QDoubleSpinBox
+ """
+ child = self.find_widget(QtWidgets.QDoubleSpinBox, name)
+ return child
+
+ def find_radio_button(self, name: str) -> QtWidgets.QRadioButton:
+ """
+ convenient method of find_widget for QRadioButton
+ :param name: (str) name of the QRadioButton Object
+ :return: (QRadioButton) reference to the QSpinBox
+ """
+ child = self.find_widget(QtWidgets.QRadioButton, name)
+ return child
+
+ def find_slider(self, name: str) -> QtWidgets.QSlider:
+ """
+ convenient method of find_widget for QSlider
+ :param name: (str) name of the QSlider Object
+ :return: (QSlider) reference to the QSlider
+ """
+ child = self.find_widget(QtWidgets.QSlider, name)
+ return child
+
+ def find_table_widget(self, name: str) -> QtWidgets.QTableWidget:
+ """
+ convenient method of find_widget for QTableWidget
+ :param name: (str) name of the QTableWidget Object
+ :return: (QTableWidget) reference to the QTableWidget
+ """
+ child: QtWidgets.QTableWidget = self.find_widget(QtWidgets.QTableWidget, name)
+ return child
+
+ def find_list_widget(self, name: str) -> QtWidgets.QListWidget:
+ """
+ convenient method of find_widget for QListWidget
+ :param name: (str) name of the QListWidget Object
+ :return: (QListWidget) reference to the QListWidget
+ """
+ child: QtWidgets.QListWidget = self.find_widget(QtWidgets.QListWidget, name)
+ return child
+
+ def find_tool_button(self, name: str) -> QtWidgets.QToolButton:
+ """
+ convenient method of find_widget for QToolButton
+ :param name: (str) name of the QToolButton Object
+ :return: (QToolButton) reference to the QToolButton
+ """
+ child: QtWidgets.QToolButton = self.find_widget(QtWidgets.QToolButton, name)
+ return child
+
Index: engine/engine.py
===================================================================
diff -u
--- engine/engine.py (revision 0)
+++ engine/engine.py (revision d711fdaf5fdcd83ec84ec7a05965768429483c29)
@@ -0,0 +1,147 @@
+"""
+the plugin loader class
+"""
+# PySide2 import
+from PySide2 import QtWidgets
+from PySide2.QtCore import Qt
+
+# simulator imports
+from engine.dynamicloader import DynamicLoader
+
+# it loads all the classes in plugin folder dynamically
+# so used * in here to load all
+from plugins import *
+
+
+class Engine(DynamicLoader):
+ """
+ The simulator class which loads the ui file dynamically and initializes the objects
+ and can be eventually shown.
+ Note: this class is growing fast and seems like needs to be multiple classes
+ """
+ mdiArea: QtWidgets.QMdiArea
+ menuBar: QtWidgets.QMenuBar
+ menuWindows: QtWidgets.QMenu
+ lblStatusCANBus: QtWidgets.QLabel
+ lblStatusMessages: QtWidgets.QLabel
+
+ plugins = []
+
+ def __init__(self):
+ super().__init__(os.path.dirname(__file__))
+
+ self.__init_plugins()
+ self.__init_actions()
+
+ def _init_loader(self):
+ """
+ initializes the class by calling it's initializer methods to make objects ready
+ :return: none
+ """
+ self.lblStatusCANBus = self.find_label('lblStatusCANBus')
+ self.lblStatusMessages = self.find_label('lblStatusMessages')
+ self.mdiArea = self.find_widget(QtWidgets.QMdiArea, 'mdiArea')
+ self.menuBar = self.find_widget(QtWidgets.QMenuBar, 'menuBar')
+ self.menuWindows = self.find_widget(QtWidgets.QMenu, 'menuWindows')
+ self.action_show_all = self.find_action('actionShowAll')
+
+ def _init_widgets(self):
+ pass
+
+ def _init_connections(self):
+ pass
+
+ def __init_plugins(self):
+ """
+ initializes the widgets' properties
+ :return: none
+ """
+ # loading/registering plugins
+ # the loaded_plugins has been filled and exposed
+ # from within the __init__.py in the plugins folder
+ # folders with '__' and '.' have been ignored.
+ for plugin in available_plugins:
+ self.plugins.append(eval(plugin)())
+ self.__register_plugin()
+
+ def __init_actions(self):
+ """
+ initializes the widgets connections
+ :return: none
+ """
+ self.action_show_all.toggled.connect(self.set_all_windows_visible)
+
+ def __create_actions(self, name: str, window: QtWidgets.QWidget):
+ """
+ creates a menu action item with the name/title name and set to show/hide the plugin form object
+ :param name: the action menu title
+ :param window: the object to show/hide
+ :return: false if the object is None
+ """
+ if window is None:
+ return False
+
+ top_menu_name = "View"
+ sub_menu_name = name
+ names = name.split("/")
+ if len(names) > 1:
+ top_menu_name = names[0]
+ sub_menu_name = names[1]
+
+ menu = None
+ if top_menu_name != "":
+ found = False
+ for item in self.menuBar.actions():
+ if item.text().replace('&', '').strip().lower() == top_menu_name.replace('&', '').strip().lower():
+ menu = item.menu()
+ found = True
+ break
+
+ if not found:
+ menu = self.menuBar.addMenu(top_menu_name)
+
+ action = menu.addAction(sub_menu_name)
+ action.triggered.connect(lambda: self.__action_triggered(window))
+ self.action_show_all.toggled.connect(action.setChecked)
+
+ def __action_triggered(self, window):
+ if window.isVisible():
+ if self.mdiArea.activeSubWindow() == window:
+ window.setVisible(False)
+ else:
+ self.mdiArea.setActiveSubWindow(window)
+ else:
+ window.setVisible(True)
+
+ def __register_plugin(self):
+ """
+ creates the plugin object and adds it to the mdi and creates an action menu for it
+ :param obj: the plugin object
+ :return: False if the passed obj is None
+ """
+
+ titles = {}
+ for obj in self.plugins:
+ titles[obj.window.windowTitle()] = obj
+
+ for title in sorted(titles):
+ wgt = titles[title].window
+ sub = self.mdiArea.addSubWindow(wgt)
+
+ sub.setWindowFlags(Qt.WindowMinimizeButtonHint | Qt.WindowTitleHint)
+ sub.setWindowFlag(Qt.WindowMaximizeButtonHint, obj.canMaximize)
+ sub.setVisible(obj.isVisible)
+
+ self.__create_actions(title, sub)
+ wgt.setWindowTitle(wgt.setWindowTitle(title.replace('&', '')))
+
+ def set_all_windows_visible(self, visible: bool):
+ """
+ sets all the windows (in)visible
+ :param visible: if true the windows are set to visible
+ :return: None
+ """
+ for sub in self.mdiArea.subWindowList():
+ sub.setVisible(visible)
+ self.mdiArea.cascadeSubWindows()
+
Index: engine/interface.ui
===================================================================
diff -u
--- engine/interface.ui (revision 0)
+++ engine/interface.ui (revision d711fdaf5fdcd83ec84ec7a05965768429483c29)
@@ -0,0 +1,163 @@
+
+
+ MainWindow
+
+
+
+ 0
+ 0
+ 800
+ 600
+
+
+
+ Firmware Simulator
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Raised
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 1
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+ QFrame::StyledPanel
+
+
+
+
+
+
+ -
+
+
+ QFrame::StyledPanel
+
+
+
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+
+
+
+ -
+
+
+
+
+ 20
+ 49
+ 76
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+ Cascade
+
+
+ true
+
+
+
+
+ true
+
+
+ Show All
+
+
+
+
+
+
+
+
+ actionCascade
+ triggered()
+ mdiArea
+ cascadeSubWindows()
+
+
+ -1
+ -1
+
+
+ 600
+ 409
+
+
+
+
+
Index: engine/resources.qrc
===================================================================
diff -u
--- engine/resources.qrc (revision 0)
+++ engine/resources.qrc (revision d711fdaf5fdcd83ec84ec7a05965768429483c29)
@@ -0,0 +1,5 @@
+
+
+ diality-logo.jpg
+
+
Index: plugins
===================================================================
diff -u
--- plugins (revision 0)
+++ plugins (revision d711fdaf5fdcd83ec84ec7a05965768429483c29)
@@ -0,0 +1 @@
+../plugins
\ No newline at end of file
Index: run.sh
===================================================================
diff -u
--- run.sh (revision 0)
+++ run.sh (revision d711fdaf5fdcd83ec84ec7a05965768429483c29)
@@ -0,0 +1,17 @@
+#!/bin/sh
+location() {
+ if [ -L "$0" ]; then
+ local filename="$0"
+ local linkinfo=$(readlink $filename)
+ local filepath=$(dirname $linkinfo)
+ echo $filepath
+ else
+ echo $(dirname "$0")
+ fi
+}
+
+cd $(location)
+
+export QT_QPA_PLATFORM=xcb
+export XDG_SESSION_TYPE=xcb
+python3 ./simulator.py $1 $2 $3 $4 $5 $6 &
Index: setup.py
===================================================================
diff -u
--- setup.py (revision 0)
+++ setup.py (revision d711fdaf5fdcd83ec84ec7a05965768429483c29)
@@ -0,0 +1,15 @@
+import setuptools
+from xlrd.timemachine import python_version
+
+if __name__ == '__main__':
+ setuptools.setup(
+ name="simulator",
+ python_version="~3.8",
+ entry_points = {
+ 'console_scripts': ['clinical=clinical.simulator:main'],
+ },
+ install_requires = [
+ "PySide2",
+ "openpyxl"
+ ]
+ )
Index: simulator.py
===================================================================
diff -u
--- simulator.py (revision 0)
+++ simulator.py (revision d711fdaf5fdcd83ec84ec7a05965768429483c29)
@@ -0,0 +1,32 @@
+"""
+the main function
+to load the main simulator loader widget
+"""
+import sys
+from PySide2 import QtCore, QtWidgets
+from engine.engine import Engine
+
+# https://pyinstaller.org/en/stable/spec-files.html
+# https://www.pythonguis.com/tutorials/packaging-pyside2-applications-windows-pyinstaller/
+# https://forum.qt.io/topic/148718/qt-qpa-plugin-could-not-load-the-qt-platform-plugin-xcb-in-even-though-it-was-found
+
+def main():
+ """
+ the main function which initializes the Simulator and starts it.
+ :return: none
+ """
+ # create qt application
+ app = QtWidgets.QApplication(sys.argv)
+
+ engine = Engine()
+
+ engine.show()
+
+ # start qt application main loop
+ sys.exit(app.exec_())
+
+
+if __name__ == "__main__":
+ QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_ShareOpenGLContexts)
+
+ main()
Index: simulator.spec
===================================================================
diff -u
--- simulator.spec (revision 0)
+++ simulator.spec (revision d711fdaf5fdcd83ec84ec7a05965768429483c29)
@@ -0,0 +1,44 @@
+# -*- mode: python ; coding: utf-8 -*-
+
+
+a = Analysis(
+ ['simulator.py'],
+ pathex=[],
+ binaries=[],
+ datas=[('engine', 'engine'), ('plugins', 'plugins')],
+ hiddenimports=['openpyxl'],
+ hookspath=[],
+ hooksconfig={},
+ runtime_hooks=[],
+ excludes=[],
+ noarchive=False,
+ optimize=0,
+)
+pyz = PYZ(a.pure)
+
+exe = EXE(
+ pyz,
+ a.scripts,
+ [],
+ exclude_binaries=True,
+ name='simulator',
+ debug=False,
+ bootloader_ignore_signals=False,
+ strip=False,
+ upx=True,
+ console=False,
+ disable_windowed_traceback=False,
+ argv_emulation=False,
+ target_arch=None,
+ codesign_identity=None,
+ entitlements_file=None,
+)
+coll = COLLECT(
+ exe,
+ a.binaries,
+ a.datas,
+ strip=False,
+ upx=True,
+ upx_exclude=[],
+ name='simulator',
+)