Proper positioning of popup widgets in PyQt

it haunts me for eons, mainly because of how many combinations of methodologies exist to move widgets and much more. Essentially, I have a simple widget that I would like to pop up in certain areas of my application. The problem is that I can never make her float to where I want her. In addition, I would like to set it up so that I can adjust its “pointer” based on whether it appears to point to the widget in the upper left corner of the application, and, say, to the right.

Ideally, I could place the popup almost next to the edges of the parent widget and snap it based on where it is. Here is what I tried.

from PyQt4.QtCore import * from PyQt4.QtGui import * import sys class popup(QWidget): def __init__(self, parent = None, widget=None): QWidget.__init__(self, parent) layout = QGridLayout(self) button = QPushButton("Very Interesting Text Popup. Here an arrow ^") layout.addWidget(button) self.move(widget.rect().bottomLeft()) class Window(QWidget): def __init__(self): QWidget.__init__(self) self.button = QPushButton('Hit this button to show a popup', self) self.button.clicked.connect(self.handleOpenDialog) self.button.move(250, 50) self.resize(600, 200) def handleOpenDialog(self): self.popup = popup(self, self.button) self.popup.show() if __name__ == '__main__': app = QApplication(sys.argv) win = Window() win.show() sys.exit(app.exec_()) 

This code generates a button that is randomly located in the middle of the widget. What I'm trying to get in this example is a pop-up window that appears under the button with its “rotary” button in the upper right corner, so that the arrow in the pop-up button will point to the lower right corner of the widget. However, it appears in the upper left corner of the window. In all my mess with .move, .setGeometry, and playing with QRect, I can't figure it out for life. Great pleasure to someone who can lend a hand. Thanks!

+4
source share
2 answers

I know this is old, but I have been looking for it recently and this is the best answer; I have a useful addition (for those who are still looking for this recipe!)

I implemented it as a mixin, which I think gives a lot of flexibility to your dialogs:

 class PopupDialogMixin(object): # will not work (with PySide at least) unless implemented as 'new style' class. Ie inherit from object def makePopup(callWidget): """ Turns the dialog into a popup dialog. callWidget is the widget responsible for calling the dialog (eg a toolbar button) """ self.setContentsMargins(0,0,0,0) self.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.Popup) self.setObjectName('ImportDialog') # Move the dialog to the widget that called it point = callWidget.rect().bottomRight() global_point = callWidget.mapToGlobal(point) self.move(global_point - QtCore.QPoint(self.width(), 0)) 

Your own dialog box then inherits from both QtCore.QDialog and PopupDialogMixin . This gives you the option to use the dialog normally or make it a popup dialog. eg:

 dlg = MyDialog(self) dlg.makePopup(self.myButton) 

I think that implementing it as mixin gives a number of advantages:

  • No need to write popup code for every custom dialog you want as a popup
  • The "default" dialogue behavior is preserved - for example, if you want to reuse it somewhere else as a "normal" dialog, you just use it as usual,
  • No need to pass anything extra to __init__ except parent .
+1
source

Here you go - comments explain the logic behind it - since the question is an example about positioning, I left the rest of the code the same except for the pop-up class, but just mentioned the reason for my pet peeve - you shouldn't import * (when- either), but especially with something bigger than PyQt4.QtCore / QtGui ...

  from PyQt4.QtCore import * from PyQt4.QtGui import * import sys class popup(QWidget): def __init__(self, parent = None, widget=None): QWidget.__init__(self, parent) layout = QGridLayout(self) button = QPushButton("Very Interesting Text Popup. Here an arrow ^") layout.addWidget(button) # adjust the margins or you will get an invisible, unintended border layout.setContentsMargins(0, 0, 0, 0) # need to set the layout self.setLayout(layout) self.adjustSize() # tag this widget as a popup self.setWindowFlags(Qt.Popup) # calculate the botoom right point from the parents rectangle point = widget.rect().bottomRight() # map that point as a global position global_point = widget.mapToGlobal(point) # by default, a widget will be placed from its top-left corner, so # we need to move it to the left based on the widgets width self.move(global_point - QPoint(self.width(), 0)) class Window(QWidget): def __init__(self): QWidget.__init__(self) self.button = QPushButton('Hit this button to show a popup', self) self.button.clicked.connect(self.handleOpenDialog) self.button.move(250, 50) self.resize(600, 200) def handleOpenDialog(self): self.popup = popup(self, self.button) self.popup.show() if __name__ == '__main__': app = QApplication(sys.argv) win = Window() win.show() sys.exit(app.exec_()) 
0
source

All Articles