From 80a0ed170c4ba4d74b240149dcc8616c21200c96 Mon Sep 17 00:00:00 2001 From: cliechti Date: Fri, 3 Oct 2003 23:53:42 +0000 Subject: - remove unsupported baudrates on windows from the list - added terminal application for wxPython with wxGlade design file - enumerate bugfix for python < 2.3 --- pyserial/CHANGES.txt | 7 +- pyserial/examples/wxSerialConfigDialog.py | 6 + pyserial/examples/wxTerminal.py | 272 ++++++++++++++++++++++++++++++ pyserial/examples/wxTerminal.wxg | 133 +++++++++++++++ pyserial/serial/serialwin32.py | 5 +- 5 files changed, 421 insertions(+), 2 deletions(-) create mode 100644 pyserial/examples/wxTerminal.py create mode 100644 pyserial/examples/wxTerminal.wxg (limited to 'pyserial') 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 @@ + + + + + + + Serial Terminal + 1 + 1 + 546, 383 + + wxVERTICAL + + wxEXPAND + 0 + + + + + + + + + + + + ID_CLEAR + + + + ID_SAVEAS + + + + --- + --- + + + + ID_SETTINGS + + + + ID_TERM + + + + --- + + + + ID_EXIT + + + + + + + frame_terminal_statusbar + + + + + + Terminal Settings + + wxVERTICAL + + wxEXPAND + 0 + + + wxVERTICAL + + + wxALL + 4 + + + + + + + wxALL + 4 + + + + + + + 0 + + + + 0 + 0 + + + CR only + LF only + CR+LF + + + + + + + wxALL|wxALIGN_RIGHT + 4 + + + wxHORIZONTAL + + 0 + + + 1 + + + + + 0 + + + + + + + + + + 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.""" -- cgit v1.2.1