Am building a gui app using wxpython,i have created two separate windows in two different files,i.e app1.py and app2.py,I want to open the 2nd window(app2.py) using a button click on first window(app1.py). How do i achieve this. It would be great if someone can help,Thanks!!
I am assuming you're using wxPython's definition of "window" (i.e. anything that is actually viewable in the GUI), not the regular definition of "separate box with stuff in it and an X button in the corner" (which wxPython calls a "frame")
When I was writing my first wxPython GUI I was faced with a similar probem. I wanted actions in one panel to affect the data shown in another. My first solution was to write methods that blindly passed the request to the panel's parent until it reached the top level (my "main frame", if you will). This worked, but obviously it's a terrible solution.
My second solution was to use wxPython Events, just like catching button presses for a wx.Panel. My idea was I would create a button in one frame and bind wx.EVT_BUTTON with that button's ID in another. I made custom Events and CommandEvents. While trying to implement this solution I discovered that do not propogate and that CommandEvents will only propogate through the parents. So, if left uncaught a CommandEvent will eventually hit your "main frame" but it will never hit the second panel unless that panel happens to be a parent of where the Event came from. Obviously, this won't work for you since the parent can't be hidden but a child be visible. Plus it would leave your main frame cluttered with code and methods to delegate commands.
Finally, I found the answer! wx.lib.pubsub! Basically, you bind the button press in app1.py, just like normal. Now, in the handler you use pubsub to publish a custom message. Next, in the init for app2.py, you use pubsub to subscribe to your custom message (I like having a "message.py" where I declare all my messages as constants). When you subscribe to a message you assign a handler method, just like for events.\
Read this: http://wiki.wxpython.org/WxLibPubSub
Since you said you wanted the window to APPEAR, maybe in the init method for app2.py you call the Hide() method then when you receive the message, call Show().
When I implemented this in my application I was using wxPython2.8. I am now using 2.9 so I don't know if this is still necessary but I found that I need the top import in order to call pubsub.subscribe() and pubsub.sendMessage(). I kept getting errors otherwise. I can't remember how I figured this out, I think I saw it in a code sample and added it in a desperate attempt to make my code work. I suggest you read the documentation I linked above and try to implement your code WITHOUT that import first.
import wx
from wx.lib.pubsub import setupkwargs #I need this to force pubsub to work. I don't know why.
from wx.lib.pubsub import pub
ID_MYBUTTON = wx.NewId()
class App1(wx.Panel):
def __init__(self, parent):
wx.Panel.__init___(self, parent)
button = wx.Button(self, ID_MYBUTTON, "Show App2")
self.Bind(wx.EVT_BUTTON, self.handleButton, id=ID_MYBUTTON
def handleButton(self, event):
pubsub.sendMessage("mybutton.pressed") #send the message
class App2(wx.Window):
def __init__(self, parent):
wx.Window.__init__(self, parent)
self.Hide() #I don't want to be seen yet
pubsub.subscribe(self.gotMessage, "mybutton.pressed") #listen for the message
def gotMessage(self):
self.Show() #Now I want to be seen!
-----EDIT-----
I found this SO question that might help: Creating child frames of main frame in wxPython
Please tell us in detail what you're trying to do? wxPython also has "dialogs" which would be better than a frame if all you're doing is showing the user a message or asking for a bit more information. http://wxpython.org/docs/api/wx.Dialog-class.html
It really depends on what you're trying to accomplish. We can't help you unless you explain what problem you're trying to solve.
-----EDIT AGIAN-----
It's looking like the person asking the question did indeed want a wx.Dialog. For a tutorial, see http://zetcode.com/wxpython/dialogs/ Also see Python WX - Returning user input from wx Dialog
Related
I'm working on a custom cross platform UI library that needs a synchronous "ShowPopup" method that shows a popup, runs an event loop until it's finished and automatically cancels when clicking outside the popup or pressing escape. Keyboard, mouse and scroll wheel events need to be dispatched to the popup but other events (paint, draw, timers etc...) need to be dispatched to their regular targets while the loop runs.
Edit: for clarification, by popup, I mean this kind of menu style popup window, not an alert/dialog etc...
On Windows I've implemented this fairly simply by calling GetMessage/DispatchMessage and filtering and dispatching messages as appropriate. Works fine.
I've much less experience with Cocoa/OS X however and finding the whole event loop/dispatch paradigm a bit confusing. I've seen the following article which explains how to implement a mouse tracking loop which is very similar to what I need:
http://stpeterandpaul.ca/tiger/documentation/Cocoa/Conceptual/EventOverview/HandlingMouseEvents/chapter_5_section_4.html
but... there's some things about this that concern me.
The linked article states: "the application’s main thread is unable to process any other requests during an event-tracking loop and timers might not fire". Might not? Why not, when not, how to make sure they do?
The docs for nextEventMatchingMask:untilDate:inMode:dequeue: states "events that do not match one of the specified event types are left in the queue.". That seems a little odd. Does this mean that if an event loop only asks for mouse events then any pressed keys will be processed once the loop finishes? That'd be weird.
Is it possible to peek at a message in the event queue without removing it. eg: the Windows version of my library uses this to close the popup when it's clicked outside, but leaves the click event in the queue so that clicking outside the popup on a another button doesn't require a second click.
I've read and re-read about run loop modes but still don't really get it. A good explanation of what these are for would be great.
Are there any other good examples of implementing an event loop for a popup. Even better would be pseudo-code for what the built in NSApplication run loop does.
Another way of putting all this... what's the Cocoa equivalent of Windows' PeekMessage(..., PM_REMOVE), PeekMessage(..., PM_NOREMOVE) and DispatchMessage().
Any help greatly appreciated.
What exactly is a "popup" as you're using the term? That term means different things in different GUI APIs. Is it just a modal dialog window?
Update for edits to question:
It seems you just want to implement a custom menu. Apple provides a sample project, CustomMenus, which illustrates that technique. It's a companion to one of the WWDC 2010 session videos, Session 145, "Key Event Handling in Cocoa Applications".
Depending on exactly what you need to achieve, you might want to use an NSAlert. Alternatively, you can use a custom window and just run it modally using the -runModalForWindow: method of NSApplication.
To meet your requirement of ending the modal session when the user clicks outside of the window, you could use a local event monitor. There's even an example of just such functionality in the (modern, current) Cocoa Event Handling Guide: Monitoring Events.
All of that said, here are (hopefully no longer relevant) answers to your specific questions:
The linked article states: "the application’s main thread is unable to process any other requests during an event-tracking loop and
timers might not fire". Might not? Why not, when not, how to make
sure they do?
Because timers are scheduled in a particular run loop mode or set of modes. See the answer to question 4, below. You would typically use the event-tracking mode when running an event-tracking loop, so timers which are not scheduled in that mode will not run.
You could use the default mode for your event-tracking loop, but it really isn't a good idea. It might cause unexpected re-entrancy.
Assuming your pop-up is similar to a modal window, you should probably use NSModalPanelRunLoopMode.
The docs for nextEventMatchingMask:untilDate:inMode:dequeue:
states "events that do not match one of the specified event types are
left in the queue.". That seems a little odd. Does this mean that if
an event loop only asks for mouse events then any pressed keys will be
processed once the loop finishes? That'd be weird.
Yes, that's what it means. It's up to you to prevent that weird outcome. If you were to read a version of the Cocoa Event Handling Guide from this decade, you'd find there's a section on how to deal with this. ;-P
Is it possible to peek at a message in the event queue without removing it. eg: the Windows version of my library uses this to close
the popup when it's clicked outside, but leaves the click event in the
queue so that clicking outside the popup on a another button doesn't
require a second click.
Yes. Did you notice the "dequeue:" parameter of nextEventMatchingMask:untilDate:inMode:dequeue:? If you pass NO for that, then the event is left in the queue.
I've read and re-read about run loop modes but still don't really get it. A good explanation of what these are for would be great.
It's hard to know what to tell you without knowing what you're confused about and how the Apple guide failed you.
Are you familiar with handling multiple asynchronous communication channels using a loop around select(), poll(), epoll(), or kevent()? It's kind of like that, but a bit more automated. Not only do you build a data structure which lists the input sources you want to monitor and what specific events on those input sources you're interested in, but each input source also has a callback associated with it. Running the run loop is like calling one of the above functions to wait for input but also, when input arrives, calling the callback associated with the source to handle that input. You can run a single turn of that loop, run it until a specific time, or even run it indefinitely.
With run loops, the input sources can be organized into sets. The sets are called "modes" and identified by name (i.e. a string). When you run a run loop, you specify which set of input sources it should monitor by specifying which mode it should run in. The other input sources are still known to the run loop, but just ignored temporarily.
The -nextEventMatchingMask:untilDate:inMode:dequeue: method is, more or less, running the thread's run loop internally. In addition to whatever input sources were already present in the run loop, it temporarily adds an input source to monitor events from the windowing system, including mouse and key events.
Are there any other good examples of implementing an event loop for a popup. Even better would be pseudo-code for what the built in
NSApplication run loop does.
There's old Apple sample code, which is actually their implementation of GLUT. It provides a subclass of NSApplication and overrides the -run method. When you strip away some stuff that's only relevant for application start-up or GLUT, it's pretty simple. It's just a loop around -nextEventMatchingMask:... and -sendEvent:.
Right now I'm working on a program using wxPython, and I want to make a GUI in which under certain conditions, the widgets become inactive and the user can't interact with them. I'm pretty sure I have seen this in programs before, but I can't come up with a specific example. Is there a way to do this in wxPython? I have no idea what the technical name for this is, so even giving me that would be helpful.
When you say "the objects", what do you mean? If you mean a wx Frame, then you can call Frame.Freeze() to disable the frame, and Frame.Thaw() to unfreeze it. If you want to create a new dialog that must be interacted with and make all background windows unuseable, you can call Dialog.ShowModal(). Finally, many widgets have a Widget.Enable() function, to which you can pass True or False depending on if you want to enable it or not.
I am trying to write a function to dynamically resize an image displayed in a tkinter window.
Therefore I bound this function to the Configure event:
connroot.bind( "<Configure>", connresiz)
My problems are:
That the connresiz() function gets called 3 times (why 3?) at program start, and
More troublesome, that dynamically resizing the window calls the function continuously as I drag the mouse! How can avoid this?
I thought about checking at the simultaneous presence of a <Configure> and <ButtonRelease-1> events, but I don't know how to code it.
1) We don't know that, since we can't see your code...
2) Short answer is: you can't, because that's exactly what <Configure> event does! Long answer, you can, with a little trick/hack. Since anytime the window is changing, it will call all the binded functions to <Configure>, and the same happens anytime as the mouse button released (right after the last <Configure> call) we can create a flag/switch which will tell us, if the window was "configured" then we can check that switch anytime the mouse button is released, and switch it back to the default value after we ran some actions.
So if you want the image to resized only, when the mouse was released and the window was changed this is the code you need:
from tkinter import *
class Run:
def __init__(self):
self.root = Tk()
self.clicked = False
self.root.bind('<ButtonRelease-1>', self.image_resize)
self.root.bind('<Configure>', lambda e: self.click(True))
def image_resize(self, event):
if self.clicked:
print("I'm printed after <Configure>.") # the action goes here!
self.click(False)
def click(self, value):
self.clicked = value
app = Run()
app.root.mainloop()
According to the official tk documentation, <Configure> events fire "whenever its size, position, or border width changes, and sometimes when it has changed position in the stacking order." This can happen several times during startup.
It is called continuously while you resize the window because the size of the widget is changing. That's what it's defined to do. You can't prevent it from being called, though you can certainly modify what you do in the callback. For example, you could delay resizing the image until you've not received another <Configure> event for a second or two -- which likely means the user has stopped interactive resizing.
I am learning PyQt4 (I am using version 4.4.4) and I'm pretty new to Python (Python 2.5). I have a GUI with a QListWidget and a QPushButton. I want a user to be able to click to select an entry in the list and then click the QPushButton and have the selected entry go away (be deleted from the QList). I've been banging my head against this problem for over a week and I would deeply appreciate some help.
Currently, my GUI comes up and I can select different list items (only one at a time right now), but when I click the QPushButton, nothing happens. The selection color goes from blue to grey, but the entry is not removed. No error is shown in Command Prompt (Windows 7).
I have defined a function, remove(),which I am using as the slot for the QPushButton. I believe the QPushButton.connect is defined correctly for a Qt Signal to Python Slot, based on what I've seen of answers to similar problems, but the items are not being deleted. However, the remove function is not even being triggered. I have a print statement within the function, but it is not being called when I click the QPushButton, which is how I know that the function is not being called.
Here is my most recent code: (I read a very rant-y post on meta-SO about big blocks of code, so I've cut this down to the bits I think are relevant: the list creation, the button creation and the remove function, which I'm trying to use as a slot. I've left in comments that indicate what the other sections are, so if you think I've left out something that could help, let me know and I'll add it back in)
class questionGUI(QtGui.QWidget):
#This class is the window of the gui.
def __init__(self):
super(questionGUI,self).__init__()
#Layout
grid = QtGui.QGridLayout()
grid.setSpacing(10)
#Labels Needed
...
#Question List
self.qList = QtGui.QListWidget()
#print self.qList
self.qList.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
entries = ['[Pick Image] <Default>','[Slider Question] <Default>', '[Comment Box] <Default>']
for i in entries:
item = QtGui.QListWidgetItem(i)
self.qList.addItem(item)
#Type select
...
#Text insert Needed
...
#Buttons Needed
deleteButton = QtGui.QPushButton('Delete Question')
deleteButton.connect(deleteButton,QtCore.SIGNAL('itemClicked(clicked)'),lambda: self.remove)
addQuestionButton = QtGui.QPushButton('Add Question')
...
doneButton = QtGui.QPushButton('Done')
...
#Parameters Needed
...
#Layout Placement and Window dimensions
...
def addQuestion(self):
...
def remove(self):
print 'remove triggered'
print self.qList.currentItem()
self.qList.removeItemWidget(self.qList.currentItem())
...
I tried to post an image, but I don't have enough reputation. If you think an image would be useful, let me know and I can send it to you.
You mixed the signals:
deleteButton.connect(deleteButton,QtCore.SIGNAL('itemClicked(clicked)'),lambda: self.remove)
deleteButton is a QPushButton, but itemClicked(clicked) looks like the signal from QListWidget with wrong signature. Since, QPushButton doesn't have this signal, no connection is made. Qt doesn't raise errors for failed connections, but .connect method has a bool return value indicating success/failure of the attempted connection.
Also, lambda: self.remove as a slot doesn't make sense. Slot should be a callable that is called upon signal emit. Sure, lambda creates a function, but all you do is reference the method self.remove. lambda will be called, self.remove not. Just self.remove as a slot is enough.
You should use clicked() signal (or clicked(bool), if you care about the checked value) from button:
deleteButton.connect(deleteButton, QtCore.SIGNAL('clicked()'), self.remove)
Edit
Another problem: Your remove method doesn't do what you want. removeItemWidget doesn't remove the item, it removes the widget inside the item (if you set one). It's counterpart to setItemWidget.
You should use takeItem to remove items.
def remove(self):
self.qList.takeItem(self.qList.currentRow())
I would like to connect a resize event on a graphics view to a function using PyQt. I added the QGraphicsView to the GUI using QtCreator. I have tried a few variations without success:
gv.pyqtConfigure(resize=self.printR)
QtCore.QObject.connect(gv,QtCore.SIGNAL("resized()"),self.printR)
gv.resize.connect(self.printR)
However none of the above work (I have tried using many variations of the event name).
What is the correct way of doing this?
As an expanded question, is there a definitive list anywhere of all the signals available to different widgets in Qt, i.e. all the possible values that could be passed to SIGNAL(), or the "signal" attributes available to a widget (e.g. button.clicked.connect())?
The PyQt class reference is the best place to look for signals and class methods.
Connecting simple signals can be done like this:
self.connect(senderObject, QtCore.SIGNAL("signalName()"), self.slotMethod)
As far as I can tell, however, most widgets do not normally emit a signal when they are resized. In order to do this, you would need to re-implement the QGraphicsView class and its resizeEvent method, modifying it to emit such a signal, like so:
from PyQt4.QtGui import QGraphicsView
from PyQt4.QtCore import SIGNAL
class customGraphicsView(QGraphicsView):
def __init__(self, parent=None):
QGraphicsView.__init__(self, parent)
def resizeEvent(self, evt=None):
self.emit(SIGNAL("resize()"))
Using Qt Designer, you should be able to turn your existing QGraphicsView widget into a placeholder for such a custom widget by right-clicking on it and selecting Promote to.... For help, look here.
I hope this sufficiently answers your question.