How to select a region with QRubberBand on a QLabel like in KSnapshot?
Asked Answered
K

1

6

I am writing a screenshot utility with PyQt, and the idea is take a screenshot of the whole desktop, then display it in a QLabel, make the window full screen and user selects a region by mouse.

Is it possible to do this efficiently with a QLabel? I want the rubber band to stay on screen and it still can be tweaked. In this case, would I have to use QGraphicsScene?

Desired effect: http://gfycat.com/SkinnyObeseAquaticleech

enter image description here

here is what I have so far

import sys
from PyQt4 import QtGui, QtCore
from PyQt4.QtCore import Qt, QPoint, QRect, QSize
from PyQt4.QtGui import QPixmap, QApplication, QLabel, QRubberBand


class MyLabel(QLabel):

    def __init__(self, parent=None):

        QLabel.__init__(self, parent)
        self.rubberBand = QRubberBand(QRubberBand.Rectangle, self)
        self.origin = QPoint()

    def mousePressEvent(self, event):

        if event.button() == Qt.LeftButton:

            self.origin = QPoint(event.pos())
            self.rubberBand.setGeometry(QRect(self.origin, QSize()))
            self.rubberBand.show()

    def mouseMoveEvent(self, event):

        if not self.origin.isNull():
            self.rubberBand.setGeometry(
                QRect(self.origin, event.pos()).normalized())

    def mouseReleaseEvent(self, event):

        if event.button() == Qt.LeftButton:
            self.rubberBand.hide()


class mainUI(QtGui.QWidget):

    def __init__(self):
        super(mainUI, self).__init__()
        self.initUI()

    def initUI(self):

        layout = QtGui.QVBoxLayout(self)

        label = MyLabel(self)
        pixmap = QPixmap.grabWindow(app.desktop().winId())
        label.setPixmap(pixmap)
        layout.addWidget(label)

        self.setLayout(layout)

        geometry = app.desktop().availableGeometry()

        self.setFixedSize(geometry.width(), geometry.height())

        # self.setWindowFlags( self.windowFlags() | Qt.FramelessWindowHint)
        self.show()


if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)

    window = mainUI()

    sys.exit(app.exec_())
Kaifeng answered 11/12, 2015 at 9:37 Comment(0)
M
4

Your approach is already quite far and I think it's possible to achieve what you want with a QLabel. I extended your example so that the rubber band stays on screen even after the mouse is released and you can drag the upper left and lower right corners of it.

You could extend it even more to drag the other corners and the sides and showing a label with the size in the middle.

In the picture you see the selection staying without the mouse pressed. enter image description here

from PyQt4 import QtGui, QtCore

class RubberbandEnhancedLabel(QtGui.QLabel):

    def __init__(self, parent=None):
        QtGui.QLabel.__init__(self, parent)
        self.selection = QtGui.QRubberBand(QtGui.QRubberBand.Rectangle, self)

    def mousePressEvent(self, event):
        '''
            Mouse is pressed. If selection is visible either set dragging mode (if close to border) or hide selection.
            If selection is not visible make it visible and start at this point.
        '''

        if event.button() == QtCore.Qt.LeftButton:

            position = QtCore.QPoint(event.pos())
            if self.selection.isVisible():
                # visible selection
                if (self.upper_left - position).manhattanLength() < 20:
                    # close to upper left corner, drag it
                    self.mode = "drag_upper_left"
                elif (self.lower_right - position).manhattanLength() < 20:
                    # close to lower right corner, drag it
                    self.mode = "drag_lower_right"
                else:
                    # clicked somewhere else, hide selection
                    self.selection.hide()
            else:
                # no visible selection, start new selection
                self.upper_left = position
                self.lower_right = position
                self.mode = "drag_lower_right"
                self.selection.show()

    def mouseMoveEvent(self, event):
        '''
            Mouse moved. If selection is visible, drag it according to drag mode.
        '''
        if self.selection.isVisible():
            # visible selection
            if self.mode is "drag_lower_right":
                self.lower_right = QtCore.QPoint(event.pos())
            elif self.mode is "drag_upper_left":
                self.upper_left = QtCore.QPoint(event.pos())
            # update geometry
            self.selection.setGeometry(QtCore.QRect(self.upper_left, self.lower_right).normalized())

app = QtGui.QApplication([])

screen_pixmap = QtGui.QPixmap.grabWindow(app.desktop().winId())

window = QtGui.QWidget()
layout = QtGui.QVBoxLayout(window)
label = RubberbandEnhancedLabel()
label.setPixmap(screen_pixmap)
layout.addWidget(label)
geometry = app.desktop().availableGeometry()
window.setFixedSize(geometry.width(), geometry.height())
window.show()
app.exec_()
Mullen answered 11/12, 2015 at 16:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.