Skip to content
Snippets Groups Projects
Commit 029b3a68 authored by Andreas Bolin's avatar Andreas Bolin
Browse files

Cleanup main_window.py. Fixed bug with sublass QObject in PyQt5

parent a79f8920
No related branches found
No related tags found
1 merge request!78Add scheduler GUI
Pipeline #73417 passed
...@@ -7,9 +7,10 @@ from logger import * ...@@ -7,9 +7,10 @@ from logger import *
from main_window import * from main_window import *
from graphics_graph_item import * from graphics_graph_item import *
from graphics_graph_event import * from graphics_graph_event import *
from graphics_axis_item import *
from graphics_component_item import * from graphics_component_item import *
from graphics_axes_item import *
from graphics_timeline_item import *
# __all__ = ['main_window', 'graphics_graph', 'graphics_axis', 'component_item'] # __all__ = ['main_window', 'graphics_graph', 'component_item', 'graphics_axes', 'graphics_timeline_item']
__version__ = '0.1' __version__ = '0.1'
__author__ = 'Andreas Bolin' __author__ = 'Andreas Bolin'
...@@ -43,16 +43,22 @@ from graphics_timeline_item import GraphicsTimelineItem ...@@ -43,16 +43,22 @@ from graphics_timeline_item import GraphicsTimelineItem
sys.settrace # sys.settrace
# class GraphicsGraphEvent(ABC): # class GraphicsGraphEvent(QGraphicsItemGroup, QObject): # PySide2
class GraphicsGraphEvent(QGraphicsItemGroup, QObject): class GraphicsGraphEvent(): # PyQt5
# class GraphicsGraphEvent(QGraphicsObject):
"""Event filter and handlers for GraphicsGraphItem""" """Event filter and handlers for GraphicsGraphItem"""
class Signals(QObject): # PyQt5
"""A class respresenting signals."""
component_selected = Signal(str)
schedule_time_changed = Signal()
_axes: GraphicsAxesItem _axes: GraphicsAxesItem
_current_pos: QPointF _current_pos: QPointF
_delta_time: int _delta_time: int
component_selected = Signal(str) signals: Signals # PyQt5
schedule_time_changed = Signal() # component_selected = Signal(str) # PySide2
# schedule_time_changed = Signal() # PySide2
@overload @overload
def is_component_valid_pos(self, pos: float, end_time: int) -> bool: ... def is_component_valid_pos(self, pos: float, end_time: int) -> bool: ...
...@@ -61,12 +67,15 @@ class GraphicsGraphEvent(QGraphicsItemGroup, QObject): ...@@ -61,12 +67,15 @@ class GraphicsGraphEvent(QGraphicsItemGroup, QObject):
@overload @overload
def set_schedule_time(self, delta_time: int) -> None: ... def set_schedule_time(self, delta_time: int) -> None: ...
def __init__(self, parent: Optional[QGraphicsItem] = None): # def __init__(self, parent: Optional[QGraphicsItem] = None): # PySide2
QObject.__init__(self) # QObject.__init__(self)
QGraphicsItemGroup.__init__(self, parent) # QGraphicsItemGroup.__init__(self, parent)
# QGraphicsObject.__init__(self)
# super().__init__(parent) def __init__(self, parent: Optional[QGraphicsItem] = None): # PyQt5
# super().__init__() # QGraphicsItemGroup.__init__(self, parent)
# QObject.__init__(self)
super().__init__()
self.signals = self.Signals()
################# #################
#### Filters #### #### Filters ####
...@@ -187,7 +196,8 @@ class GraphicsGraphEvent(QGraphicsItemGroup, QObject): ...@@ -187,7 +196,8 @@ class GraphicsGraphEvent(QGraphicsItemGroup, QObject):
by default be accepted, and this item is then the mouse grabber. This by default be accepted, and this item is then the mouse grabber. This
allows the item to receive future move, release and double-click events.""" allows the item to receive future move, release and double-click events."""
item: GraphicsComponentItem = self.scene().mouseGrabberItem() item: GraphicsComponentItem = self.scene().mouseGrabberItem()
self.component_selected.emit(item.op_id) self.signals.component_selected.emit(item.op_id)
# self.component_selected.emit(item.op_id)
self._current_pos = item.mapToParent(event.pos()) self._current_pos = item.mapToParent(event.pos())
item.setCursor(QCursor(Qt.ClosedHandCursor)) item.setCursor(QCursor(Qt.ClosedHandCursor))
event.accept() event.accept()
...@@ -248,5 +258,4 @@ class GraphicsGraphEvent(QGraphicsItemGroup, QObject): ...@@ -248,5 +258,4 @@ class GraphicsGraphEvent(QGraphicsItemGroup, QObject):
item.hide_label() item.hide_label()
if self._delta_time != 0: if self._delta_time != 0:
self.set_schedule_time(self._delta_time) self.set_schedule_time(self._delta_time)
self.schedule_time_changed.emit() self.signals.schedule_time_changed.emit()
...@@ -47,7 +47,8 @@ from graphics_axes_item import GraphicsAxesItem ...@@ -47,7 +47,8 @@ from graphics_axes_item import GraphicsAxesItem
from graphics_graph_event import GraphicsGraphEvent from graphics_graph_event import GraphicsGraphEvent
class GraphicsGraphItem(GraphicsGraphEvent, QGraphicsItemGroup): # class GraphicsGraphItem(GraphicsGraphEvent, QGraphicsItemGroup): # PySide2 / PyQt5
class GraphicsGraphItem(QGraphicsItemGroup, GraphicsGraphEvent): # PyQt5
"""A class to represent a graph in a QGraphicsScene. This class is a """A class to represent a graph in a QGraphicsScene. This class is a
subclass of QGraphicsItemGroup and contains the objects, axes from subclass of QGraphicsItemGroup and contains the objects, axes from
GraphicsAxesItem, as well as components from GraphicsComponentItem. It GraphicsAxesItem, as well as components from GraphicsComponentItem. It
...@@ -160,4 +161,4 @@ class GraphicsGraphItem(GraphicsGraphEvent, QGraphicsItemGroup): ...@@ -160,4 +161,4 @@ class GraphicsGraphItem(GraphicsGraphEvent, QGraphicsItemGroup):
# self._axes.update_axes(schedule_time - 2, self._components_height, self._x_axis_indent) # self._axes.update_axes(schedule_time - 2, self._components_height, self._x_axis_indent)
for component in self._components: for component in self._components:
self.addToGroup(component) self.addToGroup(component)
# self.addToGroup(self._components) # self.addToGroup(self._components)
\ No newline at end of file
...@@ -6,16 +6,12 @@ Contains the scheduler-gui MainWindow class for scheduling operations in an SFG. ...@@ -6,16 +6,12 @@ Contains the scheduler-gui MainWindow class for scheduling operations in an SFG.
Start main-window with start_gui(). Start main-window with start_gui().
""" """
from copy import deepcopy
import os import os
import sys import sys
from pathlib import Path from typing import Union
from types import ModuleType from pprint import pprint
from typing import Any, Iterable, List, Sequence, Type, Dict, Union from copy import deepcopy
from pprint import pprint from importlib.machinery import SourceFileLoader
#from matplotlib.pyplot import bar
#from diagram import *
from importlib.machinery import SourceFileLoader
import inspect import inspect
...@@ -26,18 +22,12 @@ from qtpy.QtCore import QCoreApplication, Qt, Slot, Signal, QSettings, QStand ...@@ -26,18 +22,12 @@ from qtpy.QtCore import QCoreApplication, Qt, Slot, Signal, QSettings, QStand
from qtpy.QtGui import QCloseEvent from qtpy.QtGui import QCloseEvent
from qtpy.QtWidgets import ( from qtpy.QtWidgets import (
QApplication, QMainWindow, QMessageBox, QFileDialog, QInputDialog, QCheckBox, QAbstractButton, QApplication, QMainWindow, QMessageBox, QFileDialog, QInputDialog, QCheckBox, QAbstractButton,
QTableWidgetItem, QSizePolicy) QTableWidgetItem)
# QGraphics and QPainter imports # QGraphics and QPainter imports
from qtpy.QtCore import ( from qtpy.QtCore import QRectF, QByteArray
QRect, QRectF, QPoint, QSize, QByteArray, QMarginsF, QObject) from qtpy.QtGui import QIcon
from qtpy.QtGui import ( from qtpy.QtWidgets import QGraphicsScene, QGraphicsItemGroup
QPaintEvent, QPainter, QPainterPath, QColor, QBrush, QPen, QFont, QPolygon, QIcon, QPixmap,
QLinearGradient)
from qtpy.QtWidgets import (
QGraphicsView, QGraphicsScene, QGraphicsWidget, QGraphicsScale,
QGraphicsLayout, QGraphicsLinearLayout, QGraphicsGridLayout, QGraphicsLayoutItem, QGraphicsAnchorLayout,
QGraphicsItem, QGraphicsItemGroup, QGraphicsRectItem, QHeaderView)
# B-ASIC # B-ASIC
import logger import logger
...@@ -59,7 +49,6 @@ sys.excepthook = logger.handle_exceptions ...@@ -59,7 +49,6 @@ sys.excepthook = logger.handle_exceptions
if __debug__: if __debug__:
log.setLevel('DEBUG') log.setLevel('DEBUG')
if __debug__: if __debug__:
# Print some system version information # Print some system version information
QT_API = os.environ.get('QT_API') QT_API = os.environ.get('QT_API')
...@@ -102,7 +91,7 @@ sys.path.insert(0, 'icons/') # Needed for the compiled '*_rc.py' ...@@ -102,7 +91,7 @@ sys.path.insert(0, 'icons/') # Needed for the compiled '*_rc.py'
from ui_main_window import Ui_MainWindow # Only availible when the form (.ui) is compiled from ui_main_window import Ui_MainWindow # Only availible when the form (.ui) is compiled
# The folowing QCoreApplication values is used for QSettings among others # The following QCoreApplication values is used for QSettings among others
QCoreApplication.setOrganizationName('Linöping University') QCoreApplication.setOrganizationName('Linöping University')
QCoreApplication.setOrganizationDomain('liu.se') QCoreApplication.setOrganizationDomain('liu.se')
QCoreApplication.setApplicationName('B-ASIC Scheduler') QCoreApplication.setApplicationName('B-ASIC Scheduler')
...@@ -114,22 +103,21 @@ QCoreApplication.setApplicationName('B-ASIC Scheduler') ...@@ -114,22 +103,21 @@ QCoreApplication.setApplicationName('B-ASIC Scheduler')
class MainWindow(QMainWindow, Ui_MainWindow): class MainWindow(QMainWindow, Ui_MainWindow):
"""Schedule of an SFG with scheduled Operations.""" """Schedule of an SFG with scheduled Operations."""
_scene: QGraphicsScene _scene: QGraphicsScene
_schedule: Union[Schedule, None]
_graph: Union[GraphicsGraphItem, None] _graph: Union[GraphicsGraphItem, None]
_scale: float _scale: float
_debug_rects: QGraphicsItemGroup _debug_rects: QGraphicsItemGroup
_splitter_pos: int _splitter_pos: int
_splitter_min: int _splitter_min: int
_schedule: Union[Schedule, None]
def __init__(self): def __init__(self):
"""Initialize Schedule-gui.""" """Initialize Scheduler-gui."""
super().__init__() super().__init__()
self._schedule = None
self._graph = None self._graph = None
self._open_file_dialog_opened = False self._scale = 75.0
self._scale = 75
self._debug_rects = None self._debug_rects = None
self._schedule = None
QIcon.setThemeName('breeze') QIcon.setThemeName('breeze')
log.debug('themeName: \'{}\''.format(QIcon.themeName())) log.debug('themeName: \'{}\''.format(QIcon.themeName()))
...@@ -149,9 +137,9 @@ class MainWindow(QMainWindow, Ui_MainWindow): ...@@ -149,9 +137,9 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.menu_save .triggered .connect(self.save) self.menu_save .triggered .connect(self.save)
self.menu_save_as .triggered .connect(self.save_as) self.menu_save_as .triggered .connect(self.save_as)
self.menu_quit .triggered .connect(self.close) self.menu_quit .triggered .connect(self.close)
self.menu_node_info .triggered .connect(self.toggle_component_info) self.menu_node_info .triggered .connect(self.show_info_table)
self.menu_exit_dialog .triggered .connect(self.toggle_exit_dialog) self.menu_exit_dialog .triggered .connect(self.hide_exit_dialog)
self.actionT .triggered .connect(self.actionTbtn) self.actionT .triggered .connect(self._actionTbtn)
self.splitter .splitterMoved .connect(self._splitter_moved) self.splitter .splitterMoved .connect(self._splitter_moved)
# Setup event member functions # Setup event member functions
...@@ -181,6 +169,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): ...@@ -181,6 +169,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
@property @property
def schedule(self) -> Schedule: def schedule(self) -> Schedule:
"""Get the current schedule."""
return self._schedule return self._schedule
...@@ -188,15 +177,18 @@ class MainWindow(QMainWindow, Ui_MainWindow): ...@@ -188,15 +177,18 @@ class MainWindow(QMainWindow, Ui_MainWindow):
#### Slots #### #### Slots ####
############### ###############
@Slot() @Slot()
def actionTbtn(self) -> None: def _actionTbtn(self) -> None:
# TODO: remove
self.schedule.plot_schedule() self.schedule.plot_schedule()
print(f'filtersChildEvents(): {self._graph.filtersChildEvents()}') print(f'filtersChildEvents(): {self._graph.filtersChildEvents()}')
# self.printButtonPressed('callback_pushButton()') # self._printButtonPressed('callback_pushButton()')
@Slot() @Slot()
def _load_schedule_from_pyfile(self) -> None: def _load_schedule_from_pyfile(self) -> None:
"""SLOT() for SIGNAL(menu_load_from_file.triggered)
Load a python script as a module and search for a Schedule object. If
found, opens it."""
settings = QSettings() settings = QSettings()
# open_dir = QStandardPaths.standardLocations(QStandardPaths.HomeLocation)[0] if not self._open_file_dialog_opened else ''
last_file = settings.value('mainwindow/last_opened_file', QStandardPaths.standardLocations(QStandardPaths.HomeLocation)[0], str) last_file = settings.value('mainwindow/last_opened_file', QStandardPaths.standardLocations(QStandardPaths.HomeLocation)[0], str)
if not os.path.exists(last_file): # if filename does not exist if not os.path.exists(last_file): # if filename does not exist
last_file = os.path.dirname(last_file) + '/' last_file = os.path.dirname(last_file) + '/'
...@@ -211,7 +203,6 @@ class MainWindow(QMainWindow, Ui_MainWindow): ...@@ -211,7 +203,6 @@ class MainWindow(QMainWindow, Ui_MainWindow):
if not abs_path_filename: # return if empty filename (QFileDialog was canceled) if not abs_path_filename: # return if empty filename (QFileDialog was canceled)
return return
log.debug('abs_path_filename = {}.'.format(abs_path_filename)) log.debug('abs_path_filename = {}.'.format(abs_path_filename))
self._open_file_dialog_opened = True
module_name = inspect.getmodulename(abs_path_filename) module_name = inspect.getmodulename(abs_path_filename)
if not module_name: # return if empty module name if not module_name: # return if empty module name
...@@ -253,9 +244,11 @@ class MainWindow(QMainWindow, Ui_MainWindow): ...@@ -253,9 +244,11 @@ class MainWindow(QMainWindow, Ui_MainWindow):
@Slot() @Slot()
def close_schedule(self) -> None: def close_schedule(self) -> None:
"""SLOT() for SIGNAL(menu_close_schedule.triggered)
Closes current schedule."""
if self._graph: if self._graph:
self._graph.component_selected.disconnect(self.info_table_update_component) self._graph.signals.component_selected.disconnect(self.info_table_update_component)
self._graph.schedule_time_changed.disconnect(self.info_table_update_schedule) self._graph.signals.schedule_time_changed.disconnect(self.info_table_update_schedule)
self._graph.removeSceneEventFilters(self._graph.event_items) self._graph.removeSceneEventFilters(self._graph.event_items)
self._scene.removeItem(self._graph) self._scene.removeItem(self._graph)
self.menu_close_schedule.setEnabled(False) self.menu_close_schedule.setEnabled(False)
...@@ -267,21 +260,25 @@ class MainWindow(QMainWindow, Ui_MainWindow): ...@@ -267,21 +260,25 @@ class MainWindow(QMainWindow, Ui_MainWindow):
@Slot() @Slot()
def save(self) -> None: def save(self) -> None:
"""This method save an schedule.""" """SLOT() for SIGNAL(menu_save.triggered)
This method save an schedule."""
#TODO: all #TODO: all
self.printButtonPressed('save_schedule()') self._printButtonPressed('save_schedule()')
self.update_statusbar(self.tr('Schedule saved successfully')) self.update_statusbar(self.tr('Schedule saved successfully'))
@Slot() @Slot()
def save_as(self) -> None: def save_as(self) -> None:
"""This method save as an schedule.""" """SLOT() for SIGNAL(menu_save_as.triggered)
This method save as an schedule."""
#TODO: all #TODO: all
self.printButtonPressed('save_schedule()') self._printButtonPressed('save_schedule()')
self.update_statusbar(self.tr('Schedule saved successfully')) self.update_statusbar(self.tr('Schedule saved successfully'))
@Slot(bool) @Slot(bool)
def toggle_component_info(self, checked: bool) -> None: def show_info_table(self, checked: bool) -> None:
"""This method toggles the right hand side info window.""" """SLOT(bool) for SIGNAL(menu_node_info.triggered)
Takes in a boolean and hide or show the info table accordingly with
'checked'."""
# Note: splitter handler index 0 is a hidden splitter handle far most left, use index 1 # Note: splitter handler index 0 is a hidden splitter handle far most left, use index 1
# settings = QSettings() # settings = QSettings()
_, max = self.splitter.getRange(1) # tuple(min, max) _, max = self.splitter.getRange(1) # tuple(min, max)
...@@ -295,13 +292,17 @@ class MainWindow(QMainWindow, Ui_MainWindow): ...@@ -295,13 +292,17 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.splitter.moveSplitter(max, 1) self.splitter.moveSplitter(max, 1)
@Slot(bool) @Slot(bool)
def toggle_exit_dialog(self, checked: bool) -> None: def hide_exit_dialog(self, checked: bool) -> None:
"""SLOT(bool) for SIGNAL(menu_exit_dialog.triggered)
Takes in a boolean and stores 'checked' in 'hide_exit_dialog' item in
settings."""
s = QSettings() s = QSettings()
s.setValue("mainwindow/hide_exit_dialog", checked) s.setValue("mainwindow/hide_exit_dialog", checked)
@Slot(int, int) @Slot(int, int)
def _splitter_moved(self, pos: int, index: int) -> None: def _splitter_moved(self, pos: int, index: int) -> None:
"""Callback method used to check if the right widget (info window) """SLOT(int, int) for SIGNAL(splitter.splitterMoved)
Callback method used to check if the right widget (info window)
has collapsed. Update the checkbutton accordingly.""" has collapsed. Update the checkbutton accordingly."""
width = self.splitter.sizes()[1] width = self.splitter.sizes()[1]
...@@ -315,7 +316,8 @@ class MainWindow(QMainWindow, Ui_MainWindow): ...@@ -315,7 +316,8 @@ class MainWindow(QMainWindow, Ui_MainWindow):
@Slot(str) @Slot(str)
def info_table_update_component(self, op_id: str) -> None: def info_table_update_component(self, op_id: str) -> None:
"""Taked in an operator-id, first clears the 'Operator' part of the info """SLOT(str) for SIGNAL(_graph.signals.component_selected)
Taked in an operator-id, first clears the 'Operator' part of the info
table and then fill in the table with new values from the operator table and then fill in the table with new values from the operator
associated with 'op_id'.""" associated with 'op_id'."""
self.info_table_clear_component() self.info_table_clear_component()
...@@ -323,11 +325,16 @@ class MainWindow(QMainWindow, Ui_MainWindow): ...@@ -323,11 +325,16 @@ class MainWindow(QMainWindow, Ui_MainWindow):
@Slot() @Slot()
def info_table_update_schedule(self) -> None: def info_table_update_schedule(self) -> None:
"""Updates the 'Schedule' part of the info table.""" """SLOT() for SIGNAL(_graph.signals.schedule_time_changed)
Updates the 'Schedule' part of the info table."""
self.info_table.item(1, 1).setText(str(self.schedule.schedule_time)) self.info_table.item(1, 1).setText(str(self.schedule.schedule_time))
@Slot(QRectF) @Slot(QRectF)
def shrink_scene_to_min_size(self, rect: QRectF) -> None: def shrink_scene_to_min_size(self, rect: QRectF) -> None:
"""SLOT(QRectF) for SIGNAL(_scene.sceneRectChanged)
Takes in a QRectF (unused) and shrink the scene bounding rectangle to
it's minimum size, when the bounding rectangle signals a change in
geometry."""
self._scene.setSceneRect(self._scene.itemsBoundingRect()) self._scene.setSceneRect(self._scene.itemsBoundingRect())
...@@ -336,7 +343,9 @@ class MainWindow(QMainWindow, Ui_MainWindow): ...@@ -336,7 +343,9 @@ class MainWindow(QMainWindow, Ui_MainWindow):
#### Events #### #### Events ####
################ ################
def _close_event(self, event: QCloseEvent) -> None: def _close_event(self, event: QCloseEvent) -> None:
"""Replaces QMainWindow default closeEvent(QCloseEvent) event""" """EVENT: Replaces QMainWindow default closeEvent(QCloseEvent) event. Takes
in a QCloseEvent and display an exit dialog, depending on
'hide_exit_dialog' in settings."""
s = QSettings() s = QSettings()
hide_dialog = s.value('mainwindow/hide_exit_dialog', False, bool) hide_dialog = s.value('mainwindow/hide_exit_dialog', False, bool)
ret = QMessageBox.StandardButton.Yes ret = QMessageBox.StandardButton.Yes
...@@ -370,7 +379,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): ...@@ -370,7 +379,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
################################# #################################
#### Helper member functions #### #### Helper member functions ####
################################# #################################
def printButtonPressed(self, func_name: str) -> None: def _printButtonPressed(self, func_name: str) -> None:
#TODO: remove #TODO: remove
alert = QMessageBox(self) alert = QMessageBox(self)
...@@ -386,14 +395,13 @@ class MainWindow(QMainWindow, Ui_MainWindow): ...@@ -386,14 +395,13 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.menu_close_schedule.setEnabled(True) self.menu_close_schedule.setEnabled(True)
self._scene.addItem(self._graph) self._scene.addItem(self._graph)
self._graph.installSceneEventFilters(self._graph.event_items) self._graph.installSceneEventFilters(self._graph.event_items)
self._graph.component_selected.connect(self.info_table_update_component) self._graph.signals.component_selected.connect(self.info_table_update_component)
self._graph.schedule_time_changed.connect(self.info_table_update_schedule) self._graph.signals.schedule_time_changed.connect(self.info_table_update_schedule)
self.info_table_fill_schedule(self.schedule) self.info_table_fill_schedule(self.schedule)
self.update_statusbar(self.tr('Schedule loaded successfully')) self.update_statusbar(self.tr('Schedule loaded successfully'))
def update_statusbar(self, msg: str) -> None: def update_statusbar(self, msg: str) -> None:
"""Write the given str to the statusbar with temporarily policy.""" """Takes in an str and write 'msg' to the statusbar with temporarily policy."""
self.statusbar.showMessage(msg) self.statusbar.showMessage(msg)
def _write_settings(self) -> None: def _write_settings(self) -> None:
...@@ -475,7 +483,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): ...@@ -475,7 +483,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
row = self.info_table.findItems('Operator', Qt.MatchExactly) row = self.info_table.findItems('Operator', Qt.MatchExactly)
if row: if row:
row = row[0].row() row = row[0].row()
if row > 1: if row > 2:
for _ in range(3): for _ in range(3):
self.info_table.removeRow(1) self.info_table.removeRow(1)
else: else:
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment