Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 381
thornate Avatar asked Jun 13 '12 04:06

thornate


2 Answers

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.

like image 187
Oleh Prypin Avatar answered Nov 10 '22 09:11

Oleh Prypin


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_())
like image 4
Gary Hughes Avatar answered Nov 10 '22 10:11

Gary Hughes