Wxpython learning notes of recommended view

  • 2020-04-02 13:44:18
  • OfStack

A list,

WxPython is a GUI toolkit for the Python programming language. It makes it easy for Python programmers to create programs with robust, powerful graphical user interfaces. It is the Python language's binding to the popular wxWidgets cross-platform GUI library. WxWidgets is written in C++. Like the Python language and the wxWidgets GUI library, wxPython is open source software. This means that anyone can use it for free and can view and modify its source code, or contribute patches and add features. WxPython is cross-platform. This means that the same program can run unmodified on multiple platforms. The platforms supported today are 32-bit Microsoft Windows, most Unix or unix-like systems, and apple Mac OS X. With Python as the programming language, wxPython is easy to write and easy to understand.

Two, basic use

I don't need to repeat the basic use words to this address is already very detailed, I don't need to repeat:

(link: http://wiki.wxpython.org/Getting%20Started)

Common controls

1. The menu (menu)

(link: http://wiki.wxpython.org/Getting%20Started#head-33e6dc36df2a89db146142e9a97b6e36b956875f)

2. Page layout (Sizer)

This east east USES quite troublesome, consult the following page:

(link: http://wiki.wxpython.org/Getting%20Started#head-7455553d71be4fad208480dffd53b7c68da1a982)

(link: http://purpen.javaeye.com/blog/92130)  

(link: http://purpen.javaeye.com/blog/92313)  

3. Tab notebook

(link: http://wiki.wxpython.org/Getting%20Started#head-b20d2fc488722cdb3f6193150293d1e118734db8)

4. List control

This control is more powerful and one of my favorite controls to use. It's in chapter 13 of the wxpythoninon (if you want the electronic version and the source code, ask me)

Here is a simple usage provided in list_report.py:


import wx
import sys, glob, random
import data
class DemoFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, -1,
                          "wx.ListCtrl in wx.LC_REPORT mode",
                          size=(600,400))
        il = wx.ImageList(16,16, True)
        for name in glob.glob("smicon??.png"):
            bmp = wx.Bitmap(name, wx.BITMAP_TYPE_PNG)
            il_max = il.Add(bmp)
        self.list = wx.ListCtrl(self, -1, style=wx.LC_REPORT)
        self.list.AssignImageList(il, wx.IMAGE_LIST_SMALL)
        # Add some columns
        for col, text in enumerate(data.columns):
            self.list.InsertColumn(col, text)
        # add the rows
        for item in data.rows:
            index = self.list.InsertStringItem(sys.maxint, item[0])
            for col, text in enumerate(item[1:]):
                self.list.SetStringItem(index, col+1, text)
            # give each item a random image
            img = random.randint(0, il_max)
            self.list.SetItemImage(index, img, img)

        # set the width of the columns in various ways
        self.list.SetColumnWidth(0, 120)
        self.list.SetColumnWidth(1, wx.LIST_AUTOSIZE)
        self.list.SetColumnWidth(2, wx.LIST_AUTOSIZE)
        self.list.SetColumnWidth(3, wx.LIST_AUTOSIZE_USEHEADER)

app = wx.PySimpleApp()
frame = DemoFrame()
frame.Show()
app.MainLoop()

For the ListCtrl control, a few things I'll add are:

1. How to get the selected item?

  The most common method is to get the first Item selected: GetFirstSelected(), which returns an int, the ID of the Item (Item) in ListCtrl.

  Another method is GetNextSelected(itemid), which gets the first selected item after the specified itemid, and also returns the itemid.

  With these two methods, we can iterate over all the selected items:


GetNextSelecteditemid = self.list.GetFirstSelected()
while itemid <> -1:
        #Do something
        itemid = self.list.GetNextSelected(itemid)

If you want to get the value of a row or a column, use the following method:


# For the first 0 Ok, the first 1 The value of the column 
itemtext = self.list.GetItem(0, 1).Text

2. How to add right-click menu after selected item?

In the function with the following event binding added:
Self. List.Bind(wx.evt_context_menu, self.OnContextMenu) then add the OnContextMenu method:


def OnContextMenu(self, event):
        if not hasattr(self, "popupStop"):
            self.popupStop = wx.NewId()
            self.popupPropery = wx.NewId()
            self.Bind(wx.EVT_MENU, self.OnPopupStop, id = self.popupStop)
            self.Bind(wx.EVT_MENU, self.OnPopupProperty, id = self.popupPropery)

        #  Create a menu 
        menu = wx.Menu()
        itemStop = wx.MenuItem(menu, self.popupStop, "Stop")
        itemProperty = wx.MenuItem(menu, self.popupPropery, 'Property')

        menu.AppendItem(itemStop)
        menu.AppendItem(itemProperty)

        itemProperty.Enable(False)# By default, the property button becomes invalid 

        if itemid == -1:# If nothing is selected 
            itemStop.Enable(False)
        else:
            itemStop.Enable(False)
            itemProperty.Enable(True)
        # That's where the menu pops up 
        self.PopupMenu(menu)
        # Finally, notice the destruction of the previously created menu 
        menu.Destroy()

5. Select the FileDialog

Very simple to use:


dlg = wx.FileDialog(self, 
                            message="Yes, select a place ",
                            wildcard="PNG(*.png)|*.png" ,
                            style=wx.SAVE
                            )
        savefile = ''
        if dlg.ShowModal() == wx.ID_OK:
            savefile = dlg.GetPath()
            try:
                os.remove(self.filename)
            except:
                pass
            self.img.SaveFile(savefile, wx.BITMAP_TYPE_PNG)
            self.filename = savefile
        dlg.Destroy()

6. Select folder dialog box


dialog = wx.DirDialog(None, 'Choose a directory: ',
                              style = wx.DD_DEFAULT_STYLE | wx.DD_NEW_DIR_BUTTON)
if dialog.ShowModal() == wx.ID_OK:
        for itemid in range(self.list.GetItemCount()):
                self.savechart(itemid, graphpath)
dialog.Destroy()

Four, some skills

1. Set shortcut keys

For example, if you want to press F5 to perform an operation, you can use the following method with the function of s/s:


acceltbl = wx.AcceleratorTable([(wx.ACCEL_NORMAL, wx.WXK_F5, self.btnrun.GetId())])
self.SetAcceleratorTable(acceltbl)

Another very common situation is to press the ESC key to close a window. As we know, a very simple way is to use the SetId(wx.id_cancel) method, such as:


self.btncancel = wx.Button(self.panel1, -1, 'Cancel', wx.Point(380, 280))
self.btncancel.SetId(wx.ID_CANCEL)

This will close the current Dialog when the ESC key is pressed, note! In this case, Dialog, which is a window object inherited from wx.dialog, does not seem to work with SetId for wx.frame.

2. Use a timer timer
There is an example of this in chapter 18 of wxpythonina-which is as follows:


import wx
import time
class ClockWindow(wx.Window):
    def __init__(self, parent):
        wx.Window.__init__(self, parent)
        self.Bind(wx.EVT_PAINT, self.OnPaint)
        self.timer = wx.Timer(self)
        self.Bind(wx.EVT_TIMER, self.OnTimer, self.timer)
        self.timer.Start(1000)
    def Draw(self, dc):
        t = time.localtime(time.time())
        st = time.strftime("%I:%M:%S", t)
        w, h = self.GetClientSize()
        dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
        dc.Clear()
        dc.SetFont(wx.Font(30, wx.SWISS, wx.NORMAL, wx.NORMAL))
        tw, th = dc.GetTextExtent(st)
        dc.DrawText(st, (w-tw)/2, (h)/2 - th/2)

    def OnTimer(self, evt):
        dc = wx.BufferedDC(wx.ClientDC(self))
        self.Draw(dc)
    def OnPaint(self, evt):
        dc = wx.BufferedPaintDC(self)
        self.Draw(dc)
class MyFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, title="wx.Timer")
        ClockWindow(self)
        
app = wx.PySimpleApp()
frm = MyFrame()
frm.Show()
app.MainLoop()

3. What you must know when using multithreading: wx.callafter

  In particular, when writing multithreading cases in wxpython, you must use wx.callafter to notify the window object in the thread to update its status. The same example from chapter 18:


import wx
import threading
import random
class WorkerThread(threading.Thread):
    """
    This just simulates some long-running task that periodically sends
    a message to the GUI thread.
    """
    def __init__(self, threadNum, window):
        threading.Thread.__init__(self)
        self.threadNum = threadNum
        self.window = window
        self.timeToQuit = threading.Event()
        self.timeToQuit.clear()
        self.messageCount = random.randint(10,20)
        self.messageDelay = 0.1 + 2.0 * random.random()
    def stop(self):
        self.timeToQuit.set()
    def run(self):
        msg = "Thread %d iterating %d times with a delay of %1.4fn" 
              % (self.threadNum, self.messageCount, self.messageDelay)
        wx.CallAfter(self.window.LogMessage, msg)
        for i in range(1, self.messageCount+1):
            self.timeToQuit.wait(self.messageDelay)
            if self.timeToQuit.isSet():
                break
            msg = "Message %d from thread %dn" % (i, self.threadNum)
            wx.CallAfter(self.window.LogMessage, msg)
        else:
            wx.CallAfter(self.window.ThreadFinished, self)

            
class MyFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, title="Multi-threaded GUI")
        self.threads = []
        self.count = 0

        panel = wx.Panel(self)
        startBtn = wx.Button(panel, -1, "Start a thread")
        stopBtn  = wx.Button(panel, -1, "Stop all threads")
        self.tc = wx.StaticText(panel, -1, "Worker Threads: 00")
        self.log = wx.TextCtrl(panel, -1, "",
                               style=wx.TE_RICH|wx.TE_MULTILINE)
        inner = wx.BoxSizer(wx.HORIZONTAL)
        inner.Add(startBtn, 0, wx.RIGHT, 15)
        inner.Add(stopBtn, 0, wx.RIGHT, 15)
        inner.Add(self.tc, 0, wx.ALIGN_CENTER_VERTICAL)
        main = wx.BoxSizer(wx.VERTICAL)
        main.Add(inner, 0, wx.ALL, 5)
        main.Add(self.log, 1, wx.EXPAND|wx.ALL, 5)
        panel.SetSizer(main)
        self.Bind(wx.EVT_BUTTON, self.OnStartButton, startBtn)
        self.Bind(wx.EVT_BUTTON, self.OnStopButton, stopBtn)
        self.Bind(wx.EVT_CLOSE,  self.OnCloseWindow)
        self.UpdateCount()
    def OnStartButton(self, evt):
        self.count += 1
        thread = WorkerThread(self.count, self)
        self.threads.append(thread)
        self.UpdateCount()
        thread.start()

    def OnStopButton(self, evt):
        self.StopThreads()
        self.UpdateCount()

    def OnCloseWindow(self, evt):
        self.StopThreads()
        self.Destroy()
    def StopThreads(self):
        while self.threads:
            thread = self.threads[0]
            thread.stop()
            self.threads.remove(thread)

    def UpdateCount(self):
        self.tc.SetLabel("Worker Threads: %d" % len(self.threads))

    def LogMessage(self, msg):
        self.log.AppendText(msg)

    def ThreadFinished(self, thread):
        self.threads.remove(thread)
        self.UpdateCount()
        
app = wx.PySimpleApp()
frm = MyFrame()
frm.Show()
app.MainLoop()

4. Need to launch another GUI program in the program without losing focus on the main window?
In general, we have no problem calling os.popen to run other external programs. But in wxpython, wx will lose focus, even if the program that has to be opened becomes a modal dialog. To solve this problem, use the method that comes with wx, wx.execute.


wx.Execute('notepad')

V. learning resources

1. The official: (link: http://wiki.wxpython.org/FrontPage)

2. The WIKI: woodpeckers (link: http://wiki.woodpecker.org.cn/moin/WxPythonInAction)

Author: CoderZh
Reference: http://coderzh.cnblogs.com


Related articles: