Does not come out from MainLoop() - python

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

Related

Stopping a wxPython loop

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

GetPosition() not working with wxPython

I'm new to Python (and programming in general) so forgive me if this is a dumb question.
I'm following a tutorial in a book for creating a GUI in Python. Right now I'm learning about how to make a Spin Control increment some static text. When I run it, the spin control shows up but the console says "value = event.GetPosition()
AttributeError: 'CommandEvent' object has no attribute 'GetPosition'"
The code is:
import wx
class Frame(wx.Frame):
def __init__(self, title):
wx.Frame.__init__(self, None,\
title=title, size=(300,250))
panel = wx.Panel(self)
sc = wx.SpinCtrl(panel, value='0', pos=(130, 50), size=(70, 25))
self.valueText = wx.StaticText(panel, label='', pos=(130,80))
sc.Bind(wx.EVT_SPINCTRL, self.spinControl)
def spinControl(self, event):
# Get spin control value
value = event.GetPosition()
# Update static text
self.valueText.SetLabel(str(value))
app = wx.App()
frame = Frame("wxPython Widgets!")
frame.Show()
app.MainLoop()
That code is literally copy/pasted from the book's website. I have wxPython installed and everything works perfectly up to that point.
Halp!
I've been working through that same book Python in a Day 2. I believe the content is already outdated.
If you change the line with GetPosition() to
value = event.GetEventObject().GetValue()
is correct. I can confirm that it works for Python 2.7.11.
Jeremiah
If you change the line with GetPosition() to
value = event.GetEventObject().GetValue()
it will work. The event just doesnt have the GetPosition attribute, so it cannot eb executed.
If you want to check which functions and attributes are available, you can use
print(dir(event))
This will show you everything which is available inside the event.
Michael

Mac OS Quit Application menu label same as python script name

When I run my wxPython app in Mac OS X, the "Quit" menu item under the Application menu has the label "Quit Myapp", where myapp.py is the name of the main python script. If I rename it to, for example, coolapp.py, the label on the menu item becomes "Quit Coolapp".
Even when I package the application using py2app, although the Application menu changes from 'Python' to the name specified in my setup.py, the Quit menu item remains the same.
How can I change this menu item to use a different application name? Is there any way to use the pyobjc bridge with wxPython?
As it turns out, PyObjC is not required, and this can be done from wxPython only.
The function to call is SetAppName(name), but this must be done from the OnInit(self): method. Previously I was calling this function after the App instance had been created, and it did nothing.
Your code should look like this:
class MyApp(wx.App):
def OnInit(self):
# Set application name before anything else
self.SetAppName("My App Name")
mainframe = MyMainFrame(None, wx.ID_ANY, "")
self.SetTopWindow(mainframe)
mainframe.Show()
return 1
if __name__ == '__main__':
myapp = MyApp()
myapp.MainLoop()

Thread for UI design with wxPython

I'm currently designing a software UI based.
I have created a first python script used as my main source code.
Below is my script
import OneTouchToolLogs
import UserInterface
import wx
import XML_Parse
import threading
if __name__ == '__main__':
#Init the logging - Tools and also logcat scenario
Logs = OneTouchToolLogs.Logging()
LogFileName = Logs.LogFileOpen()
Logs.LogMessage(LogFileName, "LOG" , "Starting OneTouchAutomationTools")
#Initialized User Interface
Logs.LogMessage(LogFileName, "LOG" , "Loading user interface")
app = wx.App()
frame = UserInterface.UserInterface(None, -1, 'OneTouchAutomation')
app.MainLoop()
I have created another python file which contain the class UserInterface to make it clearer.
The new python class is done as below:
import wx
class UserInterface(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent,id, title)
self.parent = parent
self.worker = None
self.initialize()
def initialize(self):
menubar =wx.MenuBar()
#CREATE FILE MENU SECTION
fileMenu = wx.Menu()
fileMenu.Append(wx.ID_NEW, '&New suites\tCTRL+N')
fileMenu.Append(wx.ID_OPEN, '&Open suites\tCTRL+O')
With the design, I have done, the UI become a blocking point for the overall execution as it's not done on a thread.
I have modified my main script to replace
app.MainLoop()
by
t = threading.Thread(target=app.MainLoop)
t.setDaemon(1)
t.start()
The result is that the thread is well created but he is killed in a second. I just see the window and it's close.
Any one know I to be able to create this interface using my UserInterface class and start it in a thread to allow the main program to continu ?
In most cases, you will want the wxPython script to be the main application (i.e. the main thread). If you have a long running task, such as parsing a file, downloading a file, etc, then you will put that into a separate thread that your wxPython program will create. To communicate from the spawned thread back to the wxPython program, you will need to use a thread-safe method, such as wx.CallAfter or wx.PostEvent.
I recommend checking out the wxPython wiki for additional information:
http://wiki.wxpython.org/LongRunningTasks

How to Restart PyQt4 Application

Is there a way to restart PyQt application QApplication
I have an app created with pyqt4 and python 2.6 using below code
app = QtGui.QApplication(sys.argv)
i have settings options where i set some settings. Now when i save settings i need to reload the application so that new settings are effected. Without the need of end user to exit and launch the app again.
I had a similar problem and simply used this at the appropriate place:
subprocess.Popen([__file__])
sys.exit(0)
It was a simple application, and didn't need any further arguments.
I explain how I did it :
I create a extra one file main.py which calls my actual main program file dash.py.
And I emits a signal for restarting (my programs auto updates at the closeEvent) so I required to emit a signal for it. This is the snippets hope this will help you.
This one is in my main program file in dash.py
def restart(self):
# create a signal equivalent to "void someSignal(int, QWidget)"
self.emit(QtCore.SIGNAL("RESTARTREQUIRED"), True)
This one in main.py which calls actual program only and restarts the app
import sys
from PyQt4 import QtGui,QtCore
from bin import dash
if __name__ == "__main__":
application = QtGui.QApplication(sys.argv)
uDesk = dash.app()
uDesk.show()
uDesk.actionRestart.triggered.disconnect()
# define restart slot
#QtCore.pyqtSlot()
def restartSlot():
print 'Restarting app'
global uDesk
uDesk.deleteLater()
uDesk = dash.app()
uDesk.show()
uDesk.actionRestart.triggered.disconnect()
uDesk.actionRestart.triggered.connect(restartSlot)
print 'New app started !'
QtCore.QObject.connect(uDesk,
QtCore.SIGNAL("RESTARTREQUIRED"),
restartSlot)
uDesk.actionRestart.triggered.connect(restartSlot)
sys.exit(application.exec_())
Hope this was helpful !!
EDIT: Changing the way to get the application path
You could just start a new process and exit yours, something like this: (CODE NOT TESTED, but based on this answer)
// Restart Application
def restart(self, abort):
// Spawn a new instance of myApplication:
proc = QProcess()
//proc.start(self.applicationFilePath());
import os
proc.start(os.path.abspath(__file__))
self.exit(0);
Code it as a method of your Qapplication or even a function if you don't feel like subclassing
This is how I restart TicTacToe game in PySide (it should be the same in PyQt):
I have a single class - a QWidget class - in which is coded the Tic Tac Toe game. To restart the application I use:
import subprocess
a QPushButton() like so:
self.button = QPushButton("Restart", self)
the connection of the button to Slot:
self.buton.clicked.connect(self.restartGame)
the Slot for this button, like so:
def restartGame(self):
self.close()
subprocess.call("python" + " TicTAcToe.py", shell=True)
All these are in the same - single - class. And what these do: close the active window of the game and create a new one.
How this code looks in the TicTacToe class:
import subprocess
class TicTacToe(QWidget):
def __init__(self):
QWidget.__init__(self)
self.button = QPushButton("Restart", self)
self.buton.clicked.connect(self.restartGame)
def restartGame(self):
self.close()
subprocess.call("python" + " TicTacToe.py", shell=True)
def main():
app = QApplication(sys.argv)
widget = TicTacToe()
widget.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
EDIT
I know this doesn't answer the question (it doesn't restart a QApplication), but I hope this helps those who want to restart their QWidget single class.

Categories

Resources