Getting pyqt application focus for popup menu to disappear when clicking away from it - menu

Let me quickly explain the background to this. I'm developing a custom menu system inside a 3D application called Softimage XSI. It has a PyQt application object created already and ProcessEvents is being called a certain number of times every second so that PyQt applications can exist in a non-modal state within XSI.
To implement the menu, I've got a webpage embedded in a toolbar which is calling a plugin for XSI that I've written to show a PyQt menu. This all works fine (albeit, slightly contrived!).
The issue is that when I show the menu, it won't disappear when I click away from it. If I move the mouse over the menu, and then click away from it, it will disappear. It's only when it first pops up.
I've tried everything I can think of. Here's a list:
Using QtGui.qApp.installEventFilter(menu) to try and catch the mousepressed signal. It never gets triggered. I suspect the application itself isn't receiving the click.
Using menu.raise_() makes no difference
Neither does QtGui.qApp.setActiveWindow(menu)
Or menu.setFocus()
I've also tried:
event = QtGui.QMouseEvent(QtCore.QEvent.MouseMove, pos, QtCore.Qt.NoButton, QtCore.Qt.NoButton, QtCore.Qt.NoModifier)
QtGui.qApp.sendEvent(menu, event)
I had a go writing my own QEventLoop, but it just crashed XSI. I suspect trying to run a modal loop inside the other one probably isn't a legal thing to do. Either that, or I really don't know what I'm doing (equally probable)
The only thing I have partial success with is using grabMouse(). This is what makes the menu close if I click away from the menu (only after the mouse has passed over the menu once), but I have to call it a couple of times for it to "stick".
So this is my code at the moment:
class MyMenu (QtGui.QMenu):
def __init__(self, parent = None):
QtGui.QMenu.__init__(self, parent)
self.grabbed=2
def getMouse(self):
if self.grabbed>0:
self.grabMouse()
self.grabbed-=1
def paintEvent(self, event):
QtGui.QMenu.paintEvent(self, event)
self.getMouse()
def hideEvent(self, event):
self.releaseMouse()
def ShowMenu():
menu = MyMenu()
menu.addAction("A")
menu.addAction("B")
menu.addAction("C")
submenu = MyMenu()
submenu.addAction("D")
submenu.addAction("E")
submenu.addAction("F")
menu.addMenu(submenu)
menu.setTearOffEnabled(True)
menu.setStyleSheet("font: 8pt \"Sans Serif\";")
submenu.setStyleSheet("font: 8pt \"Sans Serif\";")
submenu.setTitle("Window")
submenu.setTearOffEnabled(True)
pos = QtGui.QCursor.pos()
pos.setX(105)
menu.popup(pos)
#Prevent garbage collection
QtGui.XSIMenu=menu
QtGui.XSISubMenu=submenu
#Desperate acts!
menu.raise_()
QtGui.qApp.setActiveWindow(menu)
menu.setFocus()
Any thoughts or random suggestions would be very gratefully received as this is driving me nuts! Don't be afraid to suggest modifications to stuff I've already tried, as I'm relatively new to PyQt and I may well have missed something.
Many thanks,
Andy

Just before calling popup with self.trayMenu.popup(QtGui.QCursor.pos()), call self.trayMenu.activateWindow(). Putting activateWindow before popup makes the left-click menu work the same as the right-click menu and it goes away when you click elsewhere. :)

Related

FlxButtonPlus not responding to anything inside of game's pause menu

I am using the haxeflixel game framework to create a game and I was making a pause menu. I tried using the default FlxButton but the collision with the mouse never lined up properly. So I tried using FlxButtonPlus because it had some cool hooks but the button will not respond to any inputs. here is my code declaring the button in the pause menu
final unpausebut = new FlxButtonPlus((FlxG.width / 2) - 80,(FlxG.height / 2) - 10,unpause,"unpause",48,16); unpausebut.loadButtonGraphic(buttonNormal,buttonHovered); add(unpausebut);
Here is a picture of the pause menu itself so far.
but the button doesn't seem to respond to anything. I am also getting no errors inside of the terminal. unpause is a function inside of the code that also closes the substate. Can anybody help?

Are InputComponents and Dialogs incompatible?

In the simple Dialog below:
// choice of layout has no impact:
Container cont=new Container(new TextModeLayout(3, 1));
//Container cont=new Container(new BoxLayout(BoxLayout.Y_AXIS));
TextComponent firstName=new TextComponent().label("First Name").text(person.firstname);
TextComponent lastName=new TextComponent().label("Last Name").text(person.lastname);
TextComponent cost=new TextComponent().label("Cost per Session").text(person.getCostString());
cost.getField().setConstraint(TextArea.DECIMAL);
// NOTE HERE
// doesn't work: // works:
cont.add(firstName); // cont.add(firstName.getField());
cont.add(lastName); // cont.add(lastName.getField());
cont.add(cost); // cont.add(cost.getField());
Dialog.show("Edit Client", cont, new Command(CANCEL), new Command(OK));
Nothing appears in the Dialog unless I add the TextField instead of the TextComponent to my container at the NOTE HERE comment. This means I lose the nice appearance of the labelled input fields (yes I know I could label them myself, but they wouldn't look as good on different devices). My choice of layout manager at the top does not affect this issue (I've tried several). I can't find evidence online to conclude there's an incompatibility here, adding TextComponents and other InputComponents works fine on a Form, just not in a Dialog.
I'm having the same problem in another Dialog that uses PickerComponents. The PickerComponent doesn't appear unless I add the Picker itself, and then the Picker spawned from a Dialog looks all wrong. I'm hoping the simpler code question above will answer this quandary as well.
It's worth noting I've made no theme changes and this problem is noted in both the Android and Apple skins as well as on an actual Android phone. Thanks in advance for any help!
You shouldn't do input in a Dialog as it creates a very problematic user experience. If you would like things to look like they are in a dialog you can use styles and layouts to make a Form feel like a Dialog but you shouldn't use a Dialog.
The reason this fails is a bit complicated but here are the high level problems with using a dialog:
Dialogs don't grow implicitly - This is a huge problem for text input as the component needs space to resize with input and even more so for the animated TextComponent which needs to shift things around. The size of a Dialog is determined when it's shown and that's a big problem
This becomes a bigger problem on Android where the screen resizes during input and distorts the dialog completely. It's one of those things you'll only see on the device because it's really hard to simulate the virtual keyboard.
Scrollability is hard in a Dialog and text components need a scrollable parent so you can scroll between the various edit components
Picker component uses a form of Dialog to show input and this can collide with your dialog
Dialogs are hard to get right for suspend/resume behavior. Suspend/resume happens when the app is minimized or sent to the background. E.g. say you have an incoming call while typing in the dialog. When you go back to the app we want to show the last form. If we show the dialog it will block and we won't know which parent form to show anyway. So when an app is suspended dialogs are just disposed in the default code generated in the main class. It makes more sense.

PyQt: how to create a dialog as a child of another dialog

In PyQt, I have a dialog that spawns another dialog (when you click a button to do so in the first dialog). I want to maintain a strong parent-child relationship, for garbage collection purposes, and to make the .findChild and .findChildren functions usable.
The root of the question may be: how do you use .setParent() but still have the object in question be shown as a separate window, rather than shown within the parent widget?
The 'parent' dialog (actually a container widget within a tab within a dialog) is 'newEntryWidget'. It spawns 'clueDialog' when a signal (not shown here) calls newEntryWidget.quickTextClueAction as a slot. Visually, clueDialog should be a "top level window" with its own banner, its own window attributes (I want to keep it on top of everything else), etc.
class newEntryWidget(QWidget,Ui_newEntryWidget):
def __init__(self,parent,sec=0,formattedLocString='',fleet='',dev='',origLocString='',amendFlag=False,amendRow=None):
QDialog.__init__(self)
self.parent=parent # just an attribute; not the same as setParent
...
...
def quickTextClueAction(self):
self.newClueDialog=clueDialog(self,self.ui.timeField.text(),self.ui.teamField.text(),self.ui.radioLocField.text(),lastClueNumber+1)
self.newClueDialog.show()
class clueDialog(QDialog,Ui_clueDialog):
def __init__(self,parent,t,callsign,radioLoc,newClueNumber):
QDialog.__init__(self)
self.parent=parent # just an attribute; not the same as setParent
...
...
Right now, since I am using self.parent=parent, which is just an attribute and not true "parent/child relationship" in Qt terms, clueDialog does display as a top level window, which is what I want:
But, if I add 'self.setParent(parent)' in the clueDialog init function, I get this:
How can I preserve the top-level-window behavior, and have the real-honest-parent-child-relationship so that .findChild(clueDialog) will work from within the newEntryWidget object?
Ultimately, I want to enforce that the newEntryWidget object should not be closed if it still has and 'child' clueDialogs open.
Instead of calling .setParent, call QDialog.__init__(self, parent) which constructs the clue dialog with a parent from the beginning. Setting it this way allows Qt establishes the parent-child relationship at the beginning of clueDialog's lifetime.
I believe this will fix both your issues: 1) the clue window frame, caption, etc. will be painted, 2) you will be able to iterate for proper children of newEntry.

In Python3/tkinter is there a way to temporarily stop accepting clicks in a Treeview widget?

I have a GUI based in Python 3 and tkinter that has a big ttk.Treeview. I have defined methods for row selection (one click) and opening an advanced info panel (double-click). I need to ensure that, after being double-clicked, for the next one or two seconds, the Treeview state won't be changed by another click. Is it possible to deactivate Treeview mouse bindings, like what we do with buttons?
Doing a little more research, I was able to come up with a solution for this. I just created an empty method that is called when the tree widget is supposed to be inactive. So, we can use something like this to "unbind" all the mouse events and re-bind them a few seconds later, as needed:
def nothing(self, *event):
""" # Hacking moment: A function that does nothing, for those times you need it...
"""
pass
def bind_tree(self):
""" # Bind mouse and keyboard events to their respective functions or methods...
"""
self.tree.bind('<<TreeviewSelect>>', self.selectItem_popup)
self.tree.bind('<Double-1>', self.show_details)
self.tree.bind("<Button-2>", self.popupMenu)
self.tree.bind("<Button-3>", self.popupMenu)
def unbind_tree(self):
""" # Unbind all mouse and keyboard events, by binding them to an empty method...
"""
self.tree.bind('<<TreeviewSelect>>', self.nothing)
self.tree.bind('<Double-1>', self.nothing)
self.tree.bind("<Button-2>", self.nothing)
self.tree.bind("<Button-3>", self.nothing)
Then, in the rest of the code, We only need to call bind_tree() and unbind_tree() as needed.
This worked for me:
tree.bind("<ButtonRelease-1>", my_select_function)
# Do some stuff
tree.unbind("<ButtonRelease-1>")

How to change the function called when I click in the close button on php-gtk?

I load a single instance of a window on php-gtk, I have a button named "Cancel" that hide(); the window, so when the window is needed again I just show();.
But when I click on the close button instead of the cancel button the window is destroyed. Even when I redirect the event (I'm not sure if i'm doing it right) it calls the first(just hide() function) and then the destroy method.
Any idea?
PD: I wouldn't want to destroy and recreate the windows because of php's crappy garbage collector and to be able to maintain previous data without having to refill the whole window(after all is supposed to be a desktop app).
Following the advice here: delete-event.
I changed my code to return TRUE:
function on_multipleCancelButton_activate()
{
global $GladeMultiple;
$MultipleWindow = $GladeMultiple->get_widget('multipleWindow');
$MultipleWindow->hide();
return TRUE;
}
On the GTK designer I linked the delete-event to this function.

Resources