wxPython: How do you start a wxPython application from a wxPython application? - python-2.7

I am attempting to start a wxPython application but I want a banner to be displayed before it is started.
One way to do this is to start a wxPython application which in turn starts another wxPython application, the reason for doing it this way is since the App part of the second wxPython application needs to do some processing before starting and may take some time.
The issue is how do I start the other application and know that it has started?
Currently I do this which blocks for the entire GUI session:
subprocess.check_output(["python", "src/gui.py"], stderr=subprocess.STDOUT, shell=True)
I have attempted to do the following but the frame of the first application does not seem to close:
loadCompleted, EVT_LOAD_COMPLETED = wx.lib.newevent.NewEvent()
class MyRegion(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, title="My Region")
self.label = wx.StaticText(self, label="Hello, World!")
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.label, 0, wx.ALL, 5)
self.SetSizer(sizer)
self.startThread = Thread(target=self.Start)
self.startThread.start()
self.Bind(EVT_LOAD_COMPLETED, self.OnClose)
def OnClose(self, result):
self.Close()
def Start(self):
try:
subprocess.check_output(["python", "src/gui.py"], stderr=subprocess.STDOUT, shell=True)
except:
pass
wx.PostEvent(self, loadCompleted(result=(None)))
if __name__ == "__main__":
app = wx.App()
frame = MyRegion(None)
frame.Show()
app.MainLoop()

I don't think starting a second wxPython application is the way to go. Instead, I would just load the banner inside your frame's __init__ method, then do your processing. When the processing finishes, you can destroy the banner and show your main app. Here's some psuedo-code:
#
class MyFrame(wx.Frame):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, title="Test")
banner = MyBanner()
# do a bunch of stuff
banner.Destroy() # or banner.Close()
self.Show()
Now if the processing takes a really long time, you can put that into a thread and have the thread send a message back to the UI that tells it that the thread is finished. When the app receives the message, it can close the banner in the handler and show the App at that point. Please note that you need to use a thread-safe method, such as wx.CallAfter or wx.PostEvent.
Check out the following articles for ideas:
http://www.blog.pythonlibrary.org/2010/05/22/wxpython-and-threads/
http://www.blog.pythonlibrary.org/2013/09/05/wxpython-2-9-and-the-newer-pubsub-api-a-simple-tutorial/

Related

How to synchronize wxpython Gauge widget with threads in python?

My python project has multiple threads running. I want to add a gauge widget from the wxpython library to show the progress. I want the gauge to fill until my first thread completes. How do I achieve this? I am using Python 2.7 on Windows.
Use wx.CallAfter
def add_to_the_gauge(value):
your_gauge.Value += value
...
#in some thread
def some_thread_running():
wx.CallAfter(add_to_the_gauge, 2)
You need to post events from your worker thread to the main thread asking it to update the gauge, see this overview.
You can use some simple things
remember to import the module:
import os
and then put this on the frame
def __init__(self, *a, **k):
filename = 'game.exe'
self.timer = wx.Timer()
self.Bind(wx.EVT_TIMER, self.OnUpdateGauge, self.timer)
self.timer.Start()
self.proc = os.popen(filename) # start the process
def OnUpdateGauge(self, event):
if self.proc.poll() == None: # return None if still running and 0 if stopped
self.timer.Stop()
return
else:
'''do something with the gauge'''
hope those help

PyQt 4: Get Position of Toolbar

Hy guys,
in my executable program there is a toolbar. Well, the user decides to move the toolbar. Now the toolbar is floating. I know I have to conntect the floating-signals that is emittted when the toolbar ist arranged by the user. How can I save the new position of the toolbar? I know the method of adding the toolbar to the main window with a position:self.addToolBar( Qt.LeftToolBarArea , toolbar_name). In the handle_floating()-method you see what I want: There I want to get the position currently, but how? You also see I have just added one member variable, named self.toolbar_pos, to hold the position of the toolbar. My idea is, when application is terminated I want to serialize this value to a file, and later, when application is ran again its will read that file and set the toolbar accordingly. But this is no problem. Currently I don't have no idea to get the position of the toolbar.
I need your help :)
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
from PyQt4 import QtGui, QtCore
class Example(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self, parent)
self.toolbar_pos = None
self.initUI()
def initUI(self):
exitAction = QtGui.QAction(QtGui.QIcon('exit24.png'), 'Exit', self)
exitAction.setShortcut('Ctrl+Q')
exitAction.triggered.connect(QtGui.qApp.quit)
self.toolbar = QtGui.QToolBar(self)
self.toolbar.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
self.addToolBar(self.toolbar )
self.toolbar.addAction(exitAction)
self.toolbar.setAllowedAreas(QtCore.Qt.TopToolBarArea
| QtCore.Qt.BottomToolBarArea
| QtCore.Qt.LeftToolBarArea
| QtCore.Qt.RightToolBarArea)
self.addToolBar( QtCore.Qt.LeftToolBarArea , self.toolbar )
self.toolbar.topLevelChanged.connect(self.handle_floating)
def handle_floating(self, event):
# The topLevel parameter is true
# if the toolbar is now floating
if not event:
# If the toolbar no longer floats,
# then calculate the position where the
# toolbar is located currently.
self.toolbar_pos = None
print "Get position: ?"
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
ex.setGeometry(300, 300, 300, 200)
ex.setWindowTitle('Toolbar example')
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
The QMainWindow class already has APIs for this: i.e. saveState and restoreState. These can be used to save and restore the state of all the toolbars and dock-widgets in your application.
To use them, you first need to make sure that all your toolbars and dock-widgets are given a unique object-name when they are created:
class Example(QtGui.QMainWindow):
...
def initUI(self):
...
self.toolbar = QtGui.QToolBar(self)
self.toolbar.setObjectName('foobar')
Then you can override closeEvent to save the state:
class Example(QtGui.QMainWindow):
...
def closeEvent(self, event):
with open('/tmp/test.conf', 'wb') as stream:
stream.write(self.saveState().data())
(NB: I've just used a temporary file here for testing, but it would obviously be much better to use something like QSettings in your real application).
Finally, you can restore the state that was saved previously:
class Example(QtGui.QMainWindow):
def __init__(self, parent=None):
...
self.initUI()
try:
with open('/tmp/test.conf', 'rb') as stream:
self.restoreState(QtCore.QByteArray(stream.read()))
except IOError:
pass

Why won't my wxFrame close properly?

I am trying to build a basic messaging system, but I have hit a major roadblock in the process. I can't get the window to close without it not responding and me having to close it in the Task Manager. From what I've read online, it sounds like I need to close when a sys.exit(0) to exit all the threads and connections. I have been stuck on this problem for days so I would really appreciate an answer and explanation to why it doesn't work! The meat of the problem is in the close_window() function. It is run able provided you have a basic server that accepts a connection. Thanks!
import wx
import socket
import threading
import sys
class oranges(wx.Frame):
def __init__(self,parent,id):
##Unimportant stuff
wx.Frame.__init__(self,parent,id," Retro Message",size=(500,500))
self.frame=wx.Panel(self)
self.input_box=wx.TextCtrl(self.frame, -1,pos=(15,350),size=(455,120),style=wx.NO_BORDER| wx.TE_MULTILINE)
self.messaging_box=wx.TextCtrl(self.frame, -1,pos=(15,15),size=(455,285),style=wx.NO_BORDER | wx.TE_MULTILINE|wx.TE_READONLY)
send_button=wx.Button(self.frame,label="Send",pos=(350,315),size=(75,40))
self.Bind(wx.EVT_BUTTON, self.sender,send_button)
self.Bind(wx.EVT_CLOSE, self.close_window)
self.counter = 1
self.socket_connect = socket.socket()
self.setup()
def sender(self,event):
self.socket_connect.send(self.input_box.GetValue())
self.input_box.Clear()
self.Refresh()
##Important stuff
def close_window(self,event): #This is the function in question#
self.counter = 0
self.socket_connect.shutdown(socket.SHUT_RDWR)
sys.exit(0)
def setup(self):
self.ip_enter = wx.TextEntryDialog(None, "Enter in the IP:", "Setup", "192.168.1.1")
if self.ip_enter.ShowModal() ==wx.ID_OK:
self.offical_ip = self.ip_enter.GetValue()
try:
self.socket_connect.connect((self.offical_ip,5003))
self.username = "false" #Tells the server just to give the user a IP username
self.Thread1 = threading.Thread(target = self.listening_connect)
self.Thread1.start()
except socket.error:
self.error_connect()
else:
sys.exit(0)
def listening_connect(self):
self.socket_connect.send(self.username)
while self.counter != 0:
data = self.socket_connect.recv(1024)
self.messaging_box.AppendText(data)
self.Refresh()
if not data:
break
self.socket_connect.close()
def error_connect(self):
pop_ups = wx.MessageDialog(None, "Failed to Connect to Server!", 'Error', wx.OK)
pop_ups.ShowModal()
self.setup()
if __name__=="__main__":
app=wx.App(False)
window=oranges(parent=None,id=-1)
window.Show()
app.MainLoop()
Here is a basic server program that should work with it(I am unable to test it but it is very similar to the real one)
import socket
HOST = '192.168.1.1'
PORT=5003
s = socket.socket()
s.bind((HOST, PORT))
s.listen(1)
c,addr = s.accept()
while True:
data = c.recv(1024)
if not data:
break
c.close()
You need to wait for the thread to end. Otherwise it's probably going to make the script hang. Why? Well the thread is separate from the GUI thread, so it doesn't get killed just because you closed down your wxPython application. Here is what I would recommend:
def close_window(self, event):
self.Thread1.join()
self.Destroy()
This makes the script wait for the thread to finish before closing the application. If you want the frame to disappear, then you should call self.Hide() before the join. Another method would be to put some logic in your thread where you can send it a message that tells it the application is shutting down, so the thread needs to abort.
You should probably check out the following Stack answer:
Is there any way to kill a Thread in Python?

Tkinter Button disable does not work

Hi i am trying to disable a button , so that the command event does not work for some time .How can i make the button disabled for some time and then later reenable it , to get the callback function .
#! /usr/bin/python
from Tkinter import *
import Tkinter as tk
import time
class MyFrame(Frame):
def __init__(self, master):
Frame.__init__(self, master)
self.b1 = Button(self, text="Press Me!",command = self.callback)
self.count=0
self.but_flag=0
self.b1.grid()
def callback(self):
self.b1['state'] = DISABLED
for k in range(5):
time.sleep(1)
print k
self.b1['state'] = NORMAL
mainw = Tk()
mainw.f = MyFrame(mainw)
mainw.f.grid()
mainw.mainloop()
The problem is that the sleep in your callback function is blocking the UI from refreshing. Instead of using sleep, you could schedule the re-enabling of the button using after.
def callback(self):
self.b1['state'] = DISABLED
self.after(3000, self.enable)
def enable(self):
self.b1['state'] = NORMAL
But if you do any long-running task in callback, this will still freeze the UI.
Another alternative would be to create a worker thread for doing the actual work. This way, the UI thread is not blocked and the UI will be updated and the button deactivated/activated.
def callback(self):
threading.Thread(target=self.do_actual_work).start()
def do_actual_work(self):
self.b1['state'] = DISABLED
for i in range(5):
print i
time.sleep(1)
self.b1['state'] = NORMAL
Of course, you could also just add self.b1.update() after the disabling line to update the Button widget to its disabled state, but this will still leave the UI frozen until the method is finished.

Exit button doesn´t work - wxpython

I developed a GUI with wxGlade and its still working. But to start the GUI - I coded a script with some choices.
So everything works but when I push the red button with the "x" to close the window - the application doesn´t stop.
I made a method, called by a separate exit button, which calls an exit-function in my script. But normally the users are using the close button (red button with X) so my method is not being used to close the window and the window doesn't get closed ultimately.
This is the exit-function.
def stopExport(self, event): # wxGlade: MyFrame.<event_handler>
self.Close() # close the Frame
from ExportManager import Exportmanager # import the exit function
Exportmanager().exit() # call it
How can I use this function with the red button with the "x"?
As per my understanding of your question, your application is not closing when you click on the close button (The red button with X on the right top corner.)
By default when you click the close button your application should close. In your case it seems to me that you have bind the EVT_CLOSE to some method, which has no code in it to close the app window.
For eg. consider the code snippet below, I have intentionally bind the EVT_CLOSE event to a method named as closeWindow(). This method does nothing that is why I have the pass keyword there. Now if you execute the code snippet below you can see that the app window won't close.
Code:
import wx
class GUI(wx.Frame):
def __init__(self, parent, id, title):
screenWidth = 500
screenHeight = 400
screenSize = (screenWidth,screenHeight)
wx.Frame.__init__(self, None, id, title, size=screenSize)
self.Bind(wx.EVT_CLOSE, self.closeWindow) #Bind the EVT_CLOSE event to closeWindow()
def closeWindow(self, event):
pass #This won't let the app to close
if __name__=='__main__':
app = wx.App(False)
frame = GUI(parent=None, id=-1, title="Problem Demo-PSS")
frame.Show()
app.MainLoop()
So, in order to close the app window you need to change the closeWindow(). For eg: Following code snippet will use the Destroy() close the app window when you click on the close button.
import wx
class GUI(wx.Frame):
def __init__(self, parent, id, title):
screenWidth = 500
screenHeight = 400
screenSize = (screenWidth,screenHeight)
wx.Frame.__init__(self, None, id, title, size=screenSize)
self.Bind(wx.EVT_CLOSE, self.closeWindow) #Bind the EVT_CLOSE event to closeWindow()
def closeWindow(self, event):
self.Destroy() #This will close the app window.
if __name__=='__main__':
app = wx.App(False)
frame = GUI(parent=None, id=-1, title="Problem Demo-PSS")
frame.Show()
app.MainLoop()
I hope it was useful.