Compare commits
2 Commits
02268f1f7b
...
eaec12d3ff
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eaec12d3ff | ||
|
|
365118bc22 |
@ -81,19 +81,19 @@
|
|||||||
],
|
],
|
||||||
"distance_l_2": [
|
"distance_l_2": [
|
||||||
0.0275,
|
0.0275,
|
||||||
0.03,
|
0.0255,
|
||||||
0.033,
|
0.0242,
|
||||||
0.033,
|
0.0245,
|
||||||
0.033,
|
0.0228,
|
||||||
0.033,
|
0.0236,
|
||||||
0.033,
|
0.0229,
|
||||||
0.033,
|
0.0248,
|
||||||
0.033,
|
0.024,
|
||||||
0.033,
|
0.0235,
|
||||||
0.033,
|
0.025,
|
||||||
0.033,
|
0.0276,
|
||||||
0.033,
|
0.0234,
|
||||||
0.033
|
0.0215
|
||||||
],
|
],
|
||||||
"distance_h_end1": [
|
"distance_h_end1": [
|
||||||
0.003,
|
0.003,
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -17,4 +17,5 @@ class Controller(BaseController):
|
|||||||
self.signal_settings.emit(settings)
|
self.signal_settings.emit(settings)
|
||||||
|
|
||||||
def open_custom_file (self, filepath: str) -> None:
|
def open_custom_file (self, filepath: str) -> None:
|
||||||
self.signal_monitor.emit(filepath)
|
self.signal_monitor.emit(filepath)
|
||||||
|
|
||||||
Binary file not shown.
Binary file not shown.
BIN
src/gui/__pycache__/reportGui.cpython-310.pyc
Normal file
BIN
src/gui/__pycache__/reportGui.cpython-310.pyc
Normal file
Binary file not shown.
Binary file not shown.
@ -5,6 +5,7 @@ from PyQt5 import QtWidgets
|
|||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
from utils.base.base import BaseMainWindow, BaseController
|
from utils.base.base import BaseMainWindow, BaseController
|
||||||
from gui.settings_window import settingsWindow
|
from gui.settings_window import settingsWindow
|
||||||
|
from gui.reportGui import ReportSettings
|
||||||
|
|
||||||
class MainWindow(BaseMainWindow):
|
class MainWindow(BaseMainWindow):
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
@ -15,8 +16,10 @@ class MainWindow(BaseMainWindow):
|
|||||||
self.set_style(self)
|
self.set_style(self)
|
||||||
self.settings_button.clicked.connect(self._show_settings)
|
self.settings_button.clicked.connect(self._show_settings)
|
||||||
self.select_dir_button.clicked.connect(self._select_dir)
|
self.select_dir_button.clicked.connect(self._select_dir)
|
||||||
|
self.report_button.clicked.connect(self._report_window)
|
||||||
self.operSettings = settingsWindow("params/operator_params.json", 'Operator', self._updater_trigger)
|
self.operSettings = settingsWindow("params/operator_params.json", 'Operator', self._updater_trigger)
|
||||||
self.sysSettings = settingsWindow("params/system_params.json", 'System', self._updater_trigger)
|
self.sysSettings = settingsWindow("params/system_params.json", 'System', self._updater_trigger)
|
||||||
|
self.repSettings = ReportSettings()
|
||||||
|
|
||||||
def initUI(self) -> None:
|
def initUI(self) -> None:
|
||||||
self.tabWidget = QtWidgets.QTabWidget()
|
self.tabWidget = QtWidgets.QTabWidget()
|
||||||
@ -29,19 +32,30 @@ class MainWindow(BaseMainWindow):
|
|||||||
self.settings_button.setFixedWidth(160)
|
self.settings_button.setFixedWidth(160)
|
||||||
self.select_dir_button = QtWidgets.QPushButton("Open directory")
|
self.select_dir_button = QtWidgets.QPushButton("Open directory")
|
||||||
self.select_dir_button.setFixedWidth(175)
|
self.select_dir_button.setFixedWidth(175)
|
||||||
|
self.report_button = QtWidgets.QPushButton("Generate report")
|
||||||
|
self.report_button.setFixedWidth(175)
|
||||||
|
self.rep_settings_button = QtWidgets.QPushButton("Report settings")
|
||||||
|
self.rep_settings_button.setFixedWidth(175)
|
||||||
|
self.rep_settings_button.setVisible(False)
|
||||||
button_layout = QtWidgets.QHBoxLayout()
|
button_layout = QtWidgets.QHBoxLayout()
|
||||||
button_layout.setSpacing(2)
|
button_layout.setSpacing(2)
|
||||||
button_layout.addWidget(self.settings_button)
|
button_layout.addWidget(self.settings_button)
|
||||||
button_layout.addWidget(self.select_dir_button)
|
button_layout.addWidget(self.select_dir_button)
|
||||||
|
button_layout.addWidget(self.report_button)
|
||||||
|
button_layout.addWidget(self.rep_settings_button)
|
||||||
|
|
||||||
layout.addLayout(button_layout)
|
layout.addLayout(button_layout)
|
||||||
self.setLayout(layout)
|
self.setLayout(layout)
|
||||||
|
|
||||||
def show_plot_tabs(self, plot_widgets: list[QtWidgets.QWidget]) -> None:
|
def show_plot_tabs(self, plot_widgets: list[QtWidgets.QWidget]) -> None:
|
||||||
for plot_widget in plot_widgets:
|
for plot_widget in plot_widgets:
|
||||||
|
widget, reg_items, curve_items = plot_widget
|
||||||
|
|
||||||
tab = QtWidgets.QWidget()
|
tab = QtWidgets.QWidget()
|
||||||
|
tab.setProperty("reg_items", reg_items)
|
||||||
|
tab.setProperty("curve_items", curve_items)
|
||||||
grid = QtWidgets.QGridLayout()
|
grid = QtWidgets.QGridLayout()
|
||||||
grid.addWidget(plot_widget)
|
grid.addWidget(widget)
|
||||||
tab.setLayout(grid)
|
tab.setLayout(grid)
|
||||||
self.tabWidget.addTab(tab, "SF_trace_" + dt.now().strftime('%Y_%m_%d-%H_%M_%S'))
|
self.tabWidget.addTab(tab, "SF_trace_" + dt.now().strftime('%Y_%m_%d-%H_%M_%S'))
|
||||||
self.tabWidget.setCurrentWidget(tab)
|
self.tabWidget.setCurrentWidget(tab)
|
||||||
@ -51,7 +65,6 @@ class MainWindow(BaseMainWindow):
|
|||||||
for i in range(0, tab_count-2):
|
for i in range(0, tab_count-2):
|
||||||
self._close_tab(i)
|
self._close_tab(i)
|
||||||
|
|
||||||
|
|
||||||
def keyPressEvent(self, a0):
|
def keyPressEvent(self, a0):
|
||||||
if a0.key() == Qt.Key_F5:
|
if a0.key() == Qt.Key_F5:
|
||||||
pass
|
pass
|
||||||
@ -77,5 +90,22 @@ class MainWindow(BaseMainWindow):
|
|||||||
if folder_path:
|
if folder_path:
|
||||||
self._controller.open_custom_file(folder_path)
|
self._controller.open_custom_file(folder_path)
|
||||||
|
|
||||||
|
def _report_window(self):
|
||||||
|
tab = self.tabWidget.currentWidget()
|
||||||
|
reg_items = tab.property("reg_items")
|
||||||
|
curve_items = tab.property("curve_items")
|
||||||
|
print(curve_items)
|
||||||
|
print(reg_items)
|
||||||
|
|
||||||
|
self.rep_settings_button.setVisible(True)
|
||||||
|
self.rep_settings_button.clicked.connect(lambda:self.repSettings.build(reg_items, curve_items))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
import pandas as pd
|
import pandas as pd
|
||||||
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QGraphicsRectItem
|
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QGraphicsRectItem
|
||||||
import copy
|
import copy
|
||||||
|
|
||||||
|
|
||||||
import pyqtgraph as pg
|
import pyqtgraph as pg
|
||||||
from typing import Optional, Any
|
from typing import Optional, Any
|
||||||
|
|
||||||
@ -14,56 +12,6 @@ class ProcessStage():
|
|||||||
finish_index:int
|
finish_index:int
|
||||||
|
|
||||||
class PlotWidget(BasePlotWidget):
|
class PlotWidget(BasePlotWidget):
|
||||||
|
|
||||||
def _create_navigator(self,
|
|
||||||
time_region:tuple[float, float],
|
|
||||||
main_plot: pg.PlotWidget,
|
|
||||||
dataframe: pd.DataFrame,
|
|
||||||
real_signals: list[dict[str, Any]]) -> list[pg.PlotWidget, pg.LinearRegionItem]:
|
|
||||||
"""
|
|
||||||
Создаёт график-навигатор, отображающий все данные в уменьшенном масштабе.
|
|
||||||
"""
|
|
||||||
navigator = pg.PlotWidget(title="Navigator")
|
|
||||||
navigator.setFixedHeight(100)
|
|
||||||
|
|
||||||
for signal in real_signals:
|
|
||||||
if signal["name"] in dataframe.columns:
|
|
||||||
x = dataframe["time"]
|
|
||||||
y = dataframe[signal["name"]]
|
|
||||||
|
|
||||||
x_downsampled, y_downsampled = self._downsample_data(x, y, max_points=1000)
|
|
||||||
navigator.plot(x_downsampled, y_downsampled, pen=signal["pen"], name=signal["name"])
|
|
||||||
|
|
||||||
ROI_region = pg.LinearRegionItem(values=time_region, movable=True, brush=pg.mkBrush(0, 0, 255, 100))
|
|
||||||
navigator.addItem(ROI_region)
|
|
||||||
|
|
||||||
# Связываем изменение региона навигатора с обновлением области просмотра основного графика
|
|
||||||
ROI_region.sigRegionChanged.connect(lambda: self._sync_main_plot_with_navigator(main_plot, ROI_region))
|
|
||||||
|
|
||||||
return navigator, ROI_region
|
|
||||||
|
|
||||||
def _downsample_data(self, x, y, max_points=5000):
|
|
||||||
"""
|
|
||||||
Понижает разрешение данных до заданного количества точек для улучшения производительности навигатора.
|
|
||||||
"""
|
|
||||||
if len(x) > max_points:
|
|
||||||
factor = len(x) // max_points
|
|
||||||
x_downsampled = x[::factor]
|
|
||||||
y_downsampled = y[::factor]
|
|
||||||
return x_downsampled, y_downsampled
|
|
||||||
return x, y
|
|
||||||
|
|
||||||
def _sync_main_plot_with_navigator(self,
|
|
||||||
main_plot: pg.PlotWidget,
|
|
||||||
region: pg.LinearRegionItem):
|
|
||||||
"""
|
|
||||||
Синхронизирует область просмотра основного графика с регионом навигатора.
|
|
||||||
"""
|
|
||||||
x_min, x_max = region.getRegion()
|
|
||||||
if main_plot:
|
|
||||||
main_plot.blockSignals(True)
|
|
||||||
main_plot.setXRange(x_min, x_max, padding=0)
|
|
||||||
main_plot.blockSignals(False)
|
|
||||||
|
|
||||||
def _create_curve_ideal(self,
|
def _create_curve_ideal(self,
|
||||||
signal: dict[str, Any],
|
signal: dict[str, Any],
|
||||||
@ -111,6 +59,7 @@ class PlotWidget(BasePlotWidget):
|
|||||||
plot_widget: pg.PlotWidget,
|
plot_widget: pg.PlotWidget,
|
||||||
point_events: dict[str, list[float]],
|
point_events: dict[str, list[float]],
|
||||||
dataframe_headers: list[str],
|
dataframe_headers: list[str],
|
||||||
|
reg_items: dict,
|
||||||
transparency: int = 75) -> None:
|
transparency: int = 75) -> None:
|
||||||
"""
|
"""
|
||||||
Добавляет регионы для реальных этапов, если все стадии есть в заголовках датафрейма.
|
Добавляет регионы для реальных этапов, если все стадии есть в заголовках датафрейма.
|
||||||
@ -123,11 +72,14 @@ class PlotWidget(BasePlotWidget):
|
|||||||
if region is not None:
|
if region is not None:
|
||||||
region.setZValue(-20)
|
region.setZValue(-20)
|
||||||
plot_widget.addItem(region)
|
plot_widget.addItem(region)
|
||||||
|
reg_items["real"].setdefault(stage, [])
|
||||||
|
reg_items["real"][stage].append(region)
|
||||||
|
|
||||||
def _add_ideal_stage_regions(self,
|
def _add_ideal_stage_regions(self,
|
||||||
plot_widget: pg.PlotWidget,
|
plot_widget: pg.PlotWidget,
|
||||||
ideal_data: dict[str, Any],
|
ideal_data: dict[str, Any],
|
||||||
point_events: dict[str, list[float]],
|
point_events: dict[str, list[float]],
|
||||||
|
reg_items: dict,
|
||||||
transparency: int = 125) -> None:
|
transparency: int = 125) -> None:
|
||||||
"""
|
"""
|
||||||
Добавляет регионы для идеальных этапов.
|
Добавляет регионы для идеальных этапов.
|
||||||
@ -141,12 +93,15 @@ class PlotWidget(BasePlotWidget):
|
|||||||
if region:
|
if region:
|
||||||
region.setZValue(-10)
|
region.setZValue(-10)
|
||||||
plot_widget.addItem(region)
|
plot_widget.addItem(region)
|
||||||
|
reg_items["ideal"].setdefault(stage, [])
|
||||||
|
reg_items["ideal"][stage].append(region)
|
||||||
|
|
||||||
def _add_ideal_signals(self,
|
def _add_ideal_signals(self,
|
||||||
plot_widget: pg.PlotWidget,
|
plot_widget: pg.PlotWidget,
|
||||||
ideal_data: dict[str, Any],
|
ideal_data: dict[str, Any],
|
||||||
point_events: dict[str, list[float]],
|
point_events: dict[str, list[float]],
|
||||||
ideal_signals: list[dict[str, Any]]) -> None:
|
ideal_signals: list[dict[str, Any]],
|
||||||
|
curve_items: dict) -> None:
|
||||||
"""
|
"""
|
||||||
Добавляет идеальные сигналы для каждого этапа.
|
Добавляет идеальные сигналы для каждого этапа.
|
||||||
"""
|
"""
|
||||||
@ -161,12 +116,15 @@ class PlotWidget(BasePlotWidget):
|
|||||||
if curve:
|
if curve:
|
||||||
curve.setZValue(10)
|
curve.setZValue(10)
|
||||||
plot_widget.addItem(curve)
|
plot_widget.addItem(curve)
|
||||||
|
curve_items["ideal"].setdefault(signal["name"], {})
|
||||||
|
curve_items["ideal"][signal["name"]][stage] = curve
|
||||||
|
|
||||||
def _add_real_signals(self,
|
def _add_real_signals(self,
|
||||||
plot_widget: pg.PlotWidget,
|
plot_widget: pg.PlotWidget,
|
||||||
dataframe: pd.DataFrame,
|
dataframe: pd.DataFrame,
|
||||||
real_signals: list[dict[str, Any]],
|
real_signals: list[dict[str, Any]],
|
||||||
legend: pg.LegendItem) -> None:
|
legend: pg.LegendItem,
|
||||||
|
curve_items: dict) -> None:
|
||||||
"""
|
"""
|
||||||
Добавляет реальные сигналы из dataframe на виджет.
|
Добавляет реальные сигналы из dataframe на виджет.
|
||||||
"""
|
"""
|
||||||
@ -176,6 +134,8 @@ class PlotWidget(BasePlotWidget):
|
|||||||
plot = plot_widget.plot(dataframe["time"], dataframe[signal["name"]], pen=signal["pen"], fast=True)
|
plot = plot_widget.plot(dataframe["time"], dataframe[signal["name"]], pen=signal["pen"], fast=True)
|
||||||
plot.setZValue(0)
|
plot.setZValue(0)
|
||||||
legend.addItem(plot, signal["name"])
|
legend.addItem(plot, signal["name"])
|
||||||
|
curve_items["real"].setdefault(signal["name"], {})
|
||||||
|
curve_items["real"][signal["name"]] = plot
|
||||||
|
|
||||||
def _add_performance_label(self,
|
def _add_performance_label(self,
|
||||||
layout: QVBoxLayout,
|
layout: QVBoxLayout,
|
||||||
@ -197,29 +157,6 @@ class PlotWidget(BasePlotWidget):
|
|||||||
layout.addWidget(performance_label)
|
layout.addWidget(performance_label)
|
||||||
performance_label.update()
|
performance_label.update()
|
||||||
|
|
||||||
def _mirror_shift_data(self,
|
|
||||||
valid_str: str,
|
|
||||||
signals: list[dict],
|
|
||||||
dataframe: pd.DataFrame,
|
|
||||||
shift: float) -> pd.DataFrame:
|
|
||||||
keys = dataframe.keys()
|
|
||||||
for signal in signals:
|
|
||||||
if valid_str in signal["name"] and signal["name"] in keys:
|
|
||||||
dataframe[signal["name"]] = dataframe[signal["name"]].apply(lambda x: shift-x)
|
|
||||||
return dataframe
|
|
||||||
|
|
||||||
def _shift_data(self,
|
|
||||||
valid_str: str,
|
|
||||||
signals: list[dict],
|
|
||||||
dataframe: pd.DataFrame,
|
|
||||||
shift: float) -> pd.DataFrame:
|
|
||||||
keys = dataframe.keys()
|
|
||||||
for signal in signals:
|
|
||||||
if valid_str in signal["name"] and signal["name"] in keys:
|
|
||||||
dataframe[signal["name"]] = dataframe[signal["name"]].apply(lambda x: x + shift)
|
|
||||||
return dataframe
|
|
||||||
|
|
||||||
|
|
||||||
def _build_widget(self, data: list[Any]) -> QWidget:
|
def _build_widget(self, data: list[Any]) -> QWidget:
|
||||||
"""
|
"""
|
||||||
Собирает графический виджет для одного набора данных.
|
Собирает графический виджет для одного набора данных.
|
||||||
@ -227,6 +164,8 @@ class PlotWidget(BasePlotWidget):
|
|||||||
"""
|
"""
|
||||||
widget = QWidget()
|
widget = QWidget()
|
||||||
layout = QVBoxLayout(widget)
|
layout = QVBoxLayout(widget)
|
||||||
|
reg_items = {"real":{}, "ideal":{}}
|
||||||
|
curve_items = {"real":{}, "ideal":{}}
|
||||||
|
|
||||||
dataframe, points_pocket, useful_data = data
|
dataframe, points_pocket, useful_data = data
|
||||||
tesla_time = useful_data["tesla_time"]
|
tesla_time = useful_data["tesla_time"]
|
||||||
@ -267,7 +206,7 @@ class PlotWidget(BasePlotWidget):
|
|||||||
|
|
||||||
# Добавляем реальные стадии
|
# Добавляем реальные стадии
|
||||||
if settings["stages"]:
|
if settings["stages"]:
|
||||||
self._add_stage_regions(plot_widget, point_events, dataframe_headers, 75)
|
self._add_stage_regions(plot_widget, point_events, dataframe_headers, reg_items, 75)
|
||||||
|
|
||||||
# TODO: подобрать не вырвеглазные цвета, возможно ограничить зону
|
# TODO: подобрать не вырвеглазные цвета, возможно ограничить зону
|
||||||
if settings["workpiece"]:
|
if settings["workpiece"]:
|
||||||
@ -284,8 +223,8 @@ class PlotWidget(BasePlotWidget):
|
|||||||
|
|
||||||
# Добавляем идеальные стадии и идеальные сигналы
|
# Добавляем идеальные стадии и идеальные сигналы
|
||||||
if settings["ideals"]:
|
if settings["ideals"]:
|
||||||
self._add_ideal_stage_regions(plot_widget, ideal_data, point_events, 100)
|
self._add_ideal_stage_regions(plot_widget, ideal_data, point_events, reg_items, 100)
|
||||||
self._add_ideal_signals(plot_widget, ideal_data, point_events, description["Ideal_signals"])
|
self._add_ideal_signals(plot_widget, ideal_data, point_events, description["Ideal_signals"], curve_items)
|
||||||
|
|
||||||
# Подсчёт производительности
|
# Подсчёт производительности
|
||||||
if settings["performance"]:
|
if settings["performance"]:
|
||||||
@ -304,7 +243,7 @@ class PlotWidget(BasePlotWidget):
|
|||||||
worst_timeframe = point_timeframe
|
worst_timeframe = point_timeframe
|
||||||
|
|
||||||
# Добавляем реальные сигналы
|
# Добавляем реальные сигналы
|
||||||
self._add_real_signals(plot_widget, dataframe, description["Real_signals"], legend)
|
self._add_real_signals(plot_widget, dataframe, description["Real_signals"], legend, curve_items)
|
||||||
if widget_num == 0:
|
if widget_num == 0:
|
||||||
main_plot = plot_widget
|
main_plot = plot_widget
|
||||||
else:
|
else:
|
||||||
@ -323,17 +262,7 @@ class PlotWidget(BasePlotWidget):
|
|||||||
main_plot.sigXRangeChanged.connect(lambda _, plot=main_plot, region=ROI_region: self._sync_navigator_with_main(main_plot=plot, region=region))
|
main_plot.sigXRangeChanged.connect(lambda _, plot=main_plot, region=ROI_region: self._sync_navigator_with_main(main_plot=plot, region=region))
|
||||||
|
|
||||||
widget.setLayout(layout)
|
widget.setLayout(layout)
|
||||||
return widget
|
return widget, reg_items, curve_items
|
||||||
|
|
||||||
def _sync_navigator_with_main(self, main_plot: pg.PlotWidget, region:pg.LinearRegionItem):
|
|
||||||
"""
|
|
||||||
Синхронизирует регион навигатора с областью просмотра основного графика.
|
|
||||||
"""
|
|
||||||
if region:
|
|
||||||
x_min, x_max = main_plot
|
|
||||||
region.blockSignals(True) # Предотвращаем рекурсию
|
|
||||||
region.setRegion([x_min, x_max])
|
|
||||||
region.blockSignals(False)
|
|
||||||
|
|
||||||
def build(self, data: list[list[Any]]) -> None:
|
def build(self, data: list[list[Any]]) -> None:
|
||||||
"""
|
"""
|
||||||
@ -345,7 +274,8 @@ class PlotWidget(BasePlotWidget):
|
|||||||
...
|
...
|
||||||
]
|
]
|
||||||
"""
|
"""
|
||||||
widgets = [self._build_widget(data_sample) for data_sample in data]
|
widgets_datapack = [self._build_widget(data_sample) for data_sample in data]
|
||||||
self._mediator.notify(self, widgets)
|
self._mediator.notify(self, widgets_datapack)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
77
src/gui/reportGui.py
Normal file
77
src/gui/reportGui.py
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import pyqtgraph as pg
|
||||||
|
from pyqtgraph.parametertree import Parameter, ParameterTree
|
||||||
|
from typing import Union
|
||||||
|
from PyQt5 import QtWidgets
|
||||||
|
from PyQt5.QtCore import Qt
|
||||||
|
|
||||||
|
|
||||||
|
class ReportSettings(QtWidgets.QWidget):
|
||||||
|
|
||||||
|
def build(self, reg_items: dict, curve_items: dict) -> None:
|
||||||
|
param_tree = ParameterTree()
|
||||||
|
layout = QtWidgets.QVBoxLayout()
|
||||||
|
layout.addWidget(param_tree)
|
||||||
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
body= [
|
||||||
|
self._generate_reg_params(reg_items),
|
||||||
|
self._generate_curve_params(curve_items)
|
||||||
|
]
|
||||||
|
# Добавляем параметры в дерево
|
||||||
|
params = Parameter.create(name='params', type='group', children=body)
|
||||||
|
param_tree.setParameters(params, showTop=False)
|
||||||
|
self.show()
|
||||||
|
|
||||||
|
def _generate_reg_params(self, reg_items: dict) -> dict:
|
||||||
|
|
||||||
|
res = {'name': 'Sectors', 'type': 'group', 'children': [
|
||||||
|
{'name': 'Real sectors', 'type': 'group', 'children': self._create_samples(reg_items["real"])},
|
||||||
|
{'name': 'Ideal sectors', 'type': 'group', 'children': self._create_samples(reg_items["ideal"])},
|
||||||
|
]}
|
||||||
|
return res
|
||||||
|
|
||||||
|
def _generate_curve_params(self, curve_items: dict) -> dict:
|
||||||
|
|
||||||
|
res = {'name': 'Plots', 'type': 'group', 'children': [
|
||||||
|
{'name': 'Real plots', 'type': 'group', 'children': self._create_samples(curve_items["real"])},
|
||||||
|
{'name': 'Ideal plots', 'type': 'group', 'children': self._create_ideal_curves(curve_items["ideal"])},
|
||||||
|
]}
|
||||||
|
return res
|
||||||
|
|
||||||
|
def _create_ideal_curves(self, curve: dict) -> list[dict]:
|
||||||
|
"""Создаем секторы с этапами циклограммы"""
|
||||||
|
res = []
|
||||||
|
for key, item in curve.items():
|
||||||
|
param = {'name': key, 'type': 'group', 'children': self._create_samples(item)}
|
||||||
|
res.append(param)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def _create_samples(self, sector: dict) -> list[dict]:
|
||||||
|
res = []
|
||||||
|
for key, item in sector.items():
|
||||||
|
sample = item[0] if type(item) == list else item
|
||||||
|
param = {'name': key, 'type': 'group', 'children': self._create_settings(sample)}
|
||||||
|
res.append(param)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def _create_settings(self, item: Union[pg.LinearRegionItem, pg.PlotDataItem]) -> list[dict]:
|
||||||
|
"""Настройки для элемента"""
|
||||||
|
if type(item) == pg.LinearRegionItem:
|
||||||
|
pen = item.lines[0].pen
|
||||||
|
brush = item.brush
|
||||||
|
fill_color = brush.color().getRgb()
|
||||||
|
else:
|
||||||
|
pen = pg.mkPen(item.opts.get("pen"))
|
||||||
|
fill_color = None
|
||||||
|
line_color = pen.color().getRgb()
|
||||||
|
line_thickness = pen.width()
|
||||||
|
visibility = item.isVisible()
|
||||||
|
|
||||||
|
return [
|
||||||
|
{'name': 'Line color', 'type': 'color', 'value': line_color},
|
||||||
|
{'name': 'Line thickness', 'type': 'int', 'value': line_thickness, 'limits': (1, 10)},
|
||||||
|
{'name': 'Visibility', 'type': 'bool', 'value': visibility},
|
||||||
|
{'name': 'Fill color', 'type': 'color', 'value': fill_color},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
Binary file not shown.
@ -9,10 +9,8 @@ from PyQt5.QtCore import QThread, QObject, QTimer
|
|||||||
from PyQt5.QtWidgets import QWidget, QTabWidget
|
from PyQt5.QtWidgets import QWidget, QTabWidget
|
||||||
from PyQt5.QtOpenGL import QGLWidget
|
from PyQt5.QtOpenGL import QGLWidget
|
||||||
from OptAlgorithm import OptAlgorithm
|
from OptAlgorithm import OptAlgorithm
|
||||||
import pandas as pd
|
|
||||||
import pandas as pd
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
import pyqtgraph as pg
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -125,7 +123,7 @@ class BasePlotWidget:
|
|||||||
"zoom": False,
|
"zoom": False,
|
||||||
"stages": True,
|
"stages": True,
|
||||||
"performance": True,
|
"performance": True,
|
||||||
"ideals": True,
|
"ideals": False,
|
||||||
"mirror ME": False,
|
"mirror ME": False,
|
||||||
"workpiece": False,
|
"workpiece": False,
|
||||||
"force compensation FE": False
|
"force compensation FE": False
|
||||||
@ -156,7 +154,7 @@ class BasePlotWidget:
|
|||||||
"zoom": False,
|
"zoom": False,
|
||||||
"stages": True,
|
"stages": True,
|
||||||
"performance": False,
|
"performance": False,
|
||||||
"ideals": True,
|
"ideals": False,
|
||||||
"mirror ME": True,
|
"mirror ME": True,
|
||||||
"workpiece": True,
|
"workpiece": True,
|
||||||
"force compensation FE": True
|
"force compensation FE": True
|
||||||
@ -227,6 +225,88 @@ class BasePlotWidget:
|
|||||||
font-family: "Segoe UI", sans-serif;
|
font-family: "Segoe UI", sans-serif;
|
||||||
}""")
|
}""")
|
||||||
|
|
||||||
|
def _downsample_data(self, x, y, max_points=5000):
|
||||||
|
"""
|
||||||
|
Понижает разрешение данных до заданного количества точек для улучшения производительности навигатора.
|
||||||
|
"""
|
||||||
|
if len(x) > max_points:
|
||||||
|
factor = len(x) // max_points
|
||||||
|
x_downsampled = x[::factor]
|
||||||
|
y_downsampled = y[::factor]
|
||||||
|
return x_downsampled, y_downsampled
|
||||||
|
return x, y
|
||||||
|
|
||||||
|
def _create_navigator(self,
|
||||||
|
time_region:tuple[float, float],
|
||||||
|
main_plot: pg.PlotWidget,
|
||||||
|
dataframe: pd.DataFrame,
|
||||||
|
real_signals: list[dict[str, Any]]) -> list[pg.PlotWidget, pg.LinearRegionItem]:
|
||||||
|
"""
|
||||||
|
Создаёт график-навигатор, отображающий все данные в уменьшенном масштабе.
|
||||||
|
"""
|
||||||
|
navigator = pg.PlotWidget(title="Navigator")
|
||||||
|
navigator.setFixedHeight(100)
|
||||||
|
|
||||||
|
for signal in real_signals:
|
||||||
|
if signal["name"] in dataframe.columns:
|
||||||
|
x = dataframe["time"]
|
||||||
|
y = dataframe[signal["name"]]
|
||||||
|
|
||||||
|
x_downsampled, y_downsampled = self._downsample_data(x, y, max_points=1000)
|
||||||
|
navigator.plot(x_downsampled, y_downsampled, pen=signal["pen"], name=signal["name"])
|
||||||
|
|
||||||
|
ROI_region = pg.LinearRegionItem(values=time_region, movable=True, brush=pg.mkBrush(0, 0, 255, 100))
|
||||||
|
navigator.addItem(ROI_region)
|
||||||
|
|
||||||
|
# Связываем изменение региона навигатора с обновлением области просмотра основного графика
|
||||||
|
ROI_region.sigRegionChanged.connect(lambda: self._sync_main_plot_with_navigator(main_plot, ROI_region))
|
||||||
|
|
||||||
|
return navigator, ROI_region
|
||||||
|
|
||||||
|
def _sync_main_plot_with_navigator(self,
|
||||||
|
main_plot: pg.PlotWidget,
|
||||||
|
region: pg.LinearRegionItem):
|
||||||
|
"""
|
||||||
|
Синхронизирует область просмотра основного графика с регионом навигатора.
|
||||||
|
"""
|
||||||
|
x_min, x_max = region.getRegion()
|
||||||
|
if main_plot:
|
||||||
|
main_plot.blockSignals(True)
|
||||||
|
main_plot.setXRange(x_min, x_max, padding=0)
|
||||||
|
main_plot.blockSignals(False)
|
||||||
|
|
||||||
|
def _mirror_shift_data(self,
|
||||||
|
valid_str: str,
|
||||||
|
signals: list[dict],
|
||||||
|
dataframe: pd.DataFrame,
|
||||||
|
shift: float) -> pd.DataFrame:
|
||||||
|
keys = dataframe.keys()
|
||||||
|
for signal in signals:
|
||||||
|
if valid_str in signal["name"] and signal["name"] in keys:
|
||||||
|
dataframe[signal["name"]] = dataframe[signal["name"]].apply(lambda x: shift-x)
|
||||||
|
return dataframe
|
||||||
|
|
||||||
|
def _shift_data(self,
|
||||||
|
valid_str: str,
|
||||||
|
signals: list[dict],
|
||||||
|
dataframe: pd.DataFrame,
|
||||||
|
shift: float) -> pd.DataFrame:
|
||||||
|
keys = dataframe.keys()
|
||||||
|
for signal in signals:
|
||||||
|
if valid_str in signal["name"] and signal["name"] in keys:
|
||||||
|
dataframe[signal["name"]] = dataframe[signal["name"]].apply(lambda x: x + shift)
|
||||||
|
return dataframe
|
||||||
|
|
||||||
|
def _sync_navigator_with_main(self, main_plot: pg.PlotWidget, region:pg.LinearRegionItem):
|
||||||
|
"""
|
||||||
|
Синхронизирует регион навигатора с областью просмотра основного графика.
|
||||||
|
"""
|
||||||
|
if region:
|
||||||
|
x_min, x_max = main_plot
|
||||||
|
region.blockSignals(True) # Предотвращаем рекурсию
|
||||||
|
region.setRegion([x_min, x_max])
|
||||||
|
region.blockSignals(False)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def mediator(self) -> BaseMediator:
|
def mediator(self) -> BaseMediator:
|
||||||
return self._mediator
|
return self._mediator
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user