diff options
-rw-r--r-- | pyserial/CHANGES.txt | 7 | ||||
-rw-r--r-- | pyserial/examples/wxSerialConfigDialog.py | 6 | ||||
-rw-r--r-- | pyserial/examples/wxTerminal.py | 272 | ||||
-rw-r--r-- | pyserial/examples/wxTerminal.wxg | 133 | ||||
-rw-r--r-- | pyserial/serial/serialwin32.py | 5 |
5 files changed, 421 insertions, 2 deletions
diff --git a/pyserial/CHANGES.txt b/pyserial/CHANGES.txt index 7e53cda..de6d332 100644 --- a/pyserial/CHANGES.txt +++ b/pyserial/CHANGES.txt @@ -87,7 +87,7 @@ Version 1.21 30 sep 2003 - small change in miniterm.py that should mage it run on cygwin, [Bug 809904] submitted by Rolf Campbell. -Version 2.0b1 ... +Version 2.0b1 1 Oct 2003 Transition to the 2.0 series: - New implementation only supports Python 2.2+, backwards compatibility should be maintained almost everywhere. @@ -124,3 +124,8 @@ Version 2.0b1 ... work even if the platform itself s not known, it simply tries to do the posix stuff anyway (It's likely that opening ports by number fails, but by name it should work). + +Version 2.0bX ... + - Added serial port configuration dialog for wxPython to the examples. + - Added terminal application for wxPython with wxGlade design file + to the examples. diff --git a/pyserial/examples/wxSerialConfigDialog.py b/pyserial/examples/wxSerialConfigDialog.py index 9cd3358..a182114 100644 --- a/pyserial/examples/wxSerialConfigDialog.py +++ b/pyserial/examples/wxSerialConfigDialog.py @@ -10,6 +10,12 @@ SHOW_FLOW = 1<<2 SHOW_TIMEOUT = 1<<3 SHOW_ALL = SHOW_BAUDRATE|SHOW_FORMAT|SHOW_FLOW|SHOW_TIMEOUT +try: + enumerate +except NameError: + def enumerate(sequence): + return zip(range(len(sequence)), sequence) + class SerialConfigDialog(wxDialog): """Serial Port confiuration dialog, to be used with pyserial 2.0+ When instantiating a class of this dialog, then the "serial" keyword diff --git a/pyserial/examples/wxTerminal.py b/pyserial/examples/wxTerminal.py new file mode 100644 index 0000000..33723d1 --- /dev/null +++ b/pyserial/examples/wxTerminal.py @@ -0,0 +1,272 @@ +#!/usr/bin/env python +# generated by wxGlade 0.3.1 on Fri Oct 03 23:23:45 2003 + +from wxPython.wx import * +import wxSerialConfigDialog +import serial +import threading + +ID_CLEAR = wxNewId() +ID_SAVEAS = wxNewId() +ID_SETTINGS = wxNewId() +ID_TERM = wxNewId() +ID_EXIT = wxNewId() + +NEWLINE_CR = 0 +NEWLINE_LF = 1 +NEWLINE_CRLF = 2 + +class TerminalSetup: + def __init__(self): + self.echo = False + self.unprintable = False + self.newline = NEWLINE_CRLF + +class TerminalSettingsDialog(wxDialog): + """Simple dialog with common terminal settings like echo, newline mode""" + + def __init__(self, *args, **kwds): + self.settings = kwds['settings'] + del kwds['settings'] + # begin wxGlade: TerminalSettingsDialog.__init__ + kwds["style"] = wxDEFAULT_DIALOG_STYLE + wxDialog.__init__(self, *args, **kwds) + self.checkbox_echo = wxCheckBox(self, -1, "Local Echo") + self.checkbox_unprintable = wxCheckBox(self, -1, "Show unprintable characters") + self.radio_box_newline = wxRadioBox(self, -1, "Newline Handling", choices=["CR only", "LF only", "CR+LF"], majorDimension=0, style=wxRA_SPECIFY_ROWS) + self.button_ok = wxButton(self, -1, "OK") + self.button_cancel = wxButton(self, -1, "Cancel") + + self.__set_properties() + self.__do_layout() + # end wxGlade + self.__attach_events() + self.checkbox_echo.SetValue(self.settings.echo) + self.checkbox_unprintable.SetValue(self.settings.unprintable) + self.radio_box_newline.SetSelection(self.settings.newline) + + def __set_properties(self): + # begin wxGlade: TerminalSettingsDialog.__set_properties + self.SetTitle("Terminal Settings") + self.radio_box_newline.SetSelection(0) + self.button_ok.SetDefault() + # end wxGlade + + def __do_layout(self): + # begin wxGlade: TerminalSettingsDialog.__do_layout + sizer_2 = wxBoxSizer(wxVERTICAL) + sizer_3 = wxBoxSizer(wxHORIZONTAL) + sizer_4 = wxStaticBoxSizer(wxStaticBox(self, -1, "Input/Output"), wxVERTICAL) + sizer_4.Add(self.checkbox_echo, 0, wxALL, 4) + sizer_4.Add(self.checkbox_unprintable, 0, wxALL, 4) + sizer_4.Add(self.radio_box_newline, 0, 0, 0) + sizer_2.Add(sizer_4, 0, wxEXPAND, 0) + sizer_3.Add(self.button_ok, 0, 0, 0) + sizer_3.Add(self.button_cancel, 0, 0, 0) + sizer_2.Add(sizer_3, 0, wxALL|wxALIGN_RIGHT, 4) + self.SetAutoLayout(1) + self.SetSizer(sizer_2) + sizer_2.Fit(self) + sizer_2.SetSizeHints(self) + self.Layout() + # end wxGlade + + def __attach_events(self): + EVT_BUTTON(self, self.button_ok.GetId(), self.OnOK) + EVT_BUTTON(self, self.button_cancel.GetId(), self.OnCancel) + + def OnOK(self, events): + self.settings.echo = self.checkbox_echo.GetValue() + self.settings.unprintable = self.checkbox_unprintable.GetValue() + self.settings.newline = self.radio_box_newline.GetSelection() + self.EndModal(wxID_OK) + + def OnCancel(self, events): + self.EndModal(wxID_CANCEL) + +# end of class TerminalSettingsDialog + + +class TerminalFrame(wxFrame): + """Simple terminal program for wxPython""" + + def __init__(self, *args, **kwds): + self.serial = serial.Serial() + self.serial.timeout = 0.5 #make sure that the alive flag can be checked from time to time + self.settings = TerminalSetup() + self.thread = None + # begin wxGlade: TerminalFrame.__init__ + kwds["style"] = wxDEFAULT_FRAME_STYLE + wxFrame.__init__(self, *args, **kwds) + self.text_ctrl_output = wxTextCtrl(self, -1, "", style=wxTE_MULTILINE|wxTE_READONLY) + + # Menu Bar + self.frame_terminal_menubar = wxMenuBar() + self.SetMenuBar(self.frame_terminal_menubar) + wxglade_tmp_menu = wxMenu() + wxglade_tmp_menu.Append(ID_CLEAR, "&Clear", "", wxITEM_NORMAL) + wxglade_tmp_menu.Append(ID_SAVEAS, "&Save Text As...", "", wxITEM_NORMAL) + wxglade_tmp_menu.AppendSeparator() + wxglade_tmp_menu.Append(ID_SETTINGS, "&Port Settings...", "", wxITEM_NORMAL) + wxglade_tmp_menu.Append(ID_TERM, "&Terminal Settings...", "", wxITEM_NORMAL) + wxglade_tmp_menu.AppendSeparator() + wxglade_tmp_menu.Append(ID_EXIT, "&Exit", "", wxITEM_NORMAL) + self.frame_terminal_menubar.Append(wxglade_tmp_menu, "&File") + # Menu Bar end + self.frame_terminal_statusbar = self.CreateStatusBar(1) + + self.__set_properties() + self.__do_layout() + # end wxGlade + self.__attach_events() + self.OnPortSettings(None) #call setup dialog on startup, opens port + + def StartThread(self): + self.alive = True + self.thread = threading.Thread(target=self.ComPortThread) + self.thread.setDaemon(1) + self.thread.start() + + def StopThread(self): + if self.thread is not None: + self.alive = False #set termination flag for thread + self.thread.join() #wait until thread has finished + self.thread = None + + def __set_properties(self): + # begin wxGlade: TerminalFrame.__set_properties + self.SetTitle("Serial Terminal") + self.SetSize((546, 383)) + self.frame_terminal_statusbar.SetStatusWidths([-1]) + # statusbar fields + frame_terminal_statusbar_fields = ["frame_terminal_statusbar"] + for i in range(len(frame_terminal_statusbar_fields)): + self.frame_terminal_statusbar.SetStatusText(frame_terminal_statusbar_fields[i], i) + # end wxGlade + + def __do_layout(self): + # begin wxGlade: TerminalFrame.__do_layout + sizer_1 = wxBoxSizer(wxVERTICAL) + sizer_1.Add(self.text_ctrl_output, 1, wxEXPAND, 0) + self.SetAutoLayout(1) + self.SetSizer(sizer_1) + self.Layout() + # end wxGlade + + def __attach_events(self): + EVT_MENU(self, ID_CLEAR, self.OnClear) + EVT_MENU(self, ID_SAVEAS, self.OnSaveAs) + EVT_MENU(self, ID_EXIT, self.OnExit) + EVT_MENU(self, ID_SETTINGS, self.OnPortSettings) + EVT_MENU(self, ID_TERM, self.OnTermSettings) + EVT_CHAR(self, self.OnKey) + EVT_CHAR(self.text_ctrl_output, self.OnKey) + EVT_CLOSE(self, self.OnClose) + + def OnExit(self, event): + """Menu point Exit""" + self.Close() + + def OnClose(self, event): + """Called on application shutdown""" + self.StopThread() #stop reader thread + self.serial.close() #cleanup + self.Destroy() #close windows, exit app + + def OnSaveAs(self, event): + """Save contents of output window.""" + filename = None + dlg = wxFileDialog(None, "Save Text As...", ".", "", "Text File|*.txt|All Files|*", wxSAVE) + if dlg.ShowModal() == wxID_OK: + filename = dlg.GetPath() + dlg.Destroy() + + if filename is not None: + f = file(filename, 'w') + text = self.text_ctrl_output.GetValue() + if type(text) == unicode: + text = text.encode("latin1") #hm, is that a good asumption? + f.write(text) + f.close() + + def OnClear(self, event): + """Clear contents of output window.""" + self.text_ctrl_output.Clear() + + def OnPortSettings(self, event): + self.StopThread() + self.serial.close() + dialog_serial_cfg = wxSerialConfigDialog.SerialConfigDialog(None, -1, "", + show=wxSerialConfigDialog.SHOW_BAUDRATE|wxSerialConfigDialog.SHOW_FORMAT|wxSerialConfigDialog.SHOW_FLOW, + serial=self.serial + ) + result = dialog_serial_cfg.ShowModal() + dialog_serial_cfg.Destroy() + self.serial.open() + self.StartThread() + + def OnTermSettings(self, event): + """Menu point Terminal Settings. Show the settings dialog + with the current terminal settings""" + dialog = TerminalSettingsDialog(None, -1, "", settings=self.settings) + result = dialog.ShowModal() + dialog.Destroy() + + def OnKey(self, event): + """Key event handler. if the key is in the ASCII range, write it to the serial port. + Newline handling and local echo is also done here.""" + code = event.GetKeyCode() + if code < 256: #is it printable? + if code == 13: #is it a newline? (check for CR which is the RETURN key) + if self.settings.echo: #do echo if needed + self.text_ctrl_output.AppendText('\n') + if self.settings.newline == NEWLINE_CR: + self.serial.write('\r') #send CR + elif self.settings.newline == NEWLINE_LF: + self.serial.write('\n') #send LF + elif self.settings.newline == NEWLINE_CRLF: + self.serial.write('\r\n') #send CR+LF + else: + char = chr(code) + if self.settings.echo: #do echo if needed + self.text_ctrl_output.WriteText(char) + self.serial.write(char) #send the charcater + + def OnSerialRead(self, text): + if self.settings.unprintable: + text = ''.join([(c >= ' ') and c or '<%d>' % ord(c) for c in text]) + self.text_ctrl_output.AppendText(text) + + def ComPortThread(self): + """Thread that handles the incomming traffic""" + while self.alive: #loop while this flag is true + text = self.serial.read(1) #read one, with timout + if text: #check if not timeout + n = self.serial.inWaiting() #look if there is more to read + if n: + text = text + self.serial.read(n) #get it + #newline transformation + if self.settings.newline == NEWLINE_CR: + text = text.replace('\r', '\n') + elif self.settings.newline == NEWLINE_LF: + pass + elif self.settings.newline == NEWLINE_CRLF: + text = text.replace('\r\n', '\n') + self.OnSerialRead(text) #output text in window + +# end of class TerminalFrame + + +class MyApp(wxApp): + def OnInit(self): + wxInitAllImageHandlers() + frame_terminal = TerminalFrame(None, -1, "") + self.SetTopWindow(frame_terminal) + frame_terminal.Show(1) + return 1 + +# end of class MyApp + +if __name__ == "__main__": + app = MyApp(0) + app.MainLoop() diff --git a/pyserial/examples/wxTerminal.wxg b/pyserial/examples/wxTerminal.wxg new file mode 100644 index 0000000..39055db --- /dev/null +++ b/pyserial/examples/wxTerminal.wxg @@ -0,0 +1,133 @@ +<?xml version="1.0"?> +<!-- generated by wxGlade 0.3.1 on Sat Oct 04 01:25:39 2003 --> + +<application path="D:\prog\python\pyserial_sf\pyserial\examples\wxTerminal.py" name="app" class="MyApp" option="0" language="python" top_window="frame_terminal" encoding="ISO-8859-1" use_gettext="0" overwrite="0"> + <object class="TerminalFrame" name="frame_terminal" base="EditFrame"> + <style>wxDEFAULT_FRAME_STYLE</style> + <title>Serial Terminal</title> + <menubar>1</menubar> + <statusbar>1</statusbar> + <size>546, 383</size> + <object class="wxBoxSizer" name="sizer_1" base="EditBoxSizer"> + <orient>wxVERTICAL</orient> + <object class="sizeritem"> + <flag>wxEXPAND</flag> + <border>0</border> + <option>1</option> + <object class="wxTextCtrl" name="text_ctrl_output" base="EditTextCtrl"> + <style>wxTE_MULTILINE|wxTE_READONLY</style> + </object> + </object> + </object> + <object class="wxMenuBar" name="frame_terminal_menubar" base="EditMenuBar"> + <menus> + <menu name="" label="&File"> + <item> + <label>&Clear</label> + <id>ID_CLEAR</id> + </item> + <item> + <label>&Save Text As...</label> + <id>ID_SAVEAS</id> + </item> + <item> + <label>---</label> + <id>---</id> + <name>---</name> + </item> + <item> + <label>&Port Settings...</label> + <id>ID_SETTINGS</id> + </item> + <item> + <label>&Terminal Settings...</label> + <id>ID_TERM</id> + </item> + <item> + <label>---</label> + <name>---</name> + </item> + <item> + <label>&Exit</label> + <id>ID_EXIT</id> + </item> + </menu> + </menus> + </object> + <object class="wxStatusBar" name="frame_terminal_statusbar" base="EditStatusBar"> + <fields> + <field width="-1">frame_terminal_statusbar</field> + </fields> + </object> + </object> + <object class="TerminalSettingsDialog" name="dialog_terminal_Settings" base="EditDialog"> + <style>wxDEFAULT_DIALOG_STYLE</style> + <title>Terminal Settings</title> + <object class="wxBoxSizer" name="sizer_2" base="EditBoxSizer"> + <orient>wxVERTICAL</orient> + <object class="sizeritem"> + <flag>wxEXPAND</flag> + <border>0</border> + <option>0</option> + <object class="wxStaticBoxSizer" name="sizer_4" base="EditStaticBoxSizer"> + <orient>wxVERTICAL</orient> + <label>Input/Output</label> + <object class="sizeritem"> + <flag>wxALL</flag> + <border>4</border> + <option>0</option> + <object class="wxCheckBox" name="checkbox_echo" base="EditCheckBox"> + <label>Local Echo</label> + </object> + </object> + <object class="sizeritem"> + <flag>wxALL</flag> + <border>4</border> + <option>0</option> + <object class="wxCheckBox" name="checkbox_unprintable" base="EditCheckBox"> + <label>Show unprintable characters</label> + </object> + </object> + <object class="sizeritem"> + <border>0</border> + <option>0</option> + <object class="wxRadioBox" name="radio_box_newline" base="EditRadioBox"> + <style>wxRA_SPECIFY_ROWS</style> + <selection>0</selection> + <dimension>0</dimension> + <label>Newline Handling</label> + <choices> + <choice>CR only</choice> + <choice>LF only</choice> + <choice>CR+LF</choice> + </choices> + </object> + </object> + </object> + </object> + <object class="sizeritem"> + <flag>wxALL|wxALIGN_RIGHT</flag> + <border>4</border> + <option>0</option> + <object class="wxBoxSizer" name="sizer_3" base="EditBoxSizer"> + <orient>wxHORIZONTAL</orient> + <object class="sizeritem"> + <border>0</border> + <option>0</option> + <object class="wxButton" name="button_ok" base="EditButton"> + <default>1</default> + <label>OK</label> + </object> + </object> + <object class="sizeritem"> + <border>0</border> + <option>0</option> + <object class="wxButton" name="button_cancel" base="EditButton"> + <label>Cancel</label> + </object> + </object> + </object> + </object> + </object> + </object> +</application> diff --git a/pyserial/serial/serialwin32.py b/pyserial/serial/serialwin32.py index 0f33d00..ecbe2d7 100644 --- a/pyserial/serial/serialwin32.py +++ b/pyserial/serial/serialwin32.py @@ -11,7 +11,7 @@ import win32event # We use events and the WaitFor[Single|Multiple]Objects functi import win32con # constants. from serialutil import * -VERSION = "$Revision: 1.25 $".split()[1] #extract CVS version +VERSION = "$Revision: 1.26 $".split()[1] #extract CVS version #from winbase.h. these should realy be in win32con MS_CTS_ON = 16 @@ -33,6 +33,9 @@ class Serial(SerialBase): """Serial port implemenation for Win32. This implemenatation requires a win32all installation.""" + BAUDRATES = (50,75,110,134,150,200,300,600,1200,1800,2400,4800,9600, + 19200,38400,57600,115200) + def open(self): """Open port with current settings. This may throw a SerialException if the port cannot be opened.""" |