4

I have a QMainWindow with a main "display" widget, and several minor widgets. I'd like the ability to toggle the minor widgets on/off when the mouse enters or leaves the window.

I can achieve this basic functionality by implementing enterEvent and leaveEvent with calls to show/hiding for the unessential widgets. However, Qt4's default behavior is to leave the QMainWindow geometry fixed, and resize the important widget. I would rather maintain the geometry of this widget, and move/resize the QMainWindow as necessary. Is this possible?

Here's a simplified example in PyQt4

from PyQt4.QtGui import *
from PyQt4.QtCore import *

app = QApplication([''])

class MyWidget(QWidget):
    def __init__(self, parent=None):
        super(MyWidget, self).__init__(parent)

        layout = QVBoxLayout()
        self.setLayout(layout)
        self.main = QPushButton("major")
        self.main.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.minor = QPushButton("minor")
        layout.addWidget(self.main)
        layout.addWidget(self.minor)

    def enterEvent(self, event):
        self.minor.show()

    def leaveEvent(self, event):
        self.minor.hide()

mw = QMainWindow()
mw.setCentralWidget(MyWidget())
mw.show()

app.exec_()

Instead of the "major" button growing/shrinking, I would like the boundaries of MyWidget to change to wrap around this button.

ChrisB
  • 4,628
  • 7
  • 29
  • 41

2 Answers2

1

I hacked on your example on Mac OS X with PySide. After trying for quite a while, I found the following solution:

from PySide.QtGui import *
from PySide.QtCore import *

app = QApplication([''])

class MyWidget(QWidget):
    def __init__(self, parent=None):
        super(MyWidget, self).__init__(parent)

        layout = QVBoxLayout()
        self.setLayout(layout)
        self.main = QPushButton("major")
        self.main.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.minor = QPushButton("minor")
        layout.addWidget(self.main)
        layout.addWidget(self.minor)

    def resizeEvent(self, event):
        self.appendix = self.minor.frameGeometry().height() + 2
        if self.minor.isVisible():
            self.varheight = mw.height() - self.appendix
        else:
            self.varheight = mw.height()

    def enterEvent(self, event):
        self.minor.show()
        mw.resize(mw.width(), self.varheight + self.appendix)

    def leaveEvent(self, event):
        self.minor.hide()
        mw.resize(mw.width(), self.varheight)

mw = QMainWindow()
mw.setCentralWidget(MyWidget())
mw.show()

app.exec_()

This is not perfect, since the initial state does not check the mouse position. Also the magic "+ 2" should probably be computable from some other metrics of the widget geometry. But for the example it should be ok.

This implementation took several hours to get right. It was crucial to make sure that all computation is in a fixed state. Otherwise the layout engine will produce hard to understand results or even endless loops.

resizeEvent grabs the right values out of the widget sizes, but nothing else. The enterEvent and leaveEvent then use these values and do the resizing.

My variables are not initialized. You can do that, but a first resize comes earlier than our other events.

Christian Tismer
  • 1,305
  • 14
  • 16
0

You could try this code. Instead of subclassing QWidget, you could directly subclass QMainWindow.

from PyQt4.QtGui import *
from PyQt4.QtCore import *

class MyWindow( QMainWindow ):
    def __init__( self, parent = None ):
        QMainWindow.__init__( self, parent )

        self.widget = QWidget( self )
        lyt = QVBoxLayout()

        self.main = QPushButton( "major" )
        self.minor = QPushButton( "minor" )

        if not self.underMouse() :
            self.minor.hide()
            self.setMaximumHeight( self.main.sizeHint().height() )

        else :
            self.setMaximumHeight( self.minor.sizeHint().height() + self.main.sizeHint().height() )

        lyt.addWidget( self.main )
        lyt.addWidget( self.minor )

        self.widget.setLayout( lyt )
        self.setCentralWidget( self.widget )

    def enterEvent( self, event ) :

        self.minor.show()
        self.setMaximumHeight( self.minor.sizeHint().height() + self.main.sizeHint().height() )
        self.adjustSize()

        event.accept()

    def leaveEvent( self, event ):

        self.minor.hide()
        self.setMaximumHeight( self.main.sizeHint().height() )
        self.adjustSize()

        event.accept()

if __name__ == '__main__' :

    app = QApplication( [] )

    Gui = MyWindow()
    Gui.show()

    app.exec_()
Marcus
  • 1,685
  • 1
  • 18
  • 33