feat: добавил отображение графиков

This commit is contained in:
Андрей Скирченко 2024-11-14 13:45:57 +03:00
parent ac8c128484
commit dba7e3c235
10 changed files with 240 additions and 102 deletions

View File

@ -1,4 +1,4 @@
{
"trace_storage_path": "/home/andrei/Desktop/bla",
"monitor_update_period": 0.1
"monitor_update_period": 100
}

148
src/base/base.py Normal file
View File

@ -0,0 +1,148 @@
from __future__ import annotations
import os
from typing import Optional, Union
import pandas as pd
from PyQt5.QtCore import QThread, QObject, QTimer
from PyQt5.QtWidgets import QWidget
class BaseMediator:
def __init__(self,
monitor: BaseDirectoryMonitor,
converter: BaseDataConverter,
plot: BasePlotWidget,
controller: BaseController):
self._monitor = monitor
self._monitor.mediator = self
self._converter = converter
self._converter.mediator = self
self._plot = plot
self._plot.mediator = self
self._controller = controller
def notify(self,
source: Union[BaseDirectoryMonitor, BaseDataConverter, BasePlotWidget],
data: Union[list[str], list[pd.DataFrame], list[QWidget]]):
...
class BaseDirectoryMonitor:
update_timer = QTimer()
def __init__(self,
directory_path: str,
update_time: int,
mediator: Optional[BaseMediator] = None):
super().__init__()
self._directory_path = directory_path
self._update_time = update_time
self._mediator = mediator
self._files: list[str] = []
self._init_state()
@property
def directory_path(self) -> str:
return self._directory_path
@property
def update_time(self) -> int:
return self._update_time
@property
def files(self) -> list[str]:
return self._files
@property
def mediator(self) -> BaseMediator:
return self._mediator
@mediator.setter
def mediator(self, mediator: BaseMediator) -> None:
self._mediator = mediator
def _init_state(self):
files = os.listdir(self._directory_path)
self._files = files
def start(self):
self.update_timer.start(self._update_time)
class BaseDataConverter:
def __init__(self, mediator: Optional[BaseMediator] = None):
self._mediator = mediator
@property
def mediator(self) -> BaseMediator:
return self._mediator
@mediator.setter
def mediator(self, mediator: BaseMediator) -> None:
self._mediator = mediator
def convert_data(self, files: list[str]) -> None:
...
class BasePlotWidget:
def __init__(self, mediator: Optional[BaseMediator] = None):
super().__init__()
self._mediator = mediator
# "Electrode Position": ["Rotor Position, mm ME", "Rotor Position, mm FE"],
# "Electrode Speed": ["Rotor Speed, mm/s ME", "Rotor Speed, mm/s FE"]
self._plot_channels = {
"Electrode Force": [
{
"name": "Electrode Force, N ME",
"pen": 'r'
},
{
"name":"Electrode Force, N FE",
"pen": 'w'
},
],
"Electrode Position": [
{
"name": "Rotor Position, mm ME",
"pen": 'r'
},
{
"name": "Rotor Position, mm FE",
"pen": 'w'
},
],
"Electrode Speed": [
{
"name": "Rotor Speed, mm/s ME",
"pen": 'r'
},
{
"name": "Rotor Speed, mm/s FE",
"pen": 'w'
},
]
}
@property
def mediator(self) -> BaseMediator:
return self._mediator
@mediator.setter
def mediator(self, mediator: BaseMediator) -> None:
self._mediator = mediator
def build(self, data: list[pd.DataFrame]) -> list[QWidget]:
...
class BaseController(QObject):
def send_widgets(self, widgets: list[QWidget]) -> None:
...

View File

@ -1,76 +0,0 @@
from __future__ import annotations
import os
from typing import Optional, Union
import pandas as pd
from PyQt5.QtCore import QThread
class BaseMediator:
def __init__(self,
monitor: BaseDirectoryMonitor,
converter: BaseDataConverter):
self._monitor = monitor
self._monitor.mediator = self
self._converter = converter
self._converter.mediator = self
def notify(self,
source: Union[BaseDirectoryMonitor, BaseDataConverter],
data: Union[list[str], list[pd.DataFrame]]):
...
class BaseDirectoryMonitor(QThread):
def __init__(self,
directory_path: str,
update_time: int,
mediator: Optional[BaseMediator] = None):
super().__init__()
self._directory_path = directory_path
self._update_time = update_time
self._mediator = mediator
self._files: list[str] = []
@property
def directory_path(self) -> str:
return self._directory_path
@property
def update_time(self) -> int:
return self._update_time
@property
def files(self) -> list[str]:
return self._files
@property
def mediator(self) -> BaseMediator:
return self._mediator
@mediator.setter
def mediator(self, mediator: BaseMediator) -> None:
self._mediator = mediator
def _init_state(self):
files = os.listdir(self._directory_path)
self._files = files
class BaseDataConverter:
def __init__(self, mediator: Optional[BaseMediator] = None):
self._mediator = mediator
@property
def mediator(self) -> BaseMediator:
return self._mediator
@mediator.setter
def mediator(self, mediator: BaseMediator) -> None:
self._mediator = mediator
def convert_data(self, files: list[str]) -> None:
...

View File

@ -0,0 +1,12 @@
from PyQt5.QtWidgets import QWidget
from PyQt5.QtCore import pyqtSignal
from base.base import BaseController
class Controller(BaseController):
signal_widgets = pyqtSignal(list)
def send_widgets(self, widgets: list[QWidget]) -> None:
self.signal_widgets.emit(widgets)

View File

@ -3,7 +3,7 @@ import pandas as pd
#FIXME: костыль для выключения предупреждения "replace deprecated". Потом надо поправить.
pd.set_option('future.no_silent_downcasting', True)
from controller.base import BaseDataConverter
from base.base import BaseDataConverter
class DataConverter(BaseDataConverter):

View File

@ -1,19 +1,21 @@
import pandas as pd
from typing import Union
from loguru import logger
from PyQt5.QtWidgets import QWidget
from controller.base import BaseMediator, BaseDirectoryMonitor, BaseDataConverter
from base.base import BaseMediator, BaseDirectoryMonitor, BaseDataConverter, BasePlotWidget
class Mediator(BaseMediator):
def notify(self,
source: Union[BaseDirectoryMonitor, BaseDataConverter],
data: Union[list[str], list[pd.DataFrame]]):
source: Union[BaseDirectoryMonitor, BaseDataConverter, BasePlotWidget],
data: Union[list[str], list[pd.DataFrame], list[QWidget]]):
if issubclass(source.__class__, BaseDirectoryMonitor):
self._converter.convert_data(data)
if issubclass(source.__class__, BaseDataConverter):
# TODO: отправить датафреймы в конструктор графиков
logger.info(data)
self._plot.build(data)
if issubclass(source.__class__, BasePlotWidget):
self._controller.send_widgets(data)

View File

@ -3,23 +3,26 @@ import os
from loguru import logger
from controller.base import BaseDirectoryMonitor
from base.base import BaseDirectoryMonitor
class DirectoryMonitor(BaseDirectoryMonitor):
def run(self):
self._init_state()
logger.info("Monitor initiated")
def _init_state(self):
files = os.listdir(self._directory_path)
self._files = files
self.update_timer.timeout.connect(self._monitor)
logger.info("Monitor initiated!")
def _monitor(self):
files = os.listdir(self._directory_path)
new_files = sorted(list(map(lambda x: os.path.join(self._directory_path, x),
filter(lambda x: x not in self._files, files))))
if new_files:
logger.info(f"New files detected: {new_files}")
self._mediator.notify(self, new_files)
self._files = files
if not files:
self._files = []
while True:
files = os.listdir(self._directory_path)
new_files = sorted(list(map(lambda x: os.path.join(self._directory_path, x),
filter(lambda x: x not in self._files, files))))
if new_files:
logger.info(f"New files detected: {new_files}")
self._mediator.notify(self, new_files)
self._files = files
if not files:
self._files = []
sleep(self._update_time)

View File

@ -6,4 +6,13 @@ from gui.widgets import mainForm
class MainWindow(QtWidgets.QWidget, mainForm.Ui_Form):
def __init__(self):
super().__init__()
self.setupUi(self)
self.setupUi(self)
def show_plot_tabs(self, plot_widgets: list[QtWidgets.QWidget]) -> None:
for plot_widget in plot_widgets:
tab = QtWidgets.QWidget()
grid = QtWidgets.QGridLayout()
grid.addWidget(plot_widget)
tab.setLayout(grid)
self.tabWidget.addTab(tab, "text")
self.tabWidget.setCurrentWidget(tab)

33
src/gui/widgets/plot.py Normal file
View File

@ -0,0 +1,33 @@
import pandas as pd
from PyQt5.QtWidgets import QWidget, QVBoxLayout
import pyqtgraph as pg
from base.base import BasePlotWidget
class PlotWidget(BasePlotWidget):
def _create_widget(self, dataframe: pd.DataFrame) -> QWidget:
widget = QWidget()
layout = QVBoxLayout()
time_axis = dataframe["time"]
for channel, signals in self._plot_channels.items():
plot_widget = pg.PlotWidget(title=channel)
plot_widget.showGrid(x=True, y=True)
legend = pg.LegendItem((80, 60), offset=(70, 20))
legend.setParentItem(plot_widget.graphicsItem())
for signal in signals:
plot = plot_widget.plot(time_axis, dataframe[signal["name"]], pen=signal["pen"])
legend.addItem(plot, signal["name"])
layout.addWidget(plot_widget)
widget.setLayout(layout)
return widget
def build(self, data: list[pd.DataFrame]) -> None:
widgets = [self._create_widget(data_sample) for data_sample in data]
self._mediator.notify(self, widgets)

View File

@ -8,6 +8,8 @@ from cfg.schema import ConfigSchema
from controller.monitor import DirectoryMonitor
from controller.mediator import Mediator
from controller.converter import DataConverter
from gui.widgets import plot
from controller.controller import Controller
def read_json(filepath: str) -> dict:
@ -24,12 +26,17 @@ def main():
config = ConfigSchema(**read_json("config/config.json"))
monitor = DirectoryMonitor(config.trace_storage_path, config.monitor_update_period)
data_converter = DataConverter()
mediator = Mediator(monitor, data_converter)
plot_widget_builder = plot.PlotWidget()
controller = Controller()
mediator = Mediator(monitor, data_converter, plot_widget_builder, controller)
monitor.start()
window = MainWindow()
window.show()
controller.signal_widgets.connect(window.show_plot_tabs)
sys.exit(app.exec_())