I've been trying to stop a loop in wxPython and things I've been trying don't seem to work. I've tried using self.app.ExitMainLoop() and putting it in the __init__ function, didn't work, tried putting it in the varGUI function, still didn't work. I could be doing this wrong and just being really stupid.
Here's the project I'm working on :
class varMenu(wx.Frame) :
def __init__(self, *args, **kwargs) :
super(varMenu, self).__init__(*args, **kwargs)
self.varGUI()
def varGUI (self) :
global minVolAns
global frameRateAns
minVol = wx.TextEntryDialog(None, "What is the minimum volume of the video?", "Minimum Volume",
"Input Here")
if minVol.ShowModal()==wx.ID_OK :
minVolAns=minVol.GetValue()
frameRate = wx.TextEntryDialog(None, "What framerate would you like?", "Framerate",
"Input Here")
if frameRate.ShowModal()==wx.ID_OK :
frameRateAns=frameRate.GetValue()
def main() :
app = wx.App()
varMenu(None, title = 'How would you like to edit your video?')
app.MainLoop()
main()
For context, I want the program to continue after the questions so I can use the variable for my auto-cutter.
Thanks!
After reading the answer of Rolf of Saxony, I realized that my own answer covers only half the problem in the question. So here is an updated answer.
Updated Answer
I guess the phrase at the end of the question, "I want the program to continue after the questions", refers to the fact that the code given does not show any window after the two wx.TextEntryDialog are shown and closed.
In this case, the program indeed continues to run after the two wx.TextEntryDialog are shown and closed. Even if nothing is shown after the two wx.TextEntryDialog, you can see in the terminal that the terminal prompt does not return, so the program is still running.
The reason for this behavior is two-fold.
1.- After finish using the two wx.TextEntryDialog you are not showing anything else. So the main loop of the program keeps runnig but has nothing to show. This can be fixed just by changing the main method in your code to:
def main() :
app = wx.App()
frame = varMenu(None, title = 'How would you like to edit your video?')
frame.Show()
app.MainLoop()
Now the main window of your program is shown after the two wx.TextEntryDialog but if you close the main window the terminal prompt does not return meaning that your program is still running.
2.- The problem here is that you are creating an instance of wx.TextEntryDialog and then you are showing the instance with ShowModal() but you are not destroying the instance after finish using it. According to the docs of wxPython for ShowModal():
this function creates a temporary event loop which takes precedence
over the application’s main event loop (see wx.EventLoopBase) and
which is destroyed when the dialog is dismissed
For some wx.Dialog closing the window or pressing OK(Yes/No/Cancel) is enough to destroy the temporary event loop. For others, you need to call the Destroy() method to achieve this. Therefore, is better to make a habit of calling Destroy() after finish using a wx.Dialog. So your code must be modify to:
import wx
class varMenu(wx.Frame) :
def __init__(self, *args, **kwargs) :
super(varMenu, self).__init__(*args, **kwargs)
self.varGUI()
def varGUI (self) :
global minVolAns
global frameRateAns
minVol = wx.TextEntryDialog(None, "What is the minimum volume of the video?", "Minimum Volume",
"Input Here")
if minVol.ShowModal()==wx.ID_OK :
minVolAns=minVol.GetValue()
minVol.Destroy()
frameRate = wx.TextEntryDialog(None, "What framerate would you like?", "Framerate",
"Input Here")
if frameRate.ShowModal()==wx.ID_OK :
frameRateAns=frameRate.GetValue()
frameRate.Destroy()
def main() :
app = wx.App()
frame = varMenu(None, title = 'How would you like to edit your video?')
frame.Show()
app.MainLoop()
main()
Now when you finish using the two wx.TextEntryDialog and close the main window there is no loose temporary event loop and the program actually stop running.
Original Answer:
Welcome to StackOverflow
The main loop of your program continues. You can see in the terminal that the terminal prompt does not return after all the dialogs are shown because the main loop keeps running. The problem is that you are not showing anything except the dialogs.
Just change the main method to:
def main() :
app = wx.App()
frame = varMenu(None, title = 'How would you like to edit your video?')
frame.Show()
app.MainLoop()
and you will see the main window after the dialogs.
You have failed to Destroy both of your dialogs, which is why the program is not closing.
Try this:
import wx
class varMenu(wx.Frame) :
def __init__(self, *args, **kwargs) :
super(varMenu, self).__init__(*args, **kwargs)
self.panel = wx.Panel(self)
self.textCtrl = wx.TextCtrl(self.panel, -1, value="Your Main Screen")
self.minVolAns = wx.TextCtrl(self.panel, -1, value="")
self.frameRateAns = wx.TextCtrl(self.panel, -1, value="")
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.textCtrl,0,wx.EXPAND)
sizer.Add(self.minVolAns)
sizer.Add(self.frameRateAns)
self.panel.SetSizer(sizer)
self.varGUI()
self.Show()
def varGUI(self):
minVol = wx.TextEntryDialog(None, "What is the minimum volume of the video?", "Minimum Volume","")
if minVol.ShowModal() == wx.ID_OK :
self.minVolAns.SetValue(minVol.GetValue())
minVol.Destroy()
frameRate = wx.TextEntryDialog(None, "What framerate would you like?", "Framerate","")
if frameRate.ShowModal() == wx.ID_OK :
self.frameRateAns.SetValue(frameRate.GetValue())
frameRate.Destroy()
if __name__ == "__main__":
app = wx.App()
frame = varMenu(None, title = 'How would you like to edit your video?')
app.MainLoop()
Related
import wx
class MyForm(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, "Test")
panel = wx.Panel(self, wx.ID_ANY)
#Button is created; binded to onButton
button = wx.Button(panel, id=wx.ID_ANY, label="Press Me")
button.Bind(wx.EVT_BUTTON, self.onButton)
def onButton(self,EVT_BUTTON):
print("Hello world!")
if __name__ == "__main__":
app = wx.App(False)
frame = MyForm()
frame.Show()
#Runs Button command on startup
MyForm.onButton()
I want onButton() to run at startup, and have it be able to run when the
wx.Button is pressed. Unfortunetly, it comes up with this error:
>TypeError: onButton() missing 2 required positional arguments: 'self' and 'EVT_BUTTON'
It is slightly more difficult. I am guessing you are a beginner to programming. If so, I suggest, you learn some more basics. Doing GUI applications is a bit more advanced topic.
So, firstly, for your wxPython program to run, you must start an event loop, so your program should have something like this:
if __name__ == "__main__":
app = wx.App(False)
frame = MyForm()
frame.Show()
app.MainLoop()
You defined your function onButton with 2 parameters. Therefore you must supply them when calling the function. The first one is instead of the self, and that is the frame. The second is named EVT_BUTTON (and giving the variable this name suggests that you actually do not understand these concepts, and that's the reason why I suggested that you start with studying basics).
So you could call
frame.OnButton(None)
before calling app.MainLoop() and the code will run. But that's probably not enough.
Ok, so this is from a larger project that I am working on, so I apologize if it looks messy.
The issue is that when I click the 'Exit Program' Button on the GUI, the window remains active.
I know that the button is working as when I hit the 'x' on the top right corner the window; the program closes, so the run variable has been set back to 0, which stops the code from looping.
My question is how do I get the window to be closed automatically when the exit button is clicked, because the root.destroy() method isn't doing it.
#imports
from tkinter import *
import random, pickle, shelve
#global vars
run = 0
class Window(Frame):
#the class that manages the UI window
def __init__(self, master, screen_type = 0):
"""Initilize the frame"""
super(Window, self).__init__(master)
self.grid()
if screen_type == 1:
self.log_in_screen()
def log_in_screen(self):
#Program Exit Button
self.exit = Button(self, text = " Exit Program ", command = self.end)
self.exit.grid(row = 3, column = 0, columnspan = 2, sticky = W)
def end(self):
global run, root
run = 0
root.destroy()
#Main Loop
def main():
global run, root
run = 1
while run != 0:
root = Tk()
root.title("Budget Manager - 0.6.1")
root.geometry("400x120")
screen = Window(root, screen_type = run)
root.mainloop()
store = shelve.open("store.dat", "c")
main()
store.close()
My question is how do I get the window to be closed automatically when
the exit button is clicked, because the root.destroy() method isn't
doing it.
The answer is: call destroy() on the root window. You say it isn't working, but the code you posted seems to work, and what destroy() is documented to do is exactly what you describe you want to have happen: it will destroy the window. Your code creates new toplevel windows in a loop, so maybe it only appears to not work since the old window id destroyed and the new window is created in the blink of an eye.
It seems like what you're really asking is "how can I make clicking on the "x" do the same as clicking on the "Exit program" button?". If that is the case, the answer is very straight-forward, even with your unconventional code that creates root windows in a loop.
To get the "x" button on the window frame to call a function instead of destroying the window, use the wm_protocol method with the "WM_DELETE_WINDOW" constant and the function you want it to call.
For example:
while run != 0:
root = Tk()
...
screen = Window(root, screen_type = run)
root.wm_protocol("WM_DELETE_WINDOW", screen.end)
...
root.mainloop()
you could do something like the below. I've used it in my own projects etcand it works.
Mywin =tkinter.Tk()
def exit():
Mywin.quit()
# etc.
As the title says when I implement code to clear hinted text and it is run the application crashes.
As far as I know this is only on Mac OS X 10.8 but that is also all I have been able to run it on.
On other code it does run and only once I enter text into it (After giving focus) does it crash. But this app crashes immediately (I think something to do with no other widgets and so it get focus right away). Updated example so it no longer crashes right away now you have to click on the combo box and try to type in it for it to crash.
This however does not occur if the text is anything but "" it seems.
Code
import wx
class MyCrashyPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent, -1)
# another widget to take focus at first otherwise it crashes instantly!
sizer = wx.BoxSizer(wx.VERTICAL)
self.text_ctrl = wx.TextCtrl(self, -1, value = "There are major problems here.\nWithout this to auto take focus this will crash immediatly just by trying to clear the hint.\nNow you have to click on the combo ctrl and try to type.", style = wx.TE_MULTILINE)
self.search_ctrl = wx.ComboBox(self, -1)
self.search_ctrl.SetMinSize((650, -1))
self.search_ctrl.SetSize((650, -1))
self.search_ctrl.SetHint("This is are hint text; once it is clear and you try to type something in it it will crash on Mac OS X")
sizer.Add(self.text_ctrl, flag = wx.EXPAND)
sizer.Add(self.search_ctrl)
self.SetSizer(sizer)
self.FirstTimeSearchGetsFocus = True
self.Bind(wx.EVT_BUTTON, lambda e: e.Skip(), self.text_ctrl)
self.search_ctrl.Bind(wx.EVT_SET_FOCUS, self.OnSearchFocus)
self.text_ctrl.SetFocus()
def OnSearchFocus(self, event):
print "Search Focus"
if 1==1:
print "First time"
# clear the hinted text
self.search_ctrl.SetHint("")
self.search_ctrl.Clear()
self.search_ctrl.Refresh()
self.FirstTimeSearchGetsFocus = False
event.Skip()
if __name__ == "__main__":
app = wx.App(False)
f = wx.Frame(None, -1)
MyCrashyPanel(f)
f.Show()
app.MainLoop()
Crash Report
[Too big get here http://pastebin.com/9B1Sgh3P ]
If it crashes, it's a bug in wxWidgets, so the only things to do are to:
Try with a later version, i.e. 2.9.5 or svn/git if you can build it yourself.
Report the bug if it still persists there, following the usual guidelines.
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
We were using Python2.3 and wxPython V2.4.2.4 in our project. And it was working fine. Now we are upgrading it to Python2.7 and wxPython2.8.12.1. Our project is compiled fine with new version. But in our project at one stage in the code we destroy the current window and then create & open new window again. And I have noticed that our code after creating new window does not execute. While in the old version it was executing.
In the following code. It is displaying the message "doRead 1" then open the window. But it does not display the message "doRead 2". While in old Python version it was displaying the message "do Read 2" means it was executing the code after that.
I found that, it does not come out from the line "self.MainLoop()" in OnInit(...) function in new project. But it was coming out and thus executing the next line in old project.
-----------------------------------------
Here is the code:
#Close existing window.
self.Destroy()
print 'doRead 1'
#create new window
app = App()
print 'doRead 2'
app.frame.saveContents()
------------------------------------
class App(wx.App):
"""Application class.
"""
def OnInit(self):
wx.InitAllImageHandlers()
resetOptions()
setOptions()
self.frame = pdtpFrame()
self.frame.SetTitle(std.getTitle())
self.frame.Show()
self.SetTopWindow(self.frame)
self.MainLoop()
return True
def main():
""" Start up the pdtp main window application.
"""
app = App()
if __name__ == '__main__':
main()
Your trouble (as far as I can tell) is that you have your MainLoop inside of your OnInit function, which is halting your program flow. I can't speak for how it worked like that before, to be honest, because you shouldn't be able to enter the MainLoop for an App until it's OnInit has returned True. The OnInit could return False in which case the App didn't fully initialize (common if you're doing single instance apps with a lock file, for example). A more common approach (pseudo-code) would look like this:
app = wx.PySimpleApp()
f = Frame(None, -1, "Some Title For This Frame")
f.Show()
app.MainLoop()
# Down here more code can follow.
It won't execute more code until after all Top Level Windows are closed from the prior App instance, or something else calls wx.GetApp().ExitMainLoop().