chore: переработан main_widget + связь с контроллером перенесена на сигналы
This commit is contained in:
parent
2ca5034c45
commit
67d237e4ed
@ -367,6 +367,7 @@ class BaseController(QObject):
|
||||
def __init__(self,
|
||||
mediator: Optional[BaseMediator] = None,
|
||||
file_manager: Optional[BaseFileManager] = None):
|
||||
super().__init__()
|
||||
self._mediator = mediator
|
||||
self._file_manager = file_manager
|
||||
|
||||
|
||||
@ -1,37 +1,44 @@
|
||||
from typing import Union
|
||||
|
||||
from PyQt5.QtWidgets import QWidget
|
||||
from PyQt5.QtWidgets import QWidget, QTabWidget
|
||||
from PyQt5.QtGui import QPixmap
|
||||
from PyQt5.QtCore import pyqtSignal
|
||||
|
||||
from base.base import BaseController
|
||||
|
||||
|
||||
class Controller(BaseController):
|
||||
|
||||
signal_widgets = pyqtSignal(list)
|
||||
signal_progress_bar = pyqtSignal(int)
|
||||
signal_status_text = pyqtSignal(str)
|
||||
|
||||
def update_settings(self, settings: list[dict]) -> None:
|
||||
self.mediator.notify(settings)
|
||||
|
||||
# TODO: Объедини переключение режимов в один метод, и по входному аргументу (например 1 и 2) переключай их.
|
||||
def set_working_mode(self, mode:int) -> None:
|
||||
self._file_manager.set_mode(mode)
|
||||
|
||||
def open_file(self, filepath: str) -> None:
|
||||
self._file_manager.open_custom_file(filepath)
|
||||
|
||||
def update_plots(self) -> None:
|
||||
self._file_manager.replot_all()
|
||||
|
||||
def send_widgets(self, widgets: list[QWidget]) -> None:
|
||||
self.signal_widgets.emit(widgets)
|
||||
|
||||
def update_settings(self, settings: list[dict]) -> None:
|
||||
self.mediator.notify(self, settings)
|
||||
|
||||
def update_status(self, msg:str) -> None:
|
||||
self.signal_status_text.emit(msg)
|
||||
|
||||
def update_progress(self, progress:int) -> None:
|
||||
self.signal_progress_bar.emit(progress)
|
||||
|
||||
def open_file(self, filepath: str) -> None:
|
||||
self._file_manager.open_custom_file(filepath)
|
||||
|
||||
def save_file(self, data:list[str, QTabWidget]) -> None:
|
||||
filepath, tab = data
|
||||
pixmap = QPixmap(tab.size())
|
||||
tab.render(pixmap)
|
||||
pixmap.save(filepath)
|
||||
|
||||
|
||||
|
||||
@ -1,361 +0,0 @@
|
||||
from datetime import datetime as dt
|
||||
from typing import Optional
|
||||
|
||||
from PyQt5 import QtWidgets
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtGui import QPixmap, QIcon
|
||||
from PyQt5.QtWidgets import QSizePolicy as QSP
|
||||
|
||||
from base.base import BaseMainWindow, BaseController
|
||||
from gui.settings_window import SystemSettings, OperatorSettings
|
||||
from gui.reportGui import ReportSettings
|
||||
|
||||
|
||||
# FIXME: При отркытии RaportMode все прочитанные трейсы должны удаляться, но они открываются...
|
||||
class MainWindow(BaseMainWindow):
|
||||
def __init__(self,
|
||||
controller: Optional[BaseController] = None) -> None:
|
||||
# TODO: Почему контроллер Optional? Нигде нет обработки случаев, если он будет None: при вызове его методов
|
||||
# будет ошибка.
|
||||
|
||||
# TODO: Касательно передачи экземпляра контроллера в конструктор. Не лучше ли реализовать взаимодействие с ним
|
||||
# через сигналы? Как минимум это позволит исключить странную зависимость интерфейса от контроллера.
|
||||
# Логичнее было бы использовать данный класс в качестве фасада над виджетами, о чем сказано ниже.
|
||||
super().__init__()
|
||||
self._controller = controller
|
||||
# TODO: касательно ВСЕХ методов данного класса, СОЗДАЮЩИХ ВИДЖЕТЫ. Исходя из названия данного класса - это
|
||||
# финальное главное окно приложения: оно не должно создавать виджеты, а уже использовать готовые, поэтому
|
||||
# правильнее будет каждый создаваемый тут виджет вынести в отдельный модуль в отдельный класс, там выполнять
|
||||
# его создание и настройку, а тут уже получать готовый экземпляр виджета и его отображать.
|
||||
self._init_startUI()
|
||||
self._init_dock_widgets()
|
||||
self._init_menu()
|
||||
|
||||
|
||||
def _init_TabWidget(self) -> None:
|
||||
self.tabWidget = QtWidgets.QTabWidget()
|
||||
self.tabWidget.setTabsClosable(True)
|
||||
self.tabWidget.tabCloseRequested.connect(self._close_tab)
|
||||
self.tabWidget.currentChanged.connect(self._on_tab_changed)
|
||||
|
||||
def _init_startUI(self) -> None:
|
||||
self.operSettings = OperatorSettings("params/operator_params.json", 'Operator', self._upd_settings)
|
||||
self.sysSettings = SystemSettings("params/system_params.json", 'System', self._upd_settings)
|
||||
self.repSettings = ReportSettings()
|
||||
|
||||
|
||||
self._clear()
|
||||
self.resize(800,800)
|
||||
seeking_mode_btn = QtWidgets.QPushButton("Real time folder scanning")
|
||||
seeking_mode_btn.setFixedWidth(300)
|
||||
seeking_mode_btn.clicked.connect(self._init_seekingUI)
|
||||
raport_mode_btn = QtWidgets.QPushButton("Raport editor")
|
||||
raport_mode_btn.setFixedWidth(300)
|
||||
raport_mode_btn.clicked.connect(self._init_raportUI)
|
||||
|
||||
button_layout = QtWidgets.QHBoxLayout()
|
||||
button_layout.setSpacing(2)
|
||||
button_layout.addWidget(seeking_mode_btn)
|
||||
button_layout.addWidget(raport_mode_btn)
|
||||
button_widget = QtWidgets.QWidget()
|
||||
button_widget.setLayout(button_layout)
|
||||
|
||||
mainLayout = self._central_layout
|
||||
label = QtWidgets.QLabel("Select work mode")
|
||||
label.setStyleSheet(
|
||||
"""QLabel{
|
||||
color: #ffffff;
|
||||
font-size: 40px;
|
||||
font-weight: bold;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
}"""
|
||||
)
|
||||
mainLayout.addWidget(label, alignment=Qt.AlignCenter)
|
||||
mainLayout.addWidget(button_widget)
|
||||
|
||||
self._init_statusBar()
|
||||
|
||||
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("View 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))
|
||||
view_settings.triggered.connect(lambda: self._on_tab_changed(0))
|
||||
|
||||
|
||||
# Добавляем действия в меню "Режимы"
|
||||
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 _init_statusBar(self) -> None:
|
||||
# Создание пользовательского виджета для StatusBar
|
||||
note_widget = QtWidgets.QWidget()
|
||||
note_layout = QtWidgets.QHBoxLayout(note_widget)
|
||||
note_layout.setContentsMargins(10, 1, 10, 1)
|
||||
note_layout.setSpacing(15) # Устанавливаем расстояние между элементами
|
||||
|
||||
# Создание QLabel и QProgressBar
|
||||
self.mode_label = QtWidgets.QLabel()
|
||||
self.note_label = QtWidgets.QLabel()
|
||||
self.progress_bar = QtWidgets.QProgressBar()
|
||||
self.progress_bar.setRange(0, 100)
|
||||
self.progress_bar.setValue(0)
|
||||
self.progress_bar.setMinimumWidth(250)
|
||||
self.progress_bar.setMaximumHeight(10)
|
||||
self.progress_bar.setTextVisible(False)
|
||||
|
||||
# Создание QSpacerItem
|
||||
|
||||
# Установка политики размеров
|
||||
self.mode_label.setSizePolicy(QSP.Policy.Preferred, QSP.Policy.Preferred)
|
||||
self.note_label.setSizePolicy(QSP.Policy.MinimumExpanding, QSP.Policy.Preferred)
|
||||
self.progress_bar.setSizePolicy(QSP.Policy.Fixed, QSP.Policy.Preferred)
|
||||
|
||||
# Добавление виджетов в макет
|
||||
note_layout.addWidget(self.progress_bar)
|
||||
note_layout.addWidget(self.note_label)
|
||||
note_layout.addStretch(1)
|
||||
note_layout.addWidget(self.mode_label)
|
||||
|
||||
# Установка политики размеров для note_widget
|
||||
note_widget.setSizePolicy(QSP.Policy.Expanding, QSP.Policy.Preferred)
|
||||
|
||||
# Добавление пользовательского виджета в StatusBar как Permanent Widget
|
||||
self.statusBar().addPermanentWidget(note_widget, 1)
|
||||
|
||||
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:
|
||||
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()
|
||||
|
||||
# TODO: По методам self._init_seekingUI и self._init_raportUI:
|
||||
# 1. Дублирование кода (блок из первых трех строк) - вынести в отдельный метод
|
||||
# 2. Оба метода призваны проинициализировать UI, но в то же время занимаются созданием виджетов и еще что-то
|
||||
# в контроллере дергают: создание виджетов вынести отдельно, контроллеру сообщить о чем-то при помощи
|
||||
# pyqtSignal при нажатии кнопки или выборе пункта меню.
|
||||
def _init_seekingUI(self) -> None:
|
||||
self._clear()
|
||||
self._init_TabWidget()
|
||||
self._transfer_settings()
|
||||
|
||||
button_layout = QtWidgets.QHBoxLayout()
|
||||
button_layout.setSpacing(2)
|
||||
button_widget = QtWidgets.QWidget()
|
||||
button_widget.setLayout(button_layout)
|
||||
|
||||
self.mode_label.setText("online mode")
|
||||
self._central_layout.addWidget(self.tabWidget)
|
||||
self._central_layout.addWidget(button_widget)
|
||||
self._controller.seeking_mode()
|
||||
|
||||
def _init_raportUI(self) -> None:
|
||||
self._clear()
|
||||
self._init_TabWidget()
|
||||
self._transfer_settings()
|
||||
|
||||
save_screen_btn = QtWidgets.QPushButton("Save state")
|
||||
save_screen_btn.setFixedWidth(185)
|
||||
save_screen_btn.clicked.connect(self._save_plots)
|
||||
open_file_btn = QtWidgets.QPushButton("Open file")
|
||||
open_file_btn.setFixedWidth(185)
|
||||
open_file_btn.clicked.connect(self._open_file)
|
||||
|
||||
button_layout = QtWidgets.QHBoxLayout()
|
||||
button_layout.setSpacing(2)
|
||||
|
||||
button_layout.addWidget(save_screen_btn)
|
||||
button_layout.addWidget(open_file_btn)
|
||||
button_widget = QtWidgets.QWidget()
|
||||
button_widget.setLayout(button_layout)
|
||||
|
||||
self.mode_label.setText("raport mode")
|
||||
self._central_layout.addWidget(self.tabWidget)
|
||||
self._central_layout.addWidget(button_widget)
|
||||
|
||||
self._controller.raport_mode()
|
||||
|
||||
def show_plot_tabs(self, plot_widgets: list[QtWidgets.QWidget]) -> None:
|
||||
for plot_widget in plot_widgets:
|
||||
widget, reg_items, curve_items, qt_items = plot_widget
|
||||
|
||||
tab = QtWidgets.QWidget()
|
||||
tab.setProperty("reg_items", reg_items)
|
||||
tab.setProperty("curve_items", curve_items)
|
||||
tab.setProperty("qt_items", qt_items)
|
||||
grid = QtWidgets.QGridLayout()
|
||||
grid.addWidget(widget)
|
||||
tab.setLayout(grid)
|
||||
self.tabWidget.addTab(tab, "SF_trace_" + dt.now().strftime('%Y_%m_%d-%H_%M_%S'))
|
||||
self.tabWidget.setCurrentWidget(tab)
|
||||
|
||||
tab_count = self.tabWidget.count()
|
||||
if tab_count > 10:
|
||||
for i in range(0, tab_count-10):
|
||||
self._close_tab(i)
|
||||
|
||||
self.update_stateLabel("Done!")
|
||||
|
||||
def keyPressEvent(self, a0) -> None:
|
||||
if a0.key() == Qt.Key_F5:
|
||||
tab_count = self.tabWidget.count()
|
||||
for i in range(0, tab_count):
|
||||
self._close_tab(i)
|
||||
|
||||
def closeEvent(self, a0):
|
||||
self.operSettings.close()
|
||||
self.sysSettings.close()
|
||||
self.repSettings.close()
|
||||
super().closeEvent(a0)
|
||||
|
||||
def update_progressBar(self, percent:int) -> None:
|
||||
if percent > 100: percent = 100
|
||||
self.progress_bar.setValue(percent)
|
||||
|
||||
def update_stateLabel(self, msg: str = None) -> None:
|
||||
self.note_label.setText(msg)
|
||||
self.note_label.adjustSize()
|
||||
|
||||
def _transfer_settings(self) -> None:
|
||||
operator_params = self.operSettings.getParams()
|
||||
system_params = self.sysSettings.getParams()
|
||||
self._controller.update_settings([operator_params, system_params])
|
||||
|
||||
def _upd_settings(self) -> None:
|
||||
self._transfer_settings()
|
||||
if self.mode_label.text():
|
||||
self.tabWidget.clear()
|
||||
self._controller.update_plots()
|
||||
|
||||
|
||||
|
||||
def _close_tab(self, index:int) -> None:
|
||||
self.tabWidget.removeTab(index)
|
||||
|
||||
# TODO: Зачем разделять эти 2 метода? В self._select_csv путь к файлу получили и контроллеру сигналом отправили.
|
||||
def _open_file(self) -> None:
|
||||
path = self._select_csv()
|
||||
if path is not None:
|
||||
self._controller.open_file(path)
|
||||
|
||||
def _select_csv(self) -> Optional[str]:
|
||||
CSV_path, _ = QtWidgets.QFileDialog.getOpenFileName(self,"Select csv file", "", "CSV Files (*.csv)")
|
||||
if CSV_path:
|
||||
print(CSV_path)
|
||||
return CSV_path
|
||||
return None
|
||||
|
||||
def _on_tab_changed(self, index):
|
||||
try:
|
||||
tab = self.tabWidget.currentWidget()
|
||||
except:
|
||||
# TODO: Очень плохая практика - не указывать конкретный тип исключений.
|
||||
tab = None
|
||||
if tab is not None and self.report_dock.isVisible():
|
||||
reg_items = tab.property("reg_items")
|
||||
curve_items = tab.property("curve_items")
|
||||
qt_items = tab.property("qt_items")
|
||||
self.repSettings.build(index, reg_items, curve_items, qt_items)
|
||||
|
||||
def _save_plots(self) -> None:
|
||||
# TODO: Нарушение принципа единичной ответственности. Почему ИНТЕРФЕЙС занимается сохранением чего-то?
|
||||
# От него должен только исходить призыв к действию (кнопка или что-то подобное), сама логика же должна
|
||||
# быть в другом месте.
|
||||
|
||||
# TODO: эта реализация работает, но для пользователя будет больно каждый раз писать имена и расширения
|
||||
# сохраняемых файлов, не кажется? Можно сделать, как показано ниже, тогда файлам будет назначаться имя
|
||||
# по умолчанию и с расширением. ХЗ правда, как эта форма на винде будет выглядеть - надо проверить.
|
||||
# ========================================================
|
||||
# dialog = QFileDialog()
|
||||
# dialog.setOptions(QFileDialog.DontUseNativeDialog)
|
||||
# dialog.setFileMode(QFileDialog.AnyFile)
|
||||
# dialog.setAcceptMode(QFileDialog.AcceptSave)
|
||||
# dialog.setDirectory(os.getcwd())
|
||||
# dialog.selectFile("untitled.txt")
|
||||
# dialog.exec_()
|
||||
# if dialog.accepted:
|
||||
# # emit signal to save file
|
||||
# ...
|
||||
# ========================================================
|
||||
filepath, _ = QtWidgets.QFileDialog.getSaveFileName(self, "Save file", "", "Image Files (*.png *.jpeg)")
|
||||
tab = self.tabWidget.currentWidget()
|
||||
if tab is not None:
|
||||
pixmap = QPixmap(tab.size())
|
||||
tab.render(pixmap)
|
||||
pixmap.save(filepath)
|
||||
155
src/gui/main_gui.py
Normal file
155
src/gui/main_gui.py
Normal file
@ -0,0 +1,155 @@
|
||||
from PyQt5 import QtWidgets
|
||||
from PyQt5.QtCore import Qt, pyqtSignal
|
||||
|
||||
from base.base import BaseMainWindow
|
||||
from gui.start_widget import (CustomMenuBar, CustomStatusBar,
|
||||
StartWidget, CustomTabWidget,
|
||||
RaportWidget, SeekingWidget)
|
||||
from gui.settings_window import SystemSettings, OperatorSettings
|
||||
from gui.report_gui import ReportSettings
|
||||
|
||||
|
||||
# FIXME: При отркытии RaportMode все прочитанные трейсы должны удаляться, но они открываются...
|
||||
class MainWindow(BaseMainWindow):
|
||||
|
||||
signal_mode = pyqtSignal(int)
|
||||
signal_settings = pyqtSignal(list)
|
||||
signal_replot_all = pyqtSignal()
|
||||
signal_open_file = pyqtSignal(str)
|
||||
signal_save_file = pyqtSignal(list)
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self._init_startUI()
|
||||
|
||||
def _init_startUI(self) -> None:
|
||||
self._init_settings()
|
||||
self._init_menu()
|
||||
self._init_status_bar()
|
||||
start_widget = StartWidget()
|
||||
start_widget.seeking_mode_btn.clicked.connect(self._init_seekingUI)
|
||||
start_widget.raport_mode_btn.clicked.connect(self._init_raportUI)
|
||||
self.setCentralWidget(start_widget.get_widget())
|
||||
|
||||
def _init_settings(self) -> None:
|
||||
self.sysSettings = SystemSettings("params/system_params.json", 'System', self._upd_settings)
|
||||
self.repSettings = ReportSettings()
|
||||
self.operSettings = OperatorSettings("params/operator_params.json", 'Operator', self._upd_settings)
|
||||
|
||||
def _init_menu(self) -> None:
|
||||
self.menu = CustomMenuBar(self.sysSettings, self.repSettings, self.operSettings)
|
||||
self.menu.seeking_action.triggered.connect(self._init_seekingUI)
|
||||
self.menu.raport_action.triggered.connect(self._init_raportUI)
|
||||
self.menu.view_settings.triggered.connect(lambda: self._on_tab_changed(0))
|
||||
self.menu.setup(self)
|
||||
|
||||
def _init_status_bar(self) -> None:
|
||||
self.status_widget = CustomStatusBar()
|
||||
self.statusBar().addPermanentWidget(self.status_widget.get_widget(), 1)
|
||||
|
||||
def _init_tab_widget(self) -> None:
|
||||
self._tab_widget = CustomTabWidget()
|
||||
self._tab_widget.currentChanged.connect(self._on_tab_changed)
|
||||
|
||||
def _on_tab_changed(self, index):
|
||||
tab = self._tab_widget.currentWidget()
|
||||
if tab:
|
||||
reg_items = tab.property("reg_items")
|
||||
curve_items = tab.property("curve_items")
|
||||
qt_items = tab.property("qt_items")
|
||||
self.repSettings.build(index, reg_items, curve_items, qt_items)
|
||||
|
||||
def _clear(self) -> None:
|
||||
if self.layout() is not None:
|
||||
while self.layout().count():
|
||||
child = self.layout().takeAt(0)
|
||||
if child.widget() is not None:
|
||||
child.widget().deleteLater()
|
||||
self._init_tab_widget()
|
||||
self._transfer_settings()
|
||||
|
||||
def _init_raportUI(self) -> None:
|
||||
self._clear()
|
||||
self._set_mode(1)
|
||||
raport_widget = RaportWidget(self._tab_widget)
|
||||
raport_widget.open_file_btn.clicked.connect(self._open_file)
|
||||
raport_widget.save_screen_btn.clicked.connect(self._save_plots)
|
||||
self.setCentralWidget(raport_widget.get_widget())
|
||||
|
||||
def _init_seekingUI(self) -> None:
|
||||
self._clear()
|
||||
self._set_mode(2)
|
||||
seeking_widget = SeekingWidget(self._tab_widget)
|
||||
self.setCentralWidget(seeking_widget.get_widget())
|
||||
|
||||
def _set_mode(self, num:int) -> None:
|
||||
match num:
|
||||
case 1:
|
||||
self.status_widget.set_mode("raport mode")
|
||||
self.signal_mode.emit(1)
|
||||
case 2:
|
||||
self.status_widget.set_mode("online mode")
|
||||
self.signal_mode.emit(2)
|
||||
|
||||
def show_plot_tabs(self, plot_widgets: list[QtWidgets.QWidget]) -> None:
|
||||
for plot_widget in plot_widgets:
|
||||
self._tab_widget.create_tab(plot_widget)
|
||||
|
||||
tab_count = self._tab_widget.count()
|
||||
if tab_count > 10:
|
||||
for i in range(0, tab_count-10):
|
||||
self._tab_widget.close_tab(i)
|
||||
|
||||
self.status_widget.set_note("Done!")
|
||||
self.status_widget.set_progress(100)
|
||||
|
||||
def keyPressEvent(self, a0) -> None:
|
||||
if a0.key() == Qt.Key_F5:
|
||||
tab_count = self._tab_widget.count()
|
||||
for i in range(0, tab_count):
|
||||
self._tab_widget.close_tab(i)
|
||||
|
||||
def closeEvent(self, a0):
|
||||
self.operSettings.close()
|
||||
self.sysSettings.close()
|
||||
self.repSettings.close()
|
||||
super().closeEvent(a0)
|
||||
|
||||
def _transfer_settings(self) -> None:
|
||||
operator_params = self.operSettings.getParams()
|
||||
system_params = self.sysSettings.getParams()
|
||||
self.signal_settings.emit([operator_params, system_params])
|
||||
|
||||
def _upd_settings(self) -> None:
|
||||
self._transfer_settings()
|
||||
if self.status_widget._mode_label.text():
|
||||
self._tab_widget.clear()
|
||||
self.signal_replot_all.emit()
|
||||
|
||||
def _open_file(self) -> None:
|
||||
CSV_path, _ = QtWidgets.QFileDialog.getOpenFileName(self,"Select csv file", "", "CSV Files (*.csv)")
|
||||
if CSV_path:
|
||||
self.signal_open_file.emit(CSV_path)
|
||||
|
||||
def _save_plots(self) -> None:
|
||||
filepath, _ = QtWidgets.QFileDialog.getSaveFileName(self, "Save file", "", "Image Files (*.png *.jpeg)")
|
||||
tab = self._tab_widget.currentWidget()
|
||||
if tab: self.signal_save_file.emit([filepath, tab])
|
||||
|
||||
# TODO: эта реализация работает, но для пользователя будет больно каждый раз писать имена и расширения
|
||||
# сохраняемых файлов, не кажется? Можно сделать, как показано ниже, тогда файлам будет назначаться имя
|
||||
# по умолчанию и с расширением. ХЗ правда, как эта форма на винде будет выглядеть - надо проверить.
|
||||
# ========================================================
|
||||
# dialog = QFileDialog()
|
||||
# dialog.setOptions(QFileDialog.DontUseNativeDialog)
|
||||
# dialog.setFileMode(QFileDialog.AnyFile)
|
||||
# dialog.setAcceptMode(QFileDialog.AcceptSave)
|
||||
# dialog.setDirectory(os.getcwd())
|
||||
# dialog.selectFile("untitled.txt")
|
||||
# dialog.exec_()
|
||||
# if dialog.accepted:
|
||||
# # emit signal to save file
|
||||
# ...
|
||||
# ========================================================
|
||||
|
||||
|
||||
295
src/gui/start_widget.py
Normal file
295
src/gui/start_widget.py
Normal file
@ -0,0 +1,295 @@
|
||||
from datetime import datetime as dt
|
||||
|
||||
from PyQt5.QtWidgets import (QWidget, QPushButton,
|
||||
QMenuBar, QHBoxLayout,
|
||||
QVBoxLayout, QLabel,
|
||||
QDockWidget, QTabWidget,
|
||||
QProgressBar, QAction,
|
||||
QMainWindow, QGridLayout)
|
||||
from PyQt5.QtGui import QIcon
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import QSizePolicy as QSP
|
||||
|
||||
from gui.settings_window import SystemSettings, OperatorSettings
|
||||
from gui.report_gui import ReportSettings
|
||||
|
||||
|
||||
class StartWidget(QWidget):
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self._build_main_layout()
|
||||
|
||||
def get_widget(self) -> QWidget:
|
||||
return self
|
||||
|
||||
def _build_main_layout(self) -> None:
|
||||
self.resize(800,800)
|
||||
self.seeking_mode_btn = QPushButton("Real time folder scanning")
|
||||
self.seeking_mode_btn.setFixedWidth(300)
|
||||
self.raport_mode_btn = QPushButton("Raport editor")
|
||||
self.raport_mode_btn.setFixedWidth(300)
|
||||
|
||||
button_layout = QHBoxLayout()
|
||||
button_layout.setSpacing(2)
|
||||
button_layout.addWidget(self.seeking_mode_btn)
|
||||
button_layout.addWidget(self.raport_mode_btn)
|
||||
button_widget = QWidget()
|
||||
button_widget.setLayout(button_layout)
|
||||
|
||||
mainLayout = QVBoxLayout()
|
||||
label = QLabel("Select work mode")
|
||||
label.setStyleSheet(
|
||||
"""QLabel{
|
||||
color: #ffffff;
|
||||
font-size: 40px;
|
||||
font-weight: bold;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
}"""
|
||||
)
|
||||
mainLayout.addWidget(label, alignment=Qt.AlignCenter)
|
||||
mainLayout.addWidget(button_widget)
|
||||
self.setLayout(mainLayout)
|
||||
|
||||
|
||||
class CustomStatusBar(QWidget):
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self._build_status_bar()
|
||||
|
||||
def get_widget(self) -> QWidget:
|
||||
return self
|
||||
|
||||
def set_mode(self, msg:str = '') -> None:
|
||||
if not isinstance(msg, str): msg = str(msg)
|
||||
self._mode_label.setText(msg)
|
||||
self._mode_label.adjustSize()
|
||||
|
||||
def set_note(self, msg:str = '') -> None:
|
||||
if not isinstance(msg, str): msg = str(msg)
|
||||
self._note_label.setText(msg)
|
||||
self._note_label.adjustSize()
|
||||
|
||||
def set_progress(self, percent:int = 0) -> None:
|
||||
if not isinstance(percent, int): percent = 0
|
||||
elif percent < 0: percent = 0
|
||||
elif percent > 100: percent = 100
|
||||
self._progress_bar.setValue(percent)
|
||||
|
||||
|
||||
def _build_status_bar(self) -> None:
|
||||
# Создание пользовательского виджета для StatusBar
|
||||
note_layout = QHBoxLayout()
|
||||
note_layout.setContentsMargins(10, 1, 10, 1)
|
||||
note_layout.setSpacing(15) # Устанавливаем расстояние между элементами
|
||||
|
||||
# Создание QLabel и QProgressBar
|
||||
self._mode_label = QLabel()
|
||||
self._note_label = QLabel()
|
||||
self._progress_bar = QProgressBar()
|
||||
self._progress_bar.setRange(0, 100)
|
||||
self._progress_bar.setValue(0)
|
||||
self._progress_bar.setMinimumWidth(250)
|
||||
self._progress_bar.setMaximumHeight(10)
|
||||
self._progress_bar.setTextVisible(False)
|
||||
|
||||
# Установка политики размеров
|
||||
self._mode_label.setSizePolicy(QSP.Policy.Preferred, QSP.Policy.Preferred)
|
||||
self._note_label.setSizePolicy(QSP.Policy.MinimumExpanding, QSP.Policy.Preferred)
|
||||
self._progress_bar.setSizePolicy(QSP.Policy.Fixed, QSP.Policy.Preferred)
|
||||
|
||||
# Добавление виджетов в макет
|
||||
note_layout.addWidget(self._progress_bar)
|
||||
note_layout.addWidget(self._note_label)
|
||||
note_layout.addStretch(1)
|
||||
note_layout.addWidget(self._mode_label)
|
||||
|
||||
self.setLayout(note_layout)
|
||||
self.setSizePolicy(QSP.Policy.Expanding, QSP.Policy.Preferred)
|
||||
|
||||
|
||||
class CustomTabWidget(QTabWidget):
|
||||
|
||||
def __init__ (self):
|
||||
super().__init__()
|
||||
self._build_tab_widget()
|
||||
|
||||
def get_widget(self) -> QWidget:
|
||||
return self
|
||||
|
||||
def _build_tab_widget(self) -> None:
|
||||
self.setTabsClosable(True)
|
||||
self.tabCloseRequested.connect(self.close_tab)
|
||||
|
||||
#TODO: переписать обмен данными, засунуть ссылки куда-то еще
|
||||
def create_tab(self, plot_widget:QWidget) -> None:
|
||||
widget, reg_items, curve_items, qt_items = plot_widget
|
||||
tab = QWidget()
|
||||
tab.setProperty("reg_items", reg_items)
|
||||
tab.setProperty("curve_items", curve_items)
|
||||
tab.setProperty("qt_items", qt_items)
|
||||
grid = QGridLayout()
|
||||
grid.addWidget(widget)
|
||||
tab.setLayout(grid)
|
||||
self.addTab(tab, "SF_trace_" + dt.now().strftime('%Y_%m_%d-%H_%M_%S'))
|
||||
self.setCurrentWidget(tab)
|
||||
|
||||
def close_tab(self, index:int) -> None:
|
||||
if self.count() > index and index >= 0:
|
||||
self.removeTab(index)
|
||||
|
||||
|
||||
class CustomMenuBar(QMenuBar):
|
||||
|
||||
def __init__(self,
|
||||
operSettings:OperatorSettings,
|
||||
sysSettings:SystemSettings,
|
||||
repSettings:ReportSettings) -> None:
|
||||
super().__init__()
|
||||
self._operSettings = operSettings
|
||||
self._sysSettings = sysSettings
|
||||
self._repSettings = repSettings
|
||||
self._build_dock_widgets()
|
||||
self._build_menu()
|
||||
|
||||
def get_widget(self) -> QWidget:
|
||||
return self
|
||||
|
||||
def setup(self, parent:QMainWindow) -> None:
|
||||
parent.addDockWidget(Qt.RightDockWidgetArea, self._operator_dock)
|
||||
parent.addDockWidget(Qt.RightDockWidgetArea, self._system_dock)
|
||||
parent.addDockWidget(Qt.RightDockWidgetArea, self._report_dock)
|
||||
parent.setMenuBar(self)
|
||||
|
||||
def _build_dock_widgets(self) -> None:
|
||||
"""
|
||||
Инициализация док-виджетов для настроек.
|
||||
"""
|
||||
# Создаем док-виджет для OperatorSettings
|
||||
self._operator_dock = QDockWidget("Operator Settings", self)
|
||||
self._operator_dock.setWidget(self._operSettings)
|
||||
self._operator_dock.setObjectName("OperatorSettings")
|
||||
self._operator_dock.hide() # Скрываем по умолчанию
|
||||
|
||||
# Создаем док-виджет для SystemSettings
|
||||
self._system_dock = QDockWidget("System Settings", self)
|
||||
self._system_dock.setWidget(self._sysSettings)
|
||||
self._system_dock.setObjectName("SystemSettings")
|
||||
self._system_dock.hide() # Скрываем по умолчанию
|
||||
|
||||
# Создаем док-виджет для ReportSettings
|
||||
self._report_dock = QDockWidget("View settings", self)
|
||||
self._report_dock.setWidget(self._repSettings)
|
||||
self._report_dock.setObjectName("ReportSettings")
|
||||
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 _build_menu(self) -> None:
|
||||
"""
|
||||
Инициализация главного меню.
|
||||
"""
|
||||
# Создаем меню "Режимы"
|
||||
modes_menu = self.addMenu("Mode")
|
||||
settings_menu = self.addMenu("Settings")
|
||||
|
||||
# Создаем действия для меню
|
||||
self.seeking_action = QAction("Real time folder scanning", self)
|
||||
self.raport_action = QAction("Raport editor", self)
|
||||
|
||||
system_settings = 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 = QAction("Operator settings ", self)
|
||||
operator_settings.setIcon(QIcon('resources/operator_ico.png'))
|
||||
operator_settings.triggered.connect(lambda: self._toggle_visibility(self._operator_dock))
|
||||
|
||||
self.view_settings = QAction("View settings", self)
|
||||
self.view_settings.setIcon(QIcon('resources/view_ico.png'))
|
||||
self.view_settings.triggered.connect(lambda: self._toggle_visibility(self._report_dock))
|
||||
|
||||
# Добавляем действия в меню "Режимы"
|
||||
modes_menu.addAction(self.seeking_action)
|
||||
modes_menu.addAction(self.raport_action)
|
||||
|
||||
settings_menu.addAction(system_settings)
|
||||
settings_menu.addAction(operator_settings)
|
||||
settings_menu.addAction(self.view_settings)
|
||||
|
||||
def _toggle_visibility(self, body:QDockWidget = None) -> None:
|
||||
"""
|
||||
Переключение видимости док-виджета.
|
||||
"""
|
||||
if body:
|
||||
is_visible = body.isVisible()
|
||||
body.setVisible(not is_visible)
|
||||
|
||||
@staticmethod
|
||||
def _set_dock_features(body:QDockWidget = None) -> None:
|
||||
"""
|
||||
Настройка флагов док-виджета.
|
||||
"""
|
||||
if body:
|
||||
flag_move = QDockWidget.DockWidgetMovable
|
||||
flag_close = QDockWidget.DockWidgetClosable
|
||||
flag_floating = QDockWidget.DockWidgetFloatable
|
||||
body.setFeatures(flag_move | flag_close | flag_floating)
|
||||
|
||||
|
||||
class RaportWidget(QWidget):
|
||||
|
||||
def __init__(self, tabWidget:CustomTabWidget):
|
||||
super().__init__()
|
||||
self._tabWidget = tabWidget
|
||||
self._build_widget()
|
||||
|
||||
def get_widget(self) -> QWidget:
|
||||
return self
|
||||
|
||||
def _build_widget(self):
|
||||
main_layout = QVBoxLayout()
|
||||
self.save_screen_btn = QPushButton("Save state")
|
||||
self.save_screen_btn.setFixedWidth(185)
|
||||
self.open_file_btn = QPushButton("Open file")
|
||||
self.open_file_btn.setFixedWidth(185)
|
||||
|
||||
button_layout = QHBoxLayout()
|
||||
button_layout.setSpacing(2)
|
||||
|
||||
button_layout.addWidget(self.save_screen_btn)
|
||||
button_layout.addWidget(self.open_file_btn)
|
||||
button_widget = QWidget()
|
||||
button_widget.setLayout(button_layout)
|
||||
|
||||
main_layout.addWidget(self._tabWidget)
|
||||
main_layout.addWidget(button_widget)
|
||||
self.setLayout(main_layout)
|
||||
|
||||
|
||||
class SeekingWidget(QWidget):
|
||||
|
||||
def __init__(self, tabWidget:CustomTabWidget):
|
||||
super().__init__()
|
||||
self._tabWidget = tabWidget
|
||||
self._build_widget()
|
||||
|
||||
def get_widget(self) -> QWidget:
|
||||
return self
|
||||
|
||||
def _build_widget(self):
|
||||
main_layout = QVBoxLayout()
|
||||
button_layout = QHBoxLayout()
|
||||
button_layout.setSpacing(2)
|
||||
button_widget = QWidget()
|
||||
button_widget.setLayout(button_layout)
|
||||
|
||||
main_layout.addWidget(self._tabWidget)
|
||||
main_layout.addWidget(button_widget)
|
||||
self.setLayout(main_layout)
|
||||
|
||||
|
||||
26
src/main.py
26
src/main.py
@ -3,8 +3,8 @@ import sys
|
||||
import pyqtgraph as pg
|
||||
from PyQt5 import QtWidgets
|
||||
|
||||
from gui.mainGui import MainWindow
|
||||
from src.controller.file_manager import DirectoryMonitor
|
||||
from gui.main_gui import MainWindow
|
||||
from controller.file_manager import DirectoryMonitor, FileManager
|
||||
from controller.mediator import Mediator
|
||||
from controller.converter import DataConverter
|
||||
from gui.plotter import PlotWidget
|
||||
@ -17,22 +17,24 @@ def main():
|
||||
pg.setConfigOptions(useOpenGL=False, antialias=False)
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
monitor = DirectoryMonitor()
|
||||
file_manager = FileManager(monitor=monitor)
|
||||
data_converter = DataConverter()
|
||||
plot_widget_builder = PlotWidget()
|
||||
controller = Controller()
|
||||
controller = Controller(file_manager=file_manager)
|
||||
passport_former = PassportFormer()
|
||||
window = MainWindow(controller)
|
||||
mediator = Mediator(monitor, data_converter, passport_former, plot_widget_builder, controller)
|
||||
window = MainWindow()
|
||||
mediator = Mediator(data_converter, passport_former, plot_widget_builder, controller, file_manager)
|
||||
window.show()
|
||||
|
||||
window.signal_mode.connect(controller.set_working_mode)
|
||||
window.signal_settings.connect(controller.update_settings)
|
||||
window.signal_replot_all.connect(controller.update_plots)
|
||||
window.signal_open_file.connect(controller.open_file)
|
||||
window.signal_save_file.connect(controller.save_file)
|
||||
|
||||
controller.signal_widgets.connect(window.show_plot_tabs)
|
||||
controller.signal_progress_bar.connect(window.update_progressBar)
|
||||
controller.signal_status_text.connect(window.update_stateLabel)
|
||||
#controller.signal_settings.connect(mediator.update_settings)
|
||||
#controller.signal_open_file.connect(monitor.custom_csv_extract_only)
|
||||
#controller.signal_raport_mode.connect(monitor.start_raport)
|
||||
#controller.signal_seeking_mode.connect(monitor.start_seeking)
|
||||
#controller.signal_update_plots.connect(monitor.update_plots)
|
||||
controller.signal_progress_bar.connect(window.status_widget.set_progress)
|
||||
controller.signal_status_text.connect(window.status_widget.set_note)
|
||||
|
||||
sys.exit(app.exec_())
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user