PyQT custom widget fixed as square

I'm developing a custom widget (inheriting from QWidget) to use as a control. How can I fix the aspect-ratio of the widget to be square, but still allow it to be resized by the layout manager when both vertical and horizontal space allows?

I know that I can set the viewport of the QPainter so that it only draws in a central square area, but that still allows the user to click either side of the drawn area.

Answers


Apparently, my old approach had many flaws and some of the things in it are meaningless:

  • QSizePolicy.setHeightForWidth and QSizePolicy.setWidthForHeight are exclusive... and setWidthForHeight doesn't even work in most cases...
  • There is no such thing as QWidget.widthForHeight, so defining it doesn't really override anything.

It seems like there is no universal way to keep a widget square under all circumstances. You must choose one:

  • Make its height depend on its width:
class MyWidget(QWidget):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        policy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        policy.setHeightForWidth(True)
        self.setSizePolicy(policy)
    ...
    def heightForWidth(self, width):
        return width
    ...
  • Make its minimal width depend on its height:
class MyWidget(QWidget):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
    ...
    def resizeEvent(self, e):
        setMinimumWidth(height())
    ...

Such a widget will be kept square as long as there is such a possibility.

For other cases you should indeed consider changing the viewport, as you mentioned. Mouse events shouldn't be that much of a problem, just find the center of the widget (divide dimensions by 2), find min(width, height) and go from there. You should be able to validate the mouse events by coordinate. It is nice to call QMouseEvent.accept, only if the event passed the validation and you used the event.


I'd go with BlaXpirit's method, but here's an alternative that I've used before.

If you subclass the custom widget's resiseEvent() you can adjust the requested size to make it a square and then set the widget's size manually.

import sys
from PyQt4 import QtCore, QtGui

class CustomWidget(QtGui.QFrame):
    def __init__(self, parent=None):
        QtGui.QFrame.__init__(self, parent)

        # Give the frame a border so that we can see it.
        self.setFrameStyle(1)

        layout = QtGui.QVBoxLayout()
        self.label = QtGui.QLabel('Test')
        layout.addWidget(self.label)
        self.setLayout(layout)

    def resizeEvent(self, event):
        # Create a square base size of 10x10 and scale it to the new size
        # maintaining aspect ratio.
        new_size = QtCore.QSize(10, 10)
        new_size.scale(event.size(), QtCore.Qt.KeepAspectRatio)
        self.resize(new_size)

class MainWidget(QtGui.QWidget):
    def __init__(self, parent=None):
       QtGui.QWidget.__init__(self, parent)

       layout = QtGui.QVBoxLayout()
       self.custom_widget = CustomWidget()
       layout.addWidget(self.custom_widget)
       self.setLayout(layout)


app = QtGui.QApplication(sys.argv)
window = MainWidget()
window.show()
sys.exit(app.exec_())

Need Your Help

Is there any way to control where the output file of a T4 is generated to?

c# silverlight code-generation t4

I would like to generate C# code for silverlight but I dont have access to some dll's that would make my T4 code more powerful. Is there anyway to have my T4 template in a C# Class Library and have...

Check type of items in list are all same and perform task

python string list types integer

I want to be able to create a function that takes a list, checks if every item in the list is of a certain type (one item at a time) and if so, perform the calculation. For this particular function...