Marquee style progressbar in wxPython - python

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)

Related

wxpython window crashes after event handling

This code reads a picture and put it as background in a window.
There are two issues I cannot explain:
once you import the picture, clicking the red "X" in the top-right corner doesn't close the window.
if you try to drag the image, the program crashes.
Why is it so?
Thank you
import wx
import wx.lib.buttons as buttons
class Main(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, id=-1, title=title, size=(300, 300))
self.initUI()
self.panel = wx.Panel(self, wx.ID_ANY, wx.DefaultPosition, size=(10000, 10000))
self.backGroundImage=''
self.Layout()
def initUI(self):
menubar = wx.MenuBar()
fileMenu = wx.Menu()
fileMenu.AppendSeparator()
imp = wx.Menu()
importBackgroundButton = imp.Append(wx.ID_ANY, 'Import background')
self.Bind(wx.EVT_MENU, self.OnImportBackground, importBackgroundButton)
fileMenu.AppendMenu(wx.ID_ANY, 'I&mport', imp)
menubar.Append(fileMenu, '&File')
self.SetMenuBar(menubar)
self.SetTitle('test')
self.Centre()
self.Show(True)
#load background
def OnImportBackground(self, e):
app = wx.App(None)
style = wx.FD_OPEN | wx.FD_FILE_MUST_EXIST
dialog = wx.FileDialog(None, 'Open', wildcard='*.png', style=style)
if dialog.ShowModal() == wx.ID_OK:
path = dialog.GetPath()
else:
path = None
dialog.Destroy()
self.backgroundImage = ButtonImage(self, self.panel, path, (0, 0))
W = self.backgroundImage.bmp.GetSize()[0]
H = self.backgroundImage.bmp.GetSize()[1]
self.SetSize((W+16, H+58))
self.Refresh()
#crash
class ButtonImage():
def __init__(self, parent, panel, nameImage, pos):
self.panel = panel
self.bmp = wx.Bitmap(nameImage, wx.BITMAP_TYPE_ANY)
self.maxPiecePositionX = self.panel.GetSize()[0] - self.bmp.GetSize()[0]
self.maxPiecePositionY = self.panel.GetSize()[1] - self.bmp.GetSize()[1]
self.bmapBtn = wx.BitmapButton(self.panel, id=wx.ID_ANY, bitmap=self.bmp, style=wx.NO_BORDER, pos=pos)
self.bmapBtn.Bind(wx.EVT_LEFT_DOWN, self.OnClickDown, self.bmapBtn)
self.bmapBtn.Bind(wx.EVT_LEFT_UP, self.OnClickUp, self.bmapBtn)
self.bmapBtn.Bind(wx.EVT_MOTION, self.MoveButton, self.bmapBtn)
self.hold = 0
self.holdPosition = (0, 0)
def EnterButton(self, event):
pass
def LeaveButton(self, event):
self.hold = 0
def OnClickDown(self, event):
obj = event.GetEventObject()
self.hold = 1
self.holdPosition = (event.GetX(), event.GetY())
def OnClickUp(self, event):
self.hold = 0
def MoveButton(self, event):
deltaX, deltaY = 0, 0
if self.hold:
deltaX = event.GetPosition()[0] - self.holdPosition[0]
deltaY = event.GetPosition()[1] - self.holdPosition[1]
newPositionX = self.bmapBtn.GetPosition()[0] + deltaX
newPositionY = self.bmapBtn.GetPosition()[1] + deltaY
if (0 < newPositionX < self.maxPiecePositionX) and (0 < newPositionY < self.maxPiecePositionY):
self.bmapBtn.SetPosition((newPositionX, newPositionY))
else:
self.holdPosition = self.holdPosition[0] + deltaX, self.holdPosition[1] + deltaY
self.bmapBtn.Raise()
self.bmapBtn.Refresh()
app = wx.App()
frame = Main(None, "Test")
frame.Show()
app.MainLoop()
This example has so many issues that it would take a long time to explain it. E. g., you create a new ButtonImage, which is essentially a wx.BitmapButton, every time you call OnImportBackground without destroying the old one, stacking up a collection of bitmap buttons without properly layouting them.
But what is driving the nail into the coffin that you instantiate a new wx.App every time OnImportBackground is called. If you remove this line (which is completely pointless), the frame can at least be closed.
But to see for "the right way (TM)" to do it, look at this stackoverflow post.

wxpython notebook - creating new pages

I'm new here and I'm newbie in coding (since April, this year) so please be gentle ;-)
This is my simplified code. Each time I'm creating new page in the notebook by clicking "ClickMe" button. My question is: Is there any option to not use if/elif statement? There will be 10 pages or more, so a want to avoid typing each time similar code. Are there any way to use dynamically creating variable (self.page...)?
import wx
class PageOne(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
wx.StaticText(self, -1, pos = (10,10), label = "Test")
class PageTwo(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
wx.StaticText(self, -1, pos = (10,10), label = "Test 2")
self.text = wx.TextCtrl(self,-1, pos = (100,10))
def OnLoad(self, text):
self.text.AppendText(str(text))
class MainFrame(wx.Frame):
def __init__(self, parent, id, title, pos, size):
wx.Frame.__init__(self, parent, id, title, pos, size)
panel = wx.Panel(self)
self.nb = wx.Notebook(panel)
self.page1 = PageOne(self.nb)
self.nb.AddPage(self.page1, "PAGE 1")
self.button = wx.Button(self.page1, -1, pos = (100,10), label = "ClickMe")
self.Bind(wx.EVT_BUTTON, self.OnClick, self.button)
sizer = wx.BoxSizer()
sizer.Add(self.nb, 1, wx.EXPAND)
panel.SetSizer(sizer)
def OnClick(self, event):
page_number = self.nb.GetPageCount()
if page_number == 1:
self.page2 = PageTwo(self.nb)
self.nb.AddPage(self.page2, "PAGE 2")
self.page2.OnLoad("PAGE 2")
elif page_number == 2:
self.page3 = PageTwo(self.nb)
self.nb.AddPage(self.page3, "PAGE 3")
self.page3.OnLoad("PAGE 3")
elif page_number == 3:
self.page4 = PageTwo(self.nb)
self.nb.AddPage(self.page4, "PAGE 4")
self.page4.OnLoad("PAGE 4")
class App(wx.App):
def OnInit(self):
self.frame = MainFrame(None, -1, "Notebook", (250,50), (500,500))
self.frame.Show()
return True
app = App(False)
app.MainLoop()
I found here many useful informations and I hope that someone will help me with this issue or show me the right way (if I'm making some stupid mistake/assumption).
I would put all the pages in a list instead of having them as separate attributes.
def __init__(self, parent, id, title, pos, size):
#...
self.pages = [PageOne(self.nb)]
#...
def OnClick(self, event):
page = self.nb.GetPageCount() + 1
# Or: page = len(self.pages) + 1
self.pages.append(PageTwo(self.nb))
self.nb.AddPage(self.pages[page], "PAGE %d" % page)
self.pages[page].OnLoad("PAGE %d" % page)

Escape key to Close WxPython GUI

I am working on a rather simple wxpython GUI, and would like to be able to have the escape key close the window. Right now I just have a close button that executes sys.exit(0), but I'd like the escape key to do this to.
Does anyone know a way to do this?
import win32clipboard
import wx
from time import sleep
import sys
class MainFrame(wx.Frame):
def __init__(self,title):
wx.Frame.__init__(self, None, title="-RRESI Rounder-", pos=(0,0), size=(210,160))
panel=Panel(self)
icon = wx.Icon('ruler_ico.ico', wx.BITMAP_TYPE_ICO)
self.SetIcon(icon)
class Panel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
x1=10; x2=110
y1=40; dy=25; ddy=-3
boxlength=80
self.button =wx.Button(self, label="GO", pos=(100,y1+dy*3))
self.Bind(wx.EVT_BUTTON, self.OnClick,self.button)
self.button.SetDefault()
self.button2 =wx.Button(self, label="Close", pos=(100,y1+dy*4))
self.Bind(wx.EVT_BUTTON, self.OnClose, self.button2)
self.button.SetDefault()
self.Bind(wx.EVT_KEY_UP, self.OnKeyUP)
self.label1 = wx.StaticText(self, label="Input Number:", pos=(x1,y1+dy*1))
self.Input = wx.TextCtrl(self, value="1.001", pos=(x2,ddy+y1+dy*1), size=(boxlength,-1))
self.label0 = wx.StaticText(self, label="Round to closest: 1/", pos=(x1,y1+dy*0))
self.Denominator = wx.TextCtrl(self, value="64", pos=(x2,ddy+y1+dy*0), size=(boxlength,-1))
self.label2 = wx.StaticText(self, label="Output Number:", pos=(x1,y1+dy*2))
self.display = wx.TextCtrl(self, value="1.0", pos=(x2,ddy+y1+dy*2), size=(boxlength,-1))
self.display.SetBackgroundColour(wx.Colour(232, 232, 232))
self.label3 = wx.StaticText(self, label=" ", pos=(x2+7,y1+dy*2+20)) #Copied
self.label4 = wx.StaticText(self, label="Type value and hit Enter", pos=(x1-5,y1-dy*1.5))
self.label5 = wx.StaticText(self, label="Output is copied", pos=(x1-5,y1-dy*1))
font = wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.NORMAL)
self.label4.SetFont(font)
self.label5.SetFont(font)
def OnKeyUP(self, event):
keyCode = event.GetKeyCode()
if keyCode == wx.WXK_ESCAPE:
sys.exit(0)
def OnClose(self, event):
print "Closed"
sys.exit(0)
def OnClick(self,event):
print "You clicked the button!"
def openClipboard():
try:
win32clipboard.OpenClipboard()
except Exception, e:
print e
pass
def closeClipboard():
try:
win32clipboard.CloseClipboard()
except Exception, e:
print e
pass
def clearClipboard():
try:
openClipboard()
win32clipboard.EmptyClipboard()
closeClipboard()
except TypeError:
pass
def setText(txt):
openClipboard()
win32clipboard.EmptyClipboard()
win32clipboard.SetClipboardText(txt)
closeClipboard()
Denominator = float(self.Denominator.GetValue())
Input=float(self.Input.GetValue())
Output=round(Input*Denominator,0)/Denominator
self.display.SetValue(str(Output))
setText(str(Output))
self.label3.SetLabel("Copied")
self.Update()#force redraw
sleep(.5)
wx.Timer
self.label3.SetLabel(" ")
if __name__=="__main__":
app = wx.App(redirect=False) # Error messages don't go to popup window
frame = MainFrame("RRESI Rounder")
frame.Show()
app.MainLoop()
This sorta works ... albiet with some issues
[edit]ok EVT_CHAR_HOOK works much better than EVT_KEY_UP
import wx
class Test(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, title='Event Test',
size=(200, 200))
panel = wx.Panel(self)
panel.SetFocus()
self.Bind(wx.EVT_CHAR_HOOK, self.OnKeyUP)
def OnKeyUP(self, event):
print "KEY UP!"
keyCode = event.GetKeyCode()
if keyCode == wx.WXK_ESCAPE:
self.Close()
event.Skip()
class App(wx.App):
"""Application class."""
def OnInit(self):
self.frame = Test()
self.frame.Show()
self.SetTopWindow(self.frame)
self.frame.SetFocus()
return True
if __name__ == '__main__':
app = App()
app.MainLoop()
Probably an easier way is to set the frame up as a dialogue with a cancel button (which you can hide):
class MainFrame(wx.Dialog):
...
def __init__(self, ...):
...
# the frame must have a cancel button
cancel_button = wx.Button(..., id=wx.ID_CANCEL)
# it still works if you hide it
cancel_button.Hide()
This seems to provide the default handling for the escape key. You could even bind it to an event handler to perform some actions before closing - if you do the handler must use event.Skip(True) to propagate the event or event.Skip(False) to stop the cancel operation.

How to use Refresh with a wx.ColourDialog in wxpython?

i am trying to Refresh() a panel which uses the wx.ColourDialog. Once I refresh the panel once, it is unable to refresh again. Try the following to see the problem in action.
By clicking the button, it will ask you what color you would like to change the rectangle to. Once you press OK, it should change the rectangles color. It will not work it will not change the rectangle.
import wx
xcolor_of_font_dia=(0,0,0)
class MyFrame(wx.Frame):
"""a frame with a panel"""
def __init__(self, parent=None, id=wx.ID_ANY, title=None):
global xcolor_of_font_dia
global dc
wx.Frame.__init__(self, parent, wx.ID_ANY, title)
self.panel = wx.Panel(self, size=(350, 200))
self.panel.Bind(wx.EVT_PAINT, self.on_paint)
self.button2 = wx.Button(self.panel, id=wx.ID_ANY, label='Button2',pos=(8, 38), size=(175, 28))
self.button2.Bind(wx.EVT_BUTTON, self.onColorDlg)
self.Fit()
def onColorDlg(self, event):
global xcolor_of_font_dia
global dc
"""
This is mostly from the wxPython Demo!
"""
dlg = wx.ColourDialog(self)
# Ensure the full colour dialog is displayed,
# not the abbreviated version.
dlg.GetColourData().SetChooseFull(True)
if dlg.ShowModal() == wx.ID_OK:
data = dlg.GetColourData()
print 'You selected: %s\n' % str(data.GetColour().Get())
xcolor_of_font_dia='#%02x%02x%02x' % data.GetColour().Get()
dlg.Destroy()
self.panel.Refresh()
def on_paint(self, event):
global xcolor_of_font_dia
global dc
dc = wx.PaintDC(self.panel)
dc.SetPen(wx.Pen(xcolor_of_font_dia, 1))
rect = wx.Rect(50, 50, 100, 100)
dc.DrawRoundedRectangleRect(rect, 8)
# test it ...
app = wx.PySimpleApp()
frame1 = MyFrame(title='rounded-rectangle & circle')
frame1.Center()
frame1.Show()
app.MainLoop()
I cleaned your code a bit. Basically your globals were producing some problems as you were creating (and deleting) different dc instances after every size event.
You should not use globals if it is not strictly necessary (rarely is).
This works:
import wx
class MyFrame(wx.Frame):
"""a frame with a panel"""
def __init__(self, parent=None, id=wx.ID_ANY, title=None):
wx.Frame.__init__(self, parent, wx.ID_ANY, title)
self.xcolor = (0, 0, 0)
self.panel = wx.Panel(self, size=(350, 200))
self.panel.Bind(wx.EVT_PAINT, self.on_paint)
self.button2 = wx.Button(self.panel, id=wx.ID_ANY, label='Button2',
pos=(8, 38), size=(175, 28))
self.button2.Bind(wx.EVT_BUTTON, self.onColorDlg)
self.Fit()
def onColorDlg(self, event):
"""
This is mostly from the wxPython Demo!
"""
dlg = wx.ColourDialog(None)
dlg.GetColourData().SetChooseFull(True)
if dlg.ShowModal() == wx.ID_OK:
data = dlg.GetColourData()
self.xcolor = data.GetColour().Get()
print 'You selected: %s\n' % str(self.xcolor)
dlg.Destroy()
self.panel.Refresh()
def on_paint(self, event):
dc = wx.PaintDC(self.panel)
dc.SetPen(wx.Pen(self.xcolor, 2))
rect = wx.Rect(50, 50, 100, 100)
dc.DrawRoundedRectangleRect(rect, 8)
# test it ...
app = wx.PySimpleApp()
frame1 = MyFrame(title='rounded-rectangle & circle')
frame1.Center()
frame1.Show()
app.MainLoop()

How do I change variable in a thread?

Here's my situation. Let's say I would like to create a thread that continually prints the number 1. When a button is clicked the value should then change to 2. My issue is that I'm unsure how I change a variable in an already running thread. Here's my code:
import wx
from threading import Thread
import time
class testThread(Thread):
def __init__(self, parent):
self.parent = parent
Thread.__init__(self)
self.start()
def run(self):
while 1:
x = 1
print x
time.sleep(1)
class testGUI(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, "Test", size=(500,270))
panel = wx.Panel(self, -1)
self.buttonStart = wx.Button(panel, -1, label="Start thread", pos=(0,0))
self.buttonChange = wx.Button(panel, -1, label="Change var", pos=(0,30))
panel.Bind(wx.EVT_BUTTON, self.startThread, id=self.buttonStart.GetId())
panel.Bind(wx.EVT_BUTTON, self.changeVar, id=self.buttonChange.GetId())
def startThread(self, event):
testThread(self)
def changeVar(self, event):
# DO SOMETHING HERE THAT CHANGES 'x' IN THREAD TO 2...
pass
if __name__ == '__main__':
app = wx.App(redirect=False)
frame = testGUI()
frame.Show(True)
app.MainLoop()
So the question is, what do I put in the function changeVar that will modify the contents of the variable x that is in the running thread? Thanks in advance!
You can't change local variables.
What you can do is this:
class testThread(Thread):
def __init__(self, parent):
self.parent = parent
Thread.__init__(self)
self.start()
def run(self):
self.value = 1
while 1:
print self.value
time.sleep(1)
class testGUI(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, "Test", size=(500,270))
panel = wx.Panel(self, -1)
self.buttonStart = wx.Button(panel, -1, label="Start thread", pos=(0,0))
self.buttonChange = wx.Button(panel, -1, label="Change var", pos=(0,30))
panel.Bind(wx.EVT_BUTTON, self.startThread, id=self.buttonStart.GetId())
panel.Bind(wx.EVT_BUTTON, self.changeVar, id=self.buttonChange.GetId())
def startThread(self, event):
self.the_thread = testThread(self)
def changeVar(self, event):
# DO SOMETHING HERE THAT CHANGES 'x' IN THREAD TO 2...
self.the_thread.value = 2
if __name__ == '__main__':
app = wx.App(redirect=False)
frame = testGUI()
frame.Show(True)
app.MainLoop()
You don't need threads for that, at all. wx includes a wx.Timer that allows you to call your function with a time interval in the same thread. Chances are that wx includes a function to do what you want, too:
import wx
class testGUI(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, "Test", size=(500,270))
panel = wx.Panel(self, -1)
self.buttonStart = wx.Button(panel, -1, label="Start timer", pos=(0,0))
self.buttonChange = wx.Button(panel, -1, label="Change var", pos=(0,30))
panel.Bind(wx.EVT_BUTTON, self.startTimer, id=self.buttonStart.GetId())
panel.Bind(wx.EVT_BUTTON, self.changeVar, id=self.buttonChange.GetId())
self.value = 1
self.timer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.printer, self.timer)
def startTimer(self, event):
self.timer.Start(1000) # 1 second
def printer(self, event):
print self.value
def changeVar(self, event):
self.value = 2
if __name__ == '__main__':
app = wx.App(redirect=False)
frame = testGUI()
frame.Show(True)
app.MainLoop()

Categories