From 72b002dea3723233a7c23a804a45436c03b7eefa Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 25 Nov 2024 17:20:00 +0300 Subject: [PATCH] =?UTF-8?q?dev:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D0=BD=D0=B0=D1=81=D1=82=D1=80=D0=BE=D0=B9?= =?UTF-8?q?=D0=BA=D0=B8,=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=BE=20=D0=B0=D0=B2=D1=82=D0=BE=D0=BC=D0=B0=D1=82=D0=B8?= =?UTF-8?q?=D1=87=D0=B5=D1=81=D0=BA=D0=BE=D0=B5=20=D0=BE=D0=B1=D0=BD=D0=BE?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B3=D1=80=D0=B0=D1=84?= =?UTF-8?q?=D0=B8=D0=BA=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- params/system_params.json | 6 +- src/__pycache__/main.cpython-310.pyc | Bin 1391 -> 1113 bytes .../__pycache__/controller.cpython-310.pyc | Bin 698 -> 698 bytes .../__pycache__/mediator.cpython-310.pyc | Bin 993 -> 1227 bytes .../__pycache__/monitor.cpython-310.pyc | Bin 1378 -> 2132 bytes src/controller/ideal_data_builder.py | 8 - src/controller/mediator.py | 7 + src/controller/monitor.py | 23 +++ src/gui/__pycache__/mainGui.cpython-310.pyc | Bin 1560 -> 2498 bytes src/gui/__pycache__/plotter.cpython-310.pyc | Bin 5388 -> 7176 bytes .../settings_window.cpython-310.pyc | Bin 2511 -> 2526 bytes src/gui/mainGui.py | 25 ++- src/gui/plotter.py | 38 ++++- src/gui/settings_window.py | 6 +- src/main.py | 13 +- .../base/__pycache__/base.cpython-310.pyc | Bin 7743 -> 11086 bytes .../__pycache__/base_widgets.cpython-310.pyc | Bin 2066 -> 2677 bytes src/utils/base/base.py | 144 +++++++++++++----- src/utils/base/base_widgets.py | 47 ------ 19 files changed, 204 insertions(+), 113 deletions(-) delete mode 100644 src/controller/ideal_data_builder.py delete mode 100644 src/utils/base/base_widgets.py diff --git a/params/system_params.json b/params/system_params.json index 58e2c7e..d0de10c 100644 --- a/params/system_params.json +++ b/params/system_params.json @@ -17,9 +17,5 @@ "position_start_2": 0.08, "k_prop": 0.05, "time_capture": 100000, - "UML_time_scaler": 1000, - "Closure_signal": "Closing", - "Squeeze_signal": "Squeeze", - "Welding_signal": "Welding", - "Release_signal": "Relief" + "UML_time_scaler": 1000 } diff --git a/src/__pycache__/main.cpython-310.pyc b/src/__pycache__/main.cpython-310.pyc index 5c5e522fd647df44c95dfc89c5c4f4b997cc2a98..e3bc4809541b8c0ce5404d3dce6c972519110706 100644 GIT binary patch delta 443 zcmYL_Jx{|h5QcrRle!;CKU6A#5EWwu>c9XJDlBvZv2@E4RU|42q^TTNEU7>$69cI7 z#(>h1KLi4P1V4ZQ&J5tw`<%|F^Lz4V>5V&yZI=k1=%zoqrweBt9v53T?Xq9FC2liV z@$Cyk90$!g0pi2pR~={Z;;KIEUKeoELLH&f2q8%h(un91W<;_%nSnHshP04W7LZJG zq$!ITWJX3amYH*NW@TnxTA~T&@^5BQCATv(a+Ux@8hJG%d2LB_<&y#Fz-b5S>dt6y zn`kWbDuQbiHJFB8&9x`f^xR7WDZ}w7RjGx zG7-;2IO>V^cB=S5+Co&o`imNH4993tQNz|d_q=!*jyfgM7`B}>Nz*oMvvp80X@CY&HK|;87#v_ngf#8uOJrtiq~xP=TqY)0MJiW> zO8!85LE4cU{{V@9K>q|cuJAexsVuMF$9~=?_WR)<-cLWsTP+{Kmpwl||79oh~ZuJqM-A8jWwUjeQQ(L(jYXZEc zJYD0p4|wXRIw-F4wWV$CXm@T;J#8%;E2vC0L96TfvNbbQyQ5&)*7Z?mg+V0Cu13pn zg$(296b+mgmv_cYvb1=VkA-9^m(gE792I$X*}oQxw}levrbI&}*_nt6IKs{aNa_{s zrFj;cJ0|0hGX+s00eO7l zQ;Utqw0)yO+WN05>RuUE+j`m%bT8QJf7so=3`m^v46#`B9S zB)jETa&*{%#KkT^6MJAGcCZiT-~gC|Jra=6l3S)!FN#tYXHhh`EnDS7>*M~tLY@sF zw~~312syl&TS!?izgRu&m*1?tiRqedn==y?Cq*^&NacA_$ZAsmvSch3Q6#(Y`v!zn f4M%CtCy981ffaWFysGjhZW8bf^DM9DWBd0Xo-fG_ diff --git a/src/controller/__pycache__/controller.cpython-310.pyc b/src/controller/__pycache__/controller.cpython-310.pyc index f7317785c0b408d065c8558f4fa3245480eb75db..27d6a3a959c8e427862a1775b88636e9c7061ffc 100644 GIT binary patch delta 19 ZcmdnRx{H-7pO=@50SHnXHgauX0st&x1a1HT delta 19 ZcmdnRx{H-7pO=@50SKZEHgauX0stzO1QY-O diff --git a/src/controller/__pycache__/mediator.cpython-310.pyc b/src/controller/__pycache__/mediator.cpython-310.pyc index 639c4a9a5fbc409b071b39ca0170703ea2d4ccf6..759eebd6d2a6ca1451e87ec9810ce50b7561cc1b 100644 GIT binary patch delta 385 zcmaFJewvdnpO=@50SHW5T+(MTPvm>e7&P&hgE&(vdn&IaLmE>GYYJNnOB7!UdoY70 z$7FlPHpZ2ck1;keica=qDy>gu0%?W<5Stl@oq@Pm4M@~5WHBsYtYKWp$OvLHA+Z@2 zFfU|aWJqBMW+>tYD%E7U#Sx#IpO;yZUv!JVv>+w1BsIP`wWK67FTJ=(0I1>?Z(4p) za%y~HPELGEW|1aSkqCo7149wW`9-2|LJX|5ptLvxu3a1`RLlk>co=yY`53Fjd{a|0 z6M+`#p(yjyCZ& z`)Tq{p2r;FEC$q2B!Wtag18(&f)j{~d4L250}~SuBh!B=Ccb}Q8BLyBYz2vVDT&2J RGLzj{LIu!`n7o=r0RW^|9ytI2 diff --git a/src/controller/__pycache__/monitor.cpython-310.pyc b/src/controller/__pycache__/monitor.cpython-310.pyc index c3e89e85a01971174e87209d3e3099d4ac95c0a6..61a2a169652f5c156fc95cafe2e309d3e6dd93eb 100644 GIT binary patch delta 832 zcmZWoO^ee&7@nC-KANTmJpIrgrPfXR zi%Vhk9HuzE`F?aiHe>4$F^kz3lsT+%O$Rn}S@RkV9A>{pf%o<2H)Byz7Rjixcr0f; zJs%P%upPRfUZ24f&j1uE(F$Rt@EcN)!cYdohrosyB2q^LG)ApKCDnI^pqFtz=t&+8 z<50+qN255*BY8R4n=Z060VbT~unk~io@6XAD+;=tJJ7T8whv`8jrlE2nL4OU z)DmT^NI?}TEk#!tqjzmZ_!DWD4#SFkLZ6`s7>~w{(p5;26 zOk2Y}4L(3+2+0F;eGpwB1`X&H*1hPA+8E XpS+b_mWvH2&B4II#KS)M0ecAmLhuj2 diff --git a/src/controller/ideal_data_builder.py b/src/controller/ideal_data_builder.py deleted file mode 100644 index 9a25ec0..0000000 --- a/src/controller/ideal_data_builder.py +++ /dev/null @@ -1,8 +0,0 @@ -from src.utils.base.base import BaseIdealDataBuilder -import pandas as pd - - -class idealDataBuilder(BaseIdealDataBuilder): - def __init__(self,operator_config: dict, system_config: dict): - super().__init__(operator_config, system_config) - self.mul = 10000 diff --git a/src/controller/mediator.py b/src/controller/mediator.py index c564840..95a79bb 100644 --- a/src/controller/mediator.py +++ b/src/controller/mediator.py @@ -19,3 +19,10 @@ class Mediator(BaseMediator): if issubclass(source.__class__, BasePlotWidget): self._controller.send_widgets(data) + + def push_settings(self, data: list[dict]): + self._monitor.update_settings(data) + self._plot.update_settings(data) + self._monitor.force_all_dir() + + diff --git a/src/controller/monitor.py b/src/controller/monitor.py index a5229af..4e24920 100644 --- a/src/controller/monitor.py +++ b/src/controller/monitor.py @@ -26,3 +26,26 @@ class DirectoryMonitor(BaseDirectoryMonitor): if not files: self._files = [] + def update_settings(self, data: list[dict]) -> None: + self.stop() + operator_params, system_params = data + self._directory_path = system_params['trace_storage_path'] + self._update_time = system_params['monitor_update_period'] + self._init_state() + self.start() + + def force_all_dir(self): + files = os.listdir(self._directory_path) + self._files = files + all_files = [] + for x in self._files: + path = os.path.join(self._directory_path, x) + all_files.append(path) + if all_files: + logger.info(f"Plotting all files: {all_files}") + self._mediator.notify(self, all_files) + else: + logger.info(f"Failed pushing {str(all_files)}") + + + diff --git a/src/gui/__pycache__/mainGui.cpython-310.pyc b/src/gui/__pycache__/mainGui.cpython-310.pyc index 913dfe6c9ef3a8f2c0a07be43e70aab75e796fad..216cde6aa43e0a694f8d6f9892327c41c8ff97f2 100644 GIT binary patch literal 2498 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 G)&c-SbT>l) delta 178 zcmca7d|sG0pO=@50SFFAxuhT1$ScIi7&TdeQOTAyg{_4liZz8jg#$>lrEsRQr!l2) zrEs^fL~*3>r0@boIf0^FKvBLFexN9KI|B None: self.tabWidget = QtWidgets.QTabWidget() layout = QtWidgets.QVBoxLayout() layout.addWidget(self.tabWidget) + self.settings_button = QtWidgets.QPushButton("Show settings") + self.settings_button.setFixedWidth(160) + layout.addWidget(self.settings_button) self.setLayout(layout) def show_plot_tabs(self, plot_widgets: list[QtWidgets.QWidget]) -> None: @@ -29,4 +36,20 @@ class MainWindow(BaseMainWindow): if a0.key() == Qt.Key_F5: self.clear() + def _show_settings(self): + self.operSettings.show() + self.sysSettings.show() + + def push_settings(self) -> None: + self._updater_trigger() + + def _updater_trigger(self) -> None: + self.tabWidget.clear() + operator_params = self.operSettings.getParams() + system_params = self.sysSettings.getParams() + self._mediator.push_settings(data=[operator_params, system_params]) + + + + diff --git a/src/gui/plotter.py b/src/gui/plotter.py index 4b44628..716a065 100644 --- a/src/gui/plotter.py +++ b/src/gui/plotter.py @@ -6,15 +6,42 @@ from numpy import floating from typing import Optional, Any, NamedTuple from src.utils.base.base import BasePlotWidget +from src.utils.base.base import BaseIdealDataBuilder +class idealDataBuilder(BaseIdealDataBuilder): + def get_closingDF(self) -> pd.DataFrame: + return self._get_data(self.Ts['tclose'], self.calcPhaseClose) + + def get_compressionDF(self) -> pd.DataFrame: + return self._get_data(self.Ts['tgrow'], self.calcPhaseGrow) + + def get_openingDF(self) -> pd.DataFrame: + return self._get_data(self.getMarkOpen(), self.calcPhaseOpen) + + def get_tmovementDF(self) -> pd.DataFrame: + return self._get_data(self.Ts['tmovement'], self.calcPhaseMovement) + + def get_weldingDF(self, end_time: float) -> pd.DataFrame: + data = [] + X1, X2, V1, V2, F = self.calcPhaseGrow(self.Ts['tgrow']) + data.append({"time":0, "Posicion FE":X1,"Posicion ME":X2, "Rotor Speed FE":V1, "Rotor Speed ME":V2, "Force":F}) + data.append({"time":end_time, "Posicion FE":X1,"Posicion ME":X2, "Rotor Speed FE":V1, "Rotor Speed ME":V2, "Force":F}) + return pd.DataFrame(data) + + def get_ideal_timings(self) -> list[float, float, float, float]: + data = self.Ts + ideal_timings = [data['tclose'], data['tgrow'], self.getMarkOpen(), data["tmovement"]] + return ideal_timings + + class ProcessStage(NamedTuple): mean_value: floating[Any] start_index: int finish_index: int -class PlotWidget(BasePlotWidget): +class PlotWidget(BasePlotWidget): def _create_stage_ideal(self, stage: str, signal: str, @@ -23,7 +50,7 @@ class PlotWidget(BasePlotWidget): stage_diff = np.diff(dataframe[stage]) start_index = np.where(stage_diff == 1)[0] finish_index = np.where(stage_diff == -1)[0] - data = self._stage_ideals[stage]() + data = self._stage_ideals[stage] if start_index.size: start_timestamp = times[start_index[0]] @@ -32,7 +59,6 @@ class PlotWidget(BasePlotWidget): return plot return None - def _create_stage_region(self, stage: str, times: pd.Series, @@ -50,7 +76,7 @@ class PlotWidget(BasePlotWidget): return None @staticmethod - def _init_plot_widget(title: str) -> (pg.PlotWidget, pg.LegendItem): + def _init_plot_widget(title: str) -> tuple[pg.PlotWidget, pg.LegendItem]: plot_widget = pg.PlotWidget(title=title) plot_widget.showGrid(x=True, y=True) legend = pg.LegendItem((80, 60), offset=(70, 20)) @@ -155,4 +181,8 @@ class PlotWidget(BasePlotWidget): widgets = [self._build_widget(data_sample) for data_sample in data] self._mediator.notify(self, widgets) + def update_settings(self, data: list[dict]): + self._initIdealBuilder(idealDataBuilder=idealDataBuilder, data=data) + + diff --git a/src/gui/settings_window.py b/src/gui/settings_window.py index 4c052b4..c67ba93 100644 --- a/src/gui/settings_window.py +++ b/src/gui/settings_window.py @@ -17,10 +17,10 @@ class settingsWindow(QtWidgets.QWidget): self._init_ui() self.params.sigTreeStateChanged.connect(upd_func) - def load_settings(self): + def load_settings(self) -> None: self.data = read_json(self.settingsPath) - def write_settings(self): + def write_settings(self) -> None: self.getParams() write_json(self.settingsPath, self.data) @@ -31,7 +31,7 @@ class settingsWindow(QtWidgets.QWidget): params.append({'name': 'Save', 'type': 'action'}) return params - def _init_ui(self): + def _init_ui(self) -> None: temp = self._getTreeStructure() self.params = Parameter.create(name=self.name, type='group', children=temp) self.params.param('Save').sigActivated.connect(self.write_settings) diff --git a/src/main.py b/src/main.py index 02bc55d..1200be0 100644 --- a/src/main.py +++ b/src/main.py @@ -7,7 +7,6 @@ from src.gui.mainGui import MainWindow from src.controller.monitor import DirectoryMonitor from src.controller.mediator import Mediator from src.controller.converter import DataConverter -from src.controller.ideal_data_builder import idealDataBuilder from src.gui.plotter import PlotWidget from src.controller.controller import Controller from src.utils.json_tools import read_json @@ -15,17 +14,13 @@ from src.utils.json_tools import read_json def main(): app = QtWidgets.QApplication(sys.argv) - operator_params = read_json("params/operator_params.json") - system_params = read_json("params/system_params.json") - monitor = DirectoryMonitor(system_params['trace_storage_path'], system_params['monitor_update_period']) + monitor = DirectoryMonitor() data_converter = DataConverter() - ideal_data_builder = idealDataBuilder(operator_params, system_params) - plot_widget_builder = PlotWidget(idealDataBuilder=ideal_data_builder) + plot_widget_builder = PlotWidget() controller = Controller() - mediator = Mediator(monitor, data_converter, plot_widget_builder, controller) - monitor.start() window = MainWindow() - + mediator = Mediator(monitor, data_converter, plot_widget_builder, controller, window) + window.push_settings() window.show() controller.signal_widgets.connect(window.show_plot_tabs) diff --git a/src/utils/base/__pycache__/base.cpython-310.pyc b/src/utils/base/__pycache__/base.cpython-310.pyc index 2766c4384d20bd998866b30e9194b18768aa25fc..a9ebe229704795cebc7b36be948db0cf2964c7b2 100644 GIT binary patch literal 11086 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 diff --git a/src/utils/base/__pycache__/base_widgets.cpython-310.pyc b/src/utils/base/__pycache__/base_widgets.cpython-310.pyc index 0d4e21d165387499500cee27b8a26ea44e9bb152..5ec790b6154f950f1ebb833305516da3c6bff7b3 100644 GIT binary patch delta 965 zcmZuw&rj4q6rMNRcDqaW2eACm;Kjh9F(L6zJctQI1rL~*)R=MI8AwUl;!HONL$U`p z;f#AXa@?~A|CjkEyy(S?zBdK3ij&Or>zkSPeeZkir}ocIGjBBN49B>qw_Ig^}n;xH%Mwn)WF)$qj1v_J3 z;F1A5g;OTDWP5A^yK%+(W^k;u4TVViX(5EIiIVDGt|#1jMrHe&Oq>92iC)$eJbz~y=nrnyO40|ck+5Y_1j;RJLG zA2H}zd}ykqTowooNmd)Dp;`lei~QO%>+R-!^m`h7O8ze^9VQ`$ zQ~&FHya=mbw_)2~D@ES9eaF^}DugLcGPOMZ5v)FQ6ft+cYBCVo7q?uWNhrf+mTtV*g g6sKz=aoTX>D3^mwJ)#f5P!$mic^g9LK*T%z51MVoX8-^I delta 328 zcmew=GD(0hpO=@50SMOXxuhqsP2`hd44J6yEauLT!kWU?!jQt2%GJyq#goDm%%I6W zae|LqGDtNtU&7&(|k7)6)>;^jU` diff --git a/src/utils/base/base.py b/src/utils/base/base.py index 1f22ba3..b0e31d0 100644 --- a/src/utils/base/base.py +++ b/src/utils/base/base.py @@ -5,7 +5,7 @@ from typing import Optional, Union import pandas as pd from PyQt5.QtCore import QThread, QObject, QTimer -from PyQt5.QtWidgets import QWidget +from PyQt5.QtWidgets import QWidget, QTabWidget from src.OptAlgorithm import OptAlgorithm import pandas as pd @@ -15,7 +15,8 @@ class BaseMediator: monitor: BaseDirectoryMonitor, converter: BaseDataConverter, plot: BasePlotWidget, - controller: BaseController): + controller: BaseController, + window: BaseMainWindow): self._monitor = monitor self._monitor.mediator = self self._converter = converter @@ -23,30 +24,28 @@ class BaseMediator: self._plot = plot self._plot.mediator = self self._controller = controller + self._window = window + self._window.mediator = self def notify(self, - source: Union[BaseDirectoryMonitor, BaseDataConverter, BasePlotWidget], - data: Union[list[str], list[pd.DataFrame], list[QWidget]]): + source: Union[BaseDirectoryMonitor, BaseDataConverter, BasePlotWidget, BaseMainWindow], + data: Union[list[str], list[pd.DataFrame], list[QWidget], list[dict]]): + ... + def push_settings (self, data: list[dict]): ... - class BaseDirectoryMonitor: update_timer = QTimer() def __init__(self, - directory_path: str, - update_time: int, mediator: Optional[BaseMediator] = None): super().__init__() + self._directory_path = None + self._update_time = None - 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: @@ -75,6 +74,15 @@ class BaseDirectoryMonitor: def start(self): self.update_timer.start(self._update_time) + def stop(self): + self.update_timer.stop() + + def update_settings(self, data: list[dict]) -> None: + ... + + def force_all_dir(self): + ... + class BaseDataConverter: def __init__(self, mediator: Optional[BaseMediator] = None): self._mediator = mediator @@ -93,11 +101,9 @@ class BaseDataConverter: class BasePlotWidget: def __init__(self, - mediator: Optional[BaseMediator] = None, - idealDataBuilder: Optional[BaseIdealDataBuilder] = None): + mediator: Optional[BaseMediator] = None): super().__init__() self._mediator = mediator - self._opt = idealDataBuilder self._stages = [ "Relief", @@ -112,13 +118,6 @@ class BasePlotWidget: "Welding": [247, 183, 24, 100], "Relief": [0, 134, 88, 100] } - self._stage_ideals = { - "Closing": self._opt.get_closingDF, - "Squeeze": self._opt.get_compressionDF, - "Welding": self._opt.get_weldingDF, - "Relief": self._opt.get_openingDF - } - self._plt_channels = { "Electrode Force, N & Welding Current, kA": { "Settings": { @@ -199,7 +198,17 @@ class BasePlotWidget: ] }, } + def _initIdealBuilder(self, + idealDataBuilder: Optional[BaseIdealDataBuilder] = None, + data: list[dict] = None): + self.opt = idealDataBuilder(data) + self._stage_ideals = { + "Closing": self._opt.get_closingDF(), + "Squeeze": self._opt.get_compressionDF(), + "Welding": self._opt.get_weldingDF(end_time=data[0]['time_wielding']), + "Relief": self._opt.get_openingDF() + } @property def mediator(self) -> BaseMediator: return self._mediator @@ -207,10 +216,20 @@ class BasePlotWidget: @mediator.setter def mediator(self, mediator: BaseMediator) -> None: self._mediator = mediator + + @property + def opt(self) -> BaseIdealDataBuilder: + return self._opt + + @opt.setter + def opt(self, opt: BaseIdealDataBuilder): + self._opt = opt def build(self, data: list[pd.DataFrame]) -> list[QWidget]: ... + def update_settings(self, data: list[dict]) -> None: + ... class BaseController(QObject): @@ -218,8 +237,13 @@ class BaseController(QObject): ... + # FIXME: WeldingDF показывает только 1 секунду class BaseIdealDataBuilder(OptAlgorithm): + def __init__(self, data: list[dict]): + operator_params, system_params = data + self.mul = system_params['time_capture'] + super().__init__(operator_params, system_params) def _get_data(self, end_timestamp:float, func:function) -> pd.DataFrame: data = [] @@ -230,26 +254,74 @@ class BaseIdealDataBuilder(OptAlgorithm): return pd.DataFrame(data) def get_closingDF(self) -> pd.DataFrame: - return self._get_data(self.Ts['tclose'], self.calcPhaseClose) + ... def get_compressionDF(self) -> pd.DataFrame: - return self._get_data(self.Ts['tgrow'], self.calcPhaseGrow) + ... def get_openingDF(self) -> pd.DataFrame: - return self._get_data(self.getMarkOpen(), self.calcPhaseOpen) + ... def get_tmovementDF(self) -> pd.DataFrame: - return self._get_data(self.Ts['tmovement'], self.calcPhaseMovement) + ... - def get_weldingDF(self) -> pd.DataFrame: - data = [] - X1, X2, V1, V2, F = self.calcPhaseGrow(self.Ts['tgrow']) - data.append({"time":0, "Posicion FE":X1,"Posicion ME":X2, "Rotor Speed FE":V1, "Rotor Speed ME":V2, "Force":F}) - data.append({"time":1, "Posicion FE":X1,"Posicion ME":X2, "Rotor Speed FE":V1, "Rotor Speed ME":V2, "Force":F}) - return pd.DataFrame(data) + def get_weldingDF(self, end_time: float) -> pd.DataFrame: + ... def get_ideal_timings(self) -> list[float, float, float, float]: - data = self.Ts - ideal_timings = [data['tclose'], data['tgrow'], self.getMarkOpen(), data["tmovement"]] - return ideal_timings + ... + + def update_settings(self, data: list[dict]) -> None: + ... +class BaseMainWindow(QWidget): + def __init__(self, + mediator: Optional[BaseMediator] = None): + super().__init__() + self._mediator = mediator + + @property + def mediator(self) -> BaseMediator: + return self._mediator + + @mediator.setter + def mediator(self, mediator: BaseMediator) -> None: + self._mediator = mediator + + def set_style(self, object: Union[QTabWidget, QWidget]) -> None: + object.setStyleSheet(""" + QWidget { + background-color: #0D1117; + font-family: "Segoe UI", sans-serif; + font-size: 14px; + } + QMessageBox { + background-color: #161B22; + font-family: "Segoe UI", sans-serif; + font-size: 14px; + } + QPushButton { + background-color: #FFCC00; + color: #0D1117; + padding: 12px 25px; + border: 2px solid #E6B800; + border-radius: 8px; + font-family: "Segoe UI", sans-serif; + font-size: 16px; + font-weight: bold; + } + QPushButton:hover:!disabled { + background-color: #FFD700; + } + QPushButton:disabled { + background-color: #555555; + color: #cccccc; + border: none; + } + QLabel { + color: #ffffff; + font-size: 16px; + font-weight: bold; + font-family: "Segoe UI", sans-serif; + } + """) \ No newline at end of file diff --git a/src/utils/base/base_widgets.py b/src/utils/base/base_widgets.py deleted file mode 100644 index 5faf97d..0000000 --- a/src/utils/base/base_widgets.py +++ /dev/null @@ -1,47 +0,0 @@ -from __future__ import annotations - -import os -from typing import Optional, Union - -import pandas as pd -from PyQt5.QtWidgets import QTabWidget, QWidget, QVBoxLayout - -class BaseMainWindow(QWidget): - - def set_style(self, object: Union[QTabWidget, QWidget]) -> None: - object.setStyleSheet(""" - QWidget { - background-color: #0D1117; - font-family: "Segoe UI", sans-serif; - font-size: 14px; - } - QMessageBox { - background-color: #161B22; - font-family: "Segoe UI", sans-serif; - font-size: 14px; - } - QPushButton { - background-color: #FFCC00; - color: #0D1117; - padding: 12px 25px; - border: 2px solid #E6B800; - border-radius: 8px; - font-family: "Segoe UI", sans-serif; - font-size: 16px; - font-weight: bold; - } - QPushButton:hover:!disabled { - background-color: #FFD700; - } - QPushButton:disabled { - background-color: #555555; - color: #cccccc; - border: none; - } - QLabel { - color: #ffffff; - font-size: 16px; - font-weight: bold; - font-family: "Segoe UI", sans-serif; - } - """) \ No newline at end of file