review: добавил TODO

This commit is contained in:
Андрей Скирченко 2025-01-15 18:07:16 +03:00
parent 64ced613dd
commit a6a7653ca5
16 changed files with 220 additions and 26 deletions

View File

@ -20,8 +20,11 @@ class Controller(BaseController):
self.signal_widgets.emit(widgets) self.signal_widgets.emit(widgets)
def update_settings(self, settings: list[dict]) -> None: def update_settings(self, settings: list[dict]) -> None:
# TODO: Почему контроллеру просто не сообщить о медиаторе, и ему сообщать что-то через notify? Тем более, что
# медиатор уже знает про контроллер...
self.signal_settings.emit(settings) self.signal_settings.emit(settings)
# TODO: Объедини переключение режимов в один метод, и по входному аргументу (например 1 и 2) переключай их.
def raport_mode(self) -> None: def raport_mode(self) -> None:
self.signal_raport_mode.emit() self.signal_raport_mode.emit()
@ -29,12 +32,18 @@ class Controller(BaseController):
self.signal_seeking_mode.emit() self.signal_seeking_mode.emit()
def open_file(self, filepath: str) -> None: def open_file(self, filepath: str) -> None:
# TODO: Где тут открытие файла?
self.signal_open_file.emit(filepath) self.signal_open_file.emit(filepath)
def update_plots(self) -> None: def update_plots(self) -> None:
self.signal_update_plots.emit() self.signal_update_plots.emit()
def update_status(self, msg: Union[str, float, int]) -> None: def update_status(self, msg: Union[str, float, int]) -> None:
# TODO: Довольно странный набор возможных типов входных аргументов.
# TODO: Для чего сюда принимается float, если он все равно приводится к int?
# TODO: Из медиатора сюда приходит статус 0.5, который становится нулем.
# Это точно ожидаемое и корректное поведение? Сомнительно...
# TODO: if isinstance(msg, float): ...
if type(msg) == float or type(msg) == int: if type(msg) == float or type(msg) == int:
self.signal_statusBar.emit(int(msg)) self.signal_statusBar.emit(int(msg))
else: else:

View File

@ -18,6 +18,8 @@ class DataConverter(BaseDataConverter):
dataframe[bool_columns] = dataframe[bool_columns].replace({True: 1, False: 0}) dataframe[bool_columns] = dataframe[bool_columns].replace({True: 1, False: 0})
return dataframe return dataframe
except: except:
# TODO: Обработка исключений!
# TODO: Осмысленное сообщение в лог. Без traceback и прочего подобного.
return None return None
def convert_data(self, files: list[str]) -> None: def convert_data(self, files: list[str]) -> None:
@ -25,10 +27,12 @@ class DataConverter(BaseDataConverter):
dataframes = [pd.read_csv(file) if file != '' else None for file in files] dataframes = [pd.read_csv(file) if file != '' else None for file in files]
converted_dataframes = list(map(self._replace_bool, dataframes)) converted_dataframes = list(map(self._replace_bool, dataframes))
except: except:
# TODO: Обработка исключений!
# Get the traceback object # Get the traceback object
tb = sys.exc_info()[2] tb = sys.exc_info()[2]
tbinfo = traceback.format_tb(tb)[0] tbinfo = traceback.format_tb(tb)[0]
pymsg = "Traceback info:\n" + tbinfo + "\nError Info:\n" + str(sys.exc_info()[1]) pymsg = "Traceback info:\n" + tbinfo + "\nError Info:\n" + str(sys.exc_info()[1])
# TODO: Осмысленное сообщение в лог. Без traceback и прочего подобного.
logger.error(pymsg) logger.error(pymsg)
converted_dataframes = [None] converted_dataframes = [None]
finally: finally:

View File

@ -16,6 +16,7 @@ class Mediator(BaseMediator):
data: Union[list[str], list[pd.DataFrame], list[list], list[QWidget]]): data: Union[list[str], list[pd.DataFrame], list[list], list[QWidget]]):
if issubclass(source.__class__, BaseDirectoryMonitor): if issubclass(source.__class__, BaseDirectoryMonitor):
# TODO: self._controller.update_status("CSV found! Calculating...") А вот так нельзя написать?
self.update_status("CSV found! Calculating...") self.update_status("CSV found! Calculating...")
self._converter.convert_data(data) self._converter.convert_data(data)
@ -30,12 +31,17 @@ class Mediator(BaseMediator):
if issubclass(source.__class__, BasePlotWidget): if issubclass(source.__class__, BasePlotWidget):
self.update_status(100) self.update_status(100)
self._controller.send_widgets(data) self._controller.send_widgets(data)
def update_settings(self, settings: list[dict]): def update_settings(self, settings: list[dict]):
# TODO: А дописать self.notify не?
# if issubclass(source.__class__, BaseController):
# self._monitor.update_settings(data)
# self._passportFormer.update_settings(data)
self._monitor.update_settings(settings) self._monitor.update_settings(settings)
self._passportFormer.update_settings(settings) self._passportFormer.update_settings(settings)
def update_status(self, msg: Union[str, float]) -> None: def update_status(self, msg: Union[str, float]) -> None:
# TODO: Бессмысленный метод.
self._controller.update_status(msg) self._controller.update_status(msg)

View File

@ -7,10 +7,11 @@ from utils.base.base import BaseDirectoryMonitor
class DirectoryMonitor(BaseDirectoryMonitor): class DirectoryMonitor(BaseDirectoryMonitor):
def _init_state(self): def _init_state(self):
self._files = [ self._files = [
os.path.join(self._directory_path, file) os.path.join(self._directory_path, file)
for file in os.listdir(self._directory_path) for file in os.listdir(self._directory_path)
# TODO: FileNotFoundError: [Errno 2] No such file or directory: 'D:/downloads/a22'
# при выборе real time mode.
if file.lower().endswith('.csv') if file.lower().endswith('.csv')
] ]
@ -31,6 +32,9 @@ class DirectoryMonitor(BaseDirectoryMonitor):
if not current_files: if not current_files:
self._files = [] self._files = []
# TODO: Почему монитор директории обновляет какие-то настройки, графики, стартует что-то?
# Это не должно быть в его зоне ответственности.
# TODO: Может стоит сделать какой-то класс, который будет заниматься обновлениями?
def update_settings(self, data: list[dict]) -> None: def update_settings(self, data: list[dict]) -> None:
if self.isActive: if self.isActive:
self.stop() self.stop()

View File

@ -7,6 +7,7 @@ from utils.base.base import BasePointPassportFormer, BaseIdealDataBuilder
class idealDataBuilder(BaseIdealDataBuilder): class idealDataBuilder(BaseIdealDataBuilder):
# TODO: Имя класса с большой буквы.
def get_closingDF(self) -> pd.DataFrame: def get_closingDF(self) -> pd.DataFrame:
return self._get_data(self.Ts['tclose'], self.calcPhaseClose) return self._get_data(self.Ts['tclose'], self.calcPhaseClose)
@ -35,10 +36,13 @@ class idealDataBuilder(BaseIdealDataBuilder):
class PassportFormer(BasePointPassportFormer): class PassportFormer(BasePointPassportFormer):
def form_passports(self, data: list[pd.DataFrame]) -> list[list[pd.DataFrame, dict, int]]: def form_passports(self, data: list[pd.DataFrame]) -> list[list[pd.DataFrame, dict, int]]:
# TODO: сигнатура метода не соответствует сигнатуре метода класса родителя
try: try:
return_data = [self._build_passports_pocket(dataframe) for dataframe in data] return_data = [self._build_passports_pocket(dataframe) for dataframe in data]
except: except:
# TODO: обработка исключений!!!
tb = sys.exc_info()[2] tb = sys.exc_info()[2]
# TODO: Нормальные сообщения в лог!
tbinfo = traceback.format_tb(tb)[0] tbinfo = traceback.format_tb(tb)[0]
pymsg = "Traceback info:\n" + tbinfo + "\nError Info:\n" + str(sys.exc_info()[1]) pymsg = "Traceback info:\n" + tbinfo + "\nError Info:\n" + str(sys.exc_info()[1])
logger.error(pymsg) logger.error(pymsg)
@ -48,7 +52,8 @@ class PassportFormer(BasePointPassportFormer):
def _build_passports_pocket(self, dataframe: pd.DataFrame) -> list[pd.DataFrame, dict, int]: def _build_passports_pocket(self, dataframe: pd.DataFrame) -> list[pd.DataFrame, dict, int]:
# TODO: Еще раз проверь, что написано в аннотации, и что метод возвращает.
# TODO: Практически нечитаемо. Надо оптимизировать и декомпозировать.
if dataframe is not None: if dataframe is not None:
events, point_quantity = self._filter_events(dataframe["time"], dataframe) events, point_quantity = self._filter_events(dataframe["time"], dataframe)
if point_quantity == 0: if point_quantity == 0:
@ -74,16 +79,21 @@ class PassportFormer(BasePointPassportFormer):
} }
params_list = [operator_settings, system_settings] params_list = [operator_settings, system_settings]
cache_key = self._generate_cache_key(params_list) cache_key = self._generate_cache_key(params_list)
# TODO: if-else заменить на:
# ideal_data = self._ideal_data_cashe.get(cache_key, self._build_ideal_data(idealDataBuilder=idealDataBuilder, params=params_list))
# self._ideal_data_cashe[cache_key] = ideal_data
if cache_key in self._ideal_data_cashe : if cache_key in self._ideal_data_cashe :
ideal_data = self._ideal_data_cashe[cache_key] ideal_data = self._ideal_data_cashe[cache_key]
else: else:
ideal_data = self._build_ideal_data(idealDataBuilder=idealDataBuilder, params=params_list) ideal_data = self._build_ideal_data(idealDataBuilder=idealDataBuilder, params=params_list)
self._ideal_data_cashe[cache_key] = ideal_data self._ideal_data_cashe[cache_key] = ideal_data
# TODO: point_timeframe, point_events = None, None
if events is not None: if events is not None:
idx = i+1 if idx_shift else i idx = i+1 if idx_shift else i
point_timeframe = [events[self._stages[0]][0][i], events[self._stages[-1]][1][idx]] point_timeframe = [events[self._stages[0]][0][i], events[self._stages[-1]][1][idx]]
point_events = {key: [value[0][i], value[1][i]] for key, value in events.items()} point_events = {key: [value[0][i], value[1][i]] for key, value in events.items()}
else: else:
# TODO: убрать else
point_timeframe, point_events = None, None point_timeframe, point_events = None, None
useful_p_data = {"thickness": operator_settings["object_thickness"], useful_p_data = {"thickness": operator_settings["object_thickness"],
"L2": operator_settings["distance_l_2"], "L2": operator_settings["distance_l_2"],

View File

@ -14,8 +14,18 @@ from gui.reportGui import ReportSettings
class MainWindow(BaseMainWindow): class MainWindow(BaseMainWindow):
def __init__(self, def __init__(self,
controller: Optional[BaseController] = None) -> None: controller: Optional[BaseController] = None) -> None:
# TODO: Почему контроллер Optional? Нигде нет обработки случаев, если он будет None: при вызове его методов
# будет ошибка.
# TODO: Касательно передачи экземпляра контроллера в конструктор. Не лучше ли реализовать взаимодействие с ним
# через сигналы? Как минимум это позволит исключить странную зависимость интерфейса от контроллера.
# Логичнее было бы использовать данный класс в качестве фасада над виджетами, о чем сказано ниже.
super().__init__() super().__init__()
self._controller = controller self._controller = controller
# TODO: касательно ВСЕХ методов данного класса, СОЗДАЮЩИХ ВИДЖЕТЫ. Исходя из названия данного класса - это
# финальное главное окно приложения: оно не должно создавать виджеты, а уже использовать готовые, поэтому
# правильнее будет каждый создаваемый тут виджет вынести в отдельный модуль в отдельный класс, там выполнять
# его создание и настройку, а тут уже получать готовый экземпляр виджета и его отображать.
self._init_startUI() self._init_startUI()
self._init_dock_widgets() self._init_dock_widgets()
self._init_menu() self._init_menu()
@ -194,6 +204,11 @@ class MainWindow(BaseMainWindow):
if child.widget() is not None: if child.widget() is not None:
child.widget().deleteLater() child.widget().deleteLater()
# TODO: По методам self._init_seekingUI и self._init_raportUI:
# 1. Дублирование кода (блок из первых трех строк) - вынести в отдельный метод
# 2. Оба метода призваны проинициализировать UI, но в то же время занимаются созданием виджетов и еще что-то
# в контроллере дергают: создание виджетов вынести отдельно, контроллеру сообщить о чем-то при помощи
# pyqtSignal при нажатии кнопки или выборе пункта меню.
def _init_seekingUI(self) -> None: def _init_seekingUI(self) -> None:
self._clear() self._clear()
self._init_TabWidget() self._init_TabWidget()
@ -292,6 +307,7 @@ class MainWindow(BaseMainWindow):
def _close_tab(self, index:int) -> None: def _close_tab(self, index:int) -> None:
self.tabWidget.removeTab(index) self.tabWidget.removeTab(index)
# TODO: Зачем разделять эти 2 метода? В self._select_csv путь к файлу получили и контроллеру сигналом отправили.
def _open_file(self) -> None: def _open_file(self) -> None:
path = self._select_csv() path = self._select_csv()
if path is not None: if path is not None:
@ -307,7 +323,8 @@ class MainWindow(BaseMainWindow):
def _on_tab_changed(self, index): def _on_tab_changed(self, index):
try: try:
tab = self.tabWidget.currentWidget() tab = self.tabWidget.currentWidget()
except: except:
# TODO: Очень плохая практика - не указывать конкретный тип исключений.
tab = None tab = None
if tab is not None and self.report_dock.isVisible(): if tab is not None and self.report_dock.isVisible():
reg_items = tab.property("reg_items") reg_items = tab.property("reg_items")
@ -316,6 +333,9 @@ class MainWindow(BaseMainWindow):
self.repSettings.build(index, reg_items, curve_items, qt_items) self.repSettings.build(index, reg_items, curve_items, qt_items)
def _save_plots(self) -> None: def _save_plots(self) -> None:
# TODO: Нарушение принципа единичной ответственности. Почему ИНТЕРФЕЙС занимается сохранением чего-то?
# От него должен только исходить призыв к действию (кнопка или что-то подобное), сама логика же должна
# быть в другом месте.
filepath, _ = QtWidgets.QFileDialog.getSaveFileName(self, "Save file", "", "Image Files (*.png *.jpeg)") filepath, _ = QtWidgets.QFileDialog.getSaveFileName(self, "Save file", "", "Image Files (*.png *.jpeg)")
tab = self.tabWidget.currentWidget() tab = self.tabWidget.currentWidget()
if tab is not None: if tab is not None:

View File

@ -13,8 +13,12 @@ import pandas as pd
from typing import Optional, Any from typing import Optional, Any
from utils.base.base import BasePlotWidget from utils.base.base import BasePlotWidget
# TODO: Навести порядок в импортах.
class ProcessStage(): class ProcessStage():
# TODO: Для чего тут наследование от ничего?
# TODO: Если я правильно понял сценарий использования этого класса, то правильно это сделать либо отнаследовавшись
# от NamedTuple, либо использовать @dataclass.
mean_value:int mean_value:int
start_index:int start_index:int
finish_index:int finish_index:int
@ -157,6 +161,7 @@ class PlotWidget(BasePlotWidget):
""" """
Добавляет QLabel с информацией о производительности. Добавляет QLabel с информацией о производительности.
""" """
# TODO: Почему PlotWidget создает Label? Вынести в другое место.
tesla_TWC = round((1 - TWC_time/tesla_time)*100, 2) if tesla_time else 0.0 tesla_TWC = round((1 - TWC_time/tesla_time)*100, 2) if tesla_time else 0.0
tesla_ideal = round((1 - ideal_time/tesla_time)*100, 2) if tesla_time else 0.0 tesla_ideal = round((1 - ideal_time/tesla_time)*100, 2) if tesla_time else 0.0
TWC_ideal = round((ideal_time/TWC_time)*100, 2) if TWC_time else 0.0 TWC_ideal = round((ideal_time/TWC_time)*100, 2) if TWC_time else 0.0
@ -187,6 +192,12 @@ class PlotWidget(BasePlotWidget):
def _build_widget(self, data: list[Any]) -> QWidget: def _build_widget(self, data: list[Any]) -> QWidget:
# TODO: Либо передавать в метод 3 аргумента с аннотацией типа каждого, либо передавать
# NamedTuple / dataclass.
# TODO: Исходя из сигнатуры метода, он создает и возвращает виджет с графиками.
# Простыня "result_widget, reg_items, curve_items, qt_items" не похожа на виджет с графиком.
# TODO: Данный метод должен содержать только ту логику, которая относится непосредственно к графикам.
# Все остальное - вынести. Оставшийся метод декомпозировать - очень трудно вообще понять, что тут происходит.
""" """
Собирает графический виджет для одного набора данных. Собирает графический виджет для одного набора данных.
Параметр `data` предполагается списком: [dataframe, points_pocket, useful_data]. Параметр `data` предполагается списком: [dataframe, points_pocket, useful_data].
@ -353,19 +364,26 @@ class PlotWidget(BasePlotWidget):
... ...
] ]
""" """
# TODO: Про входные аргументы см. выше.
try: try:
# TODO: Про инициализацию атрибутов класса где-то уже писал. На всякий случай: она делается в конструкторе.
self._datalen = len(data) self._datalen = len(data)
widgets_datapack = [self._build_widget(data_sample) for self._datastep, data_sample in enumerate(data)] widgets_datapack = [self._build_widget(data_sample) for self._datastep, data_sample in enumerate(data)]
except: except:
# TODO: Добавить конкретные исключения.
tb = sys.exc_info()[2] tb = sys.exc_info()[2]
tbinfo = traceback.format_tb(tb)[0] tbinfo = traceback.format_tb(tb)[0]
pymsg = "Traceback info:\n" + tbinfo + "\nError Info:\n" + str(sys.exc_info()[1]) pymsg = "Traceback info:\n" + tbinfo + "\nError Info:\n" + str(sys.exc_info()[1])
# TODO: Логи должны быть понятны обычному пользователю. Traceback что-то там - это не информативно и может
# быть непонятно, поэтому пишем осознанные сообщения. Если надо, то создаем собственные
# классы исключений. (Касается всего проекта - уже не первый раз натыкаюсь на это.)
logger.error(pymsg) logger.error(pymsg)
widgets_datapack = [QLabel(pymsg)] widgets_datapack = [QLabel(pymsg)]
finally: finally:
self._mediator.notify(self, widgets_datapack) self._mediator.notify(self, widgets_datapack)
def _update_status(self, widgsteps:int, pointsteps:int, cur_widg:int, cur_point:int): def _update_status(self, widgsteps:int, pointsteps:int, cur_widg:int, cur_point:int):
# TODO: if self._datalen: ...
if self._datalen != 0: if self._datalen != 0:
sycle_start = self._datastep/self._datalen*100 + 1 sycle_start = self._datastep/self._datalen*100 + 1
period1 = 100/self._datalen period1 = 100/self._datalen
@ -377,6 +395,7 @@ class PlotWidget(BasePlotWidget):
period3 = period2/pointsteps if pointsteps != 0 else period2 period3 = period2/pointsteps if pointsteps != 0 else period2
progress = sycle_start + period2*cur_widg + period3*cur_point progress = sycle_start + period2*cur_widg + period3*cur_point
# TODO: см. модуль mediator.py
self._mediator.update_status(progress) self._mediator.update_status(progress)

View File

@ -103,7 +103,7 @@ class settingsWindow(QWidget):
for i, (_, items) in enumerate(self._data.items()): for i, (_, items) in enumerate(self._data.items()):
for j, item in enumerate(items): for j, item in enumerate(items):
self._param_table.setItem(i, j, QTableWidgetItem(str(item))) self._param_table.setItem(i, j, QTableWidgetItem(str(item)))
# TODO: if isinstance()...
if type(item) == int: if type(item) == int:
self._param_table.setItemDelegateForRow(i, int_delegate) self._param_table.setItemDelegateForRow(i, int_delegate)
elif type(item) == float: elif type(item) == float:

View File

@ -1,6 +1,8 @@
import sys import sys
import pyqtgraph as pg import pyqtgraph as pg
from PyQt5 import QtWidgets from PyQt5 import QtWidgets
# TODO: Правило хорошего тона при оформлении импортов: сначала встроенные библиотеки,
# через одну пустую строку - дополнительно установленные, и еще через одну пустую строку - собственные импорты.
from gui.mainGui import MainWindow from gui.mainGui import MainWindow
from controller.monitor import DirectoryMonitor from controller.monitor import DirectoryMonitor
@ -10,6 +12,8 @@ from gui.plotter import PlotWidget
from controller.controller import Controller from controller.controller import Controller
from controller.passportFormer import PassportFormer from controller.passportFormer import PassportFormer
# TODO: Актуализировать requirements.txt!!! Делать это регулярно после добавления новых библиотек!
# TODO: Именование модулей: lowercase / snake_case.
def main(): def main():
pg.setConfigOptions(useOpenGL=False, antialias=False) pg.setConfigOptions(useOpenGL=False, antialias=False)

View File

@ -4,15 +4,21 @@ from typing import Optional
import os import os
import numpy as np import numpy as np
import pandas as pd import pandas as pd
# TODO: Оформление импортов
class BasePerformanceFactory(ABC): class BasePerformanceFactory(ABC):
# TODO: Абстракция делается с целью определения интерфейса и только: внутри никаких реализаций быть не должно.
# Сначала делаешь абстракцию, затем от нее наследуешься и делаешь нечто базовое, затем конкретную реализацию.
# Есть вариант не делать базовое нечто, а сразу переходить к реализации.
# TODO: Убрать из этого класса все реализации и имплементировать соответствующие методы уже в наследниках.
# TODO: Возможно стоит отдельно вынести все касающееся парсинга в отдельный класс (на подумать).
@abstractmethod @abstractmethod
def factory_method(self): def factory_method(self):
... ...
# TODO: @abstractmethod
def job(self): def job(self):
... ...
@ -30,17 +36,25 @@ class BasePerformanceFactory(ABC):
inside_channel = False inside_channel = False
channels = 0 channels = 0
for line in file: for line in file:
line = line.strip() line = line.strip()
# TODO: if line in ("#BEGINCHANNELHEADER", "#BEGINGLOBALHEADER")...
if line == '#BEGINCHANNELHEADER' or line == "#BEGINGLOBALHEADER": if line == '#BEGINCHANNELHEADER' or line == "#BEGINGLOBALHEADER":
inside_channel = True inside_channel = True
# TODO: if line in ("#ENDCHANNELHEADER", "#ENDGLOBALHEADER")...
elif line == '#ENDCHANNELHEADER' or line == "#ENDGLOBALHEADER": elif line == '#ENDCHANNELHEADER' or line == "#ENDGLOBALHEADER":
inside_channel = False inside_channel = False
# TODO: для чего тут pass?
pass pass
# TODO: inside_channel = line in ("#BEGINCHANNELHEADER", "#BEGINGLOBALHEADER")
# Формирование словаря # Формирование словаря
# TODO: Предыдущие условия связаны были с line, а теперь под elif уже абсолютно другая логика.
# Не надо так делать.
elif inside_channel: elif inside_channel:
# TODO: нижним подчеркиванием обозначается переменная, которая как бы есть, но не нужна.
# Ты же на что-то проверяешь, т.е. используешь. Назови нормально.
_, data = line.split(',') _, data = line.split(',')
match _: match _:
# TODO: тут default case не нужен?
case '102': case '102':
head['rob_id'] = data head['rob_id'] = data
case '200': case '200':
@ -58,9 +72,11 @@ class BasePerformanceFactory(ABC):
case '241': case '241':
head[ch_name]['multiplyer'] = float(data) head[ch_name]['multiplyer'] = float(data)
head['channels'] = int(channels) head['channels'] = int(channels)
# TODO: Метод декомпозировать и переписать.
return head, file return head, file
def _r64_parser(self, path: str, head: dict) -> Optional[list[pd.Series, pd.DataFrame]]: def _r64_parser(self, path: str, head: dict) -> Optional[list[pd.Series, pd.DataFrame]]:
# TODO: Метод декомпозировать и переписать.
ch = head['channels'] ch = head['channels']
keys = list(head.keys())[-ch:] keys = list(head.keys())[-ch:]
len_timestamps = head['Zeit']['len'] len_timestamps = head['Zeit']['len']
@ -80,6 +96,8 @@ class BasePerformanceFactory(ABC):
class BaseProduct(ABC): class BaseProduct(ABC):
# TODO: Аналогично BasePerformanceFactory: снова в абстракции куча реализаций.
# TODO: Может стоит дать классу название осмысленное, а не из статьи по паттернам? =)
def __init__(self): def __init__(self):
self._signals = [] self._signals = []
@ -117,10 +135,16 @@ class BaseProduct(ABC):
@abstractmethod @abstractmethod
def operation(self): def operation(self):
# TODO: Аналогично: что за операция? Что она делает? Не понятно.
... ...
class Performance(BasePerformanceFactory): class Performance(BasePerformanceFactory):
# TODO: где реализация factory_method? Если не нужен, то надо его убрать из интерфейса.
# TODO: данный класс, простите, лебедь раком щуку. На все руки мастер. И то создает, и это создает... И принцип
# единичной ответственности нарушает, и принцип подстановки...
# Если реализуешь паттерн "Фабричный метод", то реализуй его правильно: для каждого порождаемого объекта свой
# класс - создатель, классы наследники в полной мере имплементируют методы класса - родителя...
def robot_method(self) -> BaseProduct: def robot_method(self) -> BaseProduct:
return RobotData() return RobotData()
@ -132,10 +156,12 @@ class Performance(BasePerformanceFactory):
return CommData() return CommData()
def job(self, path:str, TWC_raw:pd.DataFrame) -> list[pd.DataFrame, list[pd.DataFrame], list[pd.DataFrame]]: def job(self, path:str, TWC_raw:pd.DataFrame) -> list[pd.DataFrame, list[pd.DataFrame], list[pd.DataFrame]]:
# TODO: сигнатура метода не соответствует той, которая определена в родительском классе.
robot = self.robot_method() robot = self.robot_method()
TWC = self.TWC_method() TWC = self.TWC_method()
comm=self.comm_method() comm=self.comm_method()
dataframe = self._get_file_data(path) dataframe = self._get_file_data(path)
# TODO: operation в родителе определен без входных параметров, используется с ними. Не надо так делать.
rob_comm, rob_df = robot.operation(dataframe) rob_comm, rob_df = robot.operation(dataframe)
TWC_comm, TWC_df = TWC.operation(TWC_raw) TWC_comm, TWC_df = TWC.operation(TWC_raw)
comm_df = comm.operation(rob_comm, TWC_comm) comm_df = comm.operation(rob_comm, TWC_comm)
@ -156,6 +182,8 @@ class RobotData(BaseProduct):
communication_sig = {'sent':events["$OUT3244"]["fall"], 'received':events[""][""]} communication_sig = {'sent':events["$OUT3244"]["fall"], 'received':events[""][""]}
point_interval = self._form_intervals(start=events["$OUT3012"]["rise"], end=events["$OUT3012"]["fall"]) point_interval = self._form_intervals(start=events["$OUT3012"]["rise"], end=events["$OUT3012"]["fall"])
movement_interval = self._form_intervals(start=events["$OUT3244"]["rise"], end=events["$OUT3244"]["fall"]) movement_interval = self._form_intervals(start=events["$OUT3244"]["rise"], end=events["$OUT3244"]["fall"])
# TODO: возвращаемый результат не соответствует аннотированному.
# TODO: зачем возвращать DataFrame? В этом есть какой-то философский смысл?
return communication_sig, pd.DataFrame({'in_point':point_interval, return communication_sig, pd.DataFrame({'in_point':point_interval,
'in_move':movement_interval}) 'in_move':movement_interval})
@ -177,6 +205,8 @@ class TWC_Data(BaseProduct):
squeeze_interval = self._form_intervals(start=events["Squeeze"]["rise"], end=events["Squeeze"]["fall"]) squeeze_interval = self._form_intervals(start=events["Squeeze"]["rise"], end=events["Squeeze"]["fall"])
relief_interval = self._form_intervals(start=events["Relief"]["rise"], end=events["Relief"]["fall"]) relief_interval = self._form_intervals(start=events["Relief"]["rise"], end=events["Relief"]["fall"])
oncoming_interval = self._form_intervals(start=events["Oncoming"]["rise"], end=events["Oncoming"]["fall"]) oncoming_interval = self._form_intervals(start=events["Oncoming"]["rise"], end=events["Oncoming"]["fall"])
# TODO: возвращаемый результат не соответствует аннотированному.
# TODO: зачем возвращать DataFrame? В этом есть какой-то философский смысл?
return communication_sig, pd.DataFrame({'in_closing':closing_interval, return communication_sig, pd.DataFrame({'in_closing':closing_interval,
'in_squeeze':squeeze_interval, 'in_squeeze':squeeze_interval,
'in_relief':relief_interval, 'in_relief':relief_interval,

View File

@ -1,29 +1,52 @@
# TODO: Тесты должны лежать в директории tests.
# TODO: Правило хорошего тона при оформлении импортов: сначала встроенные библиотеки,
# через одну пустую строку - дополнительно установленные, и еще через одну пустую строку - собственные импорты.
from src.OptAlgorithm.OptAlgorithm import OptAlgorithm from src.OptAlgorithm.OptAlgorithm import OptAlgorithm
from src.utils import read_json from src.utils import read_json
from matplotlib import pyplot as plt, use from matplotlib import pyplot as plt, use
from numpy import cos, sin, sqrt, cbrt, arcsin, linspace, array from numpy import cos, sin, sqrt, cbrt, arcsin, linspace, array
# TODO: Отступ перед блоком if __name__ == "__main__" - 2 пустых строки.
if __name__ == "__main__": if __name__ == "__main__":
# TODO: Что означают переменные ниже? Имена переменных должны быть осмысленными, чтобы их смысл был понятен не
# не только автору кода.
tq = 1 tq = 1
ts = linspace(0, tq, 200000) ts = linspace(0, tq, 200000)
operator_params = read_json("params/operator_params.json") operator_params = read_json("../params/operator_params.json")
system_params = read_json("params/system_params.json") system_params = read_json("../params/system_params.json")
# TODO: 2 блока кода ниже (с циклами) нарушают принцип DRY (Don't Repeat Yourself):
# повторяющийся блок кода следует вынести в отдельную функцию.
# Например, в такую:
# def foo(params_dict: dict) -> dict:
# result_dict = {}
# i = 1
# for key, value in params_dict.items():
# if isinstance(value, list):
# result_dict[key] = value[i] if len(value) > i else value[0]
# else:
# result_dict[key] = value
# return result_dict
# И потом вызвать ее как-то так:
# non_array_operator_params, non_array_system_params = foo(operator_params), foo(system_params)
non_array_operator_params = {} non_array_operator_params = {}
i = 1 i = 1
for key, value in operator_params.items(): for key, value in operator_params.items():
# TODO: Не совсем понятен смысл такой проверки. Если хотим проверить принадлежность value к list,
# то лучше использовать для этого isinstance.
if hasattr(value, "__len__"): if hasattr(value, "__len__"):
# TODO: Можно заменить однострочным выражением: result_dict[key] = value[i] if len(value) > i else value[0]
if len(value) > i: if len(value) > i:
non_array_operator_params[key] = value[i] non_array_operator_params[key] = value[i]
else: else:
non_array_operator_params[key] = value[0] non_array_operator_params[key] = value[0]
else: else:
non_array_operator_params[key] = value non_array_operator_params[key] = value
non_array_system_params = {} non_array_system_params = {}
for key, value in system_params.items(): for key, value in system_params.items():
if hasattr(value, "__len__"): if hasattr(value, "__len__"):
@ -33,7 +56,7 @@ if __name__ == "__main__":
non_array_system_params[key] = value[0] non_array_system_params[key] = value[0]
else: else:
non_array_system_params[key] = value non_array_system_params[key] = value
opt = OptAlgorithm(non_array_operator_params, non_array_system_params) opt = OptAlgorithm(non_array_operator_params, non_array_system_params)
Xs = array([opt.getVar("X1", t) for t in ts]) Xs = array([opt.getVar("X1", t) for t in ts])

View File

@ -11,7 +11,7 @@ class UMLCreator:
real_data = [] real_data = []
ideal_data = [] ideal_data = []
if not self.theor_mode: if not self.theor_mode:
# TODO: Писать так условия - моветон. Как вариант, можно применить конструкцию match - case.
for key, items in self.timings_dict.items(): for key, items in self.timings_dict.items():
if key == 'closure': ideal_time = self._ideal_time[0] if key == 'closure': ideal_time = self._ideal_time[0]
elif key == 'compression': ideal_time = self._ideal_time[1] elif key == 'compression': ideal_time = self._ideal_time[1]
@ -98,7 +98,7 @@ class UMLCreator:
timings_dict: dict, timings_dict: dict,
mode: bool, mode: bool,
name:str): name:str):
# TODO: Это надо в конструкторе класса делать!
self._ideal_time = ideal_time self._ideal_time = ideal_time
self.bool_dict = bool_dict self.bool_dict = bool_dict
self.float_dict = float_dict self.float_dict = float_dict

View File

@ -1,8 +1,9 @@
from plantuml import PlantUML from plantuml import PlantUML
from os.path import abspath from os.path import abspath
# TODO: Навести порядок в импортах.
class Request: class Request:
# TODO: Имя класса не соответствует тому, что он делает.
def __init__(self, server_url: str): def __init__(self, server_url: str):
self._server_url = server_url self._server_url = server_url
@ -13,12 +14,15 @@ class Request:
self.clear() self.clear()
def _startUML(self): def _startUML(self):
# TODO: Единый стиль именования методов! (snake_case / lowercase).
self.reqArr.append('@startuml') self.reqArr.append('@startuml')
def _endUML(self): def _endUML(self):
# TODO: Единый стиль именования методов! (snake_case / lowercase).
self.reqArr.append('@enduml') self.reqArr.append('@enduml')
def clear(self): def clear(self):
# TODO: Атрибуты класса инициализируются в конструкторе класса.
self.timestamps = {} self.timestamps = {}
self.reqArr = [] self.reqArr = []
self.variables = [] self.variables = []
@ -27,28 +31,36 @@ class Request:
self._startUML() self._startUML()
def addAnalog(self, name, string): def addAnalog(self, name, string):
# TODO: Единый стиль именования методов! (snake_case / lowercase).
self.variables.append(f'analog "{string}" as {name}') self.variables.append(f'analog "{string}" as {name}')
def addBinary(self, name, string, style = ''): def addBinary(self, name, string, style = ''):
# TODO: Единый стиль именования методов! (snake_case / lowercase).
if style: name = name + '<<' + style + '>>' if style: name = name + '<<' + style + '>>'
self.variables.append(f'binary "{string}" as {name}') self.variables.append(f'binary "{string}" as {name}')
def addClock(self, name, string): def addClock(self, name, string):
# TODO: Единый стиль именования методов! (snake_case / lowercase).
self.variables.append(f'clock "{string}" as {name}') self.variables.append(f'clock "{string}" as {name}')
def addConcise(self, name, string): def addConcise(self, name, string):
# TODO: Единый стиль именования методов! (snake_case / lowercase).
self.variables.append(f'concise "{string}" as {name}') self.variables.append(f'concise "{string}" as {name}')
def addRobust(self, name, string): def addRobust(self, name, string):
# TODO: Единый стиль именования методов! (snake_case / lowercase).
self.variables.append(f'robust "{string}" as {name}') self.variables.append(f'robust "{string}" as {name}')
def appendStr(self, string = ''): def appendStr(self, string = ''):
# TODO: Единый стиль именования методов! (snake_case / lowercase).
self.variables.append(string) self.variables.append(string)
def addLineStyle(self, name, color = 'green', thicknes = 1): def addLineStyle(self, name, color = 'green', thicknes = 1):
# TODO: Единый стиль именования методов! (snake_case / lowercase).
self.lineStyles[name] = [f'LineColor {color}', f'LineThickness {thicknes}'] self.lineStyles[name] = [f'LineColor {color}', f'LineThickness {thicknes}']
def generateSVG(self, name): def generateSVG(self, name):
# TODO: Единый стиль именования методов! (snake_case / lowercase).
self._compileUML(name) self._compileUML(name)
filename = abspath(f'{name}.txt') filename = abspath(f'{name}.txt')
self.server.processes_file(filename, outfile=f'{name}.svg') self.server.processes_file(filename, outfile=f'{name}.svg')
@ -56,6 +68,7 @@ class Request:
#return result #return result
def setTimestamps(self, name, input): def setTimestamps(self, name, input):
# TODO: Единый стиль именования методов! (snake_case / lowercase).
for time, state in input: for time, state in input:
try: try:
self.timestamps[f'@{time}'].append(f'{name} is {state}') self.timestamps[f'@{time}'].append(f'{name} is {state}')
@ -63,11 +76,13 @@ class Request:
self.timestamps[f'@{time}'] = [f'{name} is {state}'] self.timestamps[f'@{time}'] = [f'{name} is {state}']
def _addTimestamp(self, timecode, vars): def _addTimestamp(self, timecode, vars):
# TODO: Единый стиль именования методов! (snake_case / lowercase).
self.reqArr.append(timecode) self.reqArr.append(timecode)
for var in vars: for var in vars:
self.reqArr.append(var) self.reqArr.append(var)
def _addHeader(self): def _addHeader(self):
# TODO: Единый стиль именования методов! (snake_case / lowercase).
self.reqArr.append('<style>') self.reqArr.append('<style>')
if self.lineStyles: if self.lineStyles:
self.reqArr.append('timingDiagram {') self.reqArr.append('timingDiagram {')
@ -80,9 +95,11 @@ class Request:
self.reqArr.append('</style>') self.reqArr.append('</style>')
def _addVariables(self): def _addVariables(self):
# TODO: Единый стиль именования методов! (snake_case / lowercase).
for var in self.variables: self.reqArr.append(str(var)) for var in self.variables: self.reqArr.append(str(var))
def _compileUML(self, name): def _compileUML(self, name):
# TODO: Единый стиль именования методов! (snake_case / lowercase).
self._addHeader() self._addHeader()
self._addVariables() self._addVariables()

View File

@ -1,5 +1,9 @@
from __future__ import annotations # TODO: Почему каталог с базовыми классами лежит внутри каталога с утилитами? Это странно.
from __future__ import annotations
# TODO: Правило хорошего тона при оформлении импортов: сначала встроенные библиотеки,
# через одну пустую строку - дополнительно установленные, и еще через одну пустую строку - собственные импорты.
import os import os
from typing import Optional, Union, Any from typing import Optional, Union, Any
from cachetools import LRUCache from cachetools import LRUCache
@ -13,12 +17,15 @@ from OptAlgorithm import OptAlgorithm
from utils.qt_settings import dark_style from utils.qt_settings import dark_style
# TODO: Глобальное замечание: Базовые классы создаются для обозначения неких базовых свойств объектов.
# Конкретная же реализация возлагается на классы - наследники.
class BaseMediator: class BaseMediator:
def __init__(self, def __init__(self,
monitor: BaseDirectoryMonitor, monitor: BaseDirectoryMonitor,
converter: BaseDataConverter, converter: BaseDataConverter,
# TODO: passport_former
passportFormer: BasePointPassportFormer, passportFormer: BasePointPassportFormer,
plot: BasePlotWidget, plot: BasePlotWidget,
controller: BaseController): controller: BaseController):
@ -37,12 +44,13 @@ class BaseMediator:
source: Union[BaseDirectoryMonitor, BaseDataConverter, BasePointPassportFormer, BasePlotWidget], source: Union[BaseDirectoryMonitor, BaseDataConverter, BasePointPassportFormer, BasePlotWidget],
data: Union[list[str], list[pd.DataFrame], list[list], list[QWidget]]): data: Union[list[str], list[pd.DataFrame], list[list], list[QWidget]]):
... ...
# TODO: Отступы между методами класса - одна пустая строка.
def update_settings (self, data: list[dict]): def update_settings (self, data: list[dict]):
... ...
def update_status(self, msg: Union[str, float]) -> None: def update_status(self, msg: Union[str, float]) -> None:
... ...
# TODO: Отступы между классами - две пустых строки.
class BaseDirectoryMonitor: class BaseDirectoryMonitor:
update_timer = QTimer() update_timer = QTimer()
@ -88,7 +96,8 @@ class BaseDirectoryMonitor:
def stop(self): def stop(self):
self.isActive = False self.isActive = False
self.update_timer.stop() self.update_timer.stop()
# TODO: Почему монитор директорий обновляет какие-то настройки, графики и т.д.? Помним про
# принцип единичной ответственности.
def update_settings(self, data: list[dict]) -> None: def update_settings(self, data: list[dict]) -> None:
... ...
@ -96,6 +105,7 @@ class BaseDirectoryMonitor:
... ...
def force_all_dir(self): def force_all_dir(self):
# TODO: По сигнатуре метода вообще не понятно, что он должен делать.
... ...
class BaseDataConverter: class BaseDataConverter:
@ -230,6 +240,9 @@ class BasePlotWidget:
}, },
} }
def set_style(self, object: Union[QTabWidget, QWidget]) -> None: def set_style(self, object: Union[QTabWidget, QWidget]) -> None:
# TODO: Данный метод статичный. Для обозначения подобных методов используется декоратор @staticmethod.
# TODO: object - зарезервированное слово. Использовать эти слова для именования переменных не правильно.
# Если хочется использовать именно такое слово, можно написать "object_".
object.setStyleSheet( object.setStyleSheet(
"""QLabel { """QLabel {
color: #ffffff; color: #ffffff;
@ -239,6 +252,8 @@ class BasePlotWidget:
}""") }""")
def _downsample_data(self, x, y, max_points=5000): def _downsample_data(self, x, y, max_points=5000):
# TODO: Данный метод статичный. Для обозначения подобных методов используется декоратор @staticmethod.
# TODO: Какой тип данных у 'x'? Какой тип данных у 'y'? Надо добавить аннотации типов.
""" """
Понижает разрешение данных до заданного количества точек для улучшения производительности навигатора. Понижает разрешение данных до заданного количества точек для улучшения производительности навигатора.
""" """
@ -273,12 +288,13 @@ class BasePlotWidget:
# Связываем изменение региона навигатора с обновлением области просмотра основного графика # Связываем изменение региона навигатора с обновлением области просмотра основного графика
ROI_region.sigRegionChanged.connect(lambda: self._sync_main_plot_with_navigator(main_plot, ROI_region)) ROI_region.sigRegionChanged.connect(lambda: self._sync_main_plot_with_navigator(main_plot, ROI_region))
# TODO: Возвращаемый результат не соответствует аннотированному в сигнатуре метода.
return navigator, ROI_region return navigator, ROI_region
def _sync_main_plot_with_navigator(self, def _sync_main_plot_with_navigator(self,
main_plot: pg.PlotItem, main_plot: pg.PlotItem,
region: pg.LinearRegionItem): region: pg.LinearRegionItem):
# TODO: Данный метод статичный. Для обозначения подобных методов используется декоратор @staticmethod.
""" """
Синхронизирует область просмотра основного графика с регионом навигатора. Синхронизирует область просмотра основного графика с регионом навигатора.
""" """
@ -288,11 +304,14 @@ class BasePlotWidget:
main_plot.setXRange(x_min, x_max, padding=0) main_plot.setXRange(x_min, x_max, padding=0)
main_plot.blockSignals(False) main_plot.blockSignals(False)
# TODO: Методы _mirror_shift_data и _shift_data нарушают принцип DRY: дублирование кода.
# Сделать ОДИН метод, одним из входных параметров которого будет lambda-функция, которая применяется к dataframe.
def _mirror_shift_data(self, def _mirror_shift_data(self,
valid_str: str, valid_str: str,
signals: list[dict], signals: list[dict],
dataframe: pd.DataFrame, dataframe: pd.DataFrame,
shift: float) -> pd.DataFrame: shift: float) -> pd.DataFrame:
# TODO: Данный метод статичный. Для обозначения подобных методов используется декоратор @staticmethod.
keys = dataframe.keys() keys = dataframe.keys()
for signal in signals: for signal in signals:
if valid_str in signal["name"] and signal["name"] in keys: if valid_str in signal["name"] and signal["name"] in keys:
@ -304,6 +323,7 @@ class BasePlotWidget:
signals: list[dict], signals: list[dict],
dataframe: pd.DataFrame, dataframe: pd.DataFrame,
shift: float) -> pd.DataFrame: shift: float) -> pd.DataFrame:
# TODO: Данный метод статичный. Для обозначения подобных методов используется декоратор @staticmethod.
keys = dataframe.keys() keys = dataframe.keys()
for signal in signals: for signal in signals:
if valid_str in signal["name"] and signal["name"] in keys: if valid_str in signal["name"] and signal["name"] in keys:
@ -311,6 +331,7 @@ class BasePlotWidget:
return dataframe return dataframe
def _sync_navigator_with_main(self, main_plot: pg.PlotItem, region:pg.LinearRegionItem): def _sync_navigator_with_main(self, main_plot: pg.PlotItem, region:pg.LinearRegionItem):
# TODO: Данный метод статичный. Для обозначения подобных методов используется декоратор @staticmethod.
""" """
Синхронизирует регион навигатора с областью просмотра основного графика. Синхронизирует регион навигатора с областью просмотра основного графика.
""" """
@ -334,6 +355,8 @@ class BasePlotWidget:
@opt.setter @opt.setter
def opt(self, opt: BaseIdealDataBuilder): def opt(self, opt: BaseIdealDataBuilder):
# TODO: Атрибуты класса следует сначала определять в конструкторе класса.
# TODO: Что такое opt? Optical? Option? Optimal? Optimus Prime?
self._opt = opt self._opt = opt
def build(self, data: list[pd.DataFrame]) -> list[QWidget]: def build(self, data: list[pd.DataFrame]) -> list[QWidget]:
@ -349,9 +372,11 @@ class BaseController(QObject):
... ...
def raport_mode (self) -> None: def raport_mode (self) -> None:
# TODO: обычно в названиях методов должен присутствовать глагол.
... ...
def seeking_mode(self) -> None: def seeking_mode(self) -> None:
# TODO: обычно в названиях методов должен присутствовать глагол.
... ...
def open_file(self, filepath: str) -> None: def open_file(self, filepath: str) -> None:
@ -429,6 +454,7 @@ class BaseMainWindow(QMainWindow):
@controller.setter @controller.setter
def controller(self, controller: BaseController) -> None: def controller(self, controller: BaseController) -> None:
# TODO: зачем делать setter для контроллера, если ты его экземпляр передаешь в конструктор?
self._controller = controller self._controller = controller
def set_style(self, object: Union[QTabWidget, QWidget, QMainWindow]) -> None: def set_style(self, object: Union[QTabWidget, QWidget, QMainWindow]) -> None:

View File

@ -9,27 +9,34 @@ class DiagramParser:
system_config["Welding_signal"], system_config["Welding_signal"],
system_config["Release_signal"], system_config["Release_signal"],
system_config["Oncomming_signal"]] system_config["Oncomming_signal"]]
# TODO: Переменные в Python именуются в lowercase либо snake_case.
self.boolDict = {} self.boolDict = {}
self.floatDict = {} self.floatDict = {}
self.timingsDict = {} self.timingsDict = {}
self.theor_mode = False self.theor_mode = False
def setData(self, path): def setData(self, path):
# TODO: Методы в Python именуются в lowercase либо snake_case.
# TODO: Данный метод устанавливает какой-то режим, читает CSV, наполняет словари некими данными.
# Декомпозировать на разные методы, каждый из которых будет решать какую-то свою задачу.
if not path: if not path:
self.theor_mode = True self.theor_mode = True
else: else:
self.data = pd.read_csv(path) self.data = pd.read_csv(path)
# TODO: 2 цикла ниже: дублирование кода. Устранить.
for signalName in self.data.columns: for signalName in self.data.columns:
# TODO: if isinstance...
if type (self.data[signalName].iloc[0]) == np.bool: if type (self.data[signalName].iloc[0]) == np.bool:
self.boolDict[signalName] = self._getBoolChanges(signalName) self.boolDict[signalName] = self._getBoolChanges(signalName)
for signalName in self.data.columns: for signalName in self.data.columns:
# TODO: if isinstance...
if type (self.data[signalName].iloc[0]) == np.float64: if type (self.data[signalName].iloc[0]) == np.float64:
self.floatDict[signalName] = self._getFloatChanges(signalName) self.floatDict[signalName] = self._getFloatChanges(signalName)
for key, items in self.boolDict.items(): for key, items in self.boolDict.items():
# TODO: Писать так условия - моветон. Как вариант, можно применить конструкцию match - case.
if key == self.signals[0]: name = "closure" if key == self.signals[0]: name = "closure"
elif key == self.signals[1]: name = "compression" elif key == self.signals[1]: name = "compression"
elif key == self.signals[2]: name = "welding" elif key == self.signals[2]: name = "welding"
@ -46,22 +53,34 @@ class DiagramParser:
self.timingsDict[name].append([items[i][0], items[i][0]+0.01]) self.timingsDict[name].append([items[i][0], items[i][0]+0.01])
def getBoolDict (self) -> dict: def getBoolDict (self) -> dict:
# TODO: сделать через декоратор @property:
# @property
# def bool_dict(self) -> dict:
# return self._bool_dict
# TODO: Методы в Python именуются в lowercase либо snake_case.
return self.boolDict return self.boolDict
def getFloatDict (self) -> dict: def getFloatDict (self) -> dict:
# TODO: сделать через декоратор @property:
# TODO: Методы в Python именуются в lowercase либо snake_case.
return self.floatDict return self.floatDict
def getRealTimings(self) -> dict: def getRealTimings(self) -> dict:
# TODO: сделать через декоратор @property:
# TODO: Методы в Python именуются в lowercase либо snake_case.
return self.timingsDict return self.timingsDict
def getMode(self) -> bool: def getMode(self) -> bool:
# TODO: сделать через декоратор @property:
# TODO: Методы в Python именуются в lowercase либо snake_case.
return self.theor_mode return self.theor_mode
def _getBoolChanges(self, signalName) -> list: def _getBoolChanges(self, signalName) -> list:
# TODO: Методы в Python именуются в lowercase либо snake_case.
timeCode = self.data['time'] timeCode = self.data['time']
signal_values = self.data[signalName] signal_values = self.data[signalName]
changes = [] changes = []
# TODO: if len(signal_values)...
if len(signal_values) > 0: if len(signal_values) > 0:
changes.append([float(timeCode.iloc[0]), 'high' if bool(signal_values.iloc[0]) else 'low']) changes.append([float(timeCode.iloc[0]), 'high' if bool(signal_values.iloc[0]) else 'low'])
@ -73,6 +92,7 @@ class DiagramParser:
def _getFloatChanges(self, signalName) -> list: def _getFloatChanges(self, signalName) -> list:
# TODO: Методы в Python именуются в lowercase либо snake_case.
timeCode = self.data['time'] timeCode = self.data['time']
signal_values = self.data[signalName] signal_values = self.data[signalName]
changes = [] changes = []

View File

@ -1,3 +1,5 @@
# TODO: Было бы логично разместить данный модуль в каталоге gui.
dark_style = """ dark_style = """
/* /*
------------------------------------------------------ ------------------------------------------------------