class ResizableRubberBand(QWidget): """ provides a rectangle which is defined by click and drag with the mouse and can be resized afterwards """ def __init__(self, parent=None): super(ResizableRubberBand, self).__init__(parent) self.setWindowFlag(Qt.SubWindow) self.setFocusPolicy(Qt.ClickFocus) self.layout = QHBoxLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) self.setCursor(Qt.SizeAllCursor) self.origin = QPoint() 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.hide() self.rubberband.hide() # self.rubberband.show() # self.show() def resizeEvent(self, event): self.rubberband.resize(self.size()) def paintEvent(self, event): super().paintEvent(event) painter = QPainter(self) painter.beginNativePainting() painter.setRenderHint(QPainter.Antialiasing) painter.setRenderHint(QPainter.SmoothPixmapTransform) painter.setPen(QPen(Qt.red, 3)) painter.drawRect(self.rubberband.rect()) painter.setPen(QPen(Qt.red, 1)) x_begin = self.rubberband.x() x_half = self.rubberband.x() + self.rubberband.width() / 2 x_full = self.rubberband.x() + self.rubberband.width() y_begin = self.rubberband.y() y_half = self.rubberband.y() + self.rubberband.height() / 2 y_full = self.rubberband.y() + self.rubberband.height() points = [ QPoint(x_half, y_begin), QPoint(x_half, y_full), QPoint(x_begin, y_half), QPoint(x_full, y_half) ] painter.drawLines(points) painter.endNativePainting() painter.end() # def moveEvent(self, event: QMoveEvent): # self.rubberband.move(event.pos()) def mousePressEvent(self, event): if event.buttons() == Qt.LeftButton: self.origin = event.globalPos() - self.pos() def mouseMoveEvent(self, event): if event.buttons() == Qt.LeftButton: self.move(event.globalPos() - self.origin)
class DynamicNeedleMask(QWidget): """ provides a rectangle with fixed centerline and x-symmetric resizability """ _gripSize = 8 update_mask_signal = Signal() def __init__(self, parent=None): super(DynamicNeedleMask, self).__init__(parent) self.settings = QSettings() self._first_show = True self.setWindowFlag(Qt.SubWindow) self.setFocusPolicy(Qt.ClickFocus) self.layout = QHBoxLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) self.setCursor(Qt.SizeAllCursor) self.setGeometry(self.parent().width() / 2 - 10, 0, 20, self.parent().height()) self._old_geo = self.geometry() self._locked = False self.origin = QPoint(0, 0) self.rubberband = QRubberBand(QRubberBand.Rectangle, self) self.rubberband.move(0, 0) self.hide() self.rubberband.hide() # self.rubberband.show() # self.show() self.sideGrips = [ SideGrip(self, Qt.LeftEdge), SideGrip(self, Qt.TopEdge), SideGrip(self, Qt.RightEdge), SideGrip(self, Qt.BottomEdge), ] # corner grips should be "on top" of everything, otherwise the side grips # will take precedence on mouse events, so we are adding them *after*; # alternatively, widget.raise_() can be used self.cornerGrips = [QSizeGrip(self) for i in range(4)] def get_mask_geometry(self) -> Tuple[int, int, int, int]: """Return the geometry of the mask :returns: the geometry of the mask as x,y,w,h tuple """ return self.geometry().normalized().getRect() def lock(self): """lock resizing """ self._locked = True def unlock(self): """unlock resizing """ self._locked = False def showEvent(self, event): """ custom show event initializes geometry for first show """ if self._first_show: geo = self.load_geo() if geo: self.setGeometry(*geo) else: self.setGeometry(self.parent().width() * 15 / 32, 0, self.parent().width() / 16, self.parent().height()) self._first_show = False @property def gripSize(self): return self._gripSize def setGripSize(self, size): if size == self._gripSize: return self._gripSize = max(2, size) self.updateGrips() def paintEvent(self, event): super().paintEvent(event) painter = QPainter(self) painter.beginNativePainting() painter.setRenderHint(QPainter.Antialiasing) painter.setRenderHint(QPainter.SmoothPixmapTransform) painter.setPen(QPen(Qt.green, 1, Qt.DotLine)) painter.drawRect(self.rubberband.rect()) painter.setPen(QPen(Qt.green, 1)) x_begin = self.rubberband.x() x_half = self.rubberband.x() + self.rubberband.width() / 2 x_full = self.rubberband.x() + self.rubberband.width() y_begin = self.rubberband.y() y_half = self.rubberband.y() + self.rubberband.height() / 2 y_full = self.rubberband.y() + self.rubberband.height() points = [QPoint(x_half, y_begin), QPoint(x_half, y_full)] painter.drawLines(points) painter.endNativePainting() painter.end() def resizeEvent(self, event: QResizeEvent): x, y, w, h = self.geometry().normalized().getRect() if (self._locked): self.setGeometry(*self._old_geo.getRect()) else: # limit size to parent boundaries if (x < 0): # remove width that has been added by leftward dragging if x is at leftmost edge w += x x = max(0, min(x, self.parent().width())) w = min(w, self.parent().width() - x) self.setGeometry(x, y, w, self.parent().height()) self.updateGrips() self.rubberband.resize(self.size()) self._old_geo = self.geometry() self.update_mask_signal.emit() def mousePressEvent(self, event): if event.buttons() == Qt.LeftButton: self.origin = event.globalPos() - self.pos() def mouseMoveEvent(self, event): if event.buttons() == Qt.LeftButton: x, y, w, h = self.geometry().normalized().getRect() new_x = event.globalPos().x() - self.origin.x() # keep inside parent boundaries new_x = max(0, min(new_x, self.parent().width() - w)) self.move(new_x, 0) self.update_mask_signal.emit() def mouseReleaseEvent(self, event): self.update_mask_signal.emit() self.save_geo() return super().mouseReleaseEvent(event) def save_geo(self): self.settings.setValue("needle_mask/geometry", self.geometry().getRect()) def load_geo(self): return self.settings.value("needle_mask/geometry") def updateGrips(self): self.setContentsMargins(*[self.gripSize] * 4) outRect = self.rect() # an "inner" rect used for reference to set the geometries of size grips inRect = outRect.adjusted(self.gripSize, self.gripSize, -self.gripSize, -self.gripSize) # top left self.cornerGrips[0].setGeometry( QRect(outRect.topLeft(), inRect.topLeft())) # top right self.cornerGrips[1].setGeometry( QRect(outRect.topRight(), inRect.topRight()).normalized()) # bottom right self.cornerGrips[2].setGeometry( QRect(inRect.bottomRight(), outRect.bottomRight())) # bottom left self.cornerGrips[3].setGeometry( QRect(outRect.bottomLeft(), inRect.bottomLeft()).normalized()) # left edge self.sideGrips[0].setGeometry(0, inRect.top(), self.gripSize, inRect.height()) # top edge self.sideGrips[1].setGeometry(inRect.left(), 0, inRect.width(), self.gripSize) # right edge self.sideGrips[2].setGeometry(inRect.left() + inRect.width(), inRect.top(), self.gripSize, inRect.height()) # bottom edge self.sideGrips[3].setGeometry(self.gripSize, inRect.top() + inRect.height(), inRect.width(), self.gripSize)