i am trying to write a simple code for timeout a session. my idea is that if user dont interact with the application for 5min, then a function will fireup and kill the application. But if user is active and interact with the application the kill function won't be able to run. Anyone help
my simple wx here
import wx
class MyForm(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, "Timeout",
size=(500, 500))
panel = wx.Panel(self, wx.ID_ANY)
self.toggleBtn = wx.Button(panel, wx.ID_ANY, "Enter")
self.toggleBtn.Bind(wx.EVT_BUTTON, self.onToggle)
def onToggle(self, event):
print("you have a action")
if __name__ == "__main__":
app = wx.App(False)
frame = MyForm().Show()
app.MainLoop()
wx.Timer is what you need.
Start a timer with 60000 ms interval (5 minutes). Bind the timer event to some handler. When the timer shots, close your app in that handler.
If the user makes some action (e.g. moving the mouse, or hitting the keyboard) the timer must be stopped and restarted.
For this you need to bind mouse and keyboard events (and let them to be processed as usual by calling Skip). In these handlers is where you restart the timer.
See, for example, this wiki
Related
I want to execute a task when a Toggle Button is clicked(on) and toggle it off after the task is completed, I execute the task in a new process because I don't want it to block the UI, event.GetEventObject().SetValue(False) seems to update correctly the value of the Toggle but it doesn't reflect on the UI.
from multiprocessing import Process
import time
import wx
class MyFrame(wx.Frame):
def __init__(self, *args, **kwds):
kwds["style"] = kwds.get("style", 0) | wx.DEFAULT_FRAME_STYLE
wx.Frame.__init__(self, *args, **kwds)
self.toggle_button = wx.ToggleButton(self, wx.ID_ANY, "OK")
control = Control()
self.Bind(wx.EVT_TOGGLEBUTTON, control.action, self.toggle_button)
self.SetTitle("Update UI with a process")
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.toggle_button, 0, 0, 0)
self.SetSizer(sizer)
self.Layout()
class Control():
def update_toggle(self, duration, event):
time.sleep(duration)
event.GetEventObject().SetValue(False)
print("Toggled")
def action(self, event):
if event.GetEventObject().GetValue():
self.update_toggle_process = Process(target = self.update_toggle,
args=(5, event,))
self.update_toggle_process.start()
else:
print("UnToggled")
class MyApp(wx.App):
def OnInit(self):
self.frame = MyFrame(None, wx.ID_ANY, "")
self.SetTopWindow(self.frame)
self.frame.Show()
return True
if __name__ == "__main__":
app = MyApp(0)
app.MainLoop()
A call to event.GetEventObject().Update() or event.GetEventObject().Refresh() after changing the value of the Toggle doesn't seem to change anything.
EDIT:
If I use a Thread instead of Process, it works correctly, but I chose Process over Thread because I want the ability to kill it cleanly whenever I need to.
Python version: 3.7
WxPython version: 4.0.1
You must remember, that the your update_toggle runs in a new process. Simply put, it has got a copy of data, so if you call event.GetEventObject().SetValue(False) it happens in the new process, and the original one with the Window and Button won't know.
You must somehow pass a message from the new process to the original. I would suggest that the first thing you try is:
self.update_toggle_process.start()
self.update_toggle_process.join()
print("the process has finished")
This will block, but at least you will see if the "update_toggle_process" has finished and if this approach works. After that, there are a few possibilities:
Set up a times and periodically call self.update_toggle_process.is_alive()
Create a new thread, call the update_toggle_process.start() from it, and also join(). When finished, tell the main thread to toggle the button (remember that you may only manipulate the UI from the main thread in wx)
Maybe you do not need a new process, a thread will be enough
Look at the multiprocessing IPC
This is a follow-up to my previous question, wxPython popup from calling imported function.
I was able to figure out a way to create a wxPython dialog window to determine if a thread my GUI called should continue its execution. What I did was simply create the dialog window in the thread itself. However, once I made the thread exit by clicking "no" in this popup, the close button in my GUI became unresponsive, and I couldn't close the GUI itself. Once again, your help is much appreciated!
GUI code:
import sys
import os
import re
import subprocess
import threading
import wx
import errno, os, stat, shutil
import extern_func
#this object redirects the external function output to the text box
class RedirectText(object):
def __init__(self,aWxTextCtrl):
self.out=aWxTextCtrl
def write(self,string):
self.out.WriteText(string)
#GUI code here
class progFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, title="functionGUI", size=(800, 600), style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER)
panel = wx.Panel(self)
#more things....
self.closeButton = wx.Button(panel, wx.ID_OK, "Run", pos=(250, 300))
self.runButton = wx.Button(panel, wx.ID_OK, "Run", pos=(200, 300))
self.out=wx.TextCtrl(panel, style=wx.TE_MULTILINE|wx.VSCROLL|wx.TE_READONLY, pos = (300, 50), size=(500, 200))
#Run button event
self.Bind(wx.EVT_BUTTON, self.OnRun, self.runButton)
#close button event
self.Bind(wx.EVT_BUTTON, self.OnClose, self.closeButton)
#command prompt output to frame
redir=RedirectText(self.out)
sys.stdout=redir
self.Show()
def OnRun(self, event):
t=threading.Thread(target=self.__run)
t.start()
def OnClose(self, event):
self.Destroy()
#external function call
def __run(self):
externFunc()
if __name__ == '__main__':
app = wx.App(False)
progFrame(None)
app.MainLoop()
External function code:
import sys
def externFunc():
print "Starting execution..."
#a bunch of code...
#this is the code for the Yes/No prompt and what introduced the buggy behavior
if(os.path.isdir(mirror_source_path) or os.path.isdir(mirror_dest_path)):
app = wx.App(False)
dlg = wx.MessageDialog(None, "Something bad happened. Continue?","Warning",wx.YES_NO | wx.ICON_QUESTION)
retCode = dlg.ShowModal()
if (retCode == wx.ID_YES):
print "Continuing."
else:
print "Aborted."
return None
sys.exit(0)
dlg.Destroy()
#more function code...
print "Success!"
You cannot have 2 wxPython main loops running at the same time. That will cause some pretty screwy behavior. Personally I think I would split this code into 2 threads. When the first one finishes, it sends a message using wx.CallAfter and pubsub or wx.PostEvent and at that time you can do your if statement in a wxPython handler.
If you continue, then you spin up a second thread with the rest of the function code.
I have a wxpython desktop application and I am using python 2.7 and wxpython 2.8.
I know how to add an accelerator table to a menuitem but I would like to fire an event when a user press a certain combination of keys without having a menuitem.
The user could have the focus on any field in my UI but when he press (for instance) CTRL-L an event should be fired. How to do this ?
Thanks for any help
You always need to bind your accelerator table items to wx.EVT_MENU, but wxPython doesn't require that you use a menu item object. Here's a simple example:
import wx
class MyForm(wx.Frame):
#----------------------------------------------------------------------
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, "Tutorial", size=(500,500))
# Add a panel so it looks the correct on all platforms
panel = wx.Panel(self, wx.ID_ANY)
randomId = wx.NewId()
self.Bind(wx.EVT_MENU, self.onKeyCombo, id=randomId)
accel_tbl = wx.AcceleratorTable([(wx.ACCEL_CTRL, ord('Q'), randomId )])
self.SetAcceleratorTable(accel_tbl)
text = wx.TextCtrl(panel)
text.SetFocus()
#----------------------------------------------------------------------
def onKeyCombo(self, event):
""""""
print "You pressed CTRL+Q!"
# Run the program
if __name__ == "__main__":
app = wx.App(False)
frame = MyForm()
frame.Show()
app.MainLoop()
In this example, we just create a random id, bind that id to an event handler and then create an accelerator that will fire that handler, which in this case is CTRL+Q. To make things more interesting, I added a text control widget and set the focus to that. Then if you press CTRL+Q, you should see the event handler fire and some text appear in your console window.
You can read more about accelerators here:
http://www.blog.pythonlibrary.org/2010/12/02/wxpython-keyboard-shortcuts-accelerators/
I have some text that goes somewhat continuously on the output (somewhat = every two seconds approx in the real application).
When just print into the terminal, everything is ok, no matter how long I keep the running loop.
However, when directing the print to a wx frame, the print action itself goes well, no problem here, but if I keep the running loop more than 10-20 cycles, I am no longer able to close the window normally (mouse or Ctrl+F4 in this example, but the same if I construct an explicit Exit event menu). The action in thread stops, but the application hangs -- it can only be closed by the OS (Windows in my case) with a "force close / wait for the program to respond" dialog. Though, it can be closed immediately with Ctrl+C when the terminal window is on focus. It looks like something fills up with too much text and locks the closing process.
Happens with either WriteText or AppendText on a multiline wxTextCtrl window, or with the SetLabel as in the test code below.
In this test, if I close the wx window almost immediately after launch, it closes well. If I keep running the loop longer (or by comment out the two time.sleep() lines), then the hang will succeed whenever trying to close the window.
What am I missing here ? (Is there something I have to flush ?)
edit Hmm, the issue could be related to "threadsafe method", as described briefly here. Will do some tests in that direction, have to find out the proper usage of each of the wx.CallAfter, wx.CallLater or wx.PostEvent.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import wx
import time
import threading
class TestFrame(wx.Frame):
def __init__(self, *args, **kwds):
kwds["style"] = wx.DEFAULT_FRAME_STYLE
wx.Frame.__init__(self, *args, **kwds)
self.sometext = wx.TextCtrl(self, wx.ID_ANY, "", style=wx.TE_READONLY)
self.thread = None
self.alive = threading.Event()
self.__set_properties()
self.__do_layout()
self.__attach_events()
self.StartThread()
def __set_properties(self):
self.SetTitle("test")
def __do_layout(self):
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.sometext, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL, 0)
self.SetSizer(sizer)
sizer.Fit(self)
self.Layout()
def __attach_events(self):
self.Bind(wx.EVT_CLOSE, self.OnClose)
def OnClose(self, event):
""" stop & close on system window close """
self.StopThread()
self.Destroy()
def StartThread(self):
""" start the thread """
self.thread = threading.Thread(target=self.TestThread)
self.thread.setDaemon(1)
self.alive.set()
self.thread.start()
def StopThread(self):
""" stop the thread, wait util it is finished """
if self.thread is not None:
self.alive.clear()
self.thread.join()
self.thread = None
def TestThread(self):
""" main thread """
while self.alive.isSet():
self.sometext.SetLabel("Hello")
time.sleep(0.5)
self.sometext.SetLabel("Python")
time.sleep(0.5)
class MyApp(wx.App):
def OnInit(self):
wx.InitAllImageHandlers()
frame = TestFrame(None, -1, "")
self.SetTopWindow(frame)
frame.Show(1)
return 1
if __name__ == "__main__":
app = MyApp(0)
app.MainLoop()
I've changed every SetLabal with AppendtText. I'm don't know why, but SetLabel function don't work correctly for me.
After a few seconds the application will stop working. This is my error output:
(python:14099): Pango-CRITICAL **: pango_layout_get_iter: assertion 'PANGO_IS_LAYOUT (layout)' failed
Segmentation fault
After that I used CallAfter function http://wiki.wxpython.org/CallAfter the application started correctly working for me. My version of TestThread:
def TestThread(self):
""" main thread """
while self.alive.isSet():
wx.CallAfter(self.sometext.AppendText, "Hello")
time.sleep(0.5)
wx.CallAfter(self.sometext.AppendText, "Python")
time.sleep(0.5)
wx.CallAfter(self.sometext.Clear)
I hope this help you.
I need to bind the EVT_CHAR event for a GUI application I am developing using wxPython. I tried the following and I cann understand the beahviour of the code.
import wx
import wx.lib.agw.flatnotebook as fnb
class DemoApp(wx.App):
def __init__(self):
wx.App.__init__(self, redirect=False)
self.mainFrame = DemoFrame()
self.mainFrame.Show()
def OnInit(self):
return True
class DemoFrame(wx.Frame):
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, wx.ID_ANY,
"FlatNotebook Tutorial",
size=(600,400)
)
panel = wx.Panel(self)
button = wx.Button(panel, label="Close", pos=(125, 10), size=(50, 50))
self.Bind(wx.EVT_CHAR, self.character)
def character(self, event):
print "Char keycode : {0}".format(event.GetKeyCode())
if __name__ == "__main__":
app = DemoApp()
app.MainLoop()
The character function never gets called. However, when I comment out the two lines call to the Frame constructor, I character function is called. Adding a panel to the frame seems to interfere with the binding of the frame's EVT_CHAR.
How do I address this problem? Am I doing something wrong in my code?
The problem is that you are catching events that happen to the frame, but the frame is not in focus. The button is. In wxPython, events are sent to the widget in focus. If you add this to the end of your init, it works:
self.SetFocus()
However, if you change the focus to the button, then it will stop working again. See also:
wxpython capture keyboard events in a wx.Frame
http://www.blog.pythonlibrary.org/2009/08/29/wxpython-catching-key-and-char-events/
http://wxpython-users.1045709.n5.nabble.com/Catching-key-events-from-a-panel-and-follow-up-to-stacked-panels-td2360109.html
I appreciate that this question was answered 2 years ago but this issue catches us all out at some point or other.
It is the classic binding to wx.Event or wx.CommandEvent problem.
In this case simply changing the self.Bind(wx.EVT_CHAR, self.character) line to read button.Bind(wx.EVT_CHAR, self.character) will solve the problem, detailed above.
The issue of wx.Event - wx.CommandEvent is covered in full here:
http://wiki.wxpython.org/EventPropagation
and here:
http://wiki.wxpython.org/self.Bind%20vs.%20self.button.Bind