13

I'd like to make my QRubberband instance user resizeable. I've seen this question here but there isn't a solution.

The use case is that the user can drag out a selection marquee over a photo and then make fine adjustments by dragging on the QRubberband margins to change geometry, or reposition existing geometry by dragging the selection. I have the repositioning implemented but I am wondering about resize handles for changing the geometry of the QRubberband.

Is it possible to use QSizeGrip here? Barring that, is there a standard way of checking for mouse-within-margin type events within Qt or some other way to implement this? This is for a research app that doesn't need or warrant lots of effort on this feature but it would be good to have.

Community
  • 1
  • 1
spring
  • 18,009
  • 15
  • 80
  • 160

2 Answers2

32

Yes, it's possible. Here is the implementation:

Header:

class Resizable_rubber_band : public QWidget {
public:
  Resizable_rubber_band(QWidget* parent = 0);

private:
  QRubberBand* rubberband;
  void resizeEvent(QResizeEvent *);

};

Source:

Resizable_rubber_band::Resizable_rubber_band(QWidget *parent) : QWidget(parent) {
  //tell QSizeGrip to resize this widget instead of top-level window
  setWindowFlags(Qt::SubWindow);
  QHBoxLayout* layout = new QHBoxLayout(this);
  layout->setContentsMargins(0, 0, 0, 0);
  QSizeGrip* grip1 = new QSizeGrip(this);
  QSizeGrip* grip2 = new QSizeGrip(this);
  layout->addWidget(grip1, 0, Qt::AlignLeft | Qt::AlignTop);
  layout->addWidget(grip2, 0, Qt::AlignRight | Qt::AlignBottom);
  rubberband = new QRubberBand(QRubberBand::Rectangle, this);
  rubberband->move(0, 0);
  rubberband->show();
  show();
}

void Resizable_rubber_band::resizeEvent(QResizeEvent *) {
  rubberband->resize(size());
}

Usage: (ui->label is the label used for displaying the image to be cropped)

Resizable_rubber_band* band = new Resizable_rubber_band(ui->label);
band->move(100, 100);
band->resize(50, 50);
band->setMinimumSize(30, 30);

screenshot

Sb0y
  • 65
  • 1
  • 8
Pavel Strakhov
  • 39,123
  • 5
  • 88
  • 127
  • Looks good. Too bad, I do not have votes available for today. – László Papp Sep 28 '13 at 13:01
  • Thanks for the input. Looks promising but I haven't got it working yet. In a new test Qt Application project with just a QLabel, I get the rubberband showing up but that's it. Are there some implied methods I need to implement? I have mouseTracking enable for the QLabel. – spring Sep 28 '13 at 14:47
  • 1
    It works fine to me in a new clean project with Qt5. Complete project sources: https://gist.github.com/Riateche/6743108 Screenshot: http://i.imgur.com/D212BtQ.png – Pavel Strakhov Sep 28 '13 at 15:25
  • Thanks for taking the trouble. Ran your project here but still no success - Qt 5.1.1 under OSX. How odd. I get the rubberband box set to the initial size/position but no resize handles/no interaction. Maybe this is an issue on OSX. – spring Sep 28 '13 at 19:09
13

In case anyone else wanders in here off Google with the intent to use this with PyQt, here's the equivalent Python code to @pavel-strakhov's C++ example and I can confirm that it works for me on *buntu Linux 14.04 LTS with the system-provided Python 3.4 and PyQt 5.2.1.

(With the caveat that my chosen widget theme doesn't know how to rotate the QSizeGrip in the top-left corner, so both corners have identical-looking grips.)

Source:

class ResizableRubberBand(QWidget):
    """Wrapper to make QRubberBand mouse-resizable using QSizeGrip

    Source: http://stackoverflow.com/a/19067132/435253
    """
    def __init__(self, parent=None):
        super(ResizableRubberBand, self).__init__(parent)

        self.setWindowFlags(Qt.SubWindow)
        self.layout = QHBoxLayout(self)
        self.layout.setContentsMargins(0, 0, 0, 0)

        self.grip1 = QSizeGrip(self)
        self.grip2 = QSizeGrip(self)
        self.layout.addWidget(self.grip1, 0, Qt.AlignLeft | Qt.AlignTop)
        self.layout.addWidget(self.grip2, 0, Qt.AlignRight | Qt.AlignBottom)

        self.rubberband = QRubberBand(QRubberBand.Rectangle, self)
        self.rubberband.move(0, 0)
        self.rubberband.show()
        self.show()

    def resizeEvent(self, event):
        self.rubberband.resize(self.size())

Usage:

self.band = ResizableRubberBand(ui.label)
self.band.move(100, 100)
self.band.resize(50, 50)
self.band.setMinimumSize(30, 30)
ssokolow
  • 14,938
  • 7
  • 52
  • 57
  • 4
    I apologize to anyone who thinks this is irrelevant enough to downvote, but it was the *only* Google result I found of any significant relevance to achieving this goal in PyQt, so I thought risking downvotes was worth it to possibly help other PyQt users who wander in. – ssokolow Apr 17 '17 at 02:29
  • Hi! I know it's been a long time since you wrote this but I'm trying to use this code now( it's the only one that I've found) and I don't understand what does the "ui.label" mean under "USAGE". Can you please explain it? Thank you in advance:) – galaxy Jan 04 '22 at 08:58
  • 1
    @galaxy `ui.label` is a `QLabel` displaying an image using `.setPixmap(QPixmap)`. – ssokolow Jan 05 '22 at 06:35