PyCharm中wxPython GUI调试运行与单步执行行为差异问题
调试模式与单步执行行为不一致问题
问题背景
以下是精简后的代码,在PyCharm调试模式运行时与单步调试时行为不一致。
环境信息
- Windows 10
- PyCharm: 2022.2.4
- Python: 3.10.8
- wxPython库: 4.2.0
代码片段
import wx class AddBoard(wx.Dialog): def __init__(self, parent): wx.Dialog.__init__(self, parent) self.archive_board_cost = 0 self.archive_board_width, self.archive_board_height = [0, 0] self.InitUI() self.onRadioButton() self.Bind(wx.EVT_RADIOBUTTON, self.onRadioButton, self.archive_radial) self.Bind(wx.EVT_RADIOBUTTON, self.onRadioButton, self.new_radial) self.Bind(wx.EVT_BUTTON, self.onOK, self.ok_button) def InitUI(self): frame_box = wx.BoxSizer(wx.VERTICAL) main_panel_box = wx.BoxSizer(wx.VERTICAL) labels_box = wx.BoxSizer(wx.HORIZONTAL) archive_box = wx.BoxSizer(wx.HORIZONTAL) new_box = wx.BoxSizer(wx.HORIZONTAL) self.main_panel = wx.Panel(self) self.label_width = wx.StaticText(self.main_panel, label='width', size=wx.Size(60, -1)) self.label_height = wx.StaticText(self.main_panel, label='height', size=wx.Size(60, -1)) self.label_cost = wx.StaticText(self.main_panel, label='cost', size=wx.Size(60, -1)) labels_box.Add((66, 0), 0, wx.EXPAND, 0) labels_box.Add(self.label_width, 0, wx.ALL, 3) labels_box.Add(self.label_height, 0, wx.ALL, 3) labels_box.Add(self.label_cost, 0, wx.ALL, 3) self.archive_radial = wx.RadioButton(self.main_panel, label='archive', size=wx.Size(60, -1)) self.archive_radial.SetValue(True) self.archive_width = wx.StaticText(self.main_panel, label=str(self.archive_board_width), size=wx.Size(60, -1)) self.archive_height = wx.StaticText(self.main_panel, label=str(self.archive_board_height), size=wx.Size(60, -1)) self.archive_cost = wx.StaticText(self.main_panel, label=str(self.archive_board_cost), size=wx.Size(60, -1)) archive_box.Add(self.archive_radial, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 3) archive_box.Add(self.archive_width, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 3) archive_box.Add(self.archive_height, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 3) archive_box.Add(self.archive_cost, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 3) self.new_radial = wx.RadioButton(self.main_panel, label='new', size=wx.Size(60, -1)) self.new_radial.SetValue(False) self.new_width = wx.TextCtrl(self.main_panel, value='0', size=wx.Size(60, -1), validator=IntValidator()) self.new_height = wx.TextCtrl(self.main_panel, value='0', size=wx.Size(60, -1), validator=IntValidator()) self.new_cost = wx.TextCtrl(self.main_panel, value='0', size=wx.Size(60, -1), validator=FloatValidator()) new_box.Add(self.new_radial, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 3) new_box.Add(self.new_width, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 3) new_box.Add(self.new_height, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 3) new_box.Add(self.new_cost, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 3) self.standard_buttons = wx.StdDialogButtonSizer() self.ok_button = wx.Button(self.main_panel, wx.ID_OK) self.cancel_button = wx.Button(self.main_panel, wx.ID_CANCEL) self.standard_buttons.AddButton(self.ok_button) self.standard_buttons.AddButton(self.cancel_button) self.standard_buttons.Realize() main_panel_box.Add(labels_box, 0, wx.EXPAND, 0) main_panel_box.Add(archive_box, 0, wx.EXPAND, 0) main_panel_box.Add(new_box, 0, wx.EXPAND, 0) main_panel_box.Add((0, 20), 0, wx.EXPAND, 0) main_panel_box.Add(self.standard_buttons, 0, wx.ALL | wx.EXPAND, 3) self.main_panel.SetSizer(main_panel_box) self.main_panel.Layout() frame_box.Add(self.main_panel, 0, wx.ALL | wx.EXPAND, 5) self.SetSizer(frame_box) self.Fit() def onRadioButton(self, evt=None): self.DisableText() if self.archive_radial.GetValue(): self.archive_width.Enable() self.archive_height.Enable() self.archive_cost.Enable() else: self.new_width.Enable() self.new_height.Enable() self.new_cost.Enable() self.CheckAllValues() self.Layout() def DisableText(self): self.archive_width.Enable(False) self.archive_height.Enable(False) self.archive_cost.Enable(False) self.new_width.Enable(False) self.new_height.Enable(False) self.new_cost.Enable(False) def CheckArchiveValues(self): colour = (255, 193, 193, 255) flag = True if self.archive_board_width <= 0: self.archive_width.SetBackgroundColour(colour) flag = False if self.archive_board_height <= 0: self.archive_height.SetBackgroundColour(colour) flag = False if self.archive_board_cost <= 0: self.archive_cost.SetBackgroundColour(colour) flag = False return flag def CheckAllValues(self): # refresh relevant values self.ClearChecks() if self.archive_radial.GetValue(): return self.CheckArchiveValues() else: return all([self.new_width.Validate(), self.new_height.Validate(), self.new_cost.Validate()]) def onOK(self, evt=None): if not self.CheckAllValues(): self.Layout() return self.EndModal(wx.ID_OK) def ClearChecks(self): self.ClearWidget(self.archive_width) self.ClearWidget(self.archive_height) self.ClearWidget(self.archive_cost) self.ClearWidget(self.new_width) self.ClearWidget(self.new_height) self.ClearWidget(self.new_cost) self.Layout() @staticmethod def ClearWidget(widget): colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW) state = widget.IsEnabled() widget.Enable(True) widget.SetBackgroundColour(colour) widget.Enable(state) class IntValidator(wx.Validator): def __init__(self): wx.Validator.__init__(self) def Clone(self): return IntValidator() def Validate(self, win): ctrl = self.GetWindow() text = ctrl.GetValue() try: if int(text) <= 0: # wx.MessageBox("Positive integers only.", "Error") ctrl.SetBackgroundColour((255, 193, 193, 255)) ctrl.SetFocus() ctrl.Refresh() return False except ValueError: # wx.MessageBox("Positive integers only.", "Error") ctrl.SetBackgroundColour((255, 193, 193, 255)) ctrl.SetFocus() ctrl.Refresh() return False ctrl.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW)) ctrl.Refresh() return True def TransferToWindow(self): return True # Not used def TransferFromWindow(self): return True # Not used class FloatValidator(wx.Validator): def __init__(self): wx.Validator.__init__(self) def Clone(self): return FloatValidator() def Validate(self, win): ctrl = self.GetWindow() text = ctrl.GetValue() try: if float(text) <= 0: # wx.MessageBox("Positive numbers only.", "Error") ctrl.SetBackgroundColour((255, 193, 193, 255)) ctrl.SetFocus() ctrl.Refresh() return False except ValueError: # wx.MessageBox("Positive numbers only.", "Error") ctrl.SetBackgroundColour((255, 193, 193, 255)) ctrl.SetFocus() ctrl.Refresh() return False ctrl.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW)) ctrl.Refresh() return True def TransferToWindow(self): return True # Not used def TransferFromWindow(self): return True # Not used def main(): app = MainApp() app.MainLoop() class MainWindow(wx.Frame): def __init__(self, parent): super(MainWindow, self).__init__(parent) dialog = AddBoard(self) if dialog.ShowModal() == wx.ID_OK: pass class MainApp(wx.App): def __init__(self): wx.App.__init__(self) main_window = MainWindow(None) main_window.Show() if __name__ == '__main__': main()
调试模式运行时行为
预期行为:选中单选按钮对应的行应触发验证失败(因值为0),另一行恢复正常状态。
实际行为:
- 启动时'archive'行显示红色(正确);
- 切换到'new'行时,'archive'行恢复正常,'new'行变红(正确);
- 切回'archive'行时,'new'行恢复正常,但'archive'行未变回红色(错误)。
单步调试时行为
在onRadioButton方法第一行(第84行self.DisableText())设置断点,单步执行时符合预期行为。
疑问
首次遇到调试运行与单步执行的行为差异,按逻辑两者应完全一致。请确认该问题是否在其他IDE或Python/wxPython版本中存在,若能避免打包成EXE测试则更佳。
内容的提问来源于stack exchange,提问作者Mike
相关产品推荐
相关产品推荐

