wxPython: show number repeatedly - python

I'm a very beginner of Python Programming, and now I'm wondering why my widgets cannot show number repeatedly.
That is, I'd like to let the text show numbers from 1 to 9, but in the while loop, it only shows 9....
Any suggestions?
Here is my code(Python version: 2.6):
#!/user/bin/python
import wx
class Frame(wx.Frame):
def __init__(self,parent,id):
wx.Frame.__init__(self, parent, id,
'Show Number',
size = (200,150),
style=wx.MINIMIZE_BOX | wx.RESIZE_BORDER
| wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX)
self.initUI()
def initUI(self):
widgetPanel=wx.Panel(self, -1)
widgetPanel.SetBackgroundColour('white')
# Buttons for play the simulation
playButton = wx.Button(widgetPanel, -1, "Play", pos=(10,10), size=(30,30))
self.Bind(wx.EVT_BUTTON, self.play, playButton)
playButton.SetDefault()
# Time
self.timeText = wx.TextCtrl(widgetPanel, -1, "", pos=(10, 50),
size =(100,30), style=wx.TE_CENTER)
self.timeText.SetBackgroundColour('white')
self.timeText.SetFont(wx.Font(20, wx.DECORATIVE, wx.NORMAL, wx.NORMAL))
def play(self, event):
#self.timeText.SetValue("19:32")
self.show_num()
def show_num(self):
x = 0
while(x < 10):
self.timeText.SetValue(str(x))
x += 1
if __name__ == "__main__":
app = wx.App(False)
frame = Frame(parent=None,id=-1)
frame.Show()
app.MainLoop()

you need to give your app the chance to update .... the easiest and most correct way of doing this is to use timers instead of a loop ... I have included a minimal example
import wx
app = wx.App(redirect=False)
frame = wx.Frame(None,-1,"Counter")
btn = wx.Button(f,-1,"Click Me!")
timer = wx.Timer() # this will update the count
def onUpdateButton(evt):
next_int = int(btn.GetLabel())+1
btn.SetLabel(str(next_int))
if next_int > 10:
timer.Stop()
btn.Enable(True)
def onButtonClick(event):
btn.SetLabel("0")
btn.Enable(False)
timer.Start(1000) # 1 second updates
timer.Bind(wx.EVT_TIMER,onUpdateButton)
btn.Bind(wx.EVT_BUTTON,onButtonClick)
frame.Show()
app.MainLoop()
it is probably obvious to most users, however may be worth mentioning that instead of keeping a variable with the current count i am just using the label string

Like the other answer said, you need to give time to your app to update.
I have made a few additions to your code to make it work:
import wx
import time
import thread
class Frame(wx.Frame):
def __init__(self,parent,id):
wx.Frame.__init__(self, parent, id,
'Show Number',
size = (200,150),
style=wx.MINIMIZE_BOX | wx.RESIZE_BORDER
| wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX)
self.initUI()
def initUI(self):
widgetPanel=wx.Panel(self, -1)
widgetPanel.SetBackgroundColour('white')
# Buttons for play the simulation
playButton = wx.Button(widgetPanel, -1, "Play", pos=(10,10), size=(30,30))
self.Bind(wx.EVT_BUTTON, self.play, playButton)
playButton.SetDefault()
# Time
self.timeText = wx.TextCtrl(widgetPanel, -1, "", pos=(10, 50),
size =(100,30), style=wx.TE_CENTER)
self.timeText.SetBackgroundColour('white')
self.timeText.SetFont(wx.Font(20, wx.DECORATIVE, wx.NORMAL, wx.NORMAL))
def play(self, event):
#self.timeText.SetValue("19:32")
#self.show_num()
thread.start_new_thread(self.show_num,())
def show_num(self):
x = 0
while(x < 10):
self.timeText.SetValue(str(x))
time.sleep(1)
x += 1
if __name__ == "__main__":
app = wx.App(False)
frame = Frame(parent=None,id=-1)
frame.Show()
app.MainLoop()
Notice that time.sleep(1) pauses the while loop for 1 second so that the display can be updated. You can reduce or increase the pause time as you desire.

I experienced an update issue with:
self.timeText.SetValue(str(x))
time.sleep(1)
x += 1
I added a wx.YieldIfNeeded() before the sleep:
self.timeText.SetValue(str(x))
wx.YieldIfNeeded()
wx.sleep(1)
x += 1
This makes sure the GUI is updated and other events are handled.
I also would use the wx.Sleep(). This prevents using an other library...

Related

wx python detect if user didn't intruct for 5min then do something

this is a simple code i am using WX python, i want if user didn't instruct within 1 min it will do something.
import wx
class QuestAsk(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None,
pos=wx.DefaultPosition, size=wx.Size(400, 100),
style=wx.MINIMIZE_BOX | wx.SYSTEM_MENU | wx.CAPTION |
wx.CLOSE_BOX | wx.CLIP_CHILDREN,
title="Question")
mainwindows = wx.Panel(self)
mysizer = wx.BoxSizer(wx.VERTICAL)
self.txt = wx.TextCtrl(mainwindows, style=wx.TE_PROCESS_ENTER, size=(100, 30))
self.txt.SetFocus()
self.txt.Bind(wx.EVT_TEXT_ENTER, self.StatNow)
mysizer.Add(self.txt, 15, wx.ALL, 5)
mainwindows.SetSizer(mysizer)
self.Show()
def StatNow(self, event):
go = self.txt.GetValue()
go = go.lower()
link = go.split()
self.txt.Clear()
self.txt.SetValue(go)
if go.startswith(''):
try:
#request
except Exception as e:
print(str(e))
if __name__ == "__main__":
app = wx.App(True)
frame = QuestAsk()
app.MainLoop()
The simplest way of doing that is to add a wx.Timer, like this:
import wx
class QuestAsk(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None,
pos=wx.DefaultPosition, size=wx.Size(400, 100),
style=wx.MINIMIZE_BOX | wx.SYSTEM_MENU | wx.CAPTION |
wx.CLOSE_BOX | wx.CLIP_CHILDREN,
title="Question")
mainwindows = wx.Panel(self)
mysizer = wx.BoxSizer(wx.VERTICAL)
self.txt = wx.TextCtrl(mainwindows, style=wx.TE_PROCESS_ENTER, size=(100, 30))
self.txt.SetFocus()
self.txt.Bind(wx.EVT_TEXT_ENTER, self.StatNow)
# Add a timer
self.timer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.OnTimer, self.timer)
self.timer.Start(60000)
mysizer.Add(self.txt, 15, wx.ALL, 5)
mainwindows.SetSizer(mysizer)
self.Show()
def OnTimer(self, evt):
print ("Timeout! Please input something")
self.timer.Stop()
self.timer.Start(60000)
def StatNow(self, event):
self.timer.Stop()
go = self.txt.GetValue()
go = go.lower()
link = go.split()
self.txt.Clear()
self.txt.SetValue(go)
if go.startswith(' '):
try:
print ("Go is empty")
except Exception as e:
print(str(e))
self.timer.Start(60000)
if __name__ == "__main__":
app = wx.App(True)
frame = QuestAsk()
app.MainLoop()
Note: The timer works in milliseconds. I have set the timer at 1 Minute
Edit to cater for key pressed restarting the timer.
The instruction to do something, logically, only occurs once the user has committed the answer i.e. pressed enter. However, if you wish to use the timer in between key depressions, then simply make use of the EVT_TEXT event and bind it to a function that restarts the timer.
import wx
class QuestAsk(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None,
pos=wx.DefaultPosition, size=wx.Size(400, 100),
style=wx.MINIMIZE_BOX | wx.SYSTEM_MENU | wx.CAPTION |
wx.CLOSE_BOX | wx.CLIP_CHILDREN,
title="Question")
mainwindows = wx.Panel(self)
mysizer = wx.BoxSizer(wx.VERTICAL)
self.txt = wx.TextCtrl(mainwindows, style=wx.TE_PROCESS_ENTER, size=(100, 30))
self.txt.SetFocus()
self.txt.Bind(wx.EVT_TEXT_ENTER, self.StatNow)
self.txt.Bind(wx.EVT_TEXT, self.CharNow)
# Add a timer
self.timer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.OnTimer, self.timer)
self.timer.Start(10000)
mysizer.Add(self.txt, 15, wx.ALL, 5)
mainwindows.SetSizer(mysizer)
self.Show()
def OnTimer(self, evt):
self.timer.Stop()
wx.MessageBox('Please input something and press enter', 'Timeout', wx.OK | wx.ICON_INFORMATION)
self.timer.Start(10000)
def CharNow(self, event):
self.timer.Stop()
self.timer.Start(10000)
def StatNow(self, event):
self.timer.Stop()
go = self.txt.GetValue()
go = go.lower()
link = go.split()
self.txt.SetValue(go)
if go.startswith(' '):
wx.MessageBox('Input is Empty. Please input something and press enter', 'Timeout', wx.OK | wx.ICON_ERROR)
self.timer.Start(10000)
if __name__ == "__main__":
app = wx.App(True)
frame = QuestAsk()
app.MainLoop()
Note: I have set the timer at 10 seconds for testing.

wxPython session timeout

I have a medium sized desktop application created with wxPython. I want to implement a session facility in this application. After some amount of time of inactivity, the application should log the user out and show login screen automatically. What will be the best way to accomplish this in wxPython?
The application uses wxPython 2.8.12.1 with Python 2.7 within Windows 8, 7, XP.
EDIT 1
Binding EVT_MOTION to wx.Frame and wx.Panel not working. It is working if I bind EVT_MOTION to all individual objects. Is there any way to get event bubbled to the outermost parent (wx.Frame)?
import wx
class MyFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, "Checking EVT_MOTION with Frame")
panel = wx.Panel(self, -1)
sizer = wx.BoxSizer(wx.VERTICAL)
panel.SetSizer(sizer)
sizer.Add(wx.Button(panel, -1, "Button 1"), 1, wx.EXPAND)
sizer.Add(wx.Button(panel, -1, "Button 2"), 1, wx.EXPAND)
sizer.Add(wx.Button(panel, -1, "Button 3"), 1, wx.EXPAND)
self.Bind(event=wx.EVT_MOTION, handler=self.OnMotion)
self.Show()
def OnMotion(self, event):
print "EVT_MOTION: " + str(event.GetEventObject())
if __name__ == '__main__':
app = wx.App(False)
frame = MyFrame()
app.MainLoop()
I would use a wx.Timer. You can restart it every time the user moves their mouse (wx.EVT_MOTION) or presses a key (wx.EVT_KEY_DOWN). I wrote up a tutorial on using wx.Timers that you might find helpful:
http://www.blog.pythonlibrary.org/2009/08/25/wxpython-using-wx-timers/
Here's the one of the examples from that tutorial that implements a super simple wx.Timer:
import wx
import time
class MyForm(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, "Timer Tutorial 1",
size=(500,500))
# Add a panel so it looks the correct on all platforms
panel = wx.Panel(self, wx.ID_ANY)
self.timer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.update, self.timer)
self.toggleBtn = wx.Button(panel, wx.ID_ANY, "Start")
self.toggleBtn.Bind(wx.EVT_BUTTON, self.onToggle)
def onToggle(self, event):
btnLabel = self.toggleBtn.GetLabel()
if btnLabel == "Start":
print "starting timer..."
self.timer.Start(1000)
self.toggleBtn.SetLabel("Stop")
else:
print "timer stopped!"
self.timer.Stop()
self.toggleBtn.SetLabel("Start")
def update(self, event):
print "\nupdated: ",
print time.ctime()
# Run the program
if __name__ == "__main__":
app = wx.PySimpleApp()
frame = MyForm().Show()
app.MainLoop()

How to refresh listctrl simultaneously when using setstringitem function

I'm so troubled by this problem:
I've create a ListCtrl object, a TextCtrl object, and a button. First I fill some data into the ListCtrl object, when I press the button, it will append some strings into TextCtrl object and use SetStringItem to modify ListCtrl object.
As you can see in the button function, I've added time.sleep(2) in each loop. When I've got is when the button is pressed, TextCtrl will be refreshed every time the strings is inserted, but ListCtrl just freeze until the LOOP IS FINISHED, then it will display the correct strings.
I want to know how to refresh the ListCtrl object as soon as SetStringItem is called.
Any help is deeply appreciated.
Here is the code:
import wx
import sys
import time
class Frame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, size=(450, 450))
self.panel = wx.Panel(self)
self.dl = wx.ListCtrl(self,-1,size=(300,100),style=wx.LC_REPORT)
self.dl.InsertColumn(0, 'File')
self.dl.InsertColumn(1, 'Progress')
self.dl.InsertColumn(2, 'State')
for row in range(3):
labels = [l+str(row) for l in "FILE PERCENT STATE".split()]
# sys.maxint inserts at the end of the list
index = self.dl.InsertStringItem(sys.maxint, labels[0])
self.dl.SetStringItem(index, 1, labels[1])
self.dl.SetStringItem(index, 2, labels[2])
self.Show(True)
button2 = wx.Button(self, label=u"test", pos=(15, 200), size=(60, 25))
self.Bind(wx.EVT_BUTTON, self.test, button2)
self.text = wx.TextCtrl(self, -1, pos=(80, 200), size=(200, 175), style=wx.TE_MULTILINE)
def test(self,event):
for i in range(3):
self.dl.SetStringItem(i,1,"HELLO")
self.text.AppendText("HELLO")
time.sleep(2)
app = wx.App()
Frame(None)
app.MainLoop()
The problem is that time.sleep blocks your GUI, what you will need to do to get the effect that you are trying for is:
On your button press add the first item & start a 2 second wx.Timer with an event handler/
In the event handler add the next string or if there are no more to add cancel the timer.
I've changed my code to this, and it works, thanks steve
import wx
import sys
import time
class Frame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, size=(450, 450))
self.panel = wx.Panel(self)
self.dl = wx.ListCtrl(self,-1,size=(300,100),style=wx.LC_REPORT)
self.dl.InsertColumn(0, 'File')
self.dl.InsertColumn(1, 'Progress')
self.dl.InsertColumn(2, 'State')
for row in range(3):
labels = [l+str(row) for l in "FILE PERCENT STATE".split()]
# sys.maxint inserts at the end of the list
index = self.dl.InsertStringItem(sys.maxint, labels[0])
self.dl.SetStringItem(index, 1, labels[1])
self.dl.SetStringItem(index, 2, labels[2])
self.Show(True)
self.timer = wx.Timer(self,-1)
#self.Bind(wx.EVT_TIMER, self.OnTimer, self.timer)
self.Bind(wx.EVT_TIMER, self.test1, self.timer)
button2 = wx.Button(self, label=u"test", pos=(15, 200), size=(60, 25))
self.Bind(wx.EVT_BUTTON, self.test, button2)
self.text = wx.TextCtrl(self, -1, pos=(80, 200), size=(200, 175), style=wx.TE_MULTILINE)
self.z=0
def test(self,event):
self.timer.Start(3000)
def test1(self,event):
for i in range(1):
self.dl.SetStringItem(self.z,1,"HELLO")
self.text.AppendText("HELLO")
self.z+=1
if self.z >2 :
self.timer.Stop()
app = wx.App()
Frame(None)
app.MainLoop()

How do I make wx.TextCtrl multi-line text update smoothly?

I'm working on an GUI program, and I use AppendText to update status in a multi-line text box(made of wx.TextCtrl). I noticed each time there's a new line written in this box, instead of smoothly adding this line to the end, the whole texts in the box just disappear(not in real, just visually) and I have to click the scroll button to check the newly updated/written status line. Why this happening? Should I add some styles? Hopefully you guys can help me out.
Here's my sample code:
import wx
import thread
import time
class TestFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, parent = None, id = -1, title = "Testing", pos=(350, 110), size=(490,530), style=wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.MINIMIZE_BOX)
panel = wx.Panel(self)
self.StartButton = wx.Button(parent = panel, id = -1, label = "Start", pos = (110, 17), size = (50, 20))
self.MultiLine = wx.TextCtrl(parent = panel, id = -1, pos = (38, 70), size = (410, 90), style = wx.TE_MULTILINE|wx.TE_READONLY|wx.TE_AUTO_URL)
self.Bind(wx.EVT_BUTTON, self.OnStart, self.StartButton)
def OnStart(self, event):
self.StartButton.Disable()
thread.start_new_thread(self.LongRunning, ())
def LongRunning(self):
Counter = 1
while True:
self.MultiLine.AppendText("Hi," + str(Counter) + "\n")
Counter = Counter + 1
time.sleep(2)
class TestApp(wx.App):
def OnInit(self):
self.TestFrame = TestFrame()
self.TestFrame.Show()
self.SetTopWindow(self.TestFrame)
return True
def main():
App = TestApp(redirect = False)
App.MainLoop()
if __name__ == "__main__":
main()
try this:
self.logs = wx.TextCtrl(self, id=-1, value='', pos=wx.DefaultPosition,
size=(-1,300),
style= wx.TE_MULTILINE | wx.SUNKEN_BORDER)
self.logs.AppendText(text + "\n")
Try calling the Refresh() method on the textCtrl
Update:
A question has already been asked regarding this problem, here is the answer which pretty much solves it, -its not perfect but maybe you can improve on it...
Here is a thread from the wxpython mailing list regarding the problem which may also be of interest to you.

Marquee style progressbar in wxPython

Could anyone tell me how to implement a marquee style progress bar in wxPython? As stated on MSDN:
you can animate it in a way that shows
activity but does not indicate what
proportion of the task is complete.
Thank you.
alt text http://i.msdn.microsoft.com/dynimg/IC100842.png
I tried this but it doesn't seem to work. The timer ticks but the gauge doesn't scroll. Any help?
import wx
import time
class MyForm(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, "Timer Tutorial 1",
size=(500,500))
# Add a panel so it looks the correct on all platforms
panel = wx.Panel(self, wx.ID_ANY)
self.timer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.update, self.timer)
self.gauProgress = wx.Gauge(panel, range=1000, pos=(30, 50), size=(440, 20))
self.toggleBtn = wx.Button(panel, wx.ID_ANY, "Start")
self.toggleBtn.Bind(wx.EVT_BUTTON, self.onToggle)
def onToggle(self, event):
btnLabel = self.toggleBtn.GetLabel()
if btnLabel == "Start":
print "starting timer..."
self.timer.Start(1000)
self.toggleBtn.SetLabel("Stop")
else:
print "timer stopped!"
self.timer.Stop()
self.toggleBtn.SetLabel("Start")
def update(self, event):
print "\nupdated: ",
print time.ctime()
self.gauProgress.Pulse()
# Run the program
if __name__ == "__main__":
app = wx.PySimpleApp()
frame = MyForm().Show()
app.MainLoop()
wxGauge has a Pulse() function
gauge.Pulse()
Here is an example:
def loadBallots(self):
self.dirtyBallots = Ballots()
self.dirtyBallots.exceptionQueue = Queue(1)
loadThread = Thread(target=self.dirtyBallots.loadUnknown, args=(self.filename,))
loadThread.start()
# Display a progress dialog
dlg = wx.ProgressDialog(\
"Loading ballots",
"Loading ballots from %s\nNumber of ballots: %d" %
(os.path.basename(self.filename), self.dirtyBallots.numBallots),
parent=self.frame, style = wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME
)
while loadThread.isAlive():
sleep(0.1)
dlg.Pulse("Loading ballots from %s\nNumber of ballots: %d" %
(os.path.basename(self.filename), self.dirtyBallots.numBallots))
dlg.Destroy()
if not self.dirtyBallots.exceptionQueue.empty():
raise RuntimeError(self.dirtyBallots.exceptionQueue.get())
This is from here.
how about something like this?
class ProgressDialog(wx.Dialog):
def __init__(self, parent, title, to_add=1):
"""Defines a gauge and a timer which updates the gauge."""
wx.Dialog.__init__(self, parent, title=title, style=wx.CAPTION)
self.count = 0
self.to_add = to_add
self.timer = wx.Timer(self)
self.gauge = wx.Gauge(self, range=100, size=(180, 30))
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.gauge, 0, wx.ALL, 10)
self.SetSizer(sizer)
sizer.Fit(self)
self.SetFocus()
self.Bind(wx.EVT_TIMER, self.on_timer, self.timer)
self.timer.Start(30) # or however often you want
def on_timer(self, event):
"""Increases the gauge's progress."""
self.count += self.to_add
if self.count > 100:
self.count = 0
self.gauge.SetValue(self.count)

Categories