diff --git a/resources/operator_ico.png b/resources/operator_ico.png new file mode 100644 index 0000000..1d0bf02 Binary files /dev/null and b/resources/operator_ico.png differ diff --git a/resources/system_ico.png b/resources/system_ico.png new file mode 100644 index 0000000..274497b Binary files /dev/null and b/resources/system_ico.png differ diff --git a/resources/view_ico.png b/resources/view_ico.png new file mode 100644 index 0000000..72e9b1c Binary files /dev/null and b/resources/view_ico.png differ diff --git a/src/OptAlgorithm/__pycache__/OptTimeCalculator.cpython-310.pyc b/src/OptAlgorithm/__pycache__/OptTimeCalculator.cpython-310.pyc index 02077f1..8594a34 100644 Binary files a/src/OptAlgorithm/__pycache__/OptTimeCalculator.cpython-310.pyc and b/src/OptAlgorithm/__pycache__/OptTimeCalculator.cpython-310.pyc differ diff --git a/src/controller/__pycache__/controller.cpython-310.pyc b/src/controller/__pycache__/controller.cpython-310.pyc index 6309ccb..e534f39 100644 Binary files a/src/controller/__pycache__/controller.cpython-310.pyc and b/src/controller/__pycache__/controller.cpython-310.pyc differ diff --git a/src/controller/__pycache__/converter.cpython-310.pyc b/src/controller/__pycache__/converter.cpython-310.pyc index 5575d0e..8433f9e 100644 Binary files a/src/controller/__pycache__/converter.cpython-310.pyc and b/src/controller/__pycache__/converter.cpython-310.pyc differ diff --git a/src/controller/__pycache__/mediator.cpython-310.pyc b/src/controller/__pycache__/mediator.cpython-310.pyc index 2131472..bb4e17d 100644 Binary files a/src/controller/__pycache__/mediator.cpython-310.pyc and b/src/controller/__pycache__/mediator.cpython-310.pyc differ diff --git a/src/controller/__pycache__/monitor.cpython-310.pyc b/src/controller/__pycache__/monitor.cpython-310.pyc index d64290e..5adb3fe 100644 Binary files a/src/controller/__pycache__/monitor.cpython-310.pyc and b/src/controller/__pycache__/monitor.cpython-310.pyc differ diff --git a/src/controller/__pycache__/passportFormer.cpython-310.pyc b/src/controller/__pycache__/passportFormer.cpython-310.pyc index 0e0d361..d3bf475 100644 Binary files a/src/controller/__pycache__/passportFormer.cpython-310.pyc and b/src/controller/__pycache__/passportFormer.cpython-310.pyc differ diff --git a/src/gui/__pycache__/mainGui.cpython-310.pyc b/src/gui/__pycache__/mainGui.cpython-310.pyc index daadeb4..df10a6f 100644 Binary files a/src/gui/__pycache__/mainGui.cpython-310.pyc and b/src/gui/__pycache__/mainGui.cpython-310.pyc differ diff --git a/src/gui/__pycache__/plotter.cpython-310.pyc b/src/gui/__pycache__/plotter.cpython-310.pyc index 8cb60bc..927972c 100644 Binary files a/src/gui/__pycache__/plotter.cpython-310.pyc and b/src/gui/__pycache__/plotter.cpython-310.pyc differ diff --git a/src/gui/__pycache__/reportGui.cpython-310.pyc b/src/gui/__pycache__/reportGui.cpython-310.pyc index b35cf55..844c044 100644 Binary files a/src/gui/__pycache__/reportGui.cpython-310.pyc and b/src/gui/__pycache__/reportGui.cpython-310.pyc differ diff --git a/src/gui/__pycache__/settings_window.cpython-310.pyc b/src/gui/__pycache__/settings_window.cpython-310.pyc index 4157ffd..a6053f5 100644 Binary files a/src/gui/__pycache__/settings_window.cpython-310.pyc and b/src/gui/__pycache__/settings_window.cpython-310.pyc differ diff --git a/src/gui/mainGui.py b/src/gui/mainGui.py index 5680fd8..a0ad32b 100644 --- a/src/gui/mainGui.py +++ b/src/gui/mainGui.py @@ -2,7 +2,7 @@ from datetime import datetime as dt from typing import Optional from PyQt5 import QtWidgets from PyQt5.QtCore import Qt -from PyQt5.QtGui import QPixmap +from PyQt5.QtGui import QPixmap, QIcon from utils.base.base import BaseMainWindow, BaseController from gui.settings_window import SystemSettings, OperatorSettings @@ -14,6 +14,9 @@ class MainWindow(BaseMainWindow): super().__init__() self._controller = controller self._init_startUI() + self._init_dock_widgets() + self._init_menu() + def _init_startUI(self) -> None: @@ -21,6 +24,9 @@ class MainWindow(BaseMainWindow): self.sysSettings = SystemSettings("params/system_params.json", 'System', self._transfer_settings) self.repSettings = ReportSettings() self.tabWidget = QtWidgets.QTabWidget() + self.tabWidget.setTabsClosable(True) + self.tabWidget.tabCloseRequested.connect(self._close_tab) + self.tabWidget.currentChanged.connect(self._on_tab_changed) self._clear() seeking_mode_btn = QtWidgets.QPushButton("Real time folder scanning") @@ -37,7 +43,7 @@ class MainWindow(BaseMainWindow): button_widget = QtWidgets.QWidget() button_widget.setLayout(button_layout) - mainLayout = self.layout() + mainLayout = self._central_layout label = QtWidgets.QLabel("Select work mode") label.setStyleSheet( """QLabel{ @@ -50,61 +56,121 @@ class MainWindow(BaseMainWindow): mainLayout.addWidget(label, alignment=Qt.AlignCenter) mainLayout.addWidget(button_widget) + def _init_dock_widgets(self) -> None: + """ + Инициализация док-виджетов для настроек. + """ + + # Создаем док-виджет для OperatorSettings + self.operator_dock = QtWidgets.QDockWidget("Operator Settings", self) + self.operator_dock.setWidget(self.operSettings) + self.operator_dock.setObjectName("OperatorSettings") + self.addDockWidget(Qt.RightDockWidgetArea, self.operator_dock) + self.operator_dock.hide() # Скрываем по умолчанию + + # Создаем док-виджет для SystemSettings + self.system_dock = QtWidgets.QDockWidget("System Settings", self) + self.system_dock.setWidget(self.sysSettings) + self.system_dock.setObjectName("SystemSettings") + self.addDockWidget(Qt.RightDockWidgetArea, self.system_dock) + self.system_dock.hide() # Скрываем по умолчанию + + # Создаем док-виджет для ReportSettings + self.report_dock = QtWidgets.QDockWidget("Report Settings", self) + self.report_dock.setWidget(self.repSettings) + self.report_dock.setObjectName("ReportSettings") + self.addDockWidget(Qt.RightDockWidgetArea, self.report_dock) + self.report_dock.hide() # Скрываем по умолчанию + + # Настройка док-виджетов + self._set_dock_features(self.operator_dock) + self._set_dock_features(self.system_dock) + self._set_dock_features(self.report_dock) + + def _init_menu(self) -> None: + """ + Инициализация главного меню. + """ + # Создаем главное меню + menu_bar = self.menuBar() + + # Создаем меню "Режимы" + modes_menu = menu_bar.addMenu("Mode") + settings_menu = menu_bar.addMenu("Settings") + + # Создаем действия для меню + seeking_action = QtWidgets.QAction("Real time folder scanning", self) + seeking_action.triggered.connect(self._init_seekingUI) + + raport_action = QtWidgets.QAction("Raport editor", self) + raport_action.triggered.connect(self._init_raportUI) + + system_settings = QtWidgets.QAction("System settings", self) + system_settings.setIcon(QIcon('resources/system_ico.png')) + system_settings.triggered.connect(lambda: self._toggle_visibility(self.system_dock)) + + operator_settings = QtWidgets.QAction("Operator settings", self) + operator_settings.setIcon(QIcon('resources/operator_ico.png')) + operator_settings.triggered.connect(lambda: self._toggle_visibility(self.operator_dock)) + + view_settings = QtWidgets.QAction("View settings", self) + view_settings.setIcon(QIcon('resources/view_ico.png')) + view_settings.triggered.connect(lambda: self._toggle_visibility(self.report_dock)) + + + # Добавляем действия в меню "Режимы" + modes_menu.addAction(seeking_action) + modes_menu.addAction(raport_action) + + settings_menu.addAction(system_settings) + settings_menu.addAction(operator_settings) + settings_menu.addAction(view_settings) + + + + def _toggle_visibility(self, body:QtWidgets.QDockWidget = None) -> None: + """ + Переключение видимости док-виджета. + """ + is_visible = body.isVisible() + body.setVisible(not is_visible) + + def _set_dock_features(self, body:QtWidgets.QDockWidget = None) -> None: + """ + Настройка флагов док-виджета. + """ + flag_move = QtWidgets.QDockWidget.DockWidgetMovable + flag_close = QtWidgets.QDockWidget.DockWidgetClosable + flag_floating = QtWidgets.QDockWidget.DockWidgetFloatable + body.setFeatures(flag_move | flag_close | flag_floating) + def _clear(self) -> None: - main = self.layout() - if self.layout() is not None: - while main.count(): - child = main.takeAt(0) + if self._central_layout is not None: + while self._central_layout.count(): + child = self._central_layout.takeAt(0) if child.widget() is not None: child.widget().deleteLater() - else: self.setLayout(QtWidgets.QVBoxLayout()) def _init_seekingUI(self) -> None: self._clear() self._transfer_settings() - self.tabWidget = QtWidgets.QTabWidget() - self.tabWidget.setTabsClosable(True) - self.tabWidget.tabCloseRequested.connect(self._close_tab) - - sys_settings_btn = QtWidgets.QPushButton("System settings") - sys_settings_btn.setFixedWidth(200) - sys_settings_btn.clicked.connect(lambda: self.sysSettings.show()) - oper_settings_btn = QtWidgets.QPushButton("Operator settings") - oper_settings_btn.setFixedWidth(200) - oper_settings_btn.clicked.connect(lambda: self.operSettings.show()) button_layout = QtWidgets.QHBoxLayout() button_layout.setSpacing(2) - button_layout.addWidget(sys_settings_btn) - button_layout.addWidget(oper_settings_btn) button_widget = QtWidgets.QWidget() button_widget.setLayout(button_layout) title = QtWidgets.QLabel("online mode") - mainLayout = self.layout() - mainLayout.addWidget(self.tabWidget) - mainLayout.addWidget(button_widget) - mainLayout.addWidget(title, alignment=Qt.AlignRight) + self._central_layout.addWidget(self.tabWidget) + self._central_layout.addWidget(button_widget) + self._central_layout.addWidget(title, alignment=Qt.AlignRight) self.resize(800,800) self._controller.seeking_mode() - # TODO:push seeking to mediator def _init_raportUI(self) -> None: self._clear() self._transfer_settings() - - self.tabWidget = QtWidgets.QTabWidget() - self.tabWidget.setTabsClosable(True) - self.tabWidget.tabCloseRequested.connect(self._close_tab) - sys_settings_btn = QtWidgets.QPushButton("System settings") - sys_settings_btn.setFixedWidth(185) - sys_settings_btn.clicked.connect(lambda: self.sysSettings.show()) - oper_settings_btn = QtWidgets.QPushButton("Operator settings") - oper_settings_btn.setFixedWidth(185) - oper_settings_btn.clicked.connect(lambda: self.operSettings.show()) - view_settings_btn = QtWidgets.QPushButton("Customize view") - view_settings_btn.setFixedWidth(185) - view_settings_btn.clicked.connect(self._customization_window) + save_screen_btn = QtWidgets.QPushButton("Save state") save_screen_btn.setFixedWidth(185) save_screen_btn.clicked.connect(self._save_plots) @@ -114,25 +180,18 @@ class MainWindow(BaseMainWindow): button_layout = QtWidgets.QHBoxLayout() button_layout.setSpacing(2) - button_layout.addWidget(sys_settings_btn) - button_layout.addWidget(oper_settings_btn) - button_layout.addWidget(view_settings_btn) + button_layout.addWidget(save_screen_btn) button_layout.addWidget(open_file_btn) button_widget = QtWidgets.QWidget() button_widget.setLayout(button_layout) title = QtWidgets.QLabel("raport mode") - mainLayout = self.layout() - mainLayout.addWidget(self.tabWidget) - mainLayout.addWidget(button_widget) - mainLayout.addWidget(title, alignment=Qt.AlignRight) + self._central_layout.addWidget(self.tabWidget) + self._central_layout.addWidget(button_widget) + self._central_layout.addWidget(title, alignment=Qt.AlignRight) self.resize(800,800) self._controller.raport_mode(None) - - #self._controller.raport_mode(path) - # TODO: push only one dir to monitor - def show_plot_tabs(self, plot_widgets: list[QtWidgets.QWidget]) -> None: for plot_widget in plot_widgets: @@ -149,7 +208,7 @@ class MainWindow(BaseMainWindow): tab_count = self.tabWidget.count() if tab_count > 10: - for i in range(0, tab_count-2): + for i in range(0, tab_count-10): self._close_tab(i) def keyPressEvent(self, a0) -> None: @@ -164,10 +223,6 @@ class MainWindow(BaseMainWindow): self.repSettings.close() super().closeEvent(a0) - def _show_settings(self) -> None: - self.operSettings.show() - self.sysSettings.show() - def _transfer_settings(self) -> None: self.tabWidget.clear() operator_params = self.operSettings.getParams() @@ -188,18 +243,20 @@ class MainWindow(BaseMainWindow): return CSV_path return None - def _customization_window(self) -> None: + def _on_tab_changed(self, index): tab = self.tabWidget.currentWidget() - reg_items = tab.property("reg_items") - curve_items = tab.property("curve_items") - self.repSettings.build(reg_items, curve_items) + if tab is not None and self.report_dock.isVisible(): + reg_items = tab.property("reg_items") + curve_items = tab.property("curve_items") + self.repSettings.build(index, reg_items, curve_items) def _save_plots(self) -> None: filepath, _ = QtWidgets.QFileDialog.getSaveFileName(self, "Save file", "", "Image Files (*.png *.jpeg)") tab = self.tabWidget.currentWidget() - pixmap = QPixmap(tab.size()) - tab.render(pixmap) - pixmap.save(filepath) + if tab is not None: + pixmap = QPixmap(tab.size()) + tab.render(pixmap) + pixmap.save(filepath) diff --git a/src/gui/reportGui.py b/src/gui/reportGui.py index 65ec102..10a7e59 100644 --- a/src/gui/reportGui.py +++ b/src/gui/reportGui.py @@ -2,28 +2,38 @@ import pyqtgraph as pg from pyqtgraph.parametertree import Parameter, ParameterTree from typing import Union from PyQt5 import QtWidgets +from cachetools import LRUCache class ReportSettings(QtWidgets.QWidget): + def __init__(self, parent = None): + super().__init__(parent) + #self._tab_cashe = LRUCache(maxsize=1000) - def build(self, reg_items: dict, curve_items: dict) -> None: + def build(self, index, reg_items: dict, curve_items: dict) -> None: """Создает ParameterTree для элементов всех графиков выбранной вкладки""" self._clear() param_tree = ParameterTree() layout = self.layout() layout.addWidget(param_tree) - + """if index in self._tab_cashe: + body = self._tab_cashe[index] + else: + body= [ + self._generate_reg_params(reg_items), + self._generate_curve_params(curve_items) + ] + self._tab_cashe[index] = body""" body= [ - self._generate_reg_params(reg_items), - self._generate_curve_params(curve_items) - ] + self._generate_reg_params(reg_items), + self._generate_curve_params(curve_items) + ] # Добавляем параметры в дерево params = Parameter.create(name='params', type='group', children=body) params.sigTreeStateChanged.connect( lambda: self._update_settings(reg_items, curve_items, params) ) param_tree.setParameters(params, showTop=False) - self.show() def _clear(self) -> None: """ diff --git a/src/utils/base/__pycache__/base.cpython-310.pyc b/src/utils/base/__pycache__/base.cpython-310.pyc index 88d1fa9..61a7458 100644 Binary files a/src/utils/base/__pycache__/base.cpython-310.pyc and b/src/utils/base/__pycache__/base.cpython-310.pyc differ diff --git a/src/utils/base/base.py b/src/utils/base/base.py index 4d865d3..9306920 100644 --- a/src/utils/base/base.py +++ b/src/utils/base/base.py @@ -6,7 +6,7 @@ from cachetools import LRUCache import pandas as pd from PyQt5.QtCore import QObject, QTimer -from PyQt5.QtWidgets import QWidget, QTabWidget +from PyQt5.QtWidgets import QWidget, QTabWidget, QMainWindow, QVBoxLayout from OptAlgorithm import OptAlgorithm import numpy as np import pyqtgraph as pg @@ -393,13 +393,20 @@ class BaseIdealDataBuilder(OptAlgorithm): result = sum(self.get_ideal_timings()) return result -class BaseMainWindow(QWidget): +class BaseMainWindow(QMainWindow): def __init__(self, controller: Optional[BaseController] = None): super().__init__() - self.set_style(self) self.resize(200,200) self._controller = controller + # Создаем центральный виджет и устанавливаем его + self._central_widget = QWidget() + self.setCentralWidget(self._central_widget) + + # Устанавливаем основной вертикальный макет для центрального виджета + self._central_layout = QVBoxLayout() + self._central_widget.setLayout(self._central_layout) + self.set_style(self._central_widget) ... @property