wxPython session timeout - python

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()

Related

Dropdown of wxPython combobox doesn't work on popup window

When I put a combo box on wxPython popup window, dropdown function doesn't work.
My example code is this.
import wx
class TestPopup(wx.PopupWindow):
def __init__(self, parent):
"""Constructor"""
wx.PopupWindow.__init__(self, parent = parent)
self.popUp = wx.Panel(self, size = (200,200))
self.popUp.SetBackgroundColour("white")
self.st = wx.StaticText(self.popUp, -1, " Select Comport", pos=(10,10))
self.selCom = wx.ComboBox(self.popUp, -1, pos=(85, 50), choices=["Com1", "Com2"])
self.mySizer = wx.BoxSizer(wx.VERTICAL)
self.mySizer.Add(self.popUp)
self.SetSizerAndFit(self.mySizer)
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, parent = None, title= "ComboBox Test", size = (300,200))
self.panel = wx.Panel(self)
self.selComButton = wx.Button(self.panel, -1, "Select Comport")
self.selComButton.Bind(wx.EVT_BUTTON, self.selectPopUp)
self.selCom = wx.ComboBox(self.panel, -1, pos = (85, 50),choices=["Com1", "Com2"])
def selectPopUp(self, event):
win = TestPopup(self.GetTopLevelParent())
btn = event.GetEventObject()
pos = btn.ClientToScreen((0, 0))
sz = btn.GetSize()
win.Position(pos, (0, sz[1]))
win.Show(True)
if __name__ == "__main__":
app = wx.App()
frame = MainFrame()
frame.Show()
app.MainLoop()
In the code, combo box in main frame works well. But, in the popup window, which is shown when 'select Comport' button is clicked, combobox doesn't work.
What's wrong with this?
It works well.
It doesn't work.
The ComboBox certainly works in a popup window under Linux, so it's difficult to address your question directly. However, I would suggest that in this case, you might well be better served, if you were to use a Dialog rather than a PopUpWindow, as it will do the heavy lifting for you.
For example:
import wx
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, parent = None, title= "Communication Port", size = (300,200))
self.panel = wx.Panel(self)
self.selComButton = wx.Button(self.panel, -1, "Select Comport")
self.selComButton.SetToolTip("Select Comport")
self.selComButton.Bind(wx.EVT_BUTTON, self.selectPopUp)
def selectPopUp(self, event):
dlg = wx.SingleChoiceDialog(None,"Pick a com port", "Com ports",["Com1","Com2","Com3","Com4"],wx.CHOICEDLG_STYLE)
if dlg.ShowModal() == wx.ID_OK:
res = dlg.GetStringSelection()
self.selComButton.SetLabel(res)
dlg.Destroy()
if __name__ == "__main__":
app = wx.App()
frame = MainFrame()
frame.Show()
app.MainLoop()

wxPython return value from Wizard to calling Frame

My problem is as follows:
I am designing a wizard for the construction of an object to be added to a list of objects in the calling frame of my program. At the end of the wizard I would like to pass the newly created object back to the calling frame to be inserted into the list. In order to simulate this basic functionality on an abstract basis I have constructed the following, scaled down app:
mainframe.py
import wx
import wiz_test
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self,None,title="Main")
panel = wx.Panel(self)
callButton = wx.Button(panel, label = "Call Wizard")
callButton.Bind(wx.EVT_BUTTON,self.launchWizard)
self.Show()
def launchWizard(self,event):
wiz = wiz_test.WizObj(self)
a = 0
if wiz == wx.wizard.EVT_WIZARD_FINISHED:
a = wiz.answer
print a
if __name__ == '__main__':
app = wx.App(False)
frame = MainFrame()
app.MainLoop()
wiz_test.py
import wx
import wx.wizard as wiz
class WizPage(wiz.WizardPageSimple):
def __init__(self, parent):
self.answer = 3
wiz.WizardPageSimple.__init__(self, parent)
sizer = wx.BoxSizer(wx.VERTICAL)
self.SetSizer(sizer)
title = wx.StaticText(self, -1, "Wizard Page")
title.SetFont(wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD))
sizer.Add(title, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
sizer.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.ALL, 5)
class WizObj(object):
def __init__(self,parent):
wizard = wx.wizard.Wizard(None, -1, "Simple Wizard")
page1 = WizPage(wizard)
wizard.FitToPage(page1)
wizard.RunWizard(page1)
wizard.Destroy()
if __name__ == "__main__":
app = wx.App(False)
main()
app.MainLoop()
The ultimate goal in this small example is to get the MainFrame instance to output the value '3' derived from the .answer member variable of the WizObj instance when the wx.wizard.EVT_WIZARD_FINISHED event is triggered. However it is clearly not working at this point as the current code only returns '0'. Am I approaching this the correct way? Should I be binding the EVT_WIZARD_FINISHED event instead, and if so, how would I access that from Mainframe?
I was able to solve this problem through the use of the "pubsub" capability within the wxPython library. Specifically, I added a pub.subscribe() instance immediately prior to the instantiation of my wizard within the calling frame. Inside of the wizard I pass the value via pub.sendMessage() just before destroying the wizard. It is important to note that the pass value had to be specified in order for the pubsub send would work effectively.
The following code is the modified version of the original code which now functions.
MainFrame.py
import wx
import wiz_test
from wx.lib.pubsub import pub
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self,None,title="Main")
panel = wx.Panel(self)
callButton = wx.Button(panel, label = "Call Wizard")
callButton.Bind(wx.EVT_BUTTON,self.launchWizard)
self.Show()
def catch_stuff(self,a):
print a
def launchWizard(self,event):
pub.subscribe(self.catch_stuff,'valPass')
wiz = wiz_test.WizObj(self,a)
if __name__ == '__main__':
app = wx.App(False)
frame = MainFrame()
app.MainLoop()
wiz_test.py
import wx
import wx.wizard as wiz
from wx.lib.pubsub import pub
class WizPage(wiz.WizardPageSimple):
def __init__(self, parent):
self.answer = 3
wiz.WizardPageSimple.__init__(self, parent)
sizer = wx.BoxSizer(wx.VERTICAL)
self.SetSizer(sizer)
title = wx.StaticText(self, -1, "Wizard Page")
title.SetFont(wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD))
sizer.Add(title, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
sizer.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.ALL, 5)
#----------------------------------------------------------------------
class WizObj(object):
def __init__(self,parent,a):
wizard = wx.wizard.Wizard(None, -1, "Simple Wizard")
page1 = WizPage(wizard)
wizard.FitToPage(page1)
wizard.RunWizard(page1)
pub.sendMessage('valPass',a = page1.answer)
wizard.Destroy()
#----------------------------------------------------------------------
if __name__ == "__main__":
app = wx.App(False)
main()
app.MainLoop()
The result is that the console prints the value 3 which was retrieved from the called wizard.

wxPython - Automatically closing nested modal dialogs

Python 2.7, WxPython 3.0.2
We are trying to automatically close an entire program under certain conditions. For various reasons, we can't just kill the process. We've had some level of success with it. We can close it if there's no modal dialogs, or a single modal dialog. Once we introduce the second modal dialog (nested), it fails to stop properly.
The actual error received appears to be:
wx._core.PyAssertionError: C++ assertion "IsRunning()" failed at ..\..\src\common\evtloopcmn.cpp(83) in wxEventLoopBase::Exit(): Use ScheduleExit() on not running loop
Here's a working example of our issue. The frame will automatically close after 5 seconds. Clicking the button will load a dialog. Clicking the button on the dialog will open another dialog. It works fine until the last dialog is opened.
from threading import Thread
from time import sleep
import wx
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title="TEST", size=(400, 400))
self.Show()
self.__someDialog = None
self.__myThread = None
self.__okButton = wx.Button(self, -1, "Press me")
self.Bind(wx.EVT_BUTTON, self.__onOK)
self.__myThread = Thread(target=self.__waitThenClose, name="Closer")
self.__myThread.setDaemon(True)
self.__myThread.start()
def __onOK(self, evt):
self.__someDialog = SomeDialog(self)
self.__someDialog.ShowModal()
def closeOpenDialogs(self):
lst = wx.GetTopLevelWindows()
for i in range(len(lst) - 1, 0, -1):
if isinstance(lst[i], wx.Dialog):
print "Closing " + str(lst[i])
lst[i].Close(True)
#lst[i].Destroy()
def __waitThenClose(self):
for x in range(0, 5):
print "Sleeping..."
sleep(1)
self.closeOpenDialogs()
wx.CallAfter(self.Close, True)
class SomeDialog(wx.Dialog):
def __init__(self, parent):
wx.Dialog.__init__(self, parent, id=-1, title='Some Dialog')
self.SetSize((300, 300))
self.__anotherDialog = None
self.__okButton = wx.Button(self, -1, "Press me")
self.Bind(wx.EVT_BUTTON, self.__onOK)
wx.EVT_CLOSE(self, self.__on_btn_cancel)
def __onOK(self, evt):
self.__anotherDialog = AnotherDialog(self)
self.__anotherDialog.ShowModal()
def __on_btn_cancel(self, event):
self.EndModal(wx.ID_CANCEL)
class AnotherDialog(wx.Dialog):
def __init__(self, parent):
wx.Dialog.__init__(self, None, id=-1, title='Another Dialog')
self.SetSize((200, 200))
wx.EVT_CLOSE(self, self.__on_btn_cancel)
def __on_btn_cancel(self, event):
self.EndModal(wx.ID_CANCEL)
if __name__ == "__main__":
app = wx.App()
mainFrame = MainFrame()
app.MainLoop()
I think what is happening here is that the first call to ShowModal() blocks the at the app level (not just the frame level) which is preventing the second dialog from becoming fully initialized. To work around this issue I would call Show() instead of ShowModal() and add wx.FRAME_FLOAT_ON_PARENT to the dialog style flags. You can also call Disable() on the parts of the program you don't want the user to interact with while the dialogs are open.
EDIT: Here is a working example:
from threading import Thread
from time import sleep
import wx
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title="TEST", size=(400, 400))
self.Show()
self.__someDialog = None
self.__okButton = wx.Button(self, -1, "Press me")
self.Bind(wx.EVT_BUTTON, self.__onOK)
self.__myThread = Thread(target=self.__waitThenClose, name="Closer")
self.__myThread.setDaemon(True)
self.__myThread.start()
def __onOK(self, evt):
self.__someDialog = SomeDialog(self)
self.__someDialog.ShowModal()
def closeOpenDialogs(self, evt=None):
lst = wx.GetTopLevelWindows()
for i in range(len(lst) - 1, 0, -1):
dialog = lst[i]
if isinstance(dialog, wx.Dialog):
print "Closing " + str(dialog)
# dialog.Close(True)
wx.CallAfter(dialog.Close)
# sleep(1)
# dialog.Destroy()
def __waitThenClose(self):
for x in range(0, 10):
print "Sleeping..."
sleep(1)
wx.CallAfter(self.closeOpenDialogs)
wx.CallAfter(self.Close, True)
class SomeDialog(wx.Dialog):
def __init__(self, parent):
wx.Dialog.__init__(self, parent, id=-1, title='Some Dialog')
self.SetSize((300, 300))
self.__anotherDialog = None
self.__okButton = wx.Button(self, -1, "Press me")
self.Bind(wx.EVT_BUTTON, self.__onOK)
wx.EVT_CLOSE(self, self.__on_btn_cancel)
def __onOK(self, evt):
self.__anotherDialog = AnotherDialog(self)
self.__anotherDialog.SetWindowStyleFlag(
wx.FRAME_FLOAT_ON_PARENT|wx.DEFAULT_DIALOG_STYLE)
self.__anotherDialog.Show()
def __on_btn_cancel(self, event):
event.Skip()
self.EndModal(wx.ID_CANCEL)
class AnotherDialog(wx.Dialog):
def __init__(self, parent):
wx.Dialog.__init__(self, parent, id=-1, title='Another Dialog')
self.SetSize((200, 200))
wx.EVT_CLOSE(self, self.__on_btn_cancel)
parent.Disable()
def __on_btn_cancel(self, event):
event.Skip()
self.GetParent().Enable()
# self.EndModal(wx.ID_CANCEL)
if __name__ == "__main__":
app = wx.App()
mainFrame = MainFrame()
app.MainLoop()
The only way to reliably gracefully close all the modal dialogs, whether they were explicitly opened by your own code or not, is to use wxModalDialogHook to remember all the opened dialogs and then close them all, in the reverse (i.e. LIFO) order, before quitting the application.
Unfortunately I don't know if wxModalDialogHook is available in Python.

StaticBox leads to segmentation fault error if layout is called

I wanted to improve my GUI by using StaticBoxes. But since I added one I can't call my layout function without crashing python (Segementation fault; no other Error messages).
The following lines reproduce this error exactly. Did I use StaticBoxes correctly?
What do I need to get it to run properly? I use the nested Sizers frequently
so the layout looks nice ;).
import wx
class MainWindow(wx.Frame):
'''test frame'''
def __init__(self,*args,**kwargs):
'''Constructor'''
super(MainWindow,self).__init__(*args,**kwargs)
self.panel = wx.Panel(self)
self.box = wx.StaticBox(self.panel, wx.ID_ANY, u'input value1')
self.button_calc = wx.Button(self.panel, wx.ID_ANY, label=u'calc_xy')
self.Bind(wx.EVT_BUTTON, self.calculate, self.button_calc)
self._layout()
def _layout(self):
box_sizer = wx.StaticBoxSizer(self.box, wx.VERTICAL)
sizer = wx.GridBagSizer()
inside_the_box = wx.GridBagSizer()
box_sizer.Add(inside_the_box, 5, wx.ALL, 5)
sizer.Add(box_sizer, (0, 0), (2, 2), wx.EXPAND)
sizer.Add(self.button_calc, (2, 0))
self.panel.SetSizerAndFit(sizer)
def calculate(self, event):
print '5'
self._layout()
if __name__ == '__main__':
app = wx.App()
frame = MainWindow(None, -1, 'test window')
frame.Show()
app.MainLoop()
You are calling self._layout() in your __init__ method, and it works corectly for me in there.
Then when you click the button you call self._layout again and at that point you are trying to assign self.box to a new box_sizer but it is already assigned to a sizer therefore the segfault.

Add Notebook Page in wxpython

I am running a wxpython application. Within the application i have a panel that contains a notebook with some number of notebook pages/tabs. On a button press (wx.button), i would like to be able to clear one of the notebook pages and replace it with new information provided in my GUI.
I have not seen any kind of Clear() function for wx.Notebook, so I had the thought of deleting the page and creating a new one. However, I cannot seem to get this to work. Here is my code...help??
def UpdatePanel(self):
self.Notebook3.DeletePage(0)
self.newpage = scrolled.ScrolledPanel(self.Notebook3, -1)
self.newpage.SetupScrolling()
self.Notebook3.AddPage(self.newpage,"Page Inserted Here")
# self.Notebook3.InsertPage(0,"Page Inserted Here")
Checkout this code and see how you can adapt it to yours:
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import wx
class Page(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
t = wx.StaticText(self, -1, "THIS IS A PAGE OBJECT", (20,20))
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title="Notebook Remove Pages Example")
pannel = wx.Panel(self)
vbox = wx.BoxSizer(wx.VERTICAL)
hbox = wx.BoxSizer(wx.HORIZONTAL)
self.buttonRemove = wx.Button(pannel, id=wx.ID_ANY, label="DELETE", size=(80, 25))
self.buttonRemove.Bind(wx.EVT_BUTTON, self.onButtonRemove)
hbox.Add(self.buttonRemove)
self.buttonInsert = wx.Button(pannel, id=wx.ID_ANY, label="CREATE", size=(80, 25))
self.buttonInsert.Bind(wx.EVT_BUTTON, self.onButtonInsert)
hbox.Add(self.buttonInsert)
vbox.Add(hbox)
self.Notebook3 = wx.Notebook(pannel)
vbox.Add(self.Notebook3, 2, flag=wx.EXPAND)
pannel.SetSizer(vbox)
self.pageCounter = 0
self.addPage()
def addPage(self):
self.pageCounter += 1
page = Page(self.Notebook3)
pageTitle = "Page: {0}".format(str(self.pageCounter))
self.Notebook3.AddPage(page, pageTitle)
def onButtonRemove(self, event):
self.Notebook3.DeletePage(0)
def onButtonInsert(self, event):
self.addPage()
if __name__ == "__main__":
app = wx.App()
MainFrame().Show()
app.MainLoop()

Categories