blob: a29b67d87b010e314d36b0c0789a538e29677d0a [file] [log] [blame]
cliechti8f376e72003-10-03 00:16:11 +00001#!/usr/bin/env python
Chris Liechtifbdd8a02015-08-09 02:37:45 +02002#
3# A serial port configuration dialog for wxPython. A number of flags can
Chris Liechti6dcdeda2015-09-29 01:03:03 +02004# be used to configure the fields that are displayed.
Chris Liechtifbdd8a02015-08-09 02:37:45 +02005#
6# (C) 2001-2015 Chris Liechti <cliechti@gmx.net>
7#
8# SPDX-License-Identifier: BSD-3-Clause
cliechti8f376e72003-10-03 00:16:11 +00009
cliechtid5d51982008-04-10 23:48:55 +000010import wx
cliechti8f376e72003-10-03 00:16:11 +000011import serial
cliechti24c3a882014-08-01 03:36:27 +000012import serial.tools.list_ports
cliechti8f376e72003-10-03 00:16:11 +000013
Chris Liechti6dcdeda2015-09-29 01:03:03 +020014SHOW_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
cliechti8f376e72003-10-03 00:16:11 +000019
cliechti80a0ed12003-10-03 23:53:42 +000020
cliechtid5d51982008-04-10 23:48:55 +000021class SerialConfigDialog(wx.Dialog):
Chris Liechtibe00ee92015-09-12 02:02:04 +020022 """\
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
Chris Liechti6dcdeda2015-09-29 01:03:03 +020027 settings. The default is SHOW_ALL which corresponds to
Chris Liechtibe00ee92015-09-12 02:02:04 +020028 SHOW_BAUDRATE|SHOW_FORMAT|SHOW_FLOW|SHOW_TIMEOUT. All constants can be
29 found in this module (not the class).
30 """
cliechti24c3a882014-08-01 03:36:27 +000031
cliechti8f376e72003-10-03 00:16:11 +000032 def __init__(self, *args, **kwds):
Chris Liechti4caf6a52015-08-04 01:07:45 +020033 # grab the serial keyword and remove it from the dict
cliechti8f376e72003-10-03 00:16:11 +000034 self.serial = kwds['serial']
35 del kwds['serial']
36 self.show = SHOW_ALL
Chris Liechti6dcdeda2015-09-29 01:03:03 +020037 if 'show' in kwds:
38 self.show = kwds.pop('show')
cliechti8f376e72003-10-03 00:16:11 +000039 # begin wxGlade: SerialConfigDialog.__init__
cliechtid5d51982008-04-10 23:48:55 +000040 kwds["style"] = wx.DEFAULT_DIALOG_STYLE
41 wx.Dialog.__init__(self, *args, **kwds)
42 self.label_2 = wx.StaticText(self, -1, "Port")
Chris Liechtibe00ee92015-09-12 02:02:04 +020043 self.choice_port = wx.Choice(self, -1, choices=[])
44 self.label_1 = wx.StaticText(self, -1, "Baudrate")
Chris Liechti809f8f22015-10-15 22:44:59 +020045 self.combo_box_baudrate = wx.ComboBox(self, -1, choices=[], style=wx.CB_DROPDOWN)
Chris Liechtibe00ee92015-09-12 02:02:04 +020046 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, "")
cliechti8f376e72003-10-03 00:16:11 +000066
67 self.__set_properties()
68 self.__do_layout()
Chris Liechtibe00ee92015-09-12 02:02:04 +020069 # 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")
Chris Liechtibe00ee92015-09-12 02:02:04 +020076 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()
Chris Liechti6dcdeda2015-09-29 01:03:03 +020086
Chris Liechtibe00ee92015-09-12 02:02:04 +020087 if not self.show & SHOW_BAUDRATE:
88 self.label_1.Hide()
Chris Liechti809f8f22015-10-15 22:44:59 +020089 self.combo_box_baudrate.Hide()
Chris Liechtibe00ee92015-09-12 02:02:04 +020090 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
cliechti24c3a882014-08-01 03:36:27 +000097 # fill in ports and select current setting
98 preferred_index = 0
Chris Liechtibe00ee92015-09-12 02:02:04 +020099 self.choice_port.Clear()
cliechti24c3a882014-08-01 03:36:27 +0000100 self.ports = []
cliechtif2f8b532014-08-03 17:31:09 +0000101 for n, (portname, desc, hwid) in enumerate(sorted(serial.tools.list_ports.comports())):
Chris Liechtibe00ee92015-09-12 02:02:04 +0200102 self.choice_port.Append('%s - %s' % (portname, desc))
cliechti24c3a882014-08-01 03:36:27 +0000103 self.ports.append(portname)
Chris Liechtibe00ee92015-09-12 02:02:04 +0200104 if self.serial.name == portname:
cliechti24c3a882014-08-01 03:36:27 +0000105 preferred_index = n
Chris Liechtibe00ee92015-09-12 02:02:04 +0200106 self.choice_port.SetSelection(preferred_index)
cliechti8f376e72003-10-03 00:16:11 +0000107 if self.show & SHOW_BAUDRATE:
Chris Liechti809f8f22015-10-15 22:44:59 +0200108 preferred_index = None
Chris Liechti4caf6a52015-08-04 01:07:45 +0200109 # fill in baud rates and select current setting
Chris Liechti809f8f22015-10-15 22:44:59 +0200110 self.combo_box_baudrate.Clear()
cliechti8f376e72003-10-03 00:16:11 +0000111 for n, baudrate in enumerate(self.serial.BAUDRATES):
Chris Liechti809f8f22015-10-15 22:44:59 +0200112 self.combo_box_baudrate.Append(str(baudrate))
cliechti8f376e72003-10-03 00:16:11 +0000113 if self.serial.baudrate == baudrate:
Chris Liechti809f8f22015-10-15 22:44:59 +0200114 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'%d' % (self.serial.baudrate,))
cliechti8f376e72003-10-03 00:16:11 +0000119 if self.show & SHOW_FORMAT:
Chris Liechti4caf6a52015-08-04 01:07:45 +0200120 # fill in data bits and select current setting
cliechti8f376e72003-10-03 00:16:11 +0000121 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)
Chris Liechti4caf6a52015-08-04 01:07:45 +0200127 # fill in stop bits and select current setting
cliechti8f376e72003-10-03 00:16:11 +0000128 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)
Chris Liechti4caf6a52015-08-04 01:07:45 +0200134 # fill in parities and select current setting
cliechti8f376e72003-10-03 00:16:11 +0000135 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:
Chris Liechti4caf6a52015-08-04 01:07:45 +0200142 # set the timeout mode and value
cliechti8f376e72003-10-03 00:16:11 +0000143 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:
Chris Liechti4caf6a52015-08-04 01:07:45 +0200151 # set the rtscts mode
cliechti8f376e72003-10-03 00:16:11 +0000152 self.checkbox_rtscts.SetValue(self.serial.rtscts)
Chris Liechti4caf6a52015-08-04 01:07:45 +0200153 # set the rtscts mode
cliechti8f376e72003-10-03 00:16:11 +0000154 self.checkbox_xonxoff.SetValue(self.serial.xonxoff)
cliechti8f376e72003-10-03 00:16:11 +0000155
156 def __do_layout(self):
157 # begin wxGlade: SerialConfigDialog.__do_layout
cliechtid5d51982008-04-10 23:48:55 +0000158 sizer_2 = wx.BoxSizer(wx.VERTICAL)
159 sizer_3 = wx.BoxSizer(wx.HORIZONTAL)
Chris Liechtibe00ee92015-09-12 02:02:04 +0200160 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)
Chris Liechti809f8f22015-10-15 22:44:59 +0200173 sizer_basics.Add(self.combo_box_baudrate, 0, wx.EXPAND, 0)
Chris Liechtibe00ee92015-09-12 02:02:04 +0200174 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)
cliechti8f376e72003-10-03 00:16:11 +0000196 sizer_3.Add(self.button_ok, 0, 0, 0)
197 sizer_3.Add(self.button_cancel, 0, 0, 0)
Chris Liechtibe00ee92015-09-12 02:02:04 +0200198 sizer_2.Add(sizer_3, 0, wx.ALL | wx.ALIGN_RIGHT, 4)
cliechti8f376e72003-10-03 00:16:11 +0000199 self.SetSizer(sizer_2)
200 sizer_2.Fit(self)
cliechti8f376e72003-10-03 00:16:11 +0000201 self.Layout()
Chris Liechtibe00ee92015-09-12 02:02:04 +0200202 # end wxGlade
cliechti8f376e72003-10-03 00:16:11 +0000203
204 def __attach_events(self):
cliechtid5d51982008-04-10 23:48:55 +0000205 wx.EVT_BUTTON(self, self.button_ok.GetId(), self.OnOK)
206 wx.EVT_BUTTON(self, self.button_cancel.GetId(), self.OnCancel)
cliechti8f376e72003-10-03 00:16:11 +0000207 if self.show & SHOW_TIMEOUT:
cliechtid5d51982008-04-10 23:48:55 +0000208 wx.EVT_CHECKBOX(self, self.checkbox_timeout.GetId(), self.OnTimeout)
cliechti8f376e72003-10-03 00:16:11 +0000209
210 def OnOK(self, events):
211 success = True
Chris Liechti6dcdeda2015-09-29 01:03:03 +0200212 self.serial.port = self.ports[self.choice_port.GetSelection()]
cliechti8f376e72003-10-03 00:16:11 +0000213 if self.show & SHOW_BAUDRATE:
Chris Liechti809f8f22015-10-15 22:44:59 +0200214 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
cliechti8f376e72003-10-03 00:16:11 +0000226 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()]
Chris Liechti6dcdeda2015-09-29 01:03:03 +0200229 self.serial.parity = self.serial.PARITIES[self.choice_parity.GetSelection()]
cliechti8f376e72003-10-03 00:16:11 +0000230 if self.show & SHOW_FLOW:
Chris Liechti6dcdeda2015-09-29 01:03:03 +0200231 self.serial.rtscts = self.checkbox_rtscts.GetValue()
232 self.serial.xonxoff = self.checkbox_xonxoff.GetValue()
cliechti8f376e72003-10-03 00:16:11 +0000233 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:
Chris Liechtibe00ee92015-09-12 02:02:04 +0200238 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()
cliechti8f376e72003-10-03 00:16:11 +0000244 success = False
245 else:
246 self.serial.timeout = None
247 if success:
cliechtid5d51982008-04-10 23:48:55 +0000248 self.EndModal(wx.ID_OK)
cliechti8f376e72003-10-03 00:16:11 +0000249
250 def OnCancel(self, events):
cliechtid5d51982008-04-10 23:48:55 +0000251 self.EndModal(wx.ID_CANCEL)
cliechti8f376e72003-10-03 00:16:11 +0000252
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
cliechtid5d51982008-04-10 23:48:55 +0000262class MyApp(wx.App):
cliechti8f376e72003-10-03 00:16:11 +0000263 """Test code"""
264 def OnInit(self):
cliechtid5d51982008-04-10 23:48:55 +0000265 wx.InitAllImageHandlers()
cliechti24c3a882014-08-01 03:36:27 +0000266
cliechti8f376e72003-10-03 00:16:11 +0000267 ser = serial.Serial()
Chris Liechti4caf6a52015-08-04 01:07:45 +0200268 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.
cliechti8f376e72003-10-03 00:16:11 +0000272 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()
Chris Liechti4caf6a52015-08-04 01:07:45 +0200276 print(ser)
cliechtid5d51982008-04-10 23:48:55 +0000277 if result != wx.ID_OK:
cliechti8f376e72003-10-03 00:16:11 +0000278 break
Chris Liechti4caf6a52015-08-04 01:07:45 +0200279 # the user can play around with the values, CANCEL aborts the loop
Chris Liechtibe00ee92015-09-12 02:02:04 +0200280 while True:
cliechti8f376e72003-10-03 00:16:11 +0000281 dialog_serial_cfg = SerialConfigDialog(None, -1, "", serial=ser)
282 self.SetTopWindow(dialog_serial_cfg)
283 result = dialog_serial_cfg.ShowModal()
Chris Liechti4caf6a52015-08-04 01:07:45 +0200284 print(ser)
cliechtid5d51982008-04-10 23:48:55 +0000285 if result != wx.ID_OK:
cliechti8f376e72003-10-03 00:16:11 +0000286 break
287 return 0
288
289# end of class MyApp
290
291if __name__ == "__main__":
292 app = MyApp(0)
293 app.MainLoop()