2

Level: Beginner

I have been working with wxPython for a month now. I almost completely my GUI application, but when it came to threading I got stuck. I am using python v2.7 and wxPython v3.0 and the OS is windows 7.

My GUI app: Well in my GUI app I am reading some values from a server and then based upon number of these values I create panels in my GUI. Then each of these panels will represent the values in form of a staticText. For eg: If I receive from server 1,2,3 values, then I create 3 panels each displaying 1, 2 and 3 respectively. Till here everything works fine.

Problem: I want to check the server every 5 seconds to fetch the values and accordingly update my GUI ie. I have to add new panels to display the new values. I read some tutorials & posts on SC regarding using threads, I understood some basic facts too. Unfortunately I don't understand how to apply this concept to my problem. I think I have to put the code that checks the values from the server in threading loop. But I don't understand what else to do. I think I may have to change the logic of my code (creating panels part) for using threads. It would be really great if I could get a working example for my this particular problem so that I could apply the same to my rest of the application.

Code: I have created a short sample code for this particular problem. The getLabels() in the class labels mimics the server by just generating some random values and returning them in a list. Then these values are used by createPanels() to create panels and to display these values. It would be really great if some one can teach me how to use threading to add new panels to my GUI without freezing/blocking my GUI.

Download: The entire code can be found below and can also be downloaded from here to avoid any identation problem.

#!/usr/bin/env python

from random import randrange
import wx
import wx.lib.scrolledpanel

class GUI(wx.Frame):

    def __init__(self, parent, id, title):
        screenWidth = 800
        screenHeight = 450
        screenSize = (screenWidth, screenHeight)
        wx.Frame.__init__(self, None, id, title, size=screenSize)
        self.locationFont = locationFont = wx.Font(15, wx.MODERN, wx.NORMAL, wx.BOLD)
        mainSizer = wx.BoxSizer(wx.VERTICAL)
        self.sizer = sizer = wx.BoxSizer(wx.VERTICAL)
        self.panel = panel = wx.lib.scrolledpanel.ScrolledPanel(self, -1, style=wx.SIMPLE_BORDER)
        panel.SetupScrolling()
        panel.SetBackgroundColour('#FFFFFF')
        self.createPanels()
        panel.SetSizer(sizer)
        mainSizer.Add(panel, 15, wx.EXPAND|wx.ALL)
        self.SetSizer(mainSizer)

    def createPanels(self):
        k = 0
        labelObj = labels()
        locations = labelObj.getLabel()
        print locations
        for i in locations:
            sPanels = 'sPanel'+str(k)
            sPanels = wx.Panel(self.panel)
            label = str(k+1)
            text = wx.StaticText(sPanels, -1, label)
            text.SetFont(self.locationFont)
            text.SetForegroundColour('#0101DF')
            self.sizer.Add(sPanels, 0, wx.ALL, 5)
            self.sizer.Add(wx.StaticLine(self.panel), 0, wx.ALL|wx.EXPAND, 0)
            k += 1

################################################
class labels():
    def getLabel(self):
    mylist =[]
    i = randrange(10)
    for k in range(1,i+1):
        list.append(k)
    return mylist
###############################################

if __name__=='__main__':
app = wx.App()
frame = GUI(parent=None, id=-1, title="Test")
frame.Show()
app.MainLoop()

If you don't see any panels after executing the code then please re execute the code.

Thank you for your time.

0

2 Answers 2

1

Hopefully this code will contain some answers. Basically: put the interface to the server in a thread, and use wx.CallAfter to talk to components in wx. Use pubsub to talk between elements in the GUI. The code has creation of a thread that does something every 5 seconds, and it has communication by direct method call for a wx element that you have a handle for, and communication by pubsub for one that you don't.

#!/usr/bin/env python

from random import randrange
import wx
import wx.lib.scrolledpanel

# imports added by GreenAsJade
import time
import threading
from wx.lib.pubsub import setupkwargs
from wx.lib.pubsub import pub

class GUI(wx.Frame):

    def __init__(self, parent, id, title):
        screenWidth = 800
        screenHeight = 450
        screenSize = (screenWidth, screenHeight)
        wx.Frame.__init__(self, None, id, title, size=screenSize)
        self.locationFont = locationFont = wx.Font(15, wx.MODERN, wx.NORMAL, wx.BOLD)
        mainSizer = wx.BoxSizer(wx.VERTICAL)
        self.sizer = sizer = wx.BoxSizer(wx.VERTICAL)
        self.panel = panel = wx.lib.scrolledpanel.ScrolledPanel(self, -1, style=wx.SIMPLE_BORDER)
        panel.SetupScrolling()
        panel.SetBackgroundColour('#FFFFFF')
        panel.SetSizer(sizer)
        mainSizer.Add(panel, 15, wx.EXPAND|wx.ALL)
        self.SetSizer(mainSizer)

        pub.subscribe(self.OnNewLabels, "NEW_LABELS")


    def OnNewLabels(self, labels):
        k = 0
        locations = labels
        print locations
        for i in locations:
            sPanels = 'sPanel'+str(k)
            sPanels = wx.Panel(self.panel)
            label = str(k+1)
            print "doing", label
            text = wx.StaticText(sPanels, -1, label)
            text.SetFont(self.locationFont)
            text.SetForegroundColour('#0101DF')
            self.sizer.Add(sPanels, 0, wx.ALL, 5)
            self.sizer.Add(wx.StaticLine(self.panel), 0, wx.ALL|wx.EXPAND, 0)
            k += 1
        self.sizer.Layout()


###############################
#
#

def InterfaceThread(id, log):
    label_generator = Labels()
    while True:
        labels = label_generator.getLabel()   # get the info from the server
        # Tell the GUI about them
        wx.CallAfter(pub.sendMessage, "NEW_LABELS", labels = labels)
        # Tell the logger about them
        wx.CallAfter(log.AppendText, "Sent %s \n" % str(labels))
        time.sleep(5)


class ServerInterface(wx.Frame):

    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, None, id, title)
        self.log = wx.TextCtrl(self, style = wx.TE_MULTILINE)
        interface_thread = threading.Thread(target = InterfaceThread, args = (1, self.log)) 
        interface_thread.start()



################################################
class Labels():
    def getLabel(self):
        mylist =[]
        i = 4 #randrange(10)
        for k in range(1,i+1):
            mylist.append(k)
        return mylist
###############################################

if __name__=='__main__':
    app = wx.App()
    frame = GUI(parent=None, id=-1, title="Test")
    frame.Show()
    server_interface = ServerInterface(parent = None, id=-1, title="Server Log")
    server_interface.Show()
    app.MainLoop()
Sign up to request clarification or add additional context in comments.

6 Comments

Note: the ServerInterface doesn't have to be a graphical element. It could just be a plain class - I made it a graphical object only to show how you might implement a graphical interface to it, and the communication to it from the thread (where you have a handle).
I first of all thanks for considering my question. I executed your code. Most of the part works as I wanted. The problem is that now when server sends the values it is creating new panels on the main panel. My problems is that when server sends the values these values should be replaced/update old values. It should not add new panels with new values. It would be really great if you just use my code and keep the answer as simple as much you can. You can remove the server GUI too.
I think that I have answered your question, which is "how to set up a threaded server interface". If you have other questions, you should accept my answer, and ask a separate question. It sounds like you want to ask "how do I replace panels with new ones?". Or maybe you want code to update the values in existing panels? Whatever it is that you want, determine the question, then ask it :)
Please read my question again. Unfortunately you haven't answered my actual question. I don't need a server interface at first place. That is the reason why I mimic the server behavior by labels() class. My question was how to update the values of the staticTexts on the panels after getting these values from the server without freezing my GUI. If you still think that my question is confusing then I can post it again for the problem regarding the updating the values of the staticTexts on the panels.
As you say - Your question is how to update the panels without freezing the GUI. As you say: to do this, you need the server interface in a thread. I have shown you how to do this: how to place code that sleeps for 5 seconds in a separate thread from the code that manipulates the GUI. The code above is the answer to this question. What you do with the text controls is a separate issue. If you want to change the text in the existing StaticText controls, create them first (or as you discover more are needed) and use SetLabel() on them in OnNewLabels
|
0

Followup: here is a version where the SeverInterface is not a graphical element, for clarity.

#!/usr/bin/env python

from random import randrange
import wx
import wx.lib.scrolledpanel

# imports added by GreenAsJade
import time
import threading
from wx.lib.pubsub import setupkwargs
from wx.lib.pubsub import pub

class GUI(wx.Frame):

    def __init__(self, parent, id, title):
        screenWidth = 800
        screenHeight = 450
        screenSize = (screenWidth, screenHeight)
        wx.Frame.__init__(self, None, id, title, size=screenSize)
        self.locationFont = locationFont = wx.Font(15, wx.MODERN, wx.NORMAL, wx.BOLD)
        mainSizer = wx.BoxSizer(wx.VERTICAL)
        self.sizer = sizer = wx.BoxSizer(wx.VERTICAL)
        self.panel = panel = wx.lib.scrolledpanel.ScrolledPanel(self, -1, style=wx.SIMPLE_BORDER)
        panel.SetupScrolling()
        panel.SetBackgroundColour('#FFFFFF')
        panel.SetSizer(sizer)
        mainSizer.Add(panel, 15, wx.EXPAND|wx.ALL)
        self.SetSizer(mainSizer)

        pub.subscribe(self.OnNewLabels, "NEW_LABELS")


    def OnNewLabels(self, labels):
        k = 0
        locations = labels
        print locations
        for i in locations:
            sPanels = 'sPanel'+str(k)
            sPanels = wx.Panel(self.panel)
            label = str(k+1)
            print "doing", label
            text = wx.StaticText(sPanels, -1, label)
            text.SetFont(self.locationFont)
            text.SetForegroundColour('#0101DF')
            self.sizer.Add(sPanels, 0, wx.ALL, 5)
            self.sizer.Add(wx.StaticLine(self.panel), 0, wx.ALL|wx.EXPAND, 0)
            k += 1
        self.sizer.Layout()


###############################
#
#

def InterfaceThread():
    label_generator = Labels()
    while True:
        labels = label_generator.getLabel()   # get the info from the server
        # Tell the GUI about them
        wx.CallAfter(pub.sendMessage, "NEW_LABELS", labels = labels)
        time.sleep(5)


class ServerInterface():

    def __init__(self):
        interface_thread = threading.Thread(target = InterfaceThread, args = ()) 
        interface_thread.start()



################################################
class Labels():
    def getLabel(self):
        mylist =[]
        i = 4 #randrange(10)
        for k in range(1,i+1):
            mylist.append(k)
        return mylist
###############################################

if __name__=='__main__':
    app = wx.App()
    frame = GUI(parent=None, id=-1, title="Test")
    frame.Show()
    server_interface = ServerInterface()
    app.MainLoop()

As I mentioned in the other answer - if you want to update the StaticTexts, then put them in an array when you create them, and iterate through the array in OnNewLabels updating them with SetLabel().

1 Comment

Thanks for the effort, I will edit my question before accepting your solution as it is not the actual solution what I wa looking for. I'll post my new question soon, I would really appreciate it if you could assist me on that too. :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.