2014年5月16日 星期五

Pyserial Wxpython with microblaze

target ML605,Python 2.6

在上篇建立好microblaze以address 0x7c600000 來控制LED燈
並在SDK裡面用UART來收集連續的address與data資料
所以可以用pyserial來代替tera term,並用wxGlade來建立一個超簡單的GUI好讓使用者使用

有關pyserial的wxpython範例可以在這邊看到
http://pyserial.sourceforge.net/examples.html#wxpython-examples
我會用到wxTerminal.py與wxSerialConfigDialog.py

主程式是wxTerminal, 它其實就是一個python 版的tera term

我先用wxGlade畫一個簡單的GUI,只有一個text與button
text用來寫address的data,button按下時直接送入address 0x7c600000與使用者寫入的資料
在這個例子寫入資料0x00000001 讓LED燈開始閃爍,0x00000000則讓它停止閃爍

打開wxGlade新增一個Frame,選wxFrame



加入一個boxsizer,點了boxsizer之後再點Frame的地方就會出現對話方塊,slots改成2,讓它把畫面分成兩半,使用者就可以輸入不同的功能方塊



在左邊的boxsizer加入一個textctrl 


把名子改成value,之後如果要做比較大的GUI,最好把名子改成跟所需的功能相關,這樣程式寫起來比較省力,知道自己在寫哪一塊,改完後在右邊的boxsizer再加入一個button,同樣把name改成Set


如果要改在gui上顯現的button名子要改widget裡面的label,我將它也設成Set


之後按application 可以看到產生程式的對話視窗,python與選擇輸出位置與檔案名稱,generate code



產生code以後,必須要用bind函式把setvalue定義它們的型態與自身的函式
在寫它們本身的函式,這邊在EvtSet中蒐集由EvtVal中輸入的資料並轉成hex,
EvtVal則是當使用者輸入超過8 個數字則出件警告訊息

在把pyserial整合進來,把需要的函式放進來

import 需要的module
import wxSerialConfigDialog
import serial
import threading
from time import sleep

完整的test.py為

#!/usr/bin/env python
# -*- coding: CP1252 -*-
#
# generated by wxGlade 0.6.8 (standalone edition) on Thu May 15 15:13:20 2014
#

import wx
import wxSerialConfigDialog
import serial
import threading
from time import sleep
# begin wxGlade: dependencies
import gettext
# end wxGlade

# begin wxGlade: extracode
# end wxGlade
#----------------------------------------------------------------------
SERIALRX = wx.NewEventType()
# bind to serial data receive events
EVT_SERIALRX = wx.PyEventBinder(SERIALRX, 0)

class SerialRxEvent(wx.PyCommandEvent):
    eventType = SERIALRX
    def __init__(self, windowID, data):
        wx.PyCommandEvent.__init__(self, self.eventType, windowID)
        self.data = data

    def Clone(self):
        self.__class__(self.GetId(), self.data)
#----------------------------------------------------------------------
LED_ADDR          = '7c600000'        
NEWLINE_CR      = 0
NEWLINE_LF      = 1
NEWLINE_CRLF    = 2        
        
LED  = int(0x00000000)
#----------------------------------------------------------------------
class TerminalSetup:
    """Placeholder for various terminal settings. Used to pass the
       options to the TerminalSettingsDialog."""
    def __init__(self):
        self.echo = False
        self.unprintable = False
        self.newline = NEWLINE_CRLF

class MyFrame(wx.Frame):
    def __init__(self, *args, **kwds):
        self.serial = serial.Serial()
        self.serial.timeout = 0.5
        self.settings = TerminalSetup()
        self.thread = None
        self.alive = threading.Event()
        # begin wxGlade: MyFrame.__init__
        kwds["style"] = wx.DEFAULT_FRAME_STYLE
        wx.Frame.__init__(self, *args, **kwds)
        self.Value = wx.TextCtrl(self, wx.ID_ANY, "")
        self.Set = wx.Button(self, wx.ID_ANY, _("Set"))

        self.__set_properties()
        self.__do_layout()
        # end wxGlade
        
        #-------------------------------------------------
        self.OnPortSettings(None)
        if not self.alive.isSet():
            self.Close()
        self.Bind(wx.EVT_BUTTON, self.EvtSet, self.Set)
        self.Bind(wx.EVT_TEXT,self.EVTVal,self.Value)
        
        # function 
    def Write(self, addr, data):
        writein = str(data)        
        if len(writein) == 3:
            data7 = writein[2]
            data6 = '0'
            data5 = '0'
            data4 = '0'
            data3 = '0'
            data2 = '0'
            data1 = '0'
            data0 = '0'
        elif len(writein) == 4:
            data7 = writein[3]
            data6 = writein[2]
            data5 = '0'
            data4 = '0'
            data3 = '0'
            data2 = '0'
            data1 = '0'
            data0 = '0'
        elif len(writein) == 5:
            data7 = writein[4]
            data6 = writein[3]
            data5 = writein[2]
            data4 = '0'
            data3 = '0'
            data2 = '0'
            data1 = '0'
            data0 = '0'
        elif len(writein) == 6:
            data7 = writein[5]
            data6 = writein[4]
            data5 = writein[3]
            data4 = writein[2]
            data3 = '0'
            data2 = '0'
            data1 = '0'
            data0 = '0'
        elif len(writein) == 7:
            data7 = writein[6]
            data6 = writein[5]
            data5 = writein[4]
            data4 = writein[3]
            data3 = writein[2]
            data2 = '0'
            data1 = '0'
            data0 = '0'
        elif len(writein) == 8:
            data7 = writein[7]
            data6 = writein[6]
            data5 = writein[5]
            data4 = writein[4]
            data3 = writein[3]
            data2 = writein[2]
            data1 = '0'
            data0 = '0'
        elif len(writein) == 9:
            data7 = writein[8]
            data6 = writein[7]
            data5 = writein[6]
            data4 = writein[5]
            data3 = writein[4]
            data2 = writein[3]
            data1 = writein[2]
            data0 = '0'
        elif len(writein) == 10:
            data7 = writein[9]
            data6 = writein[8]
            data5 = writein[7]
            data4 = writein[6]
            data3 = writein[5]
            data2 = writein[4]
            data1 = writein[3]
            data0 = writein[2]
        else:
            data7 = writein[9]
            data6 = writein[8]
            data5 = writein[7]
            data4 = writein[6]
            data3 = writein[5]
            data2 = writein[4]
            data1 = writein[3]
            data0 = writein[2]
        
         
        self.serial.write(addr)
        self.serial.write(data0)
        self.serial.write(data1)
        self.serial.write(data2)
        self.serial.write(data3)
        self.serial.write(data4)
        self.serial.write(data5)
        self.serial.write(data6)
        self.serial.write(data7)

    def EvtSet(self,event):        
        GetValue = hex(int(self.Value.GetValue(),16))        
        self.Write(LED_ADDR,GetValue)
        #self.Write('7c600000','00000000')
        
    def EVTVal(self,event):
        length = len(event.GetString())
        if length > 8:
            alert_dialog = wx.MessageDialog(None,u"Bits Overflow",u"Error",wx.OK             | wx.ICON_ERROR)
            if alert_dialog.ShowModal() == wx.ID_OK:
                alert_dialog.Destroy()
        

    def StartThread(self):
        """Start the receiver thread"""        
        self.thread = threading.Thread(target=self.ComPortThread)
        self.thread.setDaemon(1)
        self.alive.set()
        self.thread.start()

    def StopThread(self):
        """Stop the receiver thread, wait util it's finished."""
        if self.thread is not None:
            self.alive.clear()          #clear alive event for thread
            self.thread.join()          #wait until thread has finished
            self.thread = None

    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 OnPortSettings(self, event=None):
        """Show the portsettings dialog. The reader thread is stopped for the
           settings change."""
        if event is not None:           #will be none when called on startup
            self.StopThread()
            self.serial.close()
        ok = False
        while not ok:
            dialog_serial_cfg = wxSerialConfigDialog.SerialConfigDialog(None, -1,            "",
            show=wxSerialConfigDialog.SHOW_BAUDRATE|wxSerialConfigDialog.SHOW_FOR             MAT|wxSerialConfigDialog.SHOW_FLOW,
                serial=self.serial
            )
            result = dialog_serial_cfg.ShowModal()
            dialog_serial_cfg.Destroy()
            #open port if not called on startup, open it on startup and OK too
            if result == wx.ID_OK or event is not None:
                try:
                    self.serial.open()
                except serial.SerialException, e:
                    dlg = wx.MessageDialog(None, str(e), "Serial Port Error", wx.                     OK | wx.ICON_ERROR)
                    dlg.ShowModal()
                    dlg.Destroy()
                else:
                    self.StartThread()
                    self.SetTitle("Serial Terminal on %s [%s, %s%s%s%s%s]" % (
                        self.serial.portstr,
                        self.serial.baudrate,
                        self.serial.bytesize,
                        self.serial.parity,
                        self.serial.stopbits,
                        self.serial.rtscts and ' RTS/CTS' or '',
                        self.serial.xonxoff and ' Xon/Xoff' or '',
                        )
                    )
                    ok = True
            else:
                #on startup, dialog aborted
                self.alive.clear()
                ok = True
           
    def OnKey(self, event):
        """Key event handler. if the key is in the ASCII range, write it to the s           erial 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 w                                                 hich is the RETURN key)
                if self.settings.echo:          #do echo if needed
                    self.COM_STATUS.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.COM_STATUS.WriteText(char)
                self.serial.write(char)         #send the charcater
        else:
            print "Extra Key:", code

    def OnSerialRead(self, event):
        """Handle input from the serial port."""
        text = event.data
        if self.settings.unprintable:
            text = ''.join([(c >= ' ') and c or '<%d>' % ord(c)  for c in text])
        self.COM_STATUS.AppendText(text)
        
    def ComPortThread(self):
        """Thread that handles the incomming traffic. Does the basic input
           transformation (newlines) and generates an SerialRxEvent"""
        while self.alive.isSet():               #loop while alive event 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')
                event = SerialRxEvent(self.GetId(), text)
                self.GetEventHandler().AddPendingEvent(event)
                #~ self.OnSerialRead(text)         #output text in window               
 #----------------------------------------------------------------------
    def __set_properties(self):
        # begin wxGlade: MyFrame.__set_properties
        self.SetTitle(_("Test"))
        # end wxGlade

    def __do_layout(self):
        # begin wxGlade: MyFrame.__do_layout
        sizer_1 = wx.BoxSizer(wx.VERTICAL)
        sizer_2 = wx.BoxSizer(wx.HORIZONTAL)
        sizer_2.Add(self.Value, 0, 0, 0)
        sizer_2.Add(self.Set, 0, 0, 0)
        sizer_1.Add(sizer_2, 1, wx.EXPAND, 0)
        self.SetSizer(sizer_1)
        sizer_1.Fit(self)
        self.Layout()
        # end wxGlade

# end of class MyFrame
if __name__ == "__main__":
    gettext.install("app") # replace with the appropriate catalog name
    app = wx.PySimpleApp(0)
    wx.InitAllImageHandlers()
    frame_1 = MyFrame(None, -1, "")
    app.SetTopWindow(frame_1)
    frame_1.Show()
    app.MainLoop()
紅色的程式是從wxTerminal.py整合過來,綠色的是自己加上去的
函式write是為了如果使用者沒有完整輸入8位數,則自動補零
所以在測試的時候,即使對話框只輸入1,LED燈也是會閃爍
程式測試成功以後,把它轉成exe
寫一個py2exesetup.py,windows=['test.py'] 裡填入要轉換的py
from distutils.core import setup   
import py2exe  
setup(  
      windows=['test.py'],   
      options = {           
      "py2exe":  
       {"dll_excludes":["MSVCP90.dll"]}  
       }  
) 
把所有會引用到的py檔與py2exesetup.py複製到c下面的python26
然後打開cmd進入python26command
python py2exesetup.py install 
python py2exesetup.py py2exe
就會在同一層的資料夾中產生一個新的disk資料夾
裡面的exe檔就是轉換出來的小寶貝拉XD

沒有留言:

張貼留言