wxMessageBox with an auto-close timer in wxPython - python

Platforms: Windows, OS X
Python Version: Active State Python 2.7
wxPython Version: Version 2.9
Here is a sample code in which I use a wxMessageBox:
import wx,os
class Frame(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title, size=(100, 100),style=wx.MINIMIZE_BOX | wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.CLIP_CHILDREN)
host=os.system('hostname')
if host!='superman':
self.dialogBox=wx.MessageBox('The host name should be superman. Closing this dialog box in 2s...','Info')
self.Destroy()
else:
self.Center()
self.Show()
if __name__ == '__main__':
app = wx.App(redirect=False)
frame = Frame(None, -1, 'Sample')
app.MainLoop()
According to the above piece of code, If the host name is not 'superman' , then the user is displayed a message box and prompted to press 'OK'. If the user presses 'OK' button on the message box, then the control moves to the next line in the code (i.e., line number 10) where the frame is destroyed. I want to be to able to automatically close the dialog box and go to the next line in the code i.e., self.Destroy() if the user does not press the 'OK' button in the next 2 seconds. Any thoughts on how do I do that in wxpython ?

If you create your own custom dialog by subclassing wx.Dialog you can use a wx.Timer to generate a periodic event to which you can bind a handler which updates the message every time the timer event fires, then after x event fires you can destroy the dialog.
Working example:
import wx
import os
class MessageDialog(wx.Dialog):
def __init__(self, message, title, ttl=10):
wx.Dialog.__init__(self, None, -1, title,size=(400, 150))
self.CenterOnScreen(wx.BOTH)
self.timeToLive = ttl
stdBtnSizer = self.CreateStdDialogButtonSizer(wx.OK|wx.CANCEL)
stMsg = wx.StaticText(self, -1, message)
self.stTTLmsg = wx.StaticText(self, -1, 'Closing this dialog box in %ds...'%self.timeToLive)
vbox = wx.BoxSizer(wx.VERTICAL)
vbox.Add(stMsg, 1, wx.ALIGN_CENTER|wx.TOP, 10)
vbox.Add(self.stTTLmsg,1, wx.ALIGN_CENTER|wx.TOP, 10)
vbox.Add(stdBtnSizer,1, wx.ALIGN_CENTER|wx.TOP, 10)
self.SetSizer(vbox)
self.timer = wx.Timer(self)
self.timer.Start(1000)#Generate a timer event every second
self.timeToLive = 10
self.Bind(wx.EVT_TIMER, self.onTimer, self.timer)
def onTimer(self, evt):
self.timeToLive -= 1
self.stTTLmsg.SetLabel('Closing this dialog box in %ds...'%self.timeToLive)
if self.timeToLive == 0:
self.timer.Stop()
self.Destroy()
class Frame(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title, size=(100, 100),style=wx.MINIMIZE_BOX | wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.CLIP_CHILDREN)
host=os.system('hostname')
if host!='superman':
dlg = MessageDialog('The host name should be superman', 'Info', ttl=10)
dlg.ShowModal()
else:
self.Center()
self.Show()
if __name__ == "__main__":
app = wx.PySimpleApp()
frame = Frame(None, -1, "")
frame.Show(1)
app.MainLoop()

I think you might have to use a custom wx.Dialog for this. You can use wx.FutureCall to call trigger an event in the future. Something like:
class MessageDialog(wx.Dialog):
def __init__(self, message, title):
wx.Dialog.__init__(self, None, -1, title,size=(300, 120))
self.CenterOnScreen(wx.BOTH)
ok = wx.Button(self, wx.ID_OK, "OK")
ok.SetDefault()
text = wx.StaticText(self, -1, message)
vbox = wx.BoxSizer(wx.VERTICAL)
vbox.Add(text, 1, wx.ALIGN_CENTER|wx.TOP, 10)
vbox.Add(ok, 1, wx.ALIGN_CENTER|wx.BOTTOM, 10)
self.SetSizer(vbox)
class Frame(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title, size=(100, 100),style=wx.MINIMIZE_BOX | wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.CLIP_CHILDREN)
host=os.system('hostname')
if host!='superman':
dlg = MessageDialog('The host name should be superman. Closing this dialog box in 2s...', 'Info')
wx.FutureCall(2000, dlg.Destroy)
dlg.ShowModal()
else:
self.Center()
self.Show()

Related

wxPython append and pop message box inside panel serving like a notification center

I would like to make a wxpython program that has a notification center just like the one on windows or mac. Whenever I have a message, the message will show inside the the notification panel, and the user could close that message afterwards.
I have a sample code for illustration as follows:
import wx
import wx.lib.scrolledpanel as scrolled
class MyFrame(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title)
topPanel = wx.Panel(self)
panel1 = wx.Panel(topPanel, -1)
button1 = wx.Button(panel1, -1, label="generate message")
self.panel2 = scrolled.ScrolledPanel(
topPanel, -1, style=wx.SIMPLE_BORDER)
self.panel2.SetAutoLayout(1)
self.panel2.SetupScrolling()
button1.Bind(wx.EVT_BUTTON, self.onAdd)
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(panel1,-1,wx.EXPAND|wx.ALL,border=10)
sizer.Add(self.panel2,-1,wx.EXPAND|wx.ALL,border=10)
self.sizer2 = wx.BoxSizer(wx.VERTICAL)
topPanel.SetSizer(sizer)
self.panel2.SetSizer(self.sizer2)
def onAdd(self, event):
new_text = wx.TextCtrl(self.panel2, value="New Message")
self.sizer2.Add(new_text,0,wx.EXPAND|wx.ALL,border=1)
self.panel2.Layout()
self.panel2.SetupScrolling()
class MyApp(wx.App):
def OnInit(self):
frame = MyFrame(None, -1, 'frame')
frame.Show(True)
return True
app = MyApp(0)
app.MainLoop()
In the above I code, the right panel (i.e. panel2) serves as a notification center that all the messages should shown inside it. On the left panel (i.e. panel1) I have a button to generate message just to mimic the notification behavior. Ideally the message on the right panel should be a message box that you could close (maybe a frame? Or a MessageDialog?)
Any hint or advice is much appreciated, and an example would be the best!
Thanks!
Finally figured out myself, it was easier than I initially thought.
Here is the code:
import wx
import wx.lib.scrolledpanel as scrolled
class MyFrame(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title)
self.number_of_panels = 0
topPanel = wx.Panel(self)
panel1 = wx.Panel(topPanel, -1)
button1 = wx.Button(panel1, -1, label="generate message")
self.panel2 = scrolled.ScrolledPanel(
topPanel, -1, style=wx.SIMPLE_BORDER)
self.panel2.SetAutoLayout(1)
self.panel2.SetupScrolling()
button1.Bind(wx.EVT_BUTTON, self.onAdd)
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(panel1,0,wx.EXPAND|wx.ALL,border=5)
sizer.Add(self.panel2,1,wx.EXPAND|wx.ALL,border=5)
self.sizer2 = wx.BoxSizer(wx.VERTICAL)
topPanel.SetSizer(sizer)
self.panel2.SetSizer(self.sizer2)
def onAdd(self, event):
self.number_of_panels += 1
panel_label = "Panel %s" % self.number_of_panels
panel_name = "panel%s" % self.number_of_panels
new_panel = wx.Panel(self.panel2, name=panel_name, style=wx.SIMPLE_BORDER)
self.closeButton = wx.Button(new_panel, label='Close %s' % self.number_of_panels)
self.closeButton.panel_number = self.number_of_panels
self.closeButton.Bind(wx.EVT_BUTTON, self.OnClose)
self.sizer2.Add(new_panel,0,wx.EXPAND|wx.ALL,border=1)
self.panel2.Layout()
self.panel2.SetupScrolling()
def OnClose(self, e):
if self.panel2.GetChildren():
e.GetEventObject().GetParent().Destroy()
self.number_of_panels -= 1
self.panel2.Layout() # Reset layout after destroy the panel
class MyApp(wx.App):
def OnInit(self):
frame = MyFrame(None, -1, 'frame')
frame.Show(True)
return True
app = MyApp(0)
app.MainLoop()
Basically I can destroy the newly created panel. I just need to know which panel it is when I click the close button. This should work very similar to the Notification Center.

Get user input from a dialog before showing the mainframe

I have wxPython GUI program that has to get an input from the user to run. I want to show the dialog before the main frame, store the input, close the dialog and then run the main program. Right now I am using raw_input instead. This is my code:
import wx
import wx.lib.iewin as iewin
import subprocess
class MyBrowser(wx.Frame):
def __init__(self, parent, id):
wx.Frame.__init__(self, parent, id,
style=wx.DEFAULT_FRAME_STYLE | wx.STAY_ON_TOP)
self.transparency = 255
sizer = wx.BoxSizer(wx.VERTICAL)
self.browser = iewin.IEHtmlWindow(self)
sizer.Add(self.browser, 1, wx.EXPAND, 10)
self.Bind(wx.EVT_CHAR_HOOK, self.onKey)
def SetTransparent(self, value):
self.transparency = value
wx.Frame.SetTransparent(self, value)
def GetTransparent(self):
return self.transparency
def decreaseTransparency(self, e):
self.SetTransparent(self.GetTransparent() - 10)
def increaseTransparency(self, e):
self.SetTransparent(self.GetTransparent() + 10)
def onKey(self, evt):
if evt.GetKeyCode() == wx.WXK_DOWN:
self.decreaseTransparency(self)
elif evt.GetKeyCode() == wx.WXK_UP:
self.increaseTransparency(self)
else:
evt.Skip()
def load(self,uri):
self.browser.Navigate(uri)
#starts livestreamer process
response = raw_input("Livestreamers name:\n")
livestreamer = "livestreamer twitch.tv/"
host = subprocess.Popen(['livestreamer', 'twitch.tv/'+response, 'best'], stdout=subprocess.PIPE)
if __name__ == '__main__':
app = wx.App()
dialog = MyBrowser(None, -1)
dialog.browser.Navigate("https://www.twitch.tv/" + response+ "/chat?popout=")
dialog.Show()
app.MainLoop()
host.communicate()[0]
This is what I have in mind:
dialog example
In your browser init create a dialog box, run ShowModal on it, and then get the input with dialog.GetValue
class MyBrowser(wx.Frame):
def __init__(self, parent, id):
wx.Frame.__init__(self, parent, id,
style=wx.DEFAULT_FRAME_STYLE | wx.STAY_ON_TOP)
self.transparency = 255
self.dialog = wx.TextEntryDialog(None, message="Enter stuff")
self.dialog.ShowModal()
print self.dialog.GetValue()
self.Bind(wx.EVT_CHAR_HOOK, self.onKey)
Obviously replace printing it with whatever you want to do with the value
If you want to access it after instantiation of the MyBrowser object assign it as an instance variable rather than printing.
class MyBrowser(wx.Frame):
def __init__(self, parent, id):
wx.Frame.__init__(self, parent, id,
style=wx.DEFAULT_FRAME_STYLE | wx.STAY_ON_TOP)
self.transparency = 255
self.dialog = wx.TextEntryDialog(None, message="Enter stuff")
self.dialog.ShowModal()
self.dlg_val = self.dialog.GetValue()
self.Bind(wx.EVT_CHAR_HOOK, self.onKey)
Then use it further down
if __name__ == "__main__":
dialog = MyBrowser(None)
print dialog.dlg_val

Change FilePickerCtrl box width (wxPython)

I am new to wxpython. I am trying to write a little app that allows me to pick a file. I wonder how I can change the width of the file path box.
My code is below:
import wx
class MainWindow(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, title = title, size=(500, 400))
self.Center()
self.panel = wx.Panel(self)
self.label1 = wx.StaticText(self.panel)
self.fileCtrl = wx.FilePickerCtrl(self.panel, size=(100, 50))
row1 = wx.StaticBoxSizer(wx.StaticBox(self.panel, 1, 'Please select the input file:'), orient=wx.HORIZONTAL)
row1.Add(self.label1,0,wx.TOP | wx.RIGHT,70)
row1.Add(self.fileCtrl)
wrapper = wx.FlexGridSizer(1,1,20,20)
wrapper.AddGrowableCol(0)
wrapper.Add(row1,50,wx.TOP | wx.LEFT | wx.RIGHT | wx.ALIGN_CENTER,20)
self.panel.SetSizerAndFit(wrapper)
self.Centre()
#self.Fit()
self.Show()
app = wx.App(False)
win = MainWindow(None, "File selector")
app.MainLoop()
In short, I don't think that you can.
From the look of your example I'm guessing that you are running on MSW, my example is from Linux but it shouldn't matter in this case.
Your code looks a bit off, so I have cleaned it up a bit. try it and see if it helps.
You could always use wx.FileDialog rather than wx.FilePicker
import wx
class MainWindow(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, title = title, size=(500, 400))
self.panel = wx.Panel(self)
self.label1 = wx.StaticText(self.panel,wx.ID_ANY,"Select the Input File")
self.fileCtrl = wx.FilePickerCtrl(self.panel, message="Select the input file name")
self.selectedfile = wx.TextCtrl(self.panel,wx.ID_ANY,"None",size=(490,25))
self.fileCtrl.Bind(wx.EVT_FILEPICKER_CHANGED, self.Selected)
row1 = wx.BoxSizer(wx.VERTICAL)
row1.Add(self.label1,0,wx.TOP | wx.RIGHT,5)
row1.Add(self.fileCtrl)
row1.Add(self.selectedfile)
self.panel.SetSizer(row1)
self.Show()
def Selected(self, event):
self.selectedfile.SetValue(self.fileCtrl.GetPath())
app = wx.App(False)
win = MainWindow(None, "File selector")
app.MainLoop()
Edit:
I'm not sure if anything has changed with this widget in the years since I first answered this question but following would be how I'd code it today.
import wx
class MainWindow(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, title = title, size=(500, 200))
self.panel = wx.Panel(self)
row1 = wx.StaticBoxSizer(wx.VERTICAL, self.panel, 'Please select the input file:')
self.fileCtrl = wx.FilePickerCtrl(self.panel, message="Select file",style=wx.FLP_USE_TEXTCTRL,size=(390,25))
row1.Add(self.fileCtrl,0,wx.ALL,10)
self.panel.SetSizer(row1)
self.Show()
app = wx.App(False)
win = MainWindow(None, "File selector")
app.MainLoop()
You could use the VERTICAL & HORIZONTAL BoxSizer and keep the proportion of widget as 1 instead of the default 0
Code below:
import wx
import os
wildcard = "All files (*.*)|*.*"
class MainWindow(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title='File Selector')
self.currentDirectory = os.getcwd()
self.panel = wx.Panel(self)
vbox = wx.BoxSizer(wx.VERTICAL)
ie_box = wx.StaticBox(self.panel, -1, 'Please select the input file')
ie_sizer = wx.StaticBoxSizer(ie_box, wx.VERTICAL)
fl_box = wx.BoxSizer(wx.HORIZONTAL)
self.fl_ctrl = wx.FilePickerCtrl(self.panel, message="Choose a file")
fl_box.Add(self.fl_ctrl, 1, wx.ALL | wx.CENTER | wx.EXPAND, 5)
ie_sizer.Add(fl_box, 1, wx.ALL | wx.CENTER | wx.EXPAND, 10)
self.fl_ctrl.Bind(wx.EVT_FILEPICKER_CHANGED, self.on_open_file)
vbox.Add(ie_sizer, 0, wx.ALL | wx.CENTER | wx.EXPAND, 5)
self.panel.SetSizer(vbox)
self.Center()
self.panel.Fit()
self.Show()
def on_open_file(self, event):
self.fl_ctrl.GetPath()
if __name__ == '__main__':
app = wx.App()
frame = MainWindow()
app.MainLoop()

Wxpython Switching frames between two python files

i have two py files that each have its own frame made using wxPython, MainWindow and RecWindow. MainWindow has the RecWindow python file included using the keyword "recovery".
MainWindow code:
class MainWindow(wx.Frame):
def __init__(self,parent,id,title):
wx.Frame.__init__(self, parent, wx.ID_ANY,title,pos=(500,200), size = (650,500), style = wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE)
self.Bind(wx.EVT_CLOSE,self.OnExit)
self.SetIcon(wx.Icon('etc\icons\download.ico', wx.BITMAP_TYPE_ICO))
panel = wx.Panel(self)
RecWindow code:
class RecWindow(wx.Frame):
def __init__(self,parent,id,title):
wx.Frame.__init__(self, parent, wx.ID_ANY,title,pos=(400,200), size = (700,600), style = wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE)
self.SetIcon(wx.Icon('etc\icons\download.ico', wx.BITMAP_TYPE_ICO))
self.count = 0
when i click on a button in MainWindow , it will hide the MainWindow create an instance of RecWindow, shown below;
def OpenRec(self,event):#this will be used to open the next frame
OR = recovery(None,-1,"RAVE")
OR.Show(True)
MainWindow.Hide()
now, what i am unsure of is how i can return to the MainWindow once i close the RecWindow. RecWindow has a cancel and finish button which both map to a self.close() function. How would i then get MainWindow to show itself again?
Use pubsub to send a message to the main window telling it to Show itself again. I actually have an example of how to do that here:
http://www.blog.pythonlibrary.org/2010/06/27/wxpython-and-pubsub-a-simple-tutorial/
Note that this tutorial is using slightly older API that was available in wxPython 2.8. If you're using wxPython 2.9, then you'll have to use the slightly different API that I detail in this article:
http://www.blog.pythonlibrary.org/2013/09/05/wxpython-2-9-and-the-newer-pubsub-api-a-simple-tutorial/
When you create an instance of RecWindow keep a reference to it on main_window and bind to its close event.
In the main_window's close handler check if the window closed was the RecWindow.
If it was, clear the reference to it and show the main_window.
Elif the closed window was main_window carry out any required code.
Finally call event.Skip() so the windows get destroyed.
import wx
class MainWindow(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, -1, title, (500, 200), (650, 500),
wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE)
panel = wx.Panel(self)
button = wx.Button(panel, wx.ID_OPEN)
panel.sizer = wx.BoxSizer(wx.VERTICAL)
panel.sizer.Add(button, 0, wx.ALL, 7)
panel.SetSizer(panel.sizer)
button.Bind(wx.EVT_BUTTON, self.on_button)
self.Bind(wx.EVT_CLOSE, self.on_close)
self.rec_window = None
def on_button(self, event):
rec_window = RecWindow(self, 'Rec window')
rec_window.Show()
self.Hide()
rec_window.Bind(wx.EVT_CLOSE, self.on_close)
self.rec_window = rec_window
def on_close(self, event):
closed_window = event.EventObject
if closed_window == self.rec_window:
self.rec_window = None
self.Show()
elif closed_window == self:
print 'Carry out your code for when Main window closes'
event.Skip()
class RecWindow(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, -1, title, (400, 200), (700, 600),
wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE)
app = wx.App(False)
main_window = MainWindow(None, 'Main window')
main_window.Show()
app.MainLoop()

pop up a window with text in it and automatically close it after certain time

This is the code that i have written. It does close the window but doesnt display the text in it. I need the text displayed and then automatic close of the window.
What changes should i make for it to work
Thanks
Here is the code
import wx
from time import sleep
class Frame(wx.Frame):
def __init__(self, title):
wx.Frame.__init__(self, None, title=title, size=(300,200))
self.panel = wx.Panel(self)
box = wx.BoxSizer(wx.VERTICAL)
m_text = wx.StaticText(self.panel, -1, 'File Uploaded!')
m_text.SetSize(m_text.GetBestSize())
box.Add(m_text, 0, wx.ALL, 10)
self.panel.SetSizer(box)
self.panel.Layout()
self.Bind(wx.EVT_ACTIVATE, self.onClose)
def onClose(self, event):
sleep(5)
self.Destroy()
app = wx.App(redirect=True)
top = Frame('test')
top.Show()
app.MainLoop()
I would recommend using a wx.Timer. If you use time.sleep(), you will block wxPython's main loop which makes your application unresponsive. Here is your code modified to use the timer:
import wx
class Frame(wx.Frame):
def __init__(self, title):
wx.Frame.__init__(self, None, title=title, size=(300,200))
self.panel = wx.Panel(self)
box = wx.BoxSizer(wx.VERTICAL)
m_text = wx.StaticText(self.panel, -1, 'File Uploaded!')
m_text.SetSize(m_text.GetBestSize())
box.Add(m_text, 0, wx.ALL, 10)
self.panel.SetSizer(box)
self.panel.Layout()
self.timer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.onClose, self.timer)
self.timer.Start(5000)
def onClose(self, event):
self.Close()
app = wx.App(redirect=True)
top = Frame('test')
top.Show()
app.MainLoop()
You can read more about timers in this article:
http://www.blog.pythonlibrary.org/2009/08/25/wxpython-using-wx-timers/
>>> import wx
>>> import time
>>> app = wx.App()
>>> b = wx.BusyInfo('Upload Finished!')
>>> time.sleep(5)
>>> del b
>>>

Categories

Resources