diff --git a/gui_source/en_US.qm b/gui_source/en_US.qm index 4932169..4c0d0e9 100644 Binary files a/gui_source/en_US.qm and b/gui_source/en_US.qm differ diff --git a/gui_source/en_US.ts b/gui_source/en_US.ts index 690f052..34b4d78 100644 --- a/gui_source/en_US.ts +++ b/gui_source/en_US.ts @@ -192,170 +192,205 @@ DialogSettings - + 连接设置 Connection Settings - + - NRF24L01 Adapter - - + 串口号 Com Port - + 自动 Auto - + 波特率 Baudrate - + 无线地址 Address - + AA - + : - + 无线频道 Channel - + Mhz - + 无线功率 TX Power - + 7dBm - + 4dBm - + 3dBm - + 1dBm - + 0dBm - + -4dBm - + -6dBm - + -12dBm - + - MDP-P906 - - + 自动配对 AutoMatch - + IDCODE - + 滚轮颜色 Wheel Color - + #66CCFF - + #RRGGBB - + M01 通道 M01 CH - + CH- - + 连接指示灯 Indicator - + 常亮 Static - + 闪烁 Blink - + 应用 / Apply Apply - + 确定 / OK OK + + + 其它设置 + Other + + + + 开启输出前显示警告 + Show warning before turning on output + + + + 输出开启警告 + Output Warning + + + + 输出状态下不允许包括辅助功能在内的设定值修改操作 + Volt and current changes is not allowed when output is on + + + + 输出时锁定参数 + Lock U/I when output + + + + 硬件锁定时仍允许上位机修改输出状态 + Allow modifying the output when hardware is locked + + + + 忽略硬件锁定 + Ignore hardware lock + MDPGraphics - + 图形设置 Graphics Settings @@ -363,355 +398,360 @@ MDPMainwindow - + 未连接 Disconnected - + 已连接 Connected - + 警告 Warning - + IDCODE为空, 请先完成连接设置 IDCODE is empty, please complete the connection settings first - + 连接失败 Connect failed - + 电压 Voltage - + 电流 Current - + 功率 Power - + 阻值 Resistence - + Off - + 解除 REL - + 保持 HOLD - + 适应 AUTO - + 手动 MANUAL - + 停止 STOP - + 录制完成 Record finished - + 数据已保存至: Data has been saved to: - + 录制 REC - + 保存成功 Save OK - + 保存预设失败 Save failed - + 保存失败 Save failed - + 保存 Save - + 非法参数 Illegal Parameters - + 功能已关闭 Disabled - + 功能已开启 Enabled - + 正弦波 Sine - + 方波 Square - + 三角波 Triangle - + 锯齿波 Sawtooth - + 噪音 Noise - + 编辑动作 Edit action - + 请确保修改后动作文本格式正确,否则无法识别动作 Please make sure that the text of the modified action is correctly formatted, otherwise it will not be recognized - + 确定要清空序列吗? Are you sure? - + 编辑 Edit - + 删除 Delete - + 清空 Clear all - + 错误 Error - + 时间格式错误 Time format error - + 文本文件 (*.txt) Text file (*.txt) - + 打开 Open - + 数据验证错误: Data validation failed: - + 选择预设 Select Preset - + MDP-P906 数控电源上位机 MDP-P906 Digital Supply Controller - + 添加动作 Add Action - + 请输入延时时间: Input delay time: - + 请输入等待时间: Input wait time: - + 格式: 年-月-日 时:分:秒 Format: YY-MM-DD HH:MM:SS - + 请输入电压值: Input voltage-set: - + 请输入电流值: Input current-set: - + 打开文件路径 Open Path - + 确定要清空数据缓冲区吗? Sure you want to empty the data buffer? - + 扫描响应记录为空 Scan response record is empty - + 扫描响应结果曲线 Scan Response Result Curve - + CSV文件 (*.csv) CSV files (*.csv) - + SOC SOC - + 数据缓冲区占用率 Data buffer used - + 保存数据 Dump data to - + 保存完成 Dump success + + + 确定要打开输出? + + MDPSettings - + 自动 Auto - + 闪烁 Blink - + 常亮 Static - + 错误 Error - + 请先断开连接 Please disconnect first - + 自动配对失败 AutoMatch Failed - + 自动配对成功 AutoMatch Success - + 连接设置 Connection Settings - + 重新连接生效 Reconnect to take effect - + 应用 / Apply Apply - + 颜色格式错误 Color Format Error - + 请输入16进制RGB颜色代码(例如: 66CCFF) Enter hexadecimal RGB color (e.g.66CCFF) @@ -719,665 +759,670 @@ MainWindow - + MDP-P906 数控电源上位机 MDP-P906 Digital Supply Controller - + 电压 (V) VOUT (V) - + 电流 (A) IOUT (A) - + 功率 (W) POWER (W) - + 能量 (J) ENERGY (J) - + 平均功率 AVG-P - + 负载阻值 LOAD-R - + 输出设定 / OUTPUT OUTPUT SETTING - + 输出状态 State - + 设定电压 Voltage - + V - + 设定电流 Current - + A - + 辅助功能 / AUX FUNC AUXILIARY FUNCTION - + 1 - + 2 - + 3 - + 4 - + 5 - + 6 - + 7 - + 8 - + 9 - + 修改预设 Edit - + 保存 Save - + 预设组 Presets - + 功能已关闭 Disabled - + 目标功率 Target P - + 闭环参数 PID K - + 电压上限 Max Volt - + 执行频率 Act Freq - + 目标参数 Target - + 电压 Voltage - + 电流 Current - + 起始点 Start - + 结束点 End - + 步进值 Step - + 波形类型 Type - + 方波 Square - + 正弦波 Sine - + 三角波 Triangle - + 锯齿波 Sawtooth - + 噪音 Noise - + 周期 Period - + 高电平 High - + 低电平 Low - + 延迟 Delay - + 等待 Wait - + 载入 Load - + 单次 Run - + 循环 Loop - + 停止 Stop - + 系统状态 / SYSTEM STATE SYSTEM STATE - + NO ERROR - + 0.00V 0.00A - + UNLOCKED - + 图形设置 GRAPHICS SET - + 连接设置 CONNECT SET - + 未连接 Disconnected - + 连接/断开 LINK/UNLINK - + 数据波形 / LINE CHART LINE CHART - + 显示数据: Data: - + 功率 Power - + 阻值 Resistence - + Off - + 采样率: Sample: - + 10Hz - + 20Hz - + 30Hz - + 40Hz - + 50Hz - + 60Hz - + 70Hz - + 80Hz - + 90Hz - + 100Hz - + 0.0Hz - + 录制 REC - + 适应 AUTO - + 保持 HOLD - + 清空 CLEAR - + 回零 ZERO - + No Info - + N/A - + None - + 0kBps - + CON-ERR 0% - + TEMP 30.0℃ - + W - + Hz - + s - + 浮窗 FLOAT - + 快速设定:当设定电压/电流变化时立刻提交 Quick Setup: Submit immediately when V/I setting changes - + 上窗口数据 Upper graph window - + 下窗口数据 Lower graph window - + 设备输出数据请求频率 Output data sample rate - + 将原始数据记录到CSV文件,不受图形缓冲区限制 Record the raw data to a CSV file without being limited by the graphics buffer - + 切换数据波形是否自动适应窗口 Toggle waveform automatical adaption to window - + 停止数据波形刷新(数据缓冲区仍在更新) Stop refreshing the data waveform (the data buffer continues to update) - + 清空波形数据缓冲区 Clear the waveform data buffer - + 切换数据监控悬浮窗 Switch the data monitoring floating window - + 清零平均功率和能量累计 Reset the average power and energy accumulation - + 实时采样率 Realtime sample rate - + 扫描响应 Response - + 不记录 Off - + 查看响应曲线 View Response Curve - + 功率闭环 Power Keep - + 直流扫描 DC Sweep - + 函数发生器 Function Generator - + 序列执行 Sequence - + Discharge Time: 00:00:00 - + 加载曲线 Load Curve - + 查看曲线 View Curve - + 放电曲线 DisChr Curve - + 当前电量 Cur Level - + % - + 截止电量 Stop Level - + 单节容量 Capacity/S - + Wh - + 单节内阻 InterRes/S - + - + 多节串联 Series - + 电池模拟 Battery Simulator - + S - + - + - + 0.0% - + 显示范围 Display range / pts - + 0 - + 导出当前数据缓冲区的有效数据 Export valid data from data buffer - + 导出 DUMP + + + Title + + ResultGraphWindow - + 多项式拟合次数: Polynomial Fitting Order: @@ -1385,34 +1430,52 @@ TransparentFloatingWindow - + 电压 U Voltage - + 电流 I Current - + 功率 P Power - + 折叠 Collapse - + 展开 Expand - + 关闭 Close + + app + + + 主题缺乏颜色 + Theme lacks color + + + + 当前主题中缺乏颜色项目: + There is a lack of color items in the current theme: + + + + 退出 + Exit + + diff --git a/gui_source/mdp_custom.py b/gui_source/mdp_custom.py index 564b4e3..097aeaa 100644 --- a/gui_source/mdp_custom.py +++ b/gui_source/mdp_custom.py @@ -74,6 +74,7 @@ def __init__( message, question=False, additional_actions: List[Tuple[str, Callable[[], bool]]] = [], + override_key_strs: List[str] = [], ) -> None: super().__init__(parent) self.setWindowTitle(title) @@ -113,17 +114,23 @@ def __init__( self.horizontalLayout.setSpacing(8) layout.addLayout(self.horizontalLayout) if not question: - self.okButton = QtWidgets.QPushButton(self.tr("确定"), self) + self.okButton = QtWidgets.QPushButton( + self.tr("确定") if not override_key_strs else override_key_strs[0], self + ) self.okButton.setFont(global_font) self.okButton.clicked.connect(self.close) self.horizontalLayout.addWidget(self.okButton) else: - self.okButton = QtWidgets.QPushButton(self.tr("是"), self) + self.okButton = QtWidgets.QPushButton( + self.tr("是") if not override_key_strs else override_key_strs[0], self + ) self.okButton.setFont(global_font) self.okButton.clicked.connect(self.accept) self.horizontalLayout.addWidget(self.okButton) - self.cancelButton = QtWidgets.QPushButton(self.tr("否"), self) + self.cancelButton = QtWidgets.QPushButton( + self.tr("否") if not override_key_strs else override_key_strs[1], self + ) self.cancelButton.setFont(global_font) self.cancelButton.clicked.connect(self.reject) self.horizontalLayout.addWidget(self.cancelButton) diff --git a/gui_source/mdp_gui.py b/gui_source/mdp_gui.py index 9063dfd..03ed6d6 100644 --- a/gui_source/mdp_gui.py +++ b/gui_source/mdp_gui.py @@ -75,7 +75,7 @@ ICON_PATH = os.path.join(ABS_PATH, "icon.ico") FONT_PATH = os.path.join(ABS_PATH, "SarasaFixedSC-SemiBold.ttf") BAT_EXAMPLE_PATH = os.path.join(ABS_PATH, "Li-ion.csv") -VERSION = "Ver4.1" +VERSION = "Ver4.2" qdarktheme.enable_hi_dpi() app = QtWidgets.QApplication(sys.argv) @@ -207,7 +207,7 @@ def float_str(value, limit=1e5): def set_color(widget: QtWidgets.QWidget, rgb): - if rgb is None: + if not rgb or rgb == "default": widget.setStyleSheet("") return color = f"rgb({rgb[0]},{rgb[1]},{rgb[2]})" if isinstance(rgb, tuple) else rgb @@ -258,13 +258,22 @@ def __init__(self) -> None: self.vset_cali_b = 0.0 self.iset_cali_k = 1.0 self.iset_cali_b = 0.0 - self.theme = "dark" - self.color_palette_v2 = { + self.output_warning = False + self.lock_when_output = False + self.ignore_hw_lock = False + self.theme = "Dark" + self.color_palette = { "dark": { "off": "khaki", "on": "lightgreen", "cv": "skyblue", "cc": "tomato", + "lcd_voltage": "default", + "lcd_current": "default", + "lcd_power": "default", + "lcd_energy": "default", + "lcd_avg_power": "default", + "lcd_resistance": "default", "general_green": "mediumaquamarine", "general_red": "orangered", "general_yellow": "yellow", @@ -273,6 +282,12 @@ def __init__(self) -> None: "line2": "turquoise", }, "light": { + "lcd_voltage": "default", + "lcd_current": "default", + "lcd_power": "default", + "lcd_energy": "default", + "lcd_avg_power": "default", + "lcd_resistance": "default", "off": "darkgoldenrod", "on": "darkgreen", "cv": "darkblue", @@ -284,17 +299,42 @@ def __init__(self) -> None: "line1": "orangered", "line2": "darkcyan", }, + "modify_this_to_add_your_custom_theme": { + "based_on_dark_or_light": "dark", + "any_other_item": "any_other_color", + }, } def save(self, filename): - with open(filename, "w") as f: + with open(filename, "w", encoding="utf-8") as f: json.dump(self.__dict__, f, indent=4, ensure_ascii=False) def load(self, filename): if not os.path.exists(filename): self.save(filename) return - self.__dict__.update(json.load(open(filename, "r"))) + def_colorplate = self.color_palette + self.__dict__.update(json.load(open(filename, "r", encoding="utf-8"))) + for k, v in def_colorplate.items(): + if k not in self.color_palette: + self.color_palette[k] = v + elif len(self.color_palette[k]) < len(v): + v.update(self.color_palette[k]) + self.color_palette[k] = v + + def get_color(self, key, override_theme=None): + t = override_theme or self.theme + if t in ("dark", "light"): + return self.color_palette[t].get(key) + else: + if "based_on_dark_or_light" not in self.color_palette[t]: + self.color_palette[t]["based_on_dark_or_light"] = "dark" + return self.color_palette[t].get( + key, + self.color_palette[self.color_palette[t]["based_on_dark_or_light"]].get( + key + ), + ) def __repr__(self) -> str: return f"Setting({self.__dict__})" @@ -325,10 +365,11 @@ class MDPMainwindow(QtWidgets.QMainWindow, FramelessWindow): # QtWidgets.QMainW data_fps = 50 graph_keep_flag = False graph_record_flag = False - output_state = False locked = False _v_set = 0.0 _i_set = 0.0 + _output_state = False + output_state_str = "" open_r = 1e7 continuous_energy_counter = 0 model = "Unknown" @@ -453,6 +494,8 @@ def initTimer(self): self.func_bat_sim_timer.timeout.connect(self.func_bat_sim) self.graph_record_save_timer = QtCore.QTimer(self) self.graph_record_save_timer.timeout.connect(self.graph_record_save) + self.stable_checker_timer = QtCore.QTimer(self) + self.stable_checker_timer.timeout.connect(self.stable_checker) def initSignals(self): self.ui.comboDataFps.currentTextChanged.connect(self.set_data_fps) @@ -496,6 +539,14 @@ def stopMyTimer(self): self.stop_func_wave_gen() if self.func_keep_power_timer.isActive(): self.stop_func_keep_power() + if self.func_seq_timer.isActive(): + self.stop_func_seq() + if self.func_bat_sim_timer.isActive(): + self.stop_func_bat_sim() + if self.graph_record_save_timer.isActive(): + self.on_btnGraphRecord_clicked() + if self.stable_checker_timer.isActive(): + self.stable_checker_timer.stop() def switch_fullscreen(self): if self.isFullScreen(): @@ -544,10 +595,11 @@ def v_set(self): @v_set.setter def v_set(self, value): - self._v_set = value - self.ui.spinBoxVoltage.setValue(value) if self.api is None or self.locked: return + value = max(0, min(value, 30)) + self._v_set = value + self.ui.spinBoxVoltage.setValue(value) if setting.use_cali: value = value * setting.vset_cali_k + setting.vset_cali_b self.api.set_voltage(value) @@ -559,15 +611,39 @@ def i_set(self): @i_set.setter def i_set(self, value): - self._i_set = value - self.ui.spinBoxCurrent.setValue(value) if self.api is None or self.locked: return + value = max(0, min(value, 10)) + self._i_set = value + self.ui.spinBoxCurrent.setValue(value) if setting.use_cali: value = value * setting.iset_cali_k + setting.iset_cali_b self.api.set_current(value) self._last_state_change_t = time.perf_counter() + @property + def output_state(self): + return self._output_state + + @output_state.setter + def output_state(self, value): + if self.api is None or value == self._output_state: + return + if setting.output_warning and not self._output_state: + ok = CustomMessageBox.question( + self, + self.tr("警告"), + self.tr("确定要打开输出?") + + f"\n{self.tr('电压')}: {self.v_set:.3f}V" + + f"\n{self.tr('电流')}: {self.i_set:.3f}A", + ) + if not ok: + return + self._output_state = value + self._last_state_change_t = time.perf_counter() + self.api.set_output(self._output_state) + self.update_state() + def update_state(self): if self.api is None: return @@ -575,13 +651,9 @@ def update_state(self): errrate = self.api.speed_counter.error_rate * 100 self.ui.labelErrRate.setText(f"CON-ERR {errrate:.0f}%") clr = ( - setting.color_palette_v2[setting.theme]["general_red"] + setting.get_color("general_red") if errrate > 50 - else ( - setting.color_palette_v2[setting.theme]["general_yellow"] - if errrate > 10 - else None - ) + else (setting.get_color("general_yellow") if errrate > 10 else None) ) set_color(self.ui.labelErrRate, clr) set_color(self.ui.labelComSpeed, clr) @@ -612,11 +684,18 @@ def update_state(self): self.ui.btnOutput.setText(f"- {State.upper()} -") set_color( self.ui.btnOutput, - setting.color_palette_v2[setting.theme][State], + setting.get_color(State), ) - self.output_state = State != "off" - self.locked = Locked - self.ui.frameOutputSetting.setEnabled(not Locked) + self._output_state = State != "off" + self.output_state_str = State + self.locked = False + if not setting.ignore_hw_lock and Locked: + self.locked = True + self.ui.frameOutputSetting.setEnabled(not self.locked) + if setting.lock_when_output and self._output_state: + self.locked = True + self.ui.spinBoxVoltage.setEnabled(not self.locked) + self.ui.spinBoxCurrent.setEnabled(not self.locked) if SetVoltage >= 0: if setting.use_cali: SetVoltage = (SetVoltage - setting.vset_cali_b) / setting.vset_cali_k @@ -632,7 +711,7 @@ def update_state(self): self.ui.labelLockState.setText("[LOCKED]" if Locked else "UNLOCKED") set_color( self.ui.labelLockState, - setting.color_palette_v2[setting.theme]["general_red"] if Locked else None, + setting.get_color("general_red") if Locked else None, ) self.ui.labelInputVals.setText(f"{InputVoltage:.2f}V {InputCurrent:.2f}A") self.ui.labelTemperature.setText(f"TEMP {Temperature:.1f}℃") @@ -643,12 +722,30 @@ def update_state(self): @QtCore.pyqtSlot() def on_btnOutput_clicked(self): - if self.api is None: - return self.output_state = not self.output_state - self._last_state_change_t = time.perf_counter() - self.api.set_output(self.output_state) - self.update_state() + + _stable_callback = None + _stable_start_t = 0 + + def stable_checker(self): + dt = time.perf_counter() - self._stable_start_t + stable = ( + self.output_state_str == "cc" + or abs(self.data.voltages[self.data.update_count - 1] - self.v_set) < 0.1 + or dt > 10 + ) + if stable: + logger.info(f"Output is stable after {dt:.3f}s") + if self._stable_callback is not None: + self._stable_callback() + self._stable_callback = None + self.stable_checker_timer.stop() + + def wait_output_stable(self, func): + self.output_state = True + self._stable_callback = func + self._stable_start_t = time.perf_counter() + self.stable_checker_timer.start(50) def close_state_ui(self): self.ui.labelConnectState.setText(self.tr("未连接")) @@ -694,7 +791,7 @@ def open_state_ui(self): self.ui.labelConnectState.setText(self.tr("已连接")) set_color( self.ui.labelConnectState, - setting.color_palette_v2[setting.theme]["general_green"], + setting.get_color("general_green"), ) self.ui.frameOutputSetting.setEnabled(True) self.ui.frameGraph.setEnabled(True) @@ -945,12 +1042,8 @@ def initGraph(self): self.ui.widgetGraph2.showGrid(x=True, y=True) self.ui.widgetGraph1.setMouseEnabled(x=False, y=False) self.ui.widgetGraph2.setMouseEnabled(x=False, y=False) - self.pen1 = pg.mkPen( - color=setting.color_palette_v2[setting.theme]["line1"], width=1 - ) - self.pen2 = pg.mkPen( - color=setting.color_palette_v2[setting.theme]["line2"], width=1 - ) + self.pen1 = pg.mkPen(color=setting.get_color("line1"), width=1) + self.pen2 = pg.mkPen(color=setting.get_color("line2"), width=1) self.curve1 = self.ui.widgetGraph1.plot(pen=self.pen1, clear=True) self.curve2 = self.ui.widgetGraph2.plot(pen=self.pen2, clear=True) self._graph_auto_scale_flag = True @@ -964,12 +1057,8 @@ def initGraph(self): self.set_graph2_data(self.tr("电流"), skip_update=True) def update_pen(self): - self.pen1.setColor( - QtGui.QColor(setting.color_palette_v2[setting.theme]["line1"]) - ) - self.pen2.setColor( - QtGui.QColor(setting.color_palette_v2[setting.theme]["line2"]) - ) + self.pen1.setColor(QtGui.QColor(setting.get_color("line1"))) + self.pen2.setColor(QtGui.QColor(setting.get_color("line2"))) def get_data(self, text: str, display_pts: int, r_offset: int = 0): if text == self.tr("电压"): @@ -1106,7 +1195,7 @@ def draw_graph(self): if update_count > self.data.data_length * 0.95: set_color( self.ui.labelBufferSize, - setting.color_palette_v2[setting.theme]["general_yellow"], + setting.get_color("general_yellow"), ) _ = self._typename_dict.get(type1) @@ -1409,13 +1498,12 @@ def on_btnSweep_clicked(self): elif self._sweep_target == self.tr("电流"): self.ui.spinBoxCurrent.setEnabled(False) self.ui.scrollAreaSweep.setEnabled(False) + + def start_sweep(): + self.func_sweep_timer.start(round(self._sweep_delay * 1000)) + self.v_set = self._sweep_start - if not self.output_state: - self.on_btnOutput_clicked() - QtCore.QTimer.singleShot( - 200, # wait for 0.2s to ensure output is stable - lambda: self.func_sweep_timer.start(round(self._sweep_delay * 1000)), - ) + self.wait_output_stable(start_sweep) @QtCore.pyqtSlot() def on_btnSweepShowRecord_clicked(self): @@ -1527,14 +1615,18 @@ def on_btnWaveGen_clicked(self): 1000, lambda: self.ui.btnWaveGen.setText(self.tr("功能已关闭")) ) return - self._wavegen_start_time = time.perf_counter() - self.func_wave_gen_timer.start(round(1000 / self._wavegen_loopfreq)) self.ui.btnWaveGen.setText(self.tr("功能已开启")) self.ui.spinBoxWaveGenLoopFreq.setEnabled(False) self.ui.spinBoxVoltage.setEnabled(False) - if not self.output_state: + self._wavegen_start_time = 0 + + def start_wave_gen(): + self._wavegen_start_time = time.perf_counter() + self.func_wave_gen_timer.start(round(1000 / self._wavegen_loopfreq)) self.v_set = self._wavegen_lowlevel - self.on_btnOutput_clicked() + + self.v_set = self._wavegen_lowlevel + self.wait_output_stable(start_wave_gen) def stop_func_wave_gen(self): self.func_wave_gen_timer.stop() @@ -1749,15 +1841,9 @@ def on_btnBatSim_clicked(self): self._bat_sim_internal_r = ( self.ui.spinBoxBatSimRes.value() / 1000 ) # mOhm->Ohm - self.func_bat_sim_timer.start( - round(1000 / self.ui.spinBoxBatSimLoopFreq.value()) - ) self._bat_sim_last_e_temp = self.continuous_energy_counter - self._bat_sim_start_time = time.perf_counter() self.ui.labelBatSimTime.setText("Discharge Time: 00:00:00") self.ui.btnBatSim.setText(self.tr("功能已开启")) - if not self.output_state: - self.on_btnOutput_clicked() self.set_batsim_widget_enabled(False) self.display_data_signal.emit( self._bat_sim_soc.tolist(), @@ -1770,13 +1856,21 @@ def on_btnBatSim_clicked(self): True, ) + def start_bat_sim(): + self.func_bat_sim_timer.start( + round(1000 / self.ui.spinBoxBatSimLoopFreq.value()) + ) + self._bat_sim_start_time = time.perf_counter() + + self.v_set = 0 + self.wait_output_stable(start_bat_sim) + def func_bat_sim(self): add_e = self.continuous_energy_counter - self._bat_sim_last_e_temp self._bat_sim_last_e_temp += add_e self._bat_sim_used_energy += add_e if self._bat_sim_used_energy >= self._bat_sim_stop_energy: - if self.output_state: - self.on_btnOutput_clicked() + self.output_state = False self.on_btnBatSim_clicked() new_percent = ( (self._bat_sim_total_energy - self._bat_sim_used_energy) @@ -2149,6 +2243,9 @@ def initValues(self): self.ui.comboBoxBlink.setCurrentText( self.tr("闪烁") if setting.blink else self.tr("常亮") ) + self.ui.checkBoxOutputWarn.setChecked(setting.output_warning) + self.ui.checkBoxSetLock.setChecked(setting.lock_when_output) + self.ui.checkBoxIgnoreHWLock.setChecked(setting.ignore_hw_lock) def refreshPorts(self): self.ui.comboBoxPort.clear() @@ -2241,6 +2338,9 @@ def save_settings(self): else "" ) setting.blink = self.ui.comboBoxBlink.currentText() == self.tr("闪烁") + setting.output_warning = self.ui.checkBoxOutputWarn.isChecked() + setting.lock_when_output = self.ui.checkBoxSetLock.isChecked() + setting.ignore_hw_lock = self.ui.checkBoxIgnoreHWLock.isChecked() setting.save(SETTING_FILE) @QtCore.pyqtSlot() @@ -2257,6 +2357,18 @@ def on_btnOk_clicked(self): self.save_settings() self.close() + @QtCore.pyqtSlot(int) + def on_checkBoxIgnoreHWLock_stateChanged(self, state: int): + setting.ignore_hw_lock = state == QtCore.Qt.CheckState.Checked + + @QtCore.pyqtSlot(int) + def on_checkBoxSetLock_stateChanged(self, state: int): + setting.lock_when_output = state == QtCore.Qt.CheckState.Checked + + @QtCore.pyqtSlot(int) + def on_checkBoxOutputWarn_stateChanged(self, state: int): + setting.output_warning = state == QtCore.Qt.CheckState.Checked + class MDPGraphics(QtWidgets.QDialog, FramelessWindow): set_max_fps_sig = QtCore.pyqtSignal(float) @@ -2286,6 +2398,10 @@ def __init__(self, parent=None): self.ui.checkBoxOpenGL.setEnabled(False) self.ui.checkBoxOpenGL.setChecked(False) + for k in setting.color_palette: + if k not in ("dark", "light", "modify_this_to_add_your_custom_theme"): + self.ui.comboTheme.addItem(k) + def initValues(self): self.ui.spinMaxFps.setValue(setting.graph_max_fps) self.ui.spinStateFps.setValue(setting.state_fps) @@ -2308,7 +2424,7 @@ def initValues(self): self.ui.checkBoxAntialias.setChecked(setting.antialias) self.ui.checkBoxOpenGL.setChecked(setting.opengl) self.ui.comboTheme.setCurrentIndex( - {"light": 1, "dark": 0}.get(setting.theme, 0) + {"light": 1, "dark": 0}.get(setting.theme, setting.theme) ) self.ui.comboInput.setCurrentIndex(int(not setting.bitadjust)) @@ -2323,7 +2439,7 @@ def on_comboInput_currentIndexChanged(self, index): @QtCore.pyqtSlot(int) def on_comboTheme_currentIndexChanged(self, index): - set_theme({0: "dark", 1: "light"}[index]) + set_theme({0: "dark", 1: "light"}.get(index, self.ui.comboTheme.currentText())) @QtCore.pyqtSlot(int) def on_checkBoxAntialias_stateChanged(self, state: int): @@ -2488,29 +2604,29 @@ def __init__(self): self.v_label = QtWidgets.QLabel(self.tr("电压 U"), self) self.v_label.setFont(font_title) - set_color(self.v_label, setting.color_palette_v2["dark"]["general_red"]) + set_color(self.v_label, setting.get_color("general_red", "dark")) layout.addWidget(self.v_label) self.voltage_label = QtWidgets.QLabel("", self) self.voltage_label.setFont(font_value) - set_color(self.voltage_label, setting.color_palette_v2["dark"]["general_red"]) + set_color(self.voltage_label, setting.get_color("general_red", "dark")) layout.addWidget(self.voltage_label) self.i_label = QtWidgets.QLabel(self.tr("电流 I"), self) self.i_label.setFont(font_title) - set_color(self.i_label, setting.color_palette_v2["dark"]["general_green"]) + set_color(self.i_label, setting.get_color("general_green", "dark")) layout.addWidget(self.i_label) self.current_label = QtWidgets.QLabel("", self) self.current_label.setFont(font_value) - set_color(self.current_label, setting.color_palette_v2["dark"]["general_green"]) + set_color(self.current_label, setting.get_color("general_green", "dark")) layout.addWidget(self.current_label) self.p_label = QtWidgets.QLabel(self.tr("功率 P"), self) self.p_label.setFont(font_title) - set_color(self.p_label, setting.color_palette_v2["dark"]["general_blue"]) + set_color(self.p_label, setting.get_color("general_blue", "dark")) layout.addWidget(self.p_label) self.power_label = QtWidgets.QLabel("", self) self.power_label.setFont(font_value) - set_color(self.power_label, setting.color_palette_v2["dark"]["general_blue"]) + set_color(self.power_label, setting.get_color("general_blue", "dark")) layout.addWidget(self.power_label) self.setLayout(window_layout) @@ -2626,12 +2742,8 @@ def __init__(self, parent=None): self.plot_widget.setLabel("bottom", "X-Axis") # 创建曲线 - self.pen = pg.mkPen( - color=setting.color_palette_v2[setting.theme]["line1"], width=2 - ) - self.fit_pen = pg.mkPen( - color=setting.color_palette_v2[setting.theme]["line2"], width=2 - ) + self.pen = pg.mkPen(color=setting.get_color("line1"), width=2) + self.fit_pen = pg.mkPen(color=setting.get_color("line2"), width=2) self.curve = self.plot_widget.plot(pen=self.pen) self.curve.setData([], []) self.curve_fit = self.plot_widget.plot(pen=self.fit_pen) @@ -2758,12 +2870,12 @@ def highlightPoint(self, x, y): self.vLine = pg.InfiniteLine( angle=90, movable=False, - pen=pg.mkPen(setting.color_palette_v2[setting.theme]["line2"]), + pen=pg.mkPen(setting.get_color("line2")), ) self.hLine = pg.InfiniteLine( angle=0, movable=False, - pen=pg.mkPen(setting.color_palette_v2[setting.theme]["line2"]), + pen=pg.mkPen(setting.get_color("line2")), ) self.vLine.setPos(x) self.hLine.setPos(y) @@ -2794,6 +2906,10 @@ def highlightPoint(self, x, y): def set_theme(theme): setting.theme = theme + if theme not in ("dark", "light"): + sys_theme = setting.color_palette[theme]["based_on_dark_or_light"] + else: + sys_theme = theme additional_qss = ( "QToolTip {" " color: rgb(228, 231, 235);" @@ -2807,7 +2923,7 @@ def set_theme(theme): "QSlider::sub-page:horizontal {" " background: #368888ff;" "}" - if theme == "dark" + if sys_theme == "dark" else "QToolTip {" " color: rgb(32, 33, 36);" " background-color: white;" @@ -2822,30 +2938,41 @@ def set_theme(theme): "}" ) qdarktheme.setup_theme( - theme, + sys_theme, additional_qss=additional_qss, ) MainWindow.ui.widgetGraph1.setBackground(None) MainWindow.ui.widgetGraph2.setBackground(None) - MainWindow.CustomTitleBar.set_theme(theme) - DialogSettings.CustomTitleBar.set_theme(theme) - DialogGraphics.CustomTitleBar.set_theme(theme) + MainWindow.CustomTitleBar.set_theme(sys_theme) + DialogSettings.CustomTitleBar.set_theme(sys_theme) + DialogGraphics.CustomTitleBar.set_theme(sys_theme) set_color( DialogGraphics.ui.labelNumba, - setting.color_palette_v2[setting.theme]["general_green"], + setting.get_color("general_green"), ) if MainWindow.api is not None: set_color( MainWindow.ui.labelConnectState, - setting.color_palette_v2[setting.theme]["general_green"], + setting.get_color("general_green"), ) MainWindow.update_pen() - DialogResult.pen.setColor( - QtGui.QColor(setting.color_palette_v2[setting.theme]["line1"]) - ) + DialogResult.pen.setColor(QtGui.QColor(setting.get_color("line1"))) MainWindow.ui.horizontalSlider.setStyleSheet("background: none;") MainWindow.ui.horizontalSlider.setBarVisible(False) + set_color(MainWindow.ui.lcdVoltage, setting.get_color("lcd_voltage")) + set_color(MainWindow.ui.lcdCurrent, setting.get_color("lcd_current")) + set_color(MainWindow.ui.lcdPower, setting.get_color("lcd_power")) + set_color(MainWindow.ui.lcdEnerge, setting.get_color("lcd_energy")) + set_color( + MainWindow.ui.lcdAvgPower, + setting.get_color("lcd_avg_power"), + ) + set_color( + MainWindow.ui.lcdResistence, + setting.get_color("lcd_resistance"), + ) + set_theme(setting.theme) diff --git a/gui_source/mdp_gui_template/graphics.ui b/gui_source/mdp_gui_template/graphics.ui index b5e47d6..5ca0f76 100644 --- a/gui_source/mdp_gui_template/graphics.ui +++ b/gui_source/mdp_gui_template/graphics.ui @@ -12,7 +12,7 @@ 0 0 - 255 + 280 640 @@ -24,13 +24,13 @@ - 242 + 280 640 - 266 + 280 640 diff --git a/gui_source/mdp_gui_template/graphics_ui.py b/gui_source/mdp_gui_template/graphics_ui.py index 2b74aa2..20bfa12 100644 --- a/gui_source/mdp_gui_template/graphics_ui.py +++ b/gui_source/mdp_gui_template/graphics_ui.py @@ -16,14 +16,14 @@ def setupUi(self, DialogGraphics): DialogGraphics.setObjectName("DialogGraphics") DialogGraphics.setWindowModality(QtCore.Qt.WindowModal) DialogGraphics.setEnabled(True) - DialogGraphics.resize(255, 640) + DialogGraphics.resize(280, 640) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(DialogGraphics.sizePolicy().hasHeightForWidth()) DialogGraphics.setSizePolicy(sizePolicy) - DialogGraphics.setMinimumSize(QtCore.QSize(242, 640)) - DialogGraphics.setMaximumSize(QtCore.QSize(266, 640)) + DialogGraphics.setMinimumSize(QtCore.QSize(280, 640)) + DialogGraphics.setMaximumSize(QtCore.QSize(280, 640)) DialogGraphics.setSizeGripEnabled(False) DialogGraphics.setModal(True) self.verticalLayout = QtWidgets.QVBoxLayout(DialogGraphics) diff --git a/gui_source/mdp_gui_template/mainwindow.ui b/gui_source/mdp_gui_template/mainwindow.ui index 552adc6..33ab2c8 100644 --- a/gui_source/mdp_gui_template/mainwindow.ui +++ b/gui_source/mdp_gui_template/mainwindow.ui @@ -1035,7 +1035,7 @@ false - 设定电压 + Title Qt::AlignCenter @@ -1576,7 +1576,7 @@ 0 0 - 152 + 177 162 @@ -1910,7 +1910,7 @@ 0 0 - 146 + 177 263 @@ -2388,7 +2388,7 @@ 0 0 - 152 + 177 198 @@ -3191,7 +3191,7 @@ 0 0 - 176 + 177 301 diff --git a/gui_source/mdp_gui_template/mainwindow_ui.py b/gui_source/mdp_gui_template/mainwindow_ui.py index f48984c..62162e2 100644 --- a/gui_source/mdp_gui_template/mainwindow_ui.py +++ b/gui_source/mdp_gui_template/mainwindow_ui.py @@ -669,7 +669,7 @@ def setupUi(self, MainWindow): self.scrollAreaKeepPower.setWidgetResizable(True) self.scrollAreaKeepPower.setObjectName("scrollAreaKeepPower") self.scrollAreaWidgetContents = QtWidgets.QWidget() - self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 152, 162)) + self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 177, 162)) self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents") self.verticalLayout_15 = QtWidgets.QVBoxLayout(self.scrollAreaWidgetContents) self.verticalLayout_15.setObjectName("verticalLayout_15") @@ -807,7 +807,7 @@ def setupUi(self, MainWindow): self.scrollAreaSweep.setWidgetResizable(True) self.scrollAreaSweep.setObjectName("scrollAreaSweep") self.scrollAreaWidgetContents_2 = QtWidgets.QWidget() - self.scrollAreaWidgetContents_2.setGeometry(QtCore.QRect(0, 0, 146, 263)) + self.scrollAreaWidgetContents_2.setGeometry(QtCore.QRect(0, 0, 177, 263)) self.scrollAreaWidgetContents_2.setObjectName("scrollAreaWidgetContents_2") self.verticalLayout_16 = QtWidgets.QVBoxLayout(self.scrollAreaWidgetContents_2) self.verticalLayout_16.setObjectName("verticalLayout_16") @@ -1002,7 +1002,7 @@ def setupUi(self, MainWindow): self.scrollAreaWaveGen.setWidgetResizable(True) self.scrollAreaWaveGen.setObjectName("scrollAreaWaveGen") self.scrollAreaWidgetContents_3 = QtWidgets.QWidget() - self.scrollAreaWidgetContents_3.setGeometry(QtCore.QRect(0, 0, 152, 198)) + self.scrollAreaWidgetContents_3.setGeometry(QtCore.QRect(0, 0, 177, 198)) self.scrollAreaWidgetContents_3.setObjectName("scrollAreaWidgetContents_3") self.verticalLayout_17 = QtWidgets.QVBoxLayout(self.scrollAreaWidgetContents_3) self.verticalLayout_17.setObjectName("verticalLayout_17") @@ -1347,7 +1347,7 @@ def setupUi(self, MainWindow): self.scrollAreaBatSim.setWidgetResizable(True) self.scrollAreaBatSim.setObjectName("scrollAreaBatSim") self.scrollAreaWidgetContents_5 = QtWidgets.QWidget() - self.scrollAreaWidgetContents_5.setGeometry(QtCore.QRect(0, 0, 176, 301)) + self.scrollAreaWidgetContents_5.setGeometry(QtCore.QRect(0, 0, 177, 301)) self.scrollAreaWidgetContents_5.setObjectName("scrollAreaWidgetContents_5") self.verticalLayout_25 = QtWidgets.QVBoxLayout(self.scrollAreaWidgetContents_5) self.verticalLayout_25.setObjectName("verticalLayout_25") @@ -1980,7 +1980,7 @@ def retranslateUi(self, MainWindow): self.spinBoxCurrent.setSuffix(_translate("MainWindow", "A")) self.label_21.setText(_translate("MainWindow", "辅助功能 / AUX FUNC")) self.pushButtonLastTab.setText(_translate("MainWindow", "◀")) - self.labelTab.setText(_translate("MainWindow", "设定电压")) + self.labelTab.setText(_translate("MainWindow", "Title")) self.pushButtonNextTab.setText(_translate("MainWindow", "▶")) self.comboPreset.setItemText(0, _translate("MainWindow", "None")) self.comboPreset.setItemText(1, _translate("MainWindow", "1")) diff --git a/gui_source/mdp_gui_template/settings.ui b/gui_source/mdp_gui_template/settings.ui index 23678c4..d9e5f28 100644 --- a/gui_source/mdp_gui_template/settings.ui +++ b/gui_source/mdp_gui_template/settings.ui @@ -12,20 +12,20 @@ 0 0 - 335 - 434 + 340 + 584 - 335 - 434 + 340 + 584 - 335 - 448 + 340 + 584 @@ -883,6 +883,109 @@ + + + + + + + 0 + 28 + + + + + Sarasa Fixed SC SemiBold + + + + 其它设置 + + + Qt::AlignCenter + + + 6 + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 3 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + + 0 + 28 + + + + 开启输出前显示警告 + + + 输出开启警告 + + + + + + + + 0 + 28 + + + + 输出状态下不允许包括辅助功能在内的设定值修改操作 + + + 输出时锁定参数 + + + + + + + + 0 + 28 + + + + 硬件锁定时仍允许上位机修改输出状态 + + + 忽略硬件锁定 + + + + + + + + diff --git a/gui_source/mdp_gui_template/settings_ui.py b/gui_source/mdp_gui_template/settings_ui.py index 87628fe..3d467cb 100644 --- a/gui_source/mdp_gui_template/settings_ui.py +++ b/gui_source/mdp_gui_template/settings_ui.py @@ -16,9 +16,9 @@ def setupUi(self, DialogSettings): DialogSettings.setObjectName("DialogSettings") DialogSettings.setWindowModality(QtCore.Qt.WindowModal) DialogSettings.setEnabled(True) - DialogSettings.resize(335, 434) - DialogSettings.setMinimumSize(QtCore.QSize(335, 434)) - DialogSettings.setMaximumSize(QtCore.QSize(335, 448)) + DialogSettings.resize(340, 584) + DialogSettings.setMinimumSize(QtCore.QSize(340, 584)) + DialogSettings.setMaximumSize(QtCore.QSize(340, 584)) DialogSettings.setSizeGripEnabled(False) DialogSettings.setModal(True) self.verticalLayout = QtWidgets.QVBoxLayout(DialogSettings) @@ -352,6 +352,40 @@ def setupUi(self, DialogSettings): self.horizontalLayout_42.setStretch(0, 2) self.horizontalLayout_42.setStretch(1, 3) self.verticalLayout.addLayout(self.horizontalLayout_42) + self.horizontalLayout_43 = QtWidgets.QHBoxLayout() + self.horizontalLayout_43.setObjectName("horizontalLayout_43") + self.label_49 = QtWidgets.QLabel(DialogSettings) + self.label_49.setMinimumSize(QtCore.QSize(0, 28)) + font = QtGui.QFont() + font.setFamily("Sarasa Fixed SC SemiBold") + self.label_49.setFont(font) + self.label_49.setAlignment(QtCore.Qt.AlignCenter) + self.label_49.setObjectName("label_49") + self.horizontalLayout_43.addWidget(self.label_49) + self.frame_2 = QtWidgets.QFrame(DialogSettings) + self.frame_2.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.frame_2.setFrameShadow(QtWidgets.QFrame.Raised) + self.frame_2.setObjectName("frame_2") + self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.frame_2) + self.verticalLayout_2.setContentsMargins(2, 2, 2, 2) + self.verticalLayout_2.setSpacing(3) + self.verticalLayout_2.setObjectName("verticalLayout_2") + self.checkBoxOutputWarn = QtWidgets.QCheckBox(self.frame_2) + self.checkBoxOutputWarn.setMinimumSize(QtCore.QSize(0, 28)) + self.checkBoxOutputWarn.setObjectName("checkBoxOutputWarn") + self.verticalLayout_2.addWidget(self.checkBoxOutputWarn) + self.checkBoxSetLock = QtWidgets.QCheckBox(self.frame_2) + self.checkBoxSetLock.setMinimumSize(QtCore.QSize(0, 28)) + self.checkBoxSetLock.setObjectName("checkBoxSetLock") + self.verticalLayout_2.addWidget(self.checkBoxSetLock) + self.checkBoxIgnoreHWLock = QtWidgets.QCheckBox(self.frame_2) + self.checkBoxIgnoreHWLock.setMinimumSize(QtCore.QSize(0, 28)) + self.checkBoxIgnoreHWLock.setObjectName("checkBoxIgnoreHWLock") + self.verticalLayout_2.addWidget(self.checkBoxIgnoreHWLock) + self.horizontalLayout_43.addWidget(self.frame_2) + self.horizontalLayout_43.setStretch(0, 2) + self.horizontalLayout_43.setStretch(1, 3) + self.verticalLayout.addLayout(self.horizontalLayout_43) self.line_2 = QtWidgets.QFrame(DialogSettings) self.line_2.setFrameShadow(QtWidgets.QFrame.Plain) self.line_2.setFrameShape(QtWidgets.QFrame.HLine) @@ -420,5 +454,12 @@ def retranslateUi(self, DialogSettings): self.label_48.setText(_translate("DialogSettings", "连接指示灯")) self.comboBoxBlink.setItemText(0, _translate("DialogSettings", "常亮")) self.comboBoxBlink.setItemText(1, _translate("DialogSettings", "闪烁")) + self.label_49.setText(_translate("DialogSettings", "其它设置")) + self.checkBoxOutputWarn.setToolTip(_translate("DialogSettings", "开启输出前显示警告")) + self.checkBoxOutputWarn.setText(_translate("DialogSettings", "输出开启警告")) + self.checkBoxSetLock.setToolTip(_translate("DialogSettings", "输出状态下不允许包括辅助功能在内的设定值修改操作")) + self.checkBoxSetLock.setText(_translate("DialogSettings", "输出时锁定参数")) + self.checkBoxIgnoreHWLock.setToolTip(_translate("DialogSettings", "硬件锁定时仍允许上位机修改输出状态")) + self.checkBoxIgnoreHWLock.setText(_translate("DialogSettings", "忽略硬件锁定")) self.btnSave.setText(_translate("DialogSettings", "应用 / Apply")) self.btnOk.setText(_translate("DialogSettings", "确定 / OK"))