The package wxPython is very good for developing GUI-interface applications in Python, but so far, the only methods that I have found to exit from an application developed in wxPython launched from the Python command line always generate a runtime error when the application is closed programmatically. For example, the method Frame.Destroy() generates the error:
Runtime error
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "C:\PythonSamples\ArcGIS Examples\TIGER Data Loader.py", line 522, in
<module>
frame.Destroy()
RuntimeError: wrapped C/C++ object of type Frame has been deleted
A similar error message is generated if Frame.Close() is called. The only way that I have found to close an application window generated by wxPython WITHOUT generating a run-time error is by deleting the wx.App object:
app=wx.App()
frame = wx.Frame(etc....)
.
.
.
and somewhere in the program where you want to exit the Frame window, you issue
del app
This seems like a bad way to terminate an application. Is there a better way that does NOT generate a run-time error message?
calling frame.Destroy() in an event deletes the frame, but the program then returns to the wx mainloop.
When the mainloop finds that the frame has been destroyed, the error occurs.
Instead (like when using threads), use wx.CallAfter so wx it is executed in the main thread and somewhere where wx expects such changes. For you:
wx.CallAfter(frame.Destroy)
note as suggested in comments that it's cleaner to do:
wx.CallAfter(frame.Close)
because it gives your app a chance to call some cleanup code, unlike Destroy
How about frame.Close() ? Docs are here
For reference, the following code doesn't spit out any error on my machine:
import wx
class MyFrame(wx.Frame):
def __init__(self):
super().__init__(None, title="Close Me")
panel = wx.Panel(self)
closeBtn = wx.Button(panel, label="Close")
closeBtn.Bind(wx.EVT_BUTTON, self.onClose)
def onClose(self, event):
self.Close()
if __name__ == "__main__":
app = wx.App(False)
frame = MyFrame()
frame.Show()
app.MainLoop()
Related
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
I am getting this error
Traceback (most recent call last):
File "helloworld.py", line 66, in module
hello.main()
AttributeError: Helloworld instance has no attribute 'main'
I am running code given below on Linux machine
#!/usr/bin/env python
# example helloworld.py
import pygtk
pygtk.require('2.0')
import gtk
class HelloWorld:
# This is a callback function. The data arguments are ignored
# in this example. More on callbacks below.
def hello(self, widget, data=None):
print "Hello World"
def delete_event(self, widget, event, data=None):
# If you return FALSE in the "delete_event" signal handler,
# GTK will emit the "destroy" signal. Returning TRUE means
# you don't want the window to be destroyed.
# This is useful for popping up 'are you sure you want to quit?'
# type dialogs.
print "delete event occurred"
# Change FALSE to TRUE and the main window will not be destroyed
# with a "delete_event".
return False
def destroy(self, widget, data=None):
print "destroy signal occurred"
gtk.main_quit()
def __init__(self):
# create a new window
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
# When the window is given the "delete_event" signal (this is given
# by the window manager, usually by the "close" option, or on the
# titlebar), we ask it to call the delete_event () function
# as defined above. The data passed to the callback
# function is NULL and is ignored in the callback function.
self.window.connect("delete_event", self.delete_event)
# Here we connect the "destroy" event to a signal handler.
# This event occurs when we call gtk_widget_destroy() on the window,
# or if we return FALSE in the "delete_event" callback.
self.window.connect("destroy", self.destroy)
# Sets the border width of the window.
self.window.set_border_width(10)
# Creates a new button with the label "Hello World".
self.button = gtk.Button("Hello World")
# When the button receives the "clicked" signal, it will call the
# function hello() passing it None as its argument. The hello()
# function is defined above.
self.button.connect("clicked", self.hello, None)
# This will cause the window to be destroyed by calling
# gtk_widget_destroy(window) when "clicked". Again, the destroy
# signal could come from here, or the window manager.
self.button.connect_object("clicked", gtk.Widget.destroy, self.window)
# This packs the button into the window (a GTK container).
self.window.add(self.button)
# The final step is to display this newly created widget.
self.button.show()
# and the window
self.window.show()
def main(self):
# All PyGTK applications must have a gtk.main(). Control ends here
# and waits for an event to occur (like a key press or mouse event).
gtk.main()
# If the program is run directly or passed as an argument to the python
# interpreter then create a HelloWorld instance and show it
if __name__ == "__main__":
hello = HelloWorld()
hello.main()
Is this an indent problem? How to solve it?
I tried searching the internet, but no help.
This is the same code given in pyGTK tutorial.
Please Help.
I just copied this code to my Linux machine and ran it, and it worked just fine with no errors. Are you running this from the command line? Like $python helloworld.py ? Or are you trying to run it through a Python console session?
The indentation doesn't seem to be an issue. What version of Python are you running?
I like the idea of using the loadUi() method in PyQt to load in a QtDesigner interface file as I'm often changing the ui file and don't want to be constantly converting it to a py file.
However, I've been having difficulty understanding how to access the different widgets and views in my ui file. The below shows how I'm loading in the ui:
class MyClass(QtGui.QMainWindow):
def __init__(self, parent = None):
super().__init__(parent)
ui = uic.loadUi('MyUserInterface.ui',self)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
mainapplication = MyClass()
mainapplication.show()
app.exec_()
It displays fine, but I was expecting that I would be able to access the elements of the GUI by something along the lines of...
ui.sampleButton.makeDraggable()
My question is, how can I access the various elements within my GUI?
Edit 1: I Also have tried to without assigning the uic.load call to ui. When accessing as shown below, I can access some of the properties through using Ui_MainWindow. However, it throws an error
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
mainapplication = MyClass()
mainapplication.Ui_MainWindow.graphicsView.acceptDrops(True)
mainapplication.show()
app.exec_()
The error i get is...
Traceback (most recent call last): File "C:\Users\Me\workspaces\com.samplesoftware.software\src\SoftwareView\MyClass.py", line 17, in <module>
mainapplication.Ui_MainWindow.graphicsView.acceptDrops(True)
AttributeError: 'MyClass' object has no attribute 'Ui_MainWindow'
Any ideas?
You don't need to convert it to a .py file but you definitely have to name them to access them, otherwise you need to know what the sampleButton is called in the ui file.
you can simply rename the sampleButton to my_btn and access it..
The loadUi function takes a ui file path as the first argument, and a base-instance as the second argument.
The base-instance should be the same Qt class as the top-level widget in the UI. This is because loadUi will insert all the UI into the base-instance, and may need to call some of its methods in doing so. All the child widgets that are defined in the UI will end up as attributes of the base-instance.
So you should call loadUi like this in your __init__:
class MyClass(QtGui.QMainWindow):
def __init__(self, parent = None):
super(MyClass, self).__init__(parent)
uic.loadUi('MyUserInterface.ui', self)
And you should then be able to access graphicsView like this:
self.graphicsView.setAcceptDrops(True)
I found out the reason why was because I was trying to call the Ui_MainWindow prior to running app.exec_(). After moving the Ui_MainWindow call to the end it does work. I'm guessing, and it does make sense in hindsight, the MainWindow needs to exist before its properties can be altered.
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().
I have a CLI application, which is digging some data, in case of need, fires up a thread which creates GTK window with some information. However the CLI (main thread) still analyzes the data in the background, so there could be numerous windows created. In case I close the window, the destroy event is actually fired up, I got a debug line in CLI, but the window locks up.
Some magical command that I have to use ?
I create window like this in the main thread:
gtk.gdk.threads_init()
notifyWindow = NotifyWindow()
notifyWindow.start()
This is NotifyWindow(Thread).destroy
def destroy(self, widget, data=None):
print "destroy signal occurred"
gtk.main_quit()
This is NotifyWindow(Thread).run
def run(self):
self.window = gtk.glade.XML( "hadinfo.glade" )
self.window_main = self.window.get_widget("window_main")
if (self.window_main):
self.window_main.connect("destroy", self.destroy)
self.window_main.connect("delete_event", self.delete_event)
self.button_cancel = self.window.get_widget("button_cancel")
self.button_cancel.connect("clicked", self.destroy)
self.window.get_widget("window_main").show()
gtk.main()
using a gtk.threads_enter() and leave around your main call should help.
Take a look at the PyGtk Faq : PyGtk FAQ