1#!/usr/bin/env python 2# 3# A serial port configuration dialog for wxPython. A number of flags can 4# be used to configure the fields that are displayed. 5# 6# (C) 2001-2015 Chris Liechti <[email protected]> 7# 8# SPDX-License-Identifier: BSD-3-Clause 9 10import wx 11import serial 12import serial.tools.list_ports 13 14SHOW_BAUDRATE = 1 << 0 15SHOW_FORMAT = 1 << 1 16SHOW_FLOW = 1 << 2 17SHOW_TIMEOUT = 1 << 3 18SHOW_ALL = SHOW_BAUDRATE | SHOW_FORMAT | SHOW_FLOW | SHOW_TIMEOUT 19 20 21class SerialConfigDialog(wx.Dialog): 22 """\ 23 Serial Port configuration dialog, to be used with pySerial 2.0+ 24 When instantiating a class of this dialog, then the "serial" keyword 25 argument is mandatory. It is a reference to a serial.Serial instance. 26 the optional "show" keyword argument can be used to show/hide different 27 settings. The default is SHOW_ALL which corresponds to 28 SHOW_BAUDRATE|SHOW_FORMAT|SHOW_FLOW|SHOW_TIMEOUT. All constants can be 29 found in this module (not the class). 30 """ 31 32 def __init__(self, *args, **kwds): 33 # grab the serial keyword and remove it from the dict 34 self.serial = kwds['serial'] 35 del kwds['serial'] 36 self.show = SHOW_ALL 37 if 'show' in kwds: 38 self.show = kwds.pop('show') 39 # begin wxGlade: SerialConfigDialog.__init__ 40 kwds["style"] = wx.DEFAULT_DIALOG_STYLE 41 wx.Dialog.__init__(self, *args, **kwds) 42 self.label_2 = wx.StaticText(self, -1, "Port") 43 self.choice_port = wx.Choice(self, -1, choices=[]) 44 self.label_1 = wx.StaticText(self, -1, "Baudrate") 45 self.combo_box_baudrate = wx.ComboBox(self, -1, choices=[], style=wx.CB_DROPDOWN) 46 self.sizer_1_staticbox = wx.StaticBox(self, -1, "Basics") 47 self.panel_format = wx.Panel(self, -1) 48 self.label_3 = wx.StaticText(self.panel_format, -1, "Data Bits") 49 self.choice_databits = wx.Choice(self.panel_format, -1, choices=["choice 1"]) 50 self.label_4 = wx.StaticText(self.panel_format, -1, "Stop Bits") 51 self.choice_stopbits = wx.Choice(self.panel_format, -1, choices=["choice 1"]) 52 self.label_5 = wx.StaticText(self.panel_format, -1, "Parity") 53 self.choice_parity = wx.Choice(self.panel_format, -1, choices=["choice 1"]) 54 self.sizer_format_staticbox = wx.StaticBox(self.panel_format, -1, "Data Format") 55 self.panel_timeout = wx.Panel(self, -1) 56 self.checkbox_timeout = wx.CheckBox(self.panel_timeout, -1, "Use Timeout") 57 self.text_ctrl_timeout = wx.TextCtrl(self.panel_timeout, -1, "") 58 self.label_6 = wx.StaticText(self.panel_timeout, -1, "seconds") 59 self.sizer_timeout_staticbox = wx.StaticBox(self.panel_timeout, -1, "Timeout") 60 self.panel_flow = wx.Panel(self, -1) 61 self.checkbox_rtscts = wx.CheckBox(self.panel_flow, -1, "RTS/CTS") 62 self.checkbox_xonxoff = wx.CheckBox(self.panel_flow, -1, "Xon/Xoff") 63 self.sizer_flow_staticbox = wx.StaticBox(self.panel_flow, -1, "Flow Control") 64 self.button_ok = wx.Button(self, wx.ID_OK, "") 65 self.button_cancel = wx.Button(self, wx.ID_CANCEL, "") 66 67 self.__set_properties() 68 self.__do_layout() 69 # end wxGlade 70 # attach the event handlers 71 self.__attach_events() 72 73 def __set_properties(self): 74 # begin wxGlade: SerialConfigDialog.__set_properties 75 self.SetTitle("Serial Port Configuration") 76 self.choice_databits.SetSelection(0) 77 self.choice_stopbits.SetSelection(0) 78 self.choice_parity.SetSelection(0) 79 self.text_ctrl_timeout.Enable(False) 80 self.button_ok.SetDefault() 81 # end wxGlade 82 self.SetTitle("Serial Port Configuration") 83 if self.show & SHOW_TIMEOUT: 84 self.text_ctrl_timeout.Enable(0) 85 self.button_ok.SetDefault() 86 87 if not self.show & SHOW_BAUDRATE: 88 self.label_1.Hide() 89 self.combo_box_baudrate.Hide() 90 if not self.show & SHOW_FORMAT: 91 self.panel_format.Hide() 92 if not self.show & SHOW_TIMEOUT: 93 self.panel_timeout.Hide() 94 if not self.show & SHOW_FLOW: 95 self.panel_flow.Hide() 96 97 # fill in ports and select current setting 98 preferred_index = 0 99 self.choice_port.Clear() 100 self.ports = [] 101 for n, (portname, desc, hwid) in enumerate(sorted(serial.tools.list_ports.comports())): 102 self.choice_port.Append(u'{} - {}'.format(portname, desc)) 103 self.ports.append(portname) 104 if self.serial.name == portname: 105 preferred_index = n 106 self.choice_port.SetSelection(preferred_index) 107 if self.show & SHOW_BAUDRATE: 108 preferred_index = None 109 # fill in baud rates and select current setting 110 self.combo_box_baudrate.Clear() 111 for n, baudrate in enumerate(self.serial.BAUDRATES): 112 self.combo_box_baudrate.Append(str(baudrate)) 113 if self.serial.baudrate == baudrate: 114 preferred_index = n 115 if preferred_index is not None: 116 self.combo_box_baudrate.SetSelection(preferred_index) 117 else: 118 self.combo_box_baudrate.SetValue(u'{}'.format(self.serial.baudrate)) 119 if self.show & SHOW_FORMAT: 120 # fill in data bits and select current setting 121 self.choice_databits.Clear() 122 for n, bytesize in enumerate(self.serial.BYTESIZES): 123 self.choice_databits.Append(str(bytesize)) 124 if self.serial.bytesize == bytesize: 125 index = n 126 self.choice_databits.SetSelection(index) 127 # fill in stop bits and select current setting 128 self.choice_stopbits.Clear() 129 for n, stopbits in enumerate(self.serial.STOPBITS): 130 self.choice_stopbits.Append(str(stopbits)) 131 if self.serial.stopbits == stopbits: 132 index = n 133 self.choice_stopbits.SetSelection(index) 134 # fill in parities and select current setting 135 self.choice_parity.Clear() 136 for n, parity in enumerate(self.serial.PARITIES): 137 self.choice_parity.Append(str(serial.PARITY_NAMES[parity])) 138 if self.serial.parity == parity: 139 index = n 140 self.choice_parity.SetSelection(index) 141 if self.show & SHOW_TIMEOUT: 142 # set the timeout mode and value 143 if self.serial.timeout is None: 144 self.checkbox_timeout.SetValue(False) 145 self.text_ctrl_timeout.Enable(False) 146 else: 147 self.checkbox_timeout.SetValue(True) 148 self.text_ctrl_timeout.Enable(True) 149 self.text_ctrl_timeout.SetValue(str(self.serial.timeout)) 150 if self.show & SHOW_FLOW: 151 # set the rtscts mode 152 self.checkbox_rtscts.SetValue(self.serial.rtscts) 153 # set the rtscts mode 154 self.checkbox_xonxoff.SetValue(self.serial.xonxoff) 155 156 def __do_layout(self): 157 # begin wxGlade: SerialConfigDialog.__do_layout 158 sizer_2 = wx.BoxSizer(wx.VERTICAL) 159 sizer_3 = wx.BoxSizer(wx.HORIZONTAL) 160 self.sizer_flow_staticbox.Lower() 161 sizer_flow = wx.StaticBoxSizer(self.sizer_flow_staticbox, wx.HORIZONTAL) 162 self.sizer_timeout_staticbox.Lower() 163 sizer_timeout = wx.StaticBoxSizer(self.sizer_timeout_staticbox, wx.HORIZONTAL) 164 self.sizer_format_staticbox.Lower() 165 sizer_format = wx.StaticBoxSizer(self.sizer_format_staticbox, wx.VERTICAL) 166 grid_sizer_1 = wx.FlexGridSizer(3, 2, 0, 0) 167 self.sizer_1_staticbox.Lower() 168 sizer_1 = wx.StaticBoxSizer(self.sizer_1_staticbox, wx.VERTICAL) 169 sizer_basics = wx.FlexGridSizer(3, 2, 0, 0) 170 sizer_basics.Add(self.label_2, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 4) 171 sizer_basics.Add(self.choice_port, 0, wx.EXPAND, 0) 172 sizer_basics.Add(self.label_1, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 4) 173 sizer_basics.Add(self.combo_box_baudrate, 0, wx.EXPAND, 0) 174 sizer_basics.AddGrowableCol(1) 175 sizer_1.Add(sizer_basics, 0, wx.EXPAND, 0) 176 sizer_2.Add(sizer_1, 0, wx.EXPAND, 0) 177 grid_sizer_1.Add(self.label_3, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 4) 178 grid_sizer_1.Add(self.choice_databits, 1, wx.EXPAND | wx.ALIGN_RIGHT, 0) 179 grid_sizer_1.Add(self.label_4, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 4) 180 grid_sizer_1.Add(self.choice_stopbits, 1, wx.EXPAND | wx.ALIGN_RIGHT, 0) 181 grid_sizer_1.Add(self.label_5, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 4) 182 grid_sizer_1.Add(self.choice_parity, 1, wx.EXPAND | wx.ALIGN_RIGHT, 0) 183 sizer_format.Add(grid_sizer_1, 1, wx.EXPAND, 0) 184 self.panel_format.SetSizer(sizer_format) 185 sizer_2.Add(self.panel_format, 0, wx.EXPAND, 0) 186 sizer_timeout.Add(self.checkbox_timeout, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 4) 187 sizer_timeout.Add(self.text_ctrl_timeout, 0, 0, 0) 188 sizer_timeout.Add(self.label_6, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 4) 189 self.panel_timeout.SetSizer(sizer_timeout) 190 sizer_2.Add(self.panel_timeout, 0, wx.EXPAND, 0) 191 sizer_flow.Add(self.checkbox_rtscts, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 4) 192 sizer_flow.Add(self.checkbox_xonxoff, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 4) 193 sizer_flow.Add((10, 10), 1, wx.EXPAND, 0) 194 self.panel_flow.SetSizer(sizer_flow) 195 sizer_2.Add(self.panel_flow, 0, wx.EXPAND, 0) 196 sizer_3.Add(self.button_ok, 0, 0, 0) 197 sizer_3.Add(self.button_cancel, 0, 0, 0) 198 sizer_2.Add(sizer_3, 0, wx.ALL | wx.ALIGN_RIGHT, 4) 199 self.SetSizer(sizer_2) 200 sizer_2.Fit(self) 201 self.Layout() 202 # end wxGlade 203 204 def __attach_events(self): 205 self.button_ok.Bind(wx.EVT_BUTTON, self.OnOK) 206 self.button_cancel.Bind(wx.EVT_BUTTON, self.OnCancel) 207 if self.show & SHOW_TIMEOUT: 208 self.checkbox_timeout.Bind(wx.EVT_CHECKBOX, self.OnTimeout) 209 210 def OnOK(self, events): 211 success = True 212 self.serial.port = self.ports[self.choice_port.GetSelection()] 213 if self.show & SHOW_BAUDRATE: 214 try: 215 b = int(self.combo_box_baudrate.GetValue()) 216 except ValueError: 217 with wx.MessageDialog( 218 self, 219 'Baudrate must be a numeric value', 220 'Value Error', 221 wx.OK | wx.ICON_ERROR) as dlg: 222 dlg.ShowModal() 223 success = False 224 else: 225 self.serial.baudrate = b 226 if self.show & SHOW_FORMAT: 227 self.serial.bytesize = self.serial.BYTESIZES[self.choice_databits.GetSelection()] 228 self.serial.stopbits = self.serial.STOPBITS[self.choice_stopbits.GetSelection()] 229 self.serial.parity = self.serial.PARITIES[self.choice_parity.GetSelection()] 230 if self.show & SHOW_FLOW: 231 self.serial.rtscts = self.checkbox_rtscts.GetValue() 232 self.serial.xonxoff = self.checkbox_xonxoff.GetValue() 233 if self.show & SHOW_TIMEOUT: 234 if self.checkbox_timeout.GetValue(): 235 try: 236 self.serial.timeout = float(self.text_ctrl_timeout.GetValue()) 237 except ValueError: 238 with wx.MessageDialog( 239 self, 240 'Timeout must be a numeric value', 241 'Value Error', 242 wx.OK | wx.ICON_ERROR) as dlg: 243 dlg.ShowModal() 244 success = False 245 else: 246 self.serial.timeout = None 247 if success: 248 self.EndModal(wx.ID_OK) 249 250 def OnCancel(self, events): 251 self.EndModal(wx.ID_CANCEL) 252 253 def OnTimeout(self, events): 254 if self.checkbox_timeout.GetValue(): 255 self.text_ctrl_timeout.Enable(True) 256 else: 257 self.text_ctrl_timeout.Enable(False) 258 259# end of class SerialConfigDialog 260 261 262class MyApp(wx.App): 263 """Test code""" 264 def OnInit(self): 265 wx.InitAllImageHandlers() 266 267 ser = serial.Serial() 268 print(ser) 269 # loop until cancel is pressed, old values are used as start for the next run 270 # show the different views, one after the other 271 # value are kept. 272 for flags in (SHOW_BAUDRATE, SHOW_FLOW, SHOW_FORMAT, SHOW_TIMEOUT, SHOW_ALL): 273 dialog_serial_cfg = SerialConfigDialog(None, -1, "", serial=ser, show=flags) 274 self.SetTopWindow(dialog_serial_cfg) 275 result = dialog_serial_cfg.ShowModal() 276 print(ser) 277 if result != wx.ID_OK: 278 break 279 # the user can play around with the values, CANCEL aborts the loop 280 while True: 281 dialog_serial_cfg = SerialConfigDialog(None, -1, "", serial=ser) 282 self.SetTopWindow(dialog_serial_cfg) 283 result = dialog_serial_cfg.ShowModal() 284 print(ser) 285 if result != wx.ID_OK: 286 break 287 return 0 288 289# end of class MyApp 290 291if __name__ == "__main__": 292 app = MyApp(0) 293 app.MainLoop() 294