class Window(QLabel): def __init__(self, pixmap, parent=None): QLabel.__init__(self, parent) self.rubberBand = QRubberBand(QRubberBand.Rectangle, self) self.origin = QPoint() self.pixmap = pixmap 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() print(self.rubberBand.geometry()) def resizeEvent(self, event): self.updateScreenshotLabel() # scaledSize = self.pixmap.size() # scaledSize.scale(self.screenshotLabel.size(), Qt.KeepAspectRatio) # if not self.screenshotLabel.pixmap() or scaledSize != self.screenshotLabel.pixmap().size(): # self.updateScreenshotLabel() def updateScreenshotLabel(self): self.setPixmap( self.pixmap.scaled(self.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation))
class Label(QLabel): selection_changed = pyqtSignal(QRect, name="selectionChanged") def __init__(self): QLabel.__init__(self) self.rb = QRubberBand(QRubberBand.Rectangle, self) self.setMouseTracking(True) def mousePressEvent(self, event: QMouseEvent): self.origin = event.pos() self.rb.setGeometry(QRect(self.origin, QSize())) self.rb.show() QLabel.mousePressEvent(self, event) def mouseMoveEvent(self, event: QMouseEvent): if self.rb.isVisible(): self.rb.setGeometry( QRect(self.origin, event.pos()).normalized()) QWidget.mouseMoveEvent(self, event) def mouseReleaseEvent(self, event): if self.rb.isVisible(): self.rb.hide() self.selection_changed.emit(self.rb.geometry()) QLabel.mouseReleaseEvent(self, event)
class Label(QtWidgets.QLabel): def __init__(self): super().__init__() self.origin = QPoint() self.rubberBand = None # def paintEvent(self, event): # qp = QtGui.QPainter(self) # br = QtGui.QBrush(QtGui.QColor(100, 10, 10, 40)) # qp.setBrush(br) # qp.drawRect(QtCore.QRect(self.begin, self.end)) def mousePressEvent(self, event): super(Label, self).mousePressEvent(event) self.origin = event.pos() if not self.rubberBand: self.rubberBand = QRubberBand(QRubberBand.Rectangle, self) print("Rubber!") self.rubberBand.setGeometry(QRect(self.origin, QSize())) self.rubberBand.show() #self.update() def mouseMoveEvent(self, event): self.rubberBand.setGeometry( QRect(self.origin, event.pos()).normalized()) def mouseReleaseEvent(self, event): if self.rubberBand: self.rubberBand.hide()
class WidgetPicker(QWidget): """Widget for letting user point at another widget.""" selected = Signal() def __init__(self): super(WidgetPicker, self).__init__() self.band = QRubberBand(QRubberBand.Rectangle) self.setMouseTracking(True) self.el = QEventLoop() def mousePressEvent(self, ev): self.el.quit() self.widget = QApplication.widgetAt(ev.globalPos()) self.band.hide() def mouseMoveEvent(self, ev): widget = QApplication.widgetAt(ev.globalPos()) if widget: rect = widget.frameGeometry() if widget.parent(): rect.moveTo(widget.parent().mapToGlobal(rect.topLeft())) self.band.setGeometry(rect) self.band.show() else: self.band.hide() def run(self): self.grabMouse() try: self.el.exec_() finally: self.releaseMouse() return self.widget
class QExampleLabel(QLabel): def __init__(self, parentQWidget=None): super(QExampleLabel, self).__init__(parentQWidget) self.initUI() def initUI(self): self.setPixmap(QtGui.QPixmap('input.png')) def mousePressEvent(self, eventQMouseEvent): self.originQPoint = eventQMouseEvent.pos() self.currentQRubberBand = QRubberBand(QRubberBand.Rectangle, self) self.currentQRubberBand.setGeometry( QtCore.QRect(self.originQPoint, QtCore.QSize())) self.currentQRubberBand.show() def mouseMoveEvent(self, eventQMouseEvent): self.currentQRubberBand.setGeometry( QtCore.QRect(self.originQPoint, eventQMouseEvent.pos()).normalized()) def mouseReleaseEvent(self, eventQMouseEvent): self.currentQRubberBand.hide() currentQRect = self.currentQRubberBand.geometry() self.currentQRubberBand.deleteLater() cropQPixmap = self.pixmap().copy(currentQRect) cropQPixmap.save('output.png')
class MiniMapGraphicsView(QGraphicsView): def __init__(self, parent): super().__init__(parent) self._drag_start_pos = None self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.setFixedSize(200, 200) self.viewport().setFixedSize(self.contentsRect().size()) self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setInteractive(False) self.setFocusProxy(parent) self.band = QRubberBand(QRubberBand.Rectangle, self) self.band.hide() def centerOn(self, pos): if self.band.isVisible(): self.parent().centerOn(self.mapToScene(pos)) rect = self.band.geometry() rect.moveCenter(pos) self.band.setGeometry(rect) def mousePressEvent(self, event): if self.band.isVisible() and event.button() == Qt.LeftButton: rect = self.band.geometry() if event.pos() in rect: self._drag_start_pos = event.pos() else: self.centerOn(event.pos()) def mouseMoveEvent(self, event): if self.band.isVisible() and event.buttons() == Qt.MouseButtons( Qt.LeftButton) and self._drag_start_pos is not None: self.centerOn(event.pos()) def mouseReleaseEvent(self, event): if event.button() == Qt.LeftButton and self.band.isVisible(): self.viewport().unsetCursor() self._drag_start_pos = None def adjustRubberband(self): scene = self.scene() if scene is None: return rect = self.parent().mapToScene(self.parent().rect()).boundingRect() if not rect.contains(scene.sceneRect()): rect = self.mapFromScene(rect).boundingRect() self.band.setGeometry(rect) self.band.show() else: self.band.hide() def zoomToFit(self): self.fitInView(self.scene().sceneRect().adjusted(-20, -20, 20, 20), Qt.KeepAspectRatio)
class QImageEdit(QLabel): def __init__(self, parentQWidget=None): super(QImageEdit, self).__init__(parentQWidget) self.rubberBand = None self.move_rubberBand = False self.rubberBand_offset = None self.originPoint = None def setImage(self, image: QPixmap): self.setPixmap(image) def getImage(self) -> QPixmap: if self.rubberBand is not None: currentRect = self.rubberBand.geometry() return self.pixmap().copy(currentRect) else: return self.pixmap() def clear(self): super(QImageEdit, self).clear() if self.rubberBand is not None: self.rubberBand.deleteLater() self.rubberBand = None self.move_rubberBand = False self.rubberBand_offset = None self.originPoint = None def mousePressEvent(self, event): self.originPoint = event.pos() if self.rubberBand is None: self.rubberBand = QRubberBand(QRubberBand.Rectangle, self) self.rubberBand.setGeometry(QRect(self.originPoint, QSize())) self.rubberBand.show() else: if self.rubberBand.geometry().contains(self.originPoint): self.rubberBand_offset = \ self.originPoint - self.rubberBand.pos() self.move_rubberBand = True else: self.rubberBand.hide() self.rubberBand.deleteLater() self.rubberBand = None self.move_rubberBand = False self.rubberBand_offset = None self.mousePressEvent(event) def mouseMoveEvent(self, event): newPoint = event.pos() if self.move_rubberBand: self.rubberBand.move(newPoint - self.rubberBand_offset) else: self.rubberBand.setGeometry( QRect(self.originPoint, newPoint).normalized()) def mouseReleaseEvent(self, event): self.move_rubberBand = False
class SelectWidget(QWidget, Ui_Form2): select = [0,0,0,0] def __init__(self, parent=None): super(SelectWidget, self).__init__(parent) self.setWindowOpacity(0.5) self.setupUi(self) desktop = QtWidgets.QDesktopWidget() self.resize(desktop.availableGeometry().width(), desktop.availableGeometry().height()) self.rubberband = QRubberBand( QRubberBand.Rectangle, self) self.setMouseTracking(True) def mousePressEvent(self, event): self.select[0] = event.screenPos().x() self.select[1] = event.screenPos().y() self.origin = event.pos() self.rubberband.setGeometry( QtCore.QRect(self.origin, QtCore.QSize())) self.rubberband.show() QWidget.mousePressEvent(self, event) def mouseMoveEvent(self, event): if self.rubberband.isVisible(): self.rubberband.setGeometry( QtCore.QRect(self.origin, event.pos()).normalized()) QWidget.mouseMoveEvent(self, event) def mouseReleaseEvent(self, event): self.select[2] = event.screenPos().x() self.select[3] = event.screenPos().y() if self.rubberband.isVisible(): self.rubberband.hide() rect = self.rubberband.geometry() # ポインタ座標を左上→右下に揃える if self.select[0] > self.select[2]: tmp = self.select[0] self.select[0] = self.select[2] self.select[2] = tmp if self.select[1] > self.select[3]: tmp = self.select[1] self.select[1] = self.select[3] self.select[3] = tmp # 選択した面積がゼロの場合は何もしないで関数を終わる if self.select[0]==self.select[2] or self.select[1]==self.select[3]: QWidget.mouseReleaseEvent(self, event) self.close() return # ポインタ座標をメインウインドウに設定する area = '' for point in self.select: area = area + str(int(point)) + ',' main_window.lineEdit.setText(area[0:-1]) self.close() QWidget.mouseReleaseEvent(self, event)
class MyLabel(QLabel): def __init__(self, parent=None, func=None): self.func = func QLabel.__init__(self, parent) self.rubberBand = QRubberBand(QRubberBand.Rectangle, self) self.origin = QPoint() self.slitpos = 250 self.focus = (333, 666, 333, 666) #xs,xe,ys,ye self.geometry = (0, 0, 1000, 1000) #x,y,w,h def paintEvent(self, event): QLabel.paintEvent(self, event) painter = QPainter(self) painter.setPen(Qt.red) x, y, w, h = self.geometry d = 20 painter.drawLine(x + w // 2 - self.slitpos * w // 1000, y, x + w // 2 - self.slitpos * w // 1000, y + h) painter.drawLine(x + w // 2 - self.slitpos * w // 1000 - d, y + h / 2, x + w // 2 - self.slitpos * w // 1000, y + h / 2 - d) painter.drawLine(x + w // 2 - self.slitpos * w // 1000 - d, y + h / 2, x + w // 2 - self.slitpos * w // 1000, y + h / 2 + d) painter.drawLine(x + w // 2 + self.slitpos * w // 1000, y, x + w // 2 + self.slitpos * w // 1000, y + h) painter.drawLine(x + w // 2 + self.slitpos * w // 1000 + d, y + h // 2, x + w // 2 + self.slitpos * w // 1000, y + h // 2 - d) painter.drawLine(x + w // 2 + self.slitpos * w // 1000 + d, y + h // 2, x + w // 2 + self.slitpos * w // 1000, y + h // 2 + d) painter.setPen(Qt.green) painter.drawRect(x + w * self.focus[0] // 1000, y + h * self.focus[2] // 1000, w * (self.focus[1] - self.focus[0]) // 1000, h * (self.focus[3] - self.focus[2]) // 1000) 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() self.region = QRect(self.origin, event.pos()).normalized() if self.func is not None: self.func(self.region)
class MyLabel(QLabel): def __init__(self, parent = None, func=None): self.func = func QLabel.__init__(self, parent) self.rubberBand = QRubberBand(QRubberBand.Rectangle, self) self.origin = QPoint() self.slitpos = 250 self.focus = (333,666,333,666) #xs,xe,ys,ye self.geometry = (0,0,1000,1000) #x,y,w,h def paintEvent(self, event): QLabel.paintEvent(self, event) painter = QPainter(self) painter.setPen(Qt.red) x,y,w,h = self.geometry d = 20 painter.drawLine(x+w//2-self.slitpos*w//1000,y, x+w//2-self.slitpos*w//1000,y+h) painter.drawLine(x+w//2-self.slitpos*w//1000-d,y+h/2, x+w//2-self.slitpos*w//1000, y+h/2-d) painter.drawLine(x+w//2-self.slitpos*w//1000-d,y+h/2, x+w//2-self.slitpos*w//1000, y+h/2+d) painter.drawLine(x+w//2+self.slitpos*w//1000,y, x+w//2+self.slitpos*w//1000,y+h) painter.drawLine(x+w//2+self.slitpos*w//1000+d,y+h//2, x+w//2+self.slitpos*w//1000, y+h//2-d) painter.drawLine(x+w//2+self.slitpos*w//1000+d,y+h//2, x+w//2+self.slitpos*w//1000, y+h//2+d) painter.setPen(Qt.green) painter.drawRect(x+w*self.focus[0]//1000,y+h*self.focus[2]//1000,w*(self.focus[1]-self.focus[0])//1000,h*(self.focus[3]-self.focus[2])//1000) 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() self.region = QRect(self.origin, event.pos()).normalized() if self.func is not None: self.func(self.region)
class QLabelSelectable(QLabel): selectionFinished = pyqtSignal() def __init__(self, parent=None): QLabel.__init__(self, parent) self.rubberBand = QRubberBand(QRubberBand.Rectangle, self) self.origin = QPoint() self.startSelection = False self.endSelection = False def reset(self): self.startSelection = False self.endSelection = False def mousePressEvent(self, event): if event.button( ) == Qt.LeftButton and self.startSelection and not self.endSelection: self.origin = QPoint(event.pos()) self.rubberBand.setGeometry(QRect(self.origin, QSize())) self.rubberBand.show() def mouseMoveEvent(self, event): if not self.origin.isNull( ) and self.startSelection and not self.endSelection: self.rubberBand.setGeometry( QRect(self.origin, event.pos()).normalized()) def mouseReleaseEvent(self, event): if event.button() == Qt.LeftButton and self.startSelection: #print(self.rubberBand.geometry()) self.drawRectangle() self.rubberBand.hide() self.endSelection = True def drawRectangle(self): # create painter instance with pixmap self.painterInstance = QPainter(self.pixmap()) # set rectangle color and thickness self.penRectangle = QPen(Qt.red) self.penRectangle.setWidth(3) # draw rectangle on painter self.painterInstance.setPen(self.penRectangle) self.painterInstance.drawRect(self.rubberBand.geometry()) self.selectionFinished.emit()
class ImageWidget(QLabel): areaSelected = pyqtSignal(float, float, float, float, QPixmap) def __init__(self, parent=None): super().__init__(parent) self.rubberBand = None self.origin = None def mousePressEvent(self, event): self.origin = event.pos() if self.rubberBand is None: self.rubberBand = QRubberBand(QRubberBand.Rectangle, self) self.rubberBand.setGeometry(QRect(self.origin, QSize())) self.rubberBand.show() def mouseMoveEvent(self, event): self.rubberBand.setGeometry(QRect(self.origin, event.pos()).normalized()) def mouseReleaseEvent(self, event): self.rubberBand.hide() zoreRect = QRect(self.origin, event.pos()).normalized() pixmapSize = self.pixmap().size() pixX1 = zoreRect.x() pixX2 = zoreRect.x() + zoreRect.width() pixY1 = zoreRect.y() pixY2 = zoreRect.y() + zoreRect.height() width = pixmapSize.width() height = pixmapSize.height() x1 = pixX1/width x1 = x1 if x1 >= 0.0 else 0.0 x1 = x1 if x1 <= 1.0 else 1.0 y1 = pixY1/height y1 = y1 if y1 >= 0.0 else 0.0 y1 = y1 if y1 <= 1.0 else 1.0 x2 = pixX2/width x2 = x2 if x2 >= 0.0 else 0.0 x2 = x2 if x2 <= 1.0 else 1.0 y2 = pixY2/height y2 = y2 if y2 >= 0.0 else 0.0 y2 = y2 if y2 <= 1.0 else 1.0 rect = QRect(min(pixX1, pixX2), min(pixY1, pixY2), abs(pixX1- pixX2), abs(pixY1- pixY2)) selectedImg = self.pixmap().copy(rect) self.areaSelected.emit(min(x1, x2), min(y1, y2), np.abs(x1-x2), np.abs(y1-y2), selectedImg)
class BandMixin(object): def __init__(self, **kwargs): super(BandMixin, self).__init__(**kwargs) self.__band = None def showBand(self, *geom): if not self.__band: self.__band = QRubberBand(QRubberBand.Rectangle, parent=self) self.__band.setGeometry(*geom) self.__band.show() def hideBand(self): if self.__band: self.__band.hide() self.__band.setParent(None) self.__band = None
class Image_Display(RawImageWidget, QWidget): def __init__(self, parent=None, **kargs): RawImageWidget.__init__(self, parent=parent, scaled=True) super(QWidget, self).__init__(parent) self.setWindowTitle("image") self.rubberBand = QRubberBand(QRubberBand.Rectangle, self) self.mousePressPosition = QPoint(0, 0) self.mouseReleasePosition = QPoint(0, 0) self.clientCallback = None self.mousePressed = False self.okToClose = False self.setGeometry(QRect(10, 300, 600, 600)) def closeEvent(self, event): if not self.okToClose: self.hide() return def display(self, image, pixelLevels): self.setImage(image, levels=pixelLevels) self.show() def mousePressEvent(self, event): self.mousePressPosition = QPoint(event.pos()) self.rubberBand.setGeometry(QRect(self.mousePressPosition, QSize())) self.rubberBand.show() self.mousePressed = True def mouseMoveEvent(self, event): if not self.mousePressed: return self.rubberBand.setGeometry( QRect(self.mousePressPosition, event.pos()).normalized()) def mouseReleaseEvent(self, event): if not self.mousePressed: return self.mouseReleasePosition = QPoint(event.pos()) if not self.clientCallback == None: self.clientCallback(self.mousePressPosition, self.mouseReleasePosition) self.rubberBand.hide() self.mousePressed = False def clientReleaseEvent(self, clientCallback): self.clientCallback = clientCallback
class TimeView(QGraphicsView): time_clicked = pyqtSignal(float) time_selected = pyqtSignal(float, float) def __init__(self, parent=None): super().__init__(parent=parent) self.initialClick = QPoint() self.selectionBand = QRubberBand(QRubberBand.Rectangle, self) self.selecting = False def mouseReleaseEvent(self, event): clicked_x = self.mapToScene(event.pos()).x() self.selectionBand.hide() if clicked_x == self.mapToScene(self.initialClick).x(): self.time_clicked.emit(clicked_x) else: if self.initialClick.x() < clicked_x: self.time_selected.emit( self.mapToScene(self.initialClick).x(), clicked_x) else: self.time_selected.emit(clicked_x, self.mapToScene(self.initialClick).x()) self.selecting = False def mousePressEvent(self, event): self.selecting = True self.initialClick = event.pos() self.selectionBand.setGeometry( QRect(self.initialClick.x(), 0, 1, self.height())) self.selectionBand.show() def mouseMoveEvent(self, event): if self.selecting: current_pos = event.pos() if current_pos.x() < self.initialClick.x(): start_x = current_pos.x() width = self.initialClick.x() - start_x else: start_x = self.initialClick.x() width = current_pos.x() - self.initialClick.x() self.selectionBand.setGeometry( QRect(start_x, 0, width, self.height()))
class PageScrollArea(QScrollArea): reachbottom = pyqtSignal() reachtop = pyqtSignal() areaSelected = pyqtSignal(QRect) def __init__(self, parent): super().__init__(parent) def wheelEvent(self, event): super().wheelEvent(event) if self.verticalScrollBar().value() == self.verticalScrollBar( ).maximum() and event.angleDelta().y() == -120: self.reachbottom.emit() if self.verticalScrollBar().value() == 0 and event.angleDelta().y( ) == 120: self.reachtop.emit() def mousePressEvent(self, event): self.rubberorigin = event.pos() self.rubberband = QRubberBand(QRubberBand.Rectangle, self) self.rubberband.setGeometry(QRect(self.rubberorigin, QSize())) self.rubberband.show() def mouseReleaseEvent(self, event): rect = QRect(self.rubberband.pos(), self.rubberband.size()) self.areaSelected.emit(rect) self.rubberband.hide() def mouseMoveEvent(self, event): super().mouseMoveEvent(event) self.rubberband.setGeometry( QRect(self.rubberorigin, event.pos()).normalized()) def keyPressEvent(self, event): super().keyPressEvent(event) if self.verticalScrollBar().value() == self.verticalScrollBar( ).maximum() and (event.key() == 16777237 or event.key() == 16777239): self.reachbottom.emit() if self.verticalScrollBar().value() == 0 and ( event.key() == 16777235 or event.key() == 16777238): self.reachtop.emit()
class Window(QLabel): def __int__(self, parent=None): QLabel.__init__(self, parent) self.rubberBand = QRubberBand(QRubberBand.Rectangle, self) self.origin = QPoint() def mousePressEvent(self, event: QMouseEvent): if event.button() == Qt.LeftButton: self.origin = QPoint(event.pos()) self.rubberBand.setGeometry(QRect(self.origin, QSize())) self.rubberBand.show() def mouseMoveEvent(self, event: QMouseEvent): if not self.origin.isNull(): self.rubberBand.setGeometry( QRect(self.origin, event.pos()).normalized()) def mouseReleaseEvent(self, event: QMouseEvent): if event.button() == Qt.LeftButton: self.rubberBand.hide()
class VideoArea(QLabel): def __init__(self, parent=None, main=None): QLabel.__init__(self, parent) self.selection = QRubberBand(QRubberBand.Rectangle, self) self.main = main def mousePressEvent(self, event): if event.button() == Qt.LeftButton: position = QPoint(event.pos()) if self.selection.isVisible(): if (self.upper_left - position).manhattanLength() < 20: self.mode = "UL" elif (self.lower_right - position).manhattanLength() < 20: self.mode = "LR" else: self.selection.hide() else: self.upper_left = position self.lower_right = position self.mode = "LR" self.selection.show() def mouseMoveEvent(self, event): if self.selection.isVisible(): if self.mode == "LR": self.lower_right = QPoint(event.pos()) elif self.mode == "UL": self.upper_left = QPoint(event.pos()) self.selection.setGeometry( QRect(self.upper_left, self.lower_right).normalized()) def mouseReleaseEvent(self, event): self.main.controller.set_coordinates( *self.selection.geometry().getCoords()) self.selection.hide()
class DragListWidget(QListWidget): # 可以往外拖的QListWidget def __init__(self, *args, **kwargs): super(DragListWidget, self).__init__(*args, **kwargs) self.resize(400, 400) # 隐藏横向滚动条 self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) # 不能编辑 self.setEditTriggers(self.NoEditTriggers) # 开启拖功能 self.setDragEnabled(True) # 只能往外拖 self.setDragDropMode(self.DragOnly) # 忽略放 self.setDefaultDropAction(Qt.IgnoreAction) # ****重要的一句(作用是可以单选,多选。Ctrl、Shift多选,可从空白位置框选)**** # ****不能用ExtendedSelection,因为它可以在选中item后继续框选会和拖拽冲突**** self.setSelectionMode(self.ContiguousSelection) # 设置从左到右、自动换行、依次排列 self.setFlow(self.LeftToRight) self.setWrapping(True) self.setResizeMode(self.Adjust) # item的间隔 self.setSpacing(5) # 橡皮筋(用于框选效果) self._rubberPos = None self._rubberBand = QRubberBand(QRubberBand.Rectangle, self) self.initItems() # 实现拖拽的时候预览效果图 # 这里演示拼接所有的item截图(也可以自己写算法实现堆叠效果) def startDrag(self, supportedActions): items = self.selectedItems() drag = QDrag(self) mimeData = self.mimeData(items) # 由于QMimeData只能设置image、urls、str、bytes等等不方便 # 这里添加一个额外的属性直接把item放进去,后面可以根据item取出数据 mimeData.setProperty('myItems', items) drag.setMimeData(mimeData) pixmap = QPixmap(self.viewport().visibleRegion().boundingRect().size()) pixmap.fill(Qt.transparent) painter = QPainter() painter.begin(pixmap) for item in items: rect = self.visualRect(self.indexFromItem(item)) painter.drawPixmap(rect, self.viewport().grab(rect)) painter.end() drag.setPixmap(pixmap) drag.setHotSpot(self.viewport().mapFromGlobal(QCursor.pos())) drag.exec_(supportedActions) def mousePressEvent(self, event): # 列表框点击事件,用于设置框选工具的开始位置 super(DragListWidget, self).mousePressEvent(event) if event.buttons() != Qt.LeftButton or self.itemAt(event.pos()): return self._rubberPos = event.pos() self._rubberBand.setGeometry(QRect(self._rubberPos, QSize())) self._rubberBand.show() def mouseReleaseEvent(self, event): # 列表框点击释放事件,用于隐藏框选工具 super(DragListWidget, self).mouseReleaseEvent(event) self._rubberPos = None self._rubberBand.hide() def mouseMoveEvent(self, event): # 列表框鼠标移动事件,用于设置框选工具的矩形范围 super(DragListWidget, self).mouseMoveEvent(event) if self._rubberPos: pos = event.pos() lx, ly = self._rubberPos.x(), self._rubberPos.y() rx, ry = pos.x(), pos.y() size = QSize(abs(rx - lx), abs(ry - ly)) self._rubberBand.setGeometry( QRect(QPoint(min(lx, rx), min(ly, ry)), size)) def makeItem(self, size, cname): item = QListWidgetItem(self) item.setData(Qt.UserRole + 1, cname) # 把颜色放进自定义的data里面 item.setSizeHint(size) label = QLabel(self) # 自定义控件 label.setMargin(2) # 往内缩进2 label.resize(size) pixmap = QPixmap(size.scaled(96, 96, Qt.IgnoreAspectRatio)) # 调整尺寸 pixmap.fill(QColor(cname)) label.setPixmap(pixmap) self.setItemWidget(item, label) def initItems(self): size = QSize(100, 100) for cname in QColor.colorNames(): self.makeItem(size, cname)
class ImageViewer(QLabel): def __init__(self): super(ImageViewer, self).__init__() self.crop = Signal() #pixmap = QPixmap(640, 640) pixmap = QPixmap() self.setPixmap(pixmap) self.first_x, self.first_y = None, None self.last_x, self.last_y = None, None self.pen_color = QColor('#000000') self.scaleFactor = 0.0 self.setBackgroundRole(QPalette.Base) #self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) #self.setScaledContents(True) #self.setGeometry(QRect(110, 550, 640, 640)) self.setText("Feature") self.setAlignment(Qt.AlignTop | Qt.AlignLeft) #self.setAlignment(Qt.AlignCenter) self.setWordWrap(False) #self.setFixedSize(640,640) self.createActions() self.show() #self.showMaximized() def setImage(self, q_img): self.setPixmap(q_img) self.scaleFactor = 1.0 #self.fitToWindowAct.setEnabled(True) self.updateActions() if not self.fitToWindowAct.isChecked(): self.adjustSize() def open(self): fileName, _ = QFileDialog.getOpenFileName(self, "Open File", QDir.currentPath()) if fileName: image = QImage(fileName) if image.isNull(): QMessageBox.information(self, "Image Viewer", "Cannot load %s." % fileName) return self.setPixmap(QPixmap.fromImage(image)) self.scaleFactor = 1.0 self.fitToWindowAct.setEnabled(True) self.updateActions() if not self.fitToWindowAct.isChecked(): self.adjustSize() def zoomIn(self): self.scaleImage(1.25) def zoomOut(self): self.scaleImage(0.8) def normalSize(self): self.adjustSize() self.scaleFactor = 1.0 def fitToWindow(self): fitToWindow = self.fitToWindowAct.isChecked() self.scrollArea.setWidgetResizable(fitToWindow) if not fitToWindow: self.normalSize() self.updateActions() def createActions(self): self.openAct = QAction("&Open...", self, shortcut="Ctrl+O", triggered=self.open) self.zoomInAct = QAction("Zoom &In (25%)", self, shortcut="Ctrl++", enabled=False, triggered=self.zoomIn) self.zoomOutAct = QAction("Zoom &Out (25%)", self, shortcut="Ctrl+-", enabled=False, triggered=self.zoomOut) self.normalSizeAct = QAction("&Normal Size", self, shortcut="Ctrl+S", enabled=False, triggered=self.normalSize) self.fitToWindowAct = QAction("&Fit to Window", self, enabled=False, checkable=True, shortcut="Ctrl+F", triggered=self.fitToWindow) def updateActions(self): self.zoomInAct.setEnabled(not self.fitToWindowAct.isChecked()) self.zoomOutAct.setEnabled(not self.fitToWindowAct.isChecked()) self.normalSizeAct.setEnabled(not self.fitToWindowAct.isChecked()) def scaleImage(self, factor): self.scaleFactor *= factor self.resize(self.scaleFactor * self.pixmap().size()) self.zoomInAct.setEnabled(self.scaleFactor < 3.0) self.zoomOutAct.setEnabled(self.scaleFactor > 0.333) def mousePressEvent(self, eventQMouseEvent): self.originQPoint = self.mapFrom(self, eventQMouseEvent.pos()) self.first_x = int(eventQMouseEvent.x()) self.first_y = int(eventQMouseEvent.y()) self.currentQRubberBand = QRubberBand(QRubberBand.Rectangle, self) self.currentQRubberBand.setGeometry(QRect(self.originQPoint, QSize())) self.currentQRubberBand.show() def mouseMoveEvent(self, eventQMouseEvent): self.x = int(eventQMouseEvent.x()) self.y = int(eventQMouseEvent.y()) text1 = str(self.x) text2 = str(self.y) p = self.mapToGlobal(eventQMouseEvent.pos()) #QToolTip.showText(eventQMouseEvent.pos() , "X: "+text1+" "+"Y: "+text2,self) QToolTip.showText(p, "X: " + text1 + " " + "Y: " + text2, self) # print ('mouse QToolTip 1') 전체장에서의 위젯 시작점의 좌표가 오리진 포인트가 되어야 함. ''' if self.currentQRubberBand.isVisible(): self.currentQRubberBand.setGeometry(QRect(self.originQPoint, eventQMouseEvent.pos()).normalized() & self.pixmap().rect()) ''' pos = self.mapFromGlobal(self.mapToGlobal(eventQMouseEvent.pos())) #QToolTip.showText(eventQMouseEvent.pos(), "X: {} Y: {}".format(p.x(), p.y()), self) if self.currentQRubberBand.isVisible() and self.pixmap() is not None: self.currentQRubberBand.setGeometry( QRect(self.originQPoint, pos).normalized() & self.rect()) def mouseReleaseEvent(self, eventQMouseEvent): self.last_x = int(eventQMouseEvent.x()) self.last_y = int(eventQMouseEvent.y()) ''' self.currentQRubberBand.hide() currentQRect = self.currentQRubberBand.geometry() self.currentQRubberBand.deleteLater() if self.pixmap() is not None: tr = QTransform() if self.fitToWindowAct.isChecked(): tr.scale(self.pixmap().width() / self.scrollArea.width(), self.pixmap().height() / self.scrollArea.height()) else: tr.scale(1 / self.scaleFactor, 1 / self.scaleFactor) r = tr.mapRect(currentQRect) cropQPixmap = self.pixmap().copy(r) cropQPixmap.save('output.png') ''' self.currentQRubberBand.hide() currentQRect = self.currentQRubberBand.geometry() self.currentQRubberBand.deleteLater() cropQPixmap = self.pixmap().copy(currentQRect) #size = cropQPixmap.size() # 직접변환 해서 저장하는 코드... 나중에 #s = cropQPixmap.bits().asstring(size.width() * size.height() * image.depth() // 8) # format 0xffRRGGBB #arr = np.fromstring(s, dtype=np.uint8).reshape((size.height(), size.width(), cropQPixmap.depth() // 8)) #new_image = Image.fromarray(array) cropQPixmap.save('output.png') img = cv2.imread("output.png", cv2.IMREAD_COLOR) h, w = self.cut_imgSize() self.crop.cut_signal.emit(img, h, w) def cut_imgSize(self): h = abs(self.last_y - self.first_y) w = abs(self.last_x - self.first_x) return h, w
class ImageCropperDropper(QLabel): '''A QLabel that displays a rectangle when drawing a rectangle with the mouse''' cropped = Signal(QPixmap) fileDropped = Signal(QUrl) def __init__(self, mainwin): QLabel.__init__(self) self.setAcceptDrops(True) self.marker = QRubberBand(QRubberBand.Rectangle, self) self.markOrigin = self.markEnd = None self.setContextMenuPolicy(Qt.ActionsContextMenu) self.addAction(mainwin.cropAction) self.addAction(mainwin.saveAction) def setPixmap(self, pix): QLabel.setPixmap(self, pix) self.resize(pix.size()) self.marker.hide() self.markOrigin = self.markEnd = None @Slot() def doCrop(self): '''Crop the pixmap using the user-drawn rectangle, emits cropped(QPixmap) signal''' if not self.markEnd: QMessageBox.warning(self, 'Error', 'Select a region to crop first') return cropzone = self._makeRect(self.markOrigin, self.markEnd) croppedpix = self.pixmap().copy(cropzone) self.setPixmap(croppedpix) self.cropped.emit(croppedpix) def _makeRect(self, p1, p2): '''Make a QRect based on QPoints p1 and p2. The 2 points must be 2 corners but don't need to be upper-left&lower-right''' x1, x2 = min(p1.x(), p2.x()), max(p1.x(), p2.x()) y1, y2 = min(p1.y(), p2.y()), max(p1.y(), p2.y()) return QRect().adjusted(x1, y1, x2, y2) def mouseMoveEvent(self, ev): if ev.buttons() != Qt.LeftButton: return QLabel.mouseMoveEvent(self, ev) self.markEnd = ev.pos() diffpoint = self.markEnd - self.markOrigin #~ self.marker.resize(diffpoint.x(), diffpoint.y()) self.marker.setGeometry(self._makeRect(self.markOrigin, self.markEnd)) #~ ev.accept() def mousePressEvent(self, ev): if ev.button() != Qt.LeftButton: return QLabel.mousePressEvent(self, ev) self.markOrigin = ev.pos() self.marker.move(ev.pos()) self.marker.resize(QSize()) self.marker.show() #~ ev.accept() def dragEnterEvent(self, ev): if ev.mimeData().hasUrls(): ev.setDropAction(Qt.CopyAction) ev.accept() def dropEvent(self, ev): if ev.mimeData().hasUrls(): ev.setDropAction(Qt.CopyAction) ev.accept() self.fileDropped.emit(ev.mimeData().urls()[0])
class imageLabel(QLabel): """Subclass of QLabel for displaying image""" def __init__(self, parent, image=None): super().__init__(parent) self.parent = parent self.image = QImage() #self.image = "images/parrot.png" #self.original_image = self.image.copy self.original_image = self.image self.rubber_band = QRubberBand(QRubberBand.Rectangle, self) # setBackgroundRole() will create a bg for the image #self.setBackgroundRole(QPalette.Base) self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) self.setScaledContents(True) # Load image self.setPixmap(QPixmap().fromImage(self.image)) self.setAlignment(Qt.AlignCenter) def openImage(self): """Load a new image into the """ image_file, _ = QFileDialog.getOpenFileName( self, "Open Image", "", "PNG Files (*.png);;JPG Files (*.jpeg *.jpg );;Bitmap Files (*.bmp);;\ GIF Files (*.gif)") if image_file: # Reset values when opening an image self.parent.zoom_factor = 1 #self.parent.scroll_area.setVisible(True) self.parent.print_act.setEnabled(True) self.parent.updateActions() # Reset all sliders self.parent.brightness_slider.setValue(0) # Get image format image_format = self.image.format() self.image = QImage(image_file) self.original_image = self.image.copy() #pixmap = QPixmap(image_file) self.setPixmap(QPixmap().fromImage(self.image)) #image_size = self.image_label.sizeHint() self.resize(self.pixmap().size()) #self.scroll_area.setMinimumSize(image_size) #self.image_label.setPixmap(pixmap.scaled(self.image_label.size(), # Qt.KeepAspectRatio, Qt.SmoothTransformation)) elif image_file == "": # User selected Cancel pass else: QMessageBox.information(self, "Error", "Unable to open image.", QMessageBox.Ok) def saveImage(self): """Save the image displayed in the label.""" #TODO: Add different functionality for the way in which the user can save their image. if self.image.isNull() == False: image_file, _ = QFileDialog.getSaveFileName( self, "Save Image", "", "PNG Files (*.png);;JPG Files (*.jpeg *.jpg );;Bitmap Files (*.bmp);;\ GIF Files (*.gif)") if image_file and self.image.isNull() == False: self.image.save(image_file) else: QMessageBox.information(self, "Error", "Unable to save image.", QMessageBox.Ok) else: QMessageBox.information(self, "Empty Image", "There is no image to save.", QMessageBox.Ok) def clearImage(self): """ """ #TODO: If image is not null ask to save image first. pass def revertToOriginal(self): """Revert the image back to original image.""" #TODO: Display message dialohg to confirm actions self.image = self.original_image self.setPixmap(QPixmap().fromImage(self.image)) self.repaint() #self.parent.zoom_factor = 1 def resizeImage(self): """Resize image.""" #TODO: Resize image by specified size if self.image.isNull() == False: resize = QTransform().scale(0.5, 0.5) pixmap = QPixmap(self.image) resized_image = pixmap.transformed(resize, mode=Qt.SmoothTransformation) #rotated = pixmap.trueMatrix(transform90, pixmap.width, pixmap.height) #self.image_label.setPixmap(rotated) #self.image_label.setPixmap(rotated.scaled(self.image_label.size(), # Qt.KeepAspectRatio, Qt.SmoothTransformation)) self.image = QImage(resized_image) self.setPixmap(resized_image) #self.image = QPixmap(rotated) self.setScaledContents(True) self.repaint() # repaint the child widget else: # No image to rotate pass def cropImage(self): """Crop selected portions in the image.""" if self.image.isNull() == False: rect = QRect(10, 20, 400, 200) original_image = self.image cropped = original_image.copy(rect) self.image = QImage(cropped) self.setPixmap(QPixmap().fromImage(cropped)) def rotateImage90(self, direction): """Rotate image 90º clockwise or counterclockwise.""" if self.image.isNull() == False: if direction == "cw": transform90 = QTransform().rotate(90) elif direction == "ccw": transform90 = QTransform().rotate(-90) pixmap = QPixmap(self.image) #TODO: Try flipping the height/width when flipping the image rotated = pixmap.transformed(transform90, mode=Qt.SmoothTransformation) self.resize(self.image.height(), self.image.width()) #rotated = pixmap.trueMatrix(transform90, pixmap.width, pixmap.height) #self.image_label.setPixmap(rotated.scaled(self.image_label.size(), # Qt.KeepAspectRatio, Qt.SmoothTransformation)) self.image = QImage(rotated) #self.setPixmap(rotated) self.setPixmap( rotated.scaled(self.size(), Qt.KeepAspectRatioByExpanding, Qt.SmoothTransformation)) self.repaint() # repaint the child widget else: # No image to rotate pass def flipImage(self, axis): """ Mirror the image across the horizontal axis. """ if self.image.isNull() == False: if axis == "horizontal": flip_h = QTransform().scale(-1, 1) pixmap = QPixmap(self.image) flipped = pixmap.transformed(flip_h) elif axis == "vertical": flip_v = QTransform().scale(1, -1) pixmap = QPixmap(self.image) flipped = pixmap.transformed(flip_v) #self.image_label.setPixmap(flipped) #self.image_label.setPixmap(flipped.scaled(self.image_label.size(), # Qt.KeepAspectRatio, Qt.SmoothTransformation)) self.image = QImage(flipped) self.setPixmap(flipped) #self.image = QPixmap(flipped) self.repaint() else: # No image to flip pass def convertToGray(self): """Convert image to grayscale.""" if self.image.isNull() == False: converted_img = self.image.convertToFormat( QImage.Format_Grayscale16) #self.image = converted_img self.image = QImage(converted_img) self.setPixmap(QPixmap().fromImage(converted_img)) self.repaint() def convertToRGB(self): """Convert image to RGB format.""" if self.image.isNull() == False: converted_img = self.image.convertToFormat(QImage.Format_RGB32) #self.image = converted_img self.image = QImage(converted_img) self.setPixmap(QPixmap().fromImage(converted_img)) self.repaint() def convertToSepia(self): """Convert image to sepia filter.""" #TODO: Sepia #704214 rgb(112, 66, 20) #TODO: optimize speed that the image converts, or add to thread if self.image.isNull() == False: for row_pixel in range(self.image.width()): for col_pixel in range(self.image.height()): current_val = QColor(self.image.pixel( row_pixel, col_pixel)) # Calculate r, g, b values for current pixel red = current_val.red() green = current_val.green() blue = current_val.blue() new_red = int(0.393 * red + 0.769 * green + 0.189 * blue) new_green = int(0.349 * red + 0.686 * green + 0.168 * blue) new_blue = int(0.272 * red + 0.534 * green + 0.131 * blue) # Set the new RGB values for the current pixel if new_red > 255: red = 255 else: red = new_red if new_green > 255: green = 255 else: green = new_green if new_blue > 255: blue = 255 else: blue = new_blue new_value = qRgb(red, green, blue) self.image.setPixel(row_pixel, col_pixel, new_value) self.setPixmap(QPixmap().fromImage(self.image)) self.repaint() def changeBrighteness(self, value): #TODO: Reset the value of brightness, remember the original values # as going back to 0, i.e. keep track of original image's values #TODO: modify values based on original image if (value < -255 | value > 255): return self.image for row_pixel in range(self.image.width()): for col_pixel in range(self.image.height()): current_val = QColor(self.image.pixel(row_pixel, col_pixel)) red = current_val.red() green = current_val.green() blue = current_val.blue() new_red = red + value new_green = green + value new_blue = blue + value # Set the new RGB values for the current pixel if new_red > 255: red = 255 elif new_red < 0: red = 0 else: red = new_red if new_green > 255: green = 255 elif new_green < 0: green = 0 else: green = new_green if new_blue > 255: blue = 255 elif new_blue < 0: blue = 0 else: blue = new_blue new_value = qRgb(red, green, blue) self.image.setPixel(row_pixel, col_pixel, new_value) self.setPixmap(QPixmap().fromImage(self.image)) def changeContrast(self, contrast): """Change the contrast of the pixels in the image. Contrast is the difference between max and min pixel intensity.""" for row_pixel in range(self.image.width()): for col_pixel in range(self.image.height()): # Calculate a contrast correction factor factor = float(259 * (contrast + 255) / (255 * (259 - contrast))) current_val = QColor(self.image.pixel(row_pixel, col_pixel)) red = current_val.red() green = current_val.green() blue = current_val.blue() new_red = factor * (red - 128) + 128 new_green = factor * (green - 128) + 128 new_blue = factor * (blue - 128) + 128 new_value = qRgb(new_red, new_green, new_blue) self.image.setPixel(row_pixel, col_pixel, new_value) self.setPixmap(QPixmap().fromImage(self.image)) def changeHue(self): for row_pixel in range(self.image.width()): for col_pixel in range(self.image.height()): current_val = QColor(self.image.pixel(row_pixel, col_pixel)) hue = current_val.hue() current_val.setHsv(hue, current_val.saturation(), current_val.value(), current_val.alpha()) self.image.setPixelColor(row_pixel, col_pixel, current_val) self.setPixmap(QPixmap().fromImage(self.image)) def mousePressEvent(self, event): """Handle mouse press event.""" self.origin = event.pos() if not (self.rubber_band): self.rubber_band = QRubberBand(QRubberBand.Rectangle, self) self.rubber_band.setGeometry(QRect(self.origin, QSize())) self.rubber_band.show() #print(self.rubber_band.height()) print(self.rubber_band.x()) def mouseMoveEvent(self, event): """Handle mouse move event.""" self.rubber_band.setGeometry( QRect(self.origin, event.pos()).normalized()) def mouseReleaseEvent(self, event): """Handle when the mouse is released.""" self.rubber_band.hide()
class TcamScreen(QtWidgets.QGraphicsView): new_pixmap = pyqtSignal(QtGui.QPixmap) new_pixel_under_mouse = pyqtSignal(bool, int, int, QtGui.QColor) destroy_widget = pyqtSignal() fit_in_view = pyqtSignal() def __init__(self, parent=None): super(TcamScreen, self).__init__(parent) self.setMouseTracking(True) self.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) self.setDragMode(QGraphicsView.ScrollHandDrag) self.setFrameStyle(0) self.scene = QGraphicsScene(self) self.setScene(self.scene) self.new_pixmap.connect(self.on_new_pixmap) self.fit_in_view.connect(self.fit_view) self.pix = ViewItem() self.scene.addItem(self.pix) self.scene.setSceneRect(self.pix.boundingRect()) self.is_fullscreen = False # Flag to differentiate between actual images # and 'fake images' i.e. color background + text while # waiting for first trigger image self.display_real_image = True self.text_item = None self.fit_in_view_called = False self.mouse_position_x = -1 self.mouse_position_y = -1 self.zoom_factor = 1.0 self.first_image = True self.image_counter = 0 self.capture_roi = False self.roi_obj = None self.roi_origin = None self.roi_widgets = [] self.selection_area = None self.capture_widget = None self.origin = None def fit_view(self): """ """ self.reset_zoom() self.scene.setSceneRect(self.pix.boundingRect()) self.scene.update() self.fitInView(self.scene.sceneRect(), Qt.KeepAspectRatio) def reset_zoom(self): self.zoom_factor = 1.0 # this resets the view internal transformation matrix self.setTransform(QtGui.QTransform()) def on_new_pixmap(self, pixmap): self.image_counter += 1 self.pix.setPixmap(pixmap) if not self.display_real_image: self.text_item.hide() self.scene.removeItem(self.text_item) self.display_real_image = True if self.image_counter == 1: self.resize(self.size()) self.scene.setSceneRect(self.pix.boundingRect()) self.update() self.reset_zoom() self.first_image = False # wait for the second image # resizeEvents, etc appear before the scene has adjusted # to the actual image size. By waiting for the 2. image # we circumvent this by having the first image making all # adjustments for us. The only scenario where this will # cause problems is triggering. if self.is_fullscreen and self.image_counter == 2: self.fit_view() self.send_mouse_pixel() # don't call repaint here # it causes problems once the screen goes blank due to screensavers, etc # self.repaint() def wait_for_first_image(self): if not self.display_real_image: return self.reset_zoom() self.display_real_image = False self.text_item = QGraphicsTextItem() self.text_item.setDefaultTextColor(QColor("white")) self.text_item.setPos(100, 70) self.text_item.setPlainText("In Trigger Mode. Waiting for first image...") bg = QPixmap(1280, 720) bg.fill(QColor("grey")) self.pix.setPixmap(bg) self.image_counter += 1 self.scene.addItem(self.text_item) def send_mouse_pixel(self): # mouse positions start at 0 # we want the lower right corner to have the correct coordinates # e.g. an 1920x1080 image should have the coordinates # 1920x1080 for the last pixel self.new_pixel_under_mouse.emit(self.pix.legal_coordinates(self.mouse_position_x, self.mouse_position_y), self.mouse_position_x + 1, self.mouse_position_y + 1, self.pix.get_color_at_position(self.mouse_position_x, self.mouse_position_y)) def mouseMoveEvent(self, event): mouse_position = self.mapToScene(event.pos()) self.mouse_position_x = mouse_position.x() self.mouse_position_y = mouse_position.y() if self.selection_area: # adjust rect since we want to pull in all directions # origin can well be bottom left, thus recalc def calc_selection_rect(): x = min(self.origin.x(), event.pos().x()) y = min(self.origin.y(), event.pos().y()) x2 = max(self.origin.x(), event.pos().x()) y2 = max(self.origin.y(), event.pos().y()) return QPoint(x, y), QPoint(x2, y2) p1, p2 = calc_selection_rect() self.selection_area.setGeometry(QRect(p1, p2)) super().mouseMoveEvent(event) def mousePressEvent(self, event): """""" if self.capture_widget: self.selection_area = QRubberBand(QRubberBand.Rectangle, self) self.selection_area.setGeometry(QRect(event.pos(), QSize())) self.origin = event.pos() self.selection_area.show() super().mousePressEvent(event) def mouseReleaseEvent(self, event): if self.capture_widget: selectionBBox = self.selection_area.rect() rect = QRect(self.selection_area.pos(), selectionBBox.size()) selectionBBox = self.mapToScene(rect).boundingRect().toRect() self.capture_widget.emit(selectionBBox) self.selection_area.hide() self.selection_area = None QApplication.restoreOverrideCursor() self.capture_widget = None self.selection_area = None super().mouseReleaseEvent(event) def is_scene_larger_than_image(self): """ checks if the entire ViewItem is visible in the scene """ port_rect = self.viewport().rect() scene_rect = self.mapToScene(port_rect).boundingRect() item_rect = self.pix.mapRectFromScene(scene_rect) isec = item_rect.intersected(self.pix.boundingRect()) res = self.pix.get_resolution() if (isec.size().width() >= QSizeF(res).width() and isec.size().height() >= QSizeF(res).height()): return True return False def wheelEvent(self, event): if not self.display_real_image: return # Zoom Factor zoomInFactor = 1.25 zoomOutFactor = 1 / zoomInFactor # Set Anchors self.setTransformationAnchor(QGraphicsView.NoAnchor) self.setResizeAnchor(QGraphicsView.NoAnchor) # Save the scene pos oldPos = self.mapToScene(event.pos()) # Zoom if event.angleDelta().y() > 0: zoomFactor = zoomInFactor else: zoomFactor = zoomOutFactor if (self.is_scene_larger_than_image() and zoomFactor < 1.0): return self.zoom_factor *= zoomFactor # we scale the view itself to get infinite zoom # so that we can inspect a single pixel self.scale(zoomFactor, zoomFactor) # Get the new position newPos = self.mapToScene(event.pos()) # Move scene to old position delta = newPos - oldPos self.translate(delta.x(), delta.y()) self.scene.setSceneRect(self.pix.boundingRect()) def set_scale_position(self, scale_factor, x, y): self.scale(scale_factor, scale_factor) self.translate(x, y) def keyPressEvent(self, event): if self.isFullScreen(): if (event.key() == Qt.Key_F11 or event.key() == Qt.Key_Escape or event.key() == Qt.Key_F): self.destroy_widget.emit() elif self.capture_widget and event.key() == Qt.Key_Escape: self.abort_roi_capture() else: # Ignore event so that parent widgets can use it. # This is only called when we are not fullscreen. # Fullscreen causes us to have no parents. event.ignore() def start_roi_capture(self, finished_signal): """ Capture a region of interest """ self.capture_widget = finished_signal QApplication.setOverrideCursor(Qt.CrossCursor) def abort_roi_capture(self): """ Abort the capture of a regoin of interest """ self.capture_widget = None self.origin = None if self.selection_area: self.selection_area.hide() self.selection_area = None QApplication.restoreOverrideCursor() def add_roi(self, roi_widget): """ Add roi_widget to the QGraphicsScene for display """ if not roi_widget: return self.roi_widgets.append(roi_widget) self.scene.addItem(roi_widget) roi_widget.show() def remove_roi(self, roi_widget): """ Remove given roi widget from the scene """ if not roi_widget: return roi_widget.hide() try: self.roi_widgets.remove(roi_widget) except ValueError as e: # This means the widget is not in the list pass
class MyGraphicsView(CanvasBase): """ This is the used Canvas to print the graphical interface of dxf2gcode. All GUI things should be performed in the View and plotting functions in the scene """ def __init__(self, parent=None): """ Initialisation of the View Object. This is called by the gui created with the QTDesigner. @param parent: Main is passed as a pointer for reference. """ super(MyGraphicsView, self).__init__(parent) self.currentItem = None self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) self.setResizeAnchor(QGraphicsView.AnchorViewCenter) # self.setDragMode(QGraphicsView.RubberBandDrag ) self.setDragMode(QGraphicsView.NoDrag) self.parent = parent self.mppos = None self.rubberBand = QRubberBand(QRubberBand.Rectangle, self) self.prvRectRubberBand = QtCore.QRect() def tr(self, string_to_translate): """ Translate a string using the QCoreApplication translation framework @param string_to_translate: a unicode string @return: the translated unicode string if it was possible to translate """ return text_type(QtCore.QCoreApplication.translate('MyGraphicsView', string_to_translate)) def contextMenuEvent(self, event): """ Create the contextmenu. @purpose: Links the new Class of ContextMenu to Graphicsview. """ position = self.mapToGlobal(event.pos()) GVPos = self.mapToScene(event.pos()) real_pos = Point(GVPos.x(), -GVPos.y()) menu = MyDropDownMenu(self.scene(), position, real_pos) def wheelEvent(self, event): """ With Mouse Wheel the object is scaled @purpose: Scale by mouse wheel @param event: Event Parameters passed to function """ if c.PYQT5notPYQT4: delta = event.angleDelta().y() else: delta = event.delta() scale = (1000 + delta) / 1000.0 self.scale(scale, scale) def mousePressEvent(self, event): """ Right Mouse click shall have no function, Therefore pass only left click event @purpose: Change inherited mousePressEvent @param event: Event Parameters passed to function """ if self.dragMode() == 1: super(MyGraphicsView, self).mousePressEvent(event) elif event.button() == QtCore.Qt.LeftButton: self.mppos = event.pos() else: pass def mouseReleaseEvent(self, event): """ Right Mouse click shall have no function, Therefore pass only left click event @purpose: Change inherited mousePressEvent @param event: Event Parameters passed to function """ delta = 2 if self.dragMode() == 1: # if (event.key() == QtCore.Qt.Key_Shift): # self.setDragMode(QGraphicsView.NoDrag) super(MyGraphicsView, self).mouseReleaseEvent(event) # Selection only enabled for left Button elif event.button() == QtCore.Qt.LeftButton: self.currentItems = [] scene = self.scene() if not self.isMultiSelect: for item in scene.selectedItems(): item.setSelected(False, False) # If the mouse button is pressed without movement of rubberband if self.rubberBand.isHidden(): rect = QtCore.QRect(event.pos().x()-delta, event.pos().y() - delta, 2 * delta, 2*delta) # logger.debug(rect) point = self.mapToScene(event.pos()) min_distance = float(0x7fffffff) for item in self.items(rect): itemDistance = item.contains_point(point) if itemDistance < min_distance: min_distance = itemDistance self.currentItems = item if self.currentItems: if self.currentItems.isSelected(): self.currentItems.setSelected(False, False) else: self.currentItems.setSelected(True, False) else: rect = self.rubberBand.geometry() self.currentItems = self.items(rect) self.rubberBand.hide() # logger.debug("Rubberband Selection") # All items in the selection # self.currentItems = self.items(rect) # print self.currentItems # logger.debug(rect) for item in self.currentItems: if item.isSelected(): item.setSelected(False, False) else: # print (item.flags()) item.setSelected(True, False) else: pass self.mppos = None # super(MyGraphicsView, self).mouseReleaseEvent(event) def mouseMoveEvent(self, event): """ MouseMoveEvent of the Graphiscview. May also be used for the Statusbar. @purpose: Get the MouseMoveEvent and use it for the Rubberband Selection @param event: Event Parameters passed to function """ if self.mppos is not None: Point = event.pos() - self.mppos if Point.manhattanLength() > 3: # print 'the mouse has moved more than 3 pixels since the oldPosition' # print "Mouse Pointer is currently hovering at: ", event.pos() rect = QtCore.QRect(self.mppos, event.pos()) ''' The following is needed because of PyQt5 doesn't like to switch from sign it will keep displaying last rectangle, i.e. you can end up will multiple rectangles ''' if self.prvRectRubberBand.width() > 0 and not rect.width() > 0 or rect.width() == 0 or\ self.prvRectRubberBand.height() > 0 and not rect.height() > 0 or rect.height() == 0: self.rubberBand.hide() self.rubberBand.setGeometry(rect.normalized()) self.rubberBand.show() self.prvRectRubberBand = rect scpoint = self.mapToScene(event.pos()) # self.setStatusTip('X: %3.1f; Y: %3.1f' % (scpoint.x(), -scpoint.y())) # works not as supposed to self.setToolTip('X: %3.1f; Y: %3.1f' %(scpoint.x(), -scpoint.y())) super(MyGraphicsView, self).mouseMoveEvent(event) def autoscale(self): """ Automatically zooms to the full extend of the current GraphicsScene """ scene = self.scene() width = scene.bottomRight.x - scene.topLeft.x height = scene.topLeft.y - scene.bottomRight.y scext = QtCore.QRectF(scene.topLeft.x, -scene.topLeft.y, width * 1.05, height * 1.05) self.fitInView(scext, QtCore.Qt.KeepAspectRatio) logger.debug(self.tr("Autoscaling to extend: %s") % scext) def setShowPathDirections(self, flag): """ This function is called by the Main Window from the Menubar. @param flag: This flag is true if all Path Direction shall be shown """ scene = self.scene() for shape in scene.shapes: shape.starrow.setallwaysshow(flag) shape.enarrow.setallwaysshow(flag) shape.stmove.setallwaysshow(flag) def resetAll(self): """ Deletes the existing GraphicsScene. """ scene = self.scene() del scene
class PieView(QAbstractItemView): def __init__(self, parent=None): super(PieView, self).__init__(parent) self.horizontalScrollBar().setRange(0, 0) self.verticalScrollBar().setRange(0, 0) self.margin = 8 self.totalSize = 300 self.pieSize = self.totalSize - 2 * self.margin self.validItems = 0 self.totalValue = 0.0 self.origin = QPoint() self.rubberBand = None def dataChanged(self, topLeft, bottomRight, roles): super(PieView, self).dataChanged(topLeft, bottomRight, roles) self.validItems = 0 self.totalValue = 0.0 for row in range(self.model().rowCount(self.rootIndex())): index = self.model().index(row, 1, self.rootIndex()) value = self.model().data(index) if value is not None and value > 0.0: self.totalValue += value self.validItems += 1 self.viewport().update() def edit(self, index, trigger, event): if index.column() == 0: return super(PieView, self).edit(index, trigger, event) else: return False def indexAt(self, point): if self.validItems == 0: return QModelIndex() # Transform the view coordinates into contents widget coordinates. wx = point.x() + self.horizontalScrollBar().value() wy = point.y() + self.verticalScrollBar().value() if wx < self.totalSize: cx = wx - self.totalSize / 2 cy = self.totalSize / 2 - wy # positive cy for items above the center # Determine the distance from the center point of the pie chart. d = (cx**2 + cy**2)**0.5 if d == 0 or d > self.pieSize / 2: return QModelIndex() # Determine the angle of the point. angle = (180 / math.pi) * math.acos(cx / d) if cy < 0: angle = 360 - angle # Find the relevant slice of the pie. startAngle = 0.0 for row in range(self.model().rowCount(self.rootIndex())): index = self.model().index(row, 1, self.rootIndex()) value = self.model().data(index) if value > 0.0: sliceAngle = 360 * value / self.totalValue if angle >= startAngle and angle < (startAngle + sliceAngle): return self.model().index(row, 1, self.rootIndex()) startAngle += sliceAngle else: itemHeight = QFontMetrics(self.viewOptions().font).height() listItem = int((wy - self.margin) / itemHeight) validRow = 0 for row in range(self.model().rowCount(self.rootIndex())): index = self.model().index(row, 1, self.rootIndex()) if self.model().data(index) > 0.0: if listItem == validRow: return self.model().index(row, 0, self.rootIndex()) # Update the list index that corresponds to the next valid # row. validRow += 1 return QModelIndex() def isIndexHidden(self, index): return False def itemRect(self, index): if not index.isValid(): return QRect() # Check whether the index's row is in the list of rows represented # by slices. if index.column() != 1: valueIndex = self.model().index(index.row(), 1, self.rootIndex()) else: valueIndex = index if self.model().data(valueIndex) > 0.0: listItem = 0 for row in range(index.row() - 1, -1, -1): if self.model().data(self.model().index( row, 1, self.rootIndex())) > 0.0: listItem += 1 if index.column() == 0: itemHeight = QFontMetrics(self.viewOptions().font).height() return QRect(self.totalSize, int(self.margin + listItem * itemHeight), self.totalSize - self.margin, int(itemHeight)) elif index.column() == 1: return self.viewport().rect() return QRect() def itemRegion(self, index): if not index.isValid(): return QRegion() if index.column() != 1: return QRegion(self.itemRect(index)) if self.model().data(index) <= 0.0: return QRegion() startAngle = 0.0 for row in range(self.model().rowCount(self.rootIndex())): sliceIndex = self.model().index(row, 1, self.rootIndex()) value = self.model().data(sliceIndex) if value > 0.0: angle = 360 * value / self.totalValue if sliceIndex == index: slicePath = QPainterPath() slicePath.moveTo(self.totalSize / 2, self.totalSize / 2) slicePath.arcTo(self.margin, self.margin, self.margin + self.pieSize, self.margin + self.pieSize, startAngle, angle) slicePath.closeSubpath() return QRegion(slicePath.toFillPolygon().toPolygon()) startAngle += angle return QRegion() def horizontalOffset(self): return self.horizontalScrollBar().value() def mousePressEvent(self, event): super(PieView, self).mousePressEvent(event) self.origin = event.pos() if not self.rubberBand: self.rubberBand = QRubberBand(QRubberBand.Rectangle, self) self.rubberBand.setGeometry(QRect(self.origin, QSize())) self.rubberBand.show() def mouseMoveEvent(self, event): if self.rubberBand: self.rubberBand.setGeometry( QRect(self.origin, event.pos()).normalized()) super(PieView, self).mouseMoveEvent(event) def mouseReleaseEvent(self, event): super(PieView, self).mouseReleaseEvent(event) if self.rubberBand: self.rubberBand.hide() self.viewport().update() def moveCursor(self, cursorAction, modifiers): current = self.currentIndex() if cursorAction in (QAbstractItemView.MoveLeft, QAbstractItemView.MoveUp): if current.row() > 0: current = self.model().index(current.row() - 1, current.column(), self.rootIndex()) else: current = self.model().index(0, current.column(), self.rootIndex()) elif cursorAction in (QAbstractItemView.MoveRight, QAbstractItemView.MoveDown): if current.row() < self.rows(current) - 1: current = self.model().index(current.row() + 1, current.column(), self.rootIndex()) else: current = self.model().index( self.rows(current) - 1, current.column(), self.rootIndex()) self.viewport().update() return current def paintEvent(self, event): selections = self.selectionModel() option = self.viewOptions() state = option.state background = option.palette.base() foreground = QPen(option.palette.color(QPalette.WindowText)) textPen = QPen(option.palette.color(QPalette.Text)) highlightedPen = QPen(option.palette.color(QPalette.HighlightedText)) painter = QPainter(self.viewport()) painter.setRenderHint(QPainter.Antialiasing) painter.fillRect(event.rect(), background) painter.setPen(foreground) # Viewport rectangles pieRect = QRect(self.margin, self.margin, self.pieSize, self.pieSize) keyPoint = QPoint(self.totalSize - self.horizontalScrollBar().value(), self.margin - self.verticalScrollBar().value()) if self.validItems > 0: painter.save() painter.translate(pieRect.x() - self.horizontalScrollBar().value(), pieRect.y() - self.verticalScrollBar().value()) painter.drawEllipse(0, 0, self.pieSize, self.pieSize) startAngle = 0.0 for row in range(self.model().rowCount(self.rootIndex())): index = self.model().index(row, 1, self.rootIndex()) value = self.model().data(index) if value > 0.0: angle = 360 * value / self.totalValue colorIndex = self.model().index(row, 0, self.rootIndex()) color = self.model().data(colorIndex, Qt.DecorationRole) if self.currentIndex() == index: painter.setBrush(QBrush(color, Qt.Dense4Pattern)) elif selections.isSelected(index): painter.setBrush(QBrush(color, Qt.Dense3Pattern)) else: painter.setBrush(QBrush(color)) painter.drawPie(0, 0, self.pieSize, self.pieSize, int(startAngle * 16), int(angle * 16)) startAngle += angle painter.restore() keyNumber = 0 for row in range(self.model().rowCount(self.rootIndex())): index = self.model().index(row, 1, self.rootIndex()) value = self.model().data(index) if value > 0.0: labelIndex = self.model().index(row, 0, self.rootIndex()) option = self.viewOptions() option.rect = self.visualRect(labelIndex) if selections.isSelected(labelIndex): option.state |= QStyle.State_Selected if self.currentIndex() == labelIndex: option.state |= QStyle.State_HasFocus self.itemDelegate().paint(painter, option, labelIndex) keyNumber += 1 def resizeEvent(self, event): self.updateGeometries() def rows(self, index): return self.model().rowCount(self.model().parent(index)) def rowsInserted(self, parent, start, end): for row in range(start, end + 1): index = self.model().index(row, 1, self.rootIndex()) value = self.model().data(index) if value is not None and value > 0.0: self.totalValue += value self.validItems += 1 super(PieView, self).rowsInserted(parent, start, end) def rowsAboutToBeRemoved(self, parent, start, end): for row in range(start, end + 1): index = self.model().index(row, 1, self.rootIndex()) value = self.model().data(index) if value is not None and value > 0.0: self.totalValue -= value self.validItems -= 1 super(PieView, self).rowsAboutToBeRemoved(parent, start, end) def scrollContentsBy(self, dx, dy): self.viewport().scroll(dx, dy) def scrollTo(self, index, ScrollHint): area = self.viewport().rect() rect = self.visualRect(index) if rect.left() < area.left(): self.horizontalScrollBar().setValue( self.horizontalScrollBar().value() + rect.left() - area.left()) elif rect.right() > area.right(): self.horizontalScrollBar().setValue( self.horizontalScrollBar().value() + min(rect.right() - area.right(), rect.left() - area.left())) if rect.top() < area.top(): self.verticalScrollBar().setValue( self.verticalScrollBar().value() + rect.top() - area.top()) elif rect.bottom() > area.bottom(): self.verticalScrollBar().setValue( self.verticalScrollBar().value() + min(rect.bottom() - area.bottom(), rect.top() - area.top())) def setSelection(self, rect, command): # Use content widget coordinates because we will use the itemRegion() # function to check for intersections. contentsRect = rect.translated( self.horizontalScrollBar().value(), self.verticalScrollBar().value()).normalized() rows = self.model().rowCount(self.rootIndex()) columns = self.model().columnCount(self.rootIndex()) indexes = [] for row in range(rows): for column in range(columns): index = self.model().index(row, column, self.rootIndex()) region = self.itemRegion(index) if not region.intersect(QRegion(contentsRect)).isEmpty(): indexes.append(index) if len(indexes) > 0: firstRow = indexes[0].row() lastRow = indexes[0].row() firstColumn = indexes[0].column() lastColumn = indexes[0].column() for i in range(1, len(indexes)): firstRow = min(firstRow, indexes[i].row()) lastRow = max(lastRow, indexes[i].row()) firstColumn = min(firstColumn, indexes[i].column()) lastColumn = max(lastColumn, indexes[i].column()) selection = QItemSelection( self.model().index(firstRow, firstColumn, self.rootIndex()), self.model().index(lastRow, lastColumn, self.rootIndex())) self.selectionModel().select(selection, command) else: noIndex = QModelIndex() selection = QItemSelection(noIndex, noIndex) self.selectionModel().select(selection, command) self.update() def updateGeometries(self): self.horizontalScrollBar().setPageStep(self.viewport().width()) self.horizontalScrollBar().setRange( 0, max(0, 2 * self.totalSize - self.viewport().width())) self.verticalScrollBar().setPageStep(self.viewport().height()) self.verticalScrollBar().setRange( 0, max(0, self.totalSize - self.viewport().height())) def verticalOffset(self): return self.verticalScrollBar().value() def visualRect(self, index): rect = self.itemRect(index) if rect.isValid(): return QRect(rect.left() - self.horizontalScrollBar().value(), rect.top() - self.verticalScrollBar().value(), rect.width(), rect.height()) else: return rect def visualRegionForSelection(self, selection): region = QRegion() for span in selection: for row in range(span.top(), span.bottom() + 1): for col in range(span.left(), span.right() + 1): index = self.model().index(row, col, self.rootIndex()) region += self.visualRect(index) return region
class FsApp(QMainWindow): def __init__(self, app): super(FsApp, self).__init__() self.app = app self.selRect = None self.screenPix = None # Set up the user interface from Designer. self.ui = Ui_MainWindow() self.ui.setupUi(self) #self.setWindowOpacity(0.5) #self.setAttribute(QtCore.Qt.WA_TranslucentBackground, True) self.captureScreen() self.showMaximized() self.showFullScreen() #capture screen and display rec = QApplication.desktop().screenGeometry() height = rec.height() width = rec.width() self.ui.labPix.resize(width, height) self.ui.labPix.setScaledContents(True) self.ui.labPix.setPixmap(self.screenPix) #start rubberband self.rubberband = QRubberBand(QRubberBand.Rectangle, self) bla = QtGui.QPalette() bla.setBrush(QtGui.QPalette.Highlight, QtGui.QBrush(QtCore.Qt.red)) self.rubberband.setPalette(bla) self.rubberband.setWindowOpacity(1.0) def captureScreen(self): screens = self.app.screens() self.screenPix = screens[0].grabWindow(0) def mousePressEvent(self, event): self.origin = event.pos() self.rubberband.setGeometry(QtCore.QRect(self.origin, QtCore.QSize())) self.rubberband.show() QWidget.mousePressEvent(self, event) def mouseMoveEvent(self, event): if self.rubberband.isVisible(): self.rubberband.setGeometry(QtCore.QRect(self.origin, event.pos()).normalized()) QWidget.mouseMoveEvent(self, event) def mouseReleaseEvent(self, event): if self.rubberband.isVisible(): self.selRect = self.rubberband.geometry() self.rubberband.hide() codePix = self.screenPix.copy( self.selRect.x(), self.selRect.y(), self.selRect.width(), self.selRect.height()) QApplication.clipboard().setPixmap(codePix) self.exit() QWidget.mouseReleaseEvent(self, event) def exit(self): sys.exit(0)
class DragWidget(QWidget): spacerX = 16 spacerY = 16 clipicon = None new_window_signal = pyqtSignal(str) query = pyqtSignal() src_dragwidget = None src_selected = [] def __init__(self, path, parent=None): super(DragWidget, self).__init__(parent) self.setMinimumSize(400, 200) self.setAcceptDrops(True) self.parent = parent # self.parent.menu.connect(self.delete_icon) self.modifier = False self.rubberband = QRubberBand(QRubberBand.Rectangle, self) self.path = path self.icons = [] self.icon_offsetx = 0 self.icon_offsety = 0 # self.clipicon = None # self.moving_icons = [] self.read_drawer() self.clean_up() # print(type(IconWidget.icon)) # print(self.findChildren(ClickableIcon)) def read_drawer(self): # self.icons.clear() for item in os.scandir(self.path): if item.is_dir(): icon_widget = IconWidget(parent=self, name=item.name, path=self.path, dir=True) else: icon_widget = IconWidget(parent=self, name=item.name, path=self.path, dir=False) icon_widget.new_window.connect(self.new_window_signal.emit) icon_widget.clipboard.connect(self.on_clipboard) self.icons.append(icon_widget) self.icons[-1].setAttribute(Qt.WA_DeleteOnClose) # self.update() def updateScrollArea(self): """ set the dimension of the widget """ iconx = [] icony = [] if len(self.icons) > 0: for item in self.icons: iconx.append(item.x()) icony.append(item.y()) self.setMinimumWidth(max(iconx) + 75) self.setMinimumHeight(max(icony) + 75) def dragEnterEvent(self, event): event.accept() def dragMoveEvent(self, event): event.accept() def get_dnd_list(self, event): icon_list = [] icon_offsetx = None icon_offsety = None if len(DragWidget.src_selected) > 0: for item in DragWidget.src_selected: icon_list.append(item) else: icon_list.append(event.source()) return icon_list def create_icon(self, name, drawer): if drawer: icon_widget = IconWidget(self, name=name, path=self.path, dir=True) else: icon_widget = IconWidget(self, name=name, path=self.path, dir=False) icon_widget.new_window.connect(self.new_window_signal.emit) self.icons.append(icon_widget) def place_icon(self, x, y): self.icons[-1].move(x, y) self.icons[-1].show() def dropEvent(self, event): event.accept() icon_list = self.get_dnd_list(event) icon_offsetx = event.pos().x() icon_offsety = event.pos().y() for item in icon_list: name = item.name drawer = item.drawer src_path = item.path + "/" + name dst_path = self.path + "/" if event.mimeData().hasFormat("application/x-icon"): self.create_icon(name, drawer) self.move_data(src_path, dst_path) self.place_icon(icon_offsetx, icon_offsety) icon_offsetx += 100 if icon_offsetx > self.window().width(): icon_offsetx = event.pos().x() icon_offsety += 75 icon_offsetx = None icon_offsety = None self.updateScrollArea() def clear_dnd(self): DragWidget.src_dragwidget = None DragWidget.src_selected.clear() def get_modifier(self): return self.parent.modifier def mousePressEvent(self, event): if event.buttons() == Qt.LeftButton: for item in self.icons: item.icon.deselect_icon() self.clear_dnd() self.origin = event.pos() self.rubberband.setGeometry(QRect(self.origin, QSize())) self.rubberband.show() def mouseMoveEvent(self, event): if self.rubberband.isVisible(): self.rubberband.setGeometry( QRect(self.origin, event.pos()).normalized()) # QWidget.mouseMoveEvent(self, event) def mouseReleaseEvent(self, event): self.clear_dnd() if self.rubberband.isVisible(): self.rubberband.hide() rect = self.rubberband.geometry() for child in self.findChildren(IconWidget): if rect.intersects(child.geometry()): child.icon.select_icon() DragWidget.src_selected.append(child) if DragWidget.src_dragwidget is not self: DragWidget.src_dragwidget = self def mouseDoubleClickEvent(self, event): print(BLU, "Double Click", END) self.query.emit() def create_file(self): new_file = self.path + "/" + "newfile.txt" open(new_file, 'w').close() icon_widget = IconWidget(self, name="newfile.txt", path=self.path, dir=False) icon_widget.new_window.connect(self.new_window_signal.emit) icon_widget.show() self.icons.append(icon_widget) def create_drawer(self): print("creating new drawer") new_drawer = self.path + "/" + "NewDrawer" os.makedirs(new_drawer) icon_widget = IconWidget(self, name="NewDrawer", path=self.path, dir=True) icon_widget.new_window.connect(self.new_window_signal.emit) icon_widget.show() self.icons.append(icon_widget) def rename_file(self): print("renaming file") def clean_up(self): for item in self.icons: item.move(DragWidget.spacerX, DragWidget.spacerY) # initial icon placement DragWidget.spacerX += 100 if DragWidget.spacerX + 100 > self.window().width(): DragWidget.spacerY += 75 DragWidget.spacerX = 16 # reset placement values DragWidget.spacerX = 16 DragWidget.spacerY = 16 self.updateScrollArea() def move_data(self, source, dest): srce_path = source.rsplit('/', 1)[0] dest_path = dest.rsplit('/', 1)[0] if srce_path != dest_path: try: shutil.move(source, dest) except Exception as err: print(err) def copy_icon(self, source, dest): pass def delete_icon(self): dest = os.path.expanduser("~") + "/.Trash/" error_string = "" for item in self.icons: if item.icon.selected: source = item.path + "/" + item.name if source is not "": try: shutil.move(source, dest) except Exception as err: error_string += str(err) + "\n" + "\n" else: self.icons.remove(item) item.deleteLater() if error_string is not "": QMessageBox.information(self, 'Info', error_string, QMessageBox.Ok) def paste_icon(self): print("---") print("srce=", self.clipicon.path + "/" + self.clipicon.name) # print("res=", self.clipicon.path + "/" + self.clipicon.name) print("dest=", self.path + "/") # if os.path.isdir(os.path.join(self.clipicon.path, self.clipicon.name)): SRCE = self.clipicon.path + "/" + self.clipicon.name DEST = self.path + "/" + self.clipicon.name shutil.copytree(SRCE, DEST) def on_clipboard(self, icon): print("realpath", self.path) print("clip_icon_name=", icon.name) DragWidget.clipicon = icon
class ImageViewer(QGraphicsView, QObject): def __init__(self, parent=None): super(ImageViewer, self).__init__(parent) self.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform) #self.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop) self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) self.setDragMode(QGraphicsView.ScrollHandDrag) #self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) #self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) self._scene = ImageViewerScene(self) self.setScene(self._scene) self._create_grid() self._create_grid_lines() self._pixmap = None self._selection_mode = SELECTION_MODE.NONE # polygon selection _polygon_guide_line_pen = QPen(QtGui.QColor(235, 72, 40)) _polygon_guide_line_pen.setWidth(2) _polygon_guide_line_pen.setStyle(QtCore.Qt.DotLine) self._polygon_guide_line = QGraphicsLineItem() self._polygon_guide_line.setVisible(False) self._polygon_guide_line.setPen(_polygon_guide_line_pen) self._scene.addItem(self._polygon_guide_line) self._current_polygon = None # rectangle selection self._box_origin = QPoint() self._box_picker = QRubberBand(QRubberBand.Rectangle, self) # free selection self._current_free_path = None self._is_drawing = False self._last_point_drawn = QPoint() self._current_label = None @property def current_label(self): return self._current_label @current_label.setter def current_label(self, value): self._current_label = value @property def pixmap(self) -> ImagePixmap: return self._pixmap @pixmap.setter def pixmap(self, value: QPixmap): self.selection_mode = SELECTION_MODE.NONE self.resetTransform() if self.pixmap: self._scene.removeItem(self._pixmap) self.remove_annotations() self._pixmap = ImagePixmap() self._pixmap.setPixmap(value) self._pixmap.setOffset(-value.width() / 2, -value.height() / 2) self._pixmap.setTransformationMode(QtCore.Qt.SmoothTransformation) self._pixmap.signals.hoverEnterEventSgn.connect( self.pixmap_hoverEnterEvent_slot) self._pixmap.signals.hoverLeaveEventSgn.connect( self.pixmap_hoverLeaveEvent_slot) self._pixmap.signals.hoverMoveEventSgn.connect( self.pixmap_hoverMoveEvent_slot) self._scene.addItem(self._pixmap) # rect=self._scene.addRect(QtCore.QRectF(0,0,100,100), QtGui.QPen(QtGui.QColor("red"))) # rect.setZValue(1.0) self.fit_to_window() @property def selection_mode(self): return self._selection_mode @selection_mode.setter def selection_mode(self, value): self._polygon_guide_line.hide() self._current_polygon = None self._current_free_path = None self._is_drawing = value == SELECTION_MODE.FREE if value == SELECTION_MODE.NONE: self.enable_items(True) else: self.enable_items(False) self._selection_mode = value def remove_annotations(self): for item in self._scene.items(): if isinstance(item, EditableBox): self._scene.removeItem(item) elif isinstance(item, EditablePolygon): item.delete_polygon() def remove_annotations_by_label(self, label_name): for item in self._scene.items(): if isinstance(item, EditableBox): if item.label and item.label.name == label_name: self._scene.removeItem(item) elif isinstance(item, EditablePolygon): if item.label and item.label.name == label_name: item.delete_polygon() def enable_items(self, value): for item in self._scene.items(): if isinstance(item, EditablePolygon) or isinstance( item, EditableBox): item.setEnabled(value) def _create_grid(self): gridSize = 15 backgroundPixmap = QtGui.QPixmap(gridSize * 2, gridSize * 2) #backgroundPixmap.fill(QtGui.QColor("white")) backgroundPixmap.fill(QtGui.QColor(20, 20, 20)) #backgroundPixmap.fill(QtGui.QColor("powderblue")) painter = QtGui.QPainter(backgroundPixmap) #backgroundColor=QtGui.QColor("palegoldenrod") #backgroundColor=QtGui.QColor(237,237,237) backgroundColor = QtGui.QColor(0, 0, 0) painter.fillRect(0, 0, gridSize, gridSize, backgroundColor) painter.fillRect(gridSize, gridSize, gridSize, gridSize, backgroundColor) painter.end() self._scene.setBackgroundBrush(QtGui.QBrush(backgroundPixmap)) def _create_grid_lines(self): pen_color = QColor(255, 255, 255, 255) pen = QPen(pen_color) pen.setWidth(2) pen.setStyle(QtCore.Qt.DotLine) self.vline = QGraphicsLineItem() self.vline.setVisible(False) self.vline.setPen(pen) self.hline = QGraphicsLineItem() self.hline.setVisible(False) self.hline.setPen(pen) self._scene.addItem(self.vline) self._scene.addItem(self.hline) def wheelEvent(self, event: QWheelEvent): adj = (event.angleDelta().y() / 120) * 0.1 self.scale(1 + adj, 1 + adj) def fit_to_window(self): """Fit image within view.""" if not self.pixmap or not self._pixmap.pixmap(): return #self._pixmap.setTransformationMode(QtCore.Qt.SmoothTransformation) self.fitInView(self._pixmap, QtCore.Qt.KeepAspectRatio) def show_guide_lines(self): if self.hline and self.vline: self.hline.show() self.vline.show() def hide_guide_lines(self): if self.hline and self.vline: self.hline.hide() self.vline.hide() def pixmap_hoverEnterEvent_slot(self): self.show_guide_lines() def pixmap_hoverLeaveEvent_slot(self): self.hide_guide_lines() def pixmap_hoverMoveEvent_slot(self, evt: QGraphicsSceneHoverEvent, x, y): bbox: QRect = self._pixmap.boundingRect() offset = QPointF(bbox.width() / 2, bbox.height() / 2) self.vline.setLine(x, -offset.y(), x, bbox.height() - offset.y()) self.vline.setZValue(1) self.hline.setLine(-offset.x(), y, bbox.width() - offset.x(), y) self.hline.setZValue(1) def mouseMoveEvent(self, evt: QtGui.QMouseEvent) -> None: if self.selection_mode == SELECTION_MODE.BOX: if not self._box_origin.isNull(): self._box_picker.setGeometry( QRect(self._box_origin, evt.pos()).normalized()) elif self.selection_mode == SELECTION_MODE.POLYGON: if self._current_polygon: if self._current_polygon.count > 0: last_point: QPointF = self._current_polygon.last_point self._polygon_guide_line.setZValue(1) self._polygon_guide_line.show() mouse_pos = self.mapToScene(evt.pos()) self._polygon_guide_line.setLine(last_point.x(), last_point.y(), mouse_pos.x(), mouse_pos.y()) else: self._polygon_guide_line.hide() elif self.selection_mode == SELECTION_MODE.FREE and evt.buttons( ) and QtCore.Qt.LeftButton: if self._current_free_path: painter: QPainterPath = self._current_free_path.path() self._last_point_drawn = self.mapToScene(evt.pos()) painter.lineTo(self._last_point_drawn) self._current_free_path.setPath(painter) super(ImageViewer, self).mouseMoveEvent(evt) def mousePressEvent(self, evt: QtGui.QMouseEvent) -> None: if evt.buttons() == QtCore.Qt.LeftButton: if self.selection_mode == SELECTION_MODE.BOX: self.setDragMode(QGraphicsView.NoDrag) self._box_origin = evt.pos() self._box_picker.setGeometry(QRect(self._box_origin, QSize())) self._box_picker.show() elif self._selection_mode == SELECTION_MODE.POLYGON: pixmap_rect: QRectF = self._pixmap.boundingRect() new_point = self.mapToScene(evt.pos()) # consider only the points intothe image if pixmap_rect.contains(new_point): if self._current_polygon is None: self._current_polygon = EditablePolygon() self._current_polygon.signals.deleted.connect( self.delete_polygon_slot) self._scene.addItem(self._current_polygon) self._current_polygon.addPoint(new_point) else: self._current_polygon.addPoint(new_point) elif self._selection_mode == SELECTION_MODE.FREE: # start drawing new_point = self.mapToScene(evt.pos()) pixmap_rect: QRectF = self._pixmap.boundingRect() # consider only the points intothe image if pixmap_rect.contains(new_point): self.setDragMode(QGraphicsView.NoDrag) pen = QPen(QtGui.QColor(235, 72, 40)) pen.setWidth(10) self._last_point_drawn = new_point self._current_free_path = QGraphicsPathItem() self._current_free_path.setOpacity(0.6) self._current_free_path.setPen(pen) painter = QPainterPath() painter.moveTo(self._last_point_drawn) self._current_free_path.setPath(painter) self._scene.addItem(self._current_free_path) else: self.setDragMode(QGraphicsView.ScrollHandDrag) super(ImageViewer, self).mousePressEvent(evt) def mouseReleaseEvent(self, evt: QtGui.QMouseEvent) -> None: if evt.button() == QtCore.Qt.LeftButton: if self.selection_mode == SELECTION_MODE.BOX: roi: QRect = self._box_picker.geometry() roi: QRectF = self.mapToScene(roi).boundingRect() pixmap_rect = self._pixmap.boundingRect() self._box_picker.hide() if pixmap_rect == roi.united(pixmap_rect): rect = EditableBox(roi) rect.label = self.current_label self._scene.addItem(rect) self.selection_mode = SELECTION_MODE.NONE self.setDragMode(QGraphicsView.ScrollHandDrag) elif self.selection_mode == SELECTION_MODE.FREE and self._current_free_path: # create polygon self._current_free_path: QGraphicsPathItem path_rect = self._current_free_path.boundingRect() pixmap_rect = self._pixmap.boundingRect() if pixmap_rect == path_rect.united(pixmap_rect): path = self._current_free_path.path() path_polygon = EditablePolygon() path_polygon.label = self.current_label self._scene.addItem(path_polygon) for i in range(0, path.elementCount(), 10): x, y = path.elementAt(i).x, path.elementAt(i).y path_polygon.addPoint(QPointF(x, y)) self._scene.removeItem(self._current_free_path) self.selection_mode = SELECTION_MODE.NONE self.setDragMode(QGraphicsView.ScrollHandDrag) super(ImageViewer, self).mouseReleaseEvent(evt) def keyPressEvent(self, event: QtGui.QKeyEvent) -> None: if self._current_polygon and event.key() == QtCore.Qt.Key_Space: points = self._current_polygon.points self._current_polygon.label = self.current_label self._current_polygon = None self.selection_mode = SELECTION_MODE.NONE self._polygon_guide_line.hide() self.setDragMode(QGraphicsView.ScrollHandDrag) super(ImageViewer, self).keyPressEvent(event) def delete_polygon_slot(self, polygon: EditablePolygon): self._current_polygon = None self.selection_mode = SELECTION_MODE.NONE self._polygon_guide_line.hide()
class ImageHolder(QWidget): foregroundState = list() foreground = None mousePressPoint = None cropMode = False def __init__(self, image): super(ImageHolder, self).__init__() self.image = image self.setMinimumSize(self.image.size()) self.resize(self.image.size()) self.foreground = QImage(self.image.size(),QImage.Format_ARGB32_Premultiplied) undo = QShortcut(QKeySequence("Ctrl+z"), self) undo.activated.connect(self.undo) crop = QShortcut(QKeySequence("c"), self) crop.activated.connect(self.toggleCropMode) def applyState(self): print("apply state") if self.cropMode: rect = self.currentQRubberBand.geometry() self.image = self.image.copy(rect) self.setMinimumSize(self.image.size()) self.resize(self.image.size()) QApplication.restoreOverrideCursor() self.toggleCropMode() self.currentQRubberBand.hide() self.repaint() def cancelState(self): if cropMode: self.currentQRubberBand.hide() def toggleCropMode(self): self.cropMode = not self.cropMode if self.cropMode: QApplication.setOverrideCursor(QCursor(Qt.CrossCursor)) else: QApplication.restoreOverrideCursor() def mousePressEvent(self, event): print("ImageHolder: " + str(event.pos())) self.mousePressPoint = event.pos(); if self.cropMode: if hasattr(self, "currentQRubberBand"): self.currentQRubberBand.hide() self.currentQRubberBand = QRubberBand(QRubberBand.Rectangle, self) self.currentQRubberBand.setGeometry(QRect(self.mousePressPoint, QSize())) self.currentQRubberBand.show() def mouseMoveEvent(self, event): print("mouseMove: " + str(event.pos())) if self.cropMode: self.currentQRubberBand.setGeometry(QRect(self.mousePressPoint, event.pos()).normalized()) def undo(self): if self.cropMode: self.currentQRubberBand.hide() elif len(self.foregroundState) > 0: self.foreground = self.foregroundState.pop() self.repaint() def mouseReleaseEvent(self, event): if not self.cropMode: self.foregroundState.append(QImage(self.foreground)) self.painter.begin(self.foreground) self.painter.setPen(QPen(QBrush(QColor(255,241,18,100)), 15, Qt.SolidLine, Qt.RoundCap)) self.painter.drawLine(QLine(self.mousePressPoint.x(),self.mousePressPoint.y(), event.pos().x(), event.pos().y())) self.painter.end() self.repaint() def saveChanges(self): newImage = QImage(self.image.size(), QImage.Format_ARGB32_Premultiplied) painter = QPainter(newImage) painter.drawImage(0,0, self.image) painter.drawImage(0,0, self.foreground) painter.end() return newImage def paintEvent(self, event): self.painter = QPainter(self) self.painter.setPen(QPen(QBrush(QColor(255,241,18,100)), 15, Qt.SolidLine, Qt.RoundCap)) self.painter.drawImage(0,0, self.image) self.painter.drawImage(0,0, self.foreground) self.painter.end()
class DragWidget(QWidget): spacerX = 16 spacerY = 16 clipicon = None new_window_signal = pyqtSignal(str) query = pyqtSignal() src_dragwidget = None src_selected = [] def __init__(self, path, parent=None): super(DragWidget, self).__init__(parent) self.setMinimumSize(400, 200) self.setAcceptDrops(True) self.parent = parent # self.parent.menu.connect(self.delete_icon) self.modifier = False self.rubberband = QRubberBand(QRubberBand.Rectangle, self) self.path = path self.icons = [] self.icon_offsetx = 0 self.icon_offsety = 0 # self.clipicon = None # self.moving_icons = [] self.read_drawer() self.clean_up() # print(type(IconWidget.icon)) # print(self.findChildren(ClickableIcon)) def read_drawer(self): # self.icons.clear() for item in os.scandir(self.path): if item.is_dir(): icon_widget = IconWidget(parent=self, name=item.name, path=self.path, dir=True) else: icon_widget = IconWidget(parent=self, name=item.name, path=self.path, dir=False) icon_widget.new_window.connect(self.new_window_signal.emit) icon_widget.clipboard.connect(self.on_clipboard) self.icons.append(icon_widget) self.icons[-1].setAttribute(Qt.WA_DeleteOnClose) # self.update() def updateScrollArea(self): """ set the dimension of the widget """ iconx = [] icony = [] if len(self.icons) > 0: for item in self.icons: iconx.append(item.x()) icony.append(item.y()) self.setMinimumWidth(max(iconx)+75) self.setMinimumHeight(max(icony)+75) def dragEnterEvent(self, event): event.accept() def dragMoveEvent(self, event): event.accept() def get_dnd_list(self, event): icon_list = [] icon_offsetx = None icon_offsety = None if len(DragWidget.src_selected) > 0: for item in DragWidget.src_selected: icon_list.append(item) else: icon_list.append(event.source()) return icon_list def create_icon(self, name, drawer): if drawer: icon_widget = IconWidget(self, name=name, path=self.path, dir=True) else: icon_widget = IconWidget(self, name=name, path=self.path, dir=False) icon_widget.new_window.connect(self.new_window_signal.emit) self.icons.append(icon_widget) def place_icon(self, x, y): self.icons[-1].move(x, y) self.icons[-1].show() def dropEvent(self, event): event.accept() icon_list = self.get_dnd_list(event) icon_offsetx = event.pos().x() icon_offsety = event.pos().y() for item in icon_list: name = item.name drawer = item.drawer src_path = item.path + "/" + name dst_path = self.path + "/" if event.mimeData().hasFormat("application/x-icon"): self.create_icon(name, drawer) self.move_data(src_path, dst_path) self.place_icon(icon_offsetx, icon_offsety) icon_offsetx += 100 if icon_offsetx > self.window().width(): icon_offsetx = event.pos().x() icon_offsety += 75 icon_offsetx = None icon_offsety = None self.updateScrollArea() def clear_dnd(self): DragWidget.src_dragwidget = None DragWidget.src_selected.clear() def get_modifier(self): return self.parent.modifier def mousePressEvent(self, event): if event.buttons() == Qt.LeftButton: for item in self.icons: item.icon.deselect_icon() self.clear_dnd() self.origin = event.pos() self.rubberband.setGeometry(QRect(self.origin, QSize())) self.rubberband.show() def mouseMoveEvent(self, event): if self.rubberband.isVisible(): self.rubberband.setGeometry( QRect(self.origin, event.pos()).normalized()) # QWidget.mouseMoveEvent(self, event) def mouseReleaseEvent(self, event): self.clear_dnd() if self.rubberband.isVisible(): self.rubberband.hide() rect = self.rubberband.geometry() for child in self.findChildren(IconWidget): if rect.intersects(child.geometry()): child.icon.select_icon() DragWidget.src_selected.append(child) if DragWidget.src_dragwidget is not self: DragWidget.src_dragwidget = self def mouseDoubleClickEvent(self, event): print(BLU, "Double Click", END) self.query.emit() def create_file(self): new_file = self.path + "/" + "newfile.txt" open(new_file, 'w').close() icon_widget = IconWidget(self, name="newfile.txt", path=self.path, dir=False) icon_widget.new_window.connect(self.new_window_signal.emit) icon_widget.show() self.icons.append(icon_widget) def create_drawer(self): print("creating new drawer") new_drawer = self.path + "/" + "NewDrawer" os.makedirs(new_drawer) icon_widget = IconWidget(self, name="NewDrawer", path=self.path, dir=True) icon_widget.new_window.connect(self.new_window_signal.emit) icon_widget.show() self.icons.append(icon_widget) def rename_file(self): print("renaming file") def clean_up(self): for item in self.icons: item.move(DragWidget.spacerX, DragWidget.spacerY) # initial icon placement DragWidget.spacerX += 100 if DragWidget.spacerX + 100 > self.window().width(): DragWidget.spacerY += 75 DragWidget.spacerX = 16 # reset placement values DragWidget.spacerX = 16 DragWidget.spacerY = 16 self.updateScrollArea() def move_data(self, source, dest): srce_path = source.rsplit('/', 1)[0] dest_path = dest.rsplit('/', 1)[0] if srce_path != dest_path: try: shutil.move(source, dest) except Exception as err: print(err) def copy_icon(self, source, dest): pass def delete_icon(self): dest = os.path.expanduser("~") + "/.Trash/" error_string = "" for item in self.icons: if item.icon.selected: source = item.path + "/" + item.name if source is not "": try: shutil.move(source, dest) except Exception as err: error_string += str(err) + "\n" + "\n" else: self.icons.remove(item) item.deleteLater() if error_string is not "": QMessageBox.information(self, 'Info', error_string, QMessageBox.Ok) def paste_icon(self): print("---") print("srce=", self.clipicon.path + "/" + self.clipicon.name) # print("res=", self.clipicon.path + "/" + self.clipicon.name) print("dest=", self.path + "/") # if os.path.isdir(os.path.join(self.clipicon.path, self.clipicon.name)): SRCE = self.clipicon.path + "/" + self.clipicon.name DEST = self.path+"/" + self.clipicon.name shutil.copytree(SRCE, DEST) def on_clipboard(self, icon): print("realpath", self.path) print("clip_icon_name=", icon.name) DragWidget.clipicon = icon
class PieView(QAbstractItemView): def __init__(self, parent=None): super(PieView, self).__init__(parent) self.horizontalScrollBar().setRange(0, 0) self.verticalScrollBar().setRange(0, 0) self.margin = 8 self.totalSize = 300 self.pieSize = self.totalSize - 2*self.margin self.validItems = 0 self.totalValue = 0.0 self.origin = QPoint() self.rubberBand = None def dataChanged(self, topLeft, bottomRight, roles): super(PieView, self).dataChanged(topLeft, bottomRight, roles) self.validItems = 0 self.totalValue = 0.0 for row in range(self.model().rowCount(self.rootIndex())): index = self.model().index(row, 1, self.rootIndex()) value = self.model().data(index) if value is not None and value > 0.0: self.totalValue += value self.validItems += 1 self.viewport().update() def edit(self, index, trigger, event): if index.column() == 0: return super(PieView, self).edit(index, trigger, event) else: return False def indexAt(self, point): if self.validItems == 0: return QModelIndex() # Transform the view coordinates into contents widget coordinates. wx = point.x() + self.horizontalScrollBar().value() wy = point.y() + self.verticalScrollBar().value() if wx < self.totalSize: cx = wx - self.totalSize/2 cy = self.totalSize/2 - wy; # positive cy for items above the center # Determine the distance from the center point of the pie chart. d = (cx**2 + cy**2)**0.5 if d == 0 or d > self.pieSize/2: return QModelIndex() # Determine the angle of the point. angle = (180 / math.pi) * math.acos(cx/d) if cy < 0: angle = 360 - angle # Find the relevant slice of the pie. startAngle = 0.0 for row in range(self.model().rowCount(self.rootIndex())): index = self.model().index(row, 1, self.rootIndex()) value = self.model().data(index) if value > 0.0: sliceAngle = 360*value/self.totalValue if angle >= startAngle and angle < (startAngle + sliceAngle): return self.model().index(row, 1, self.rootIndex()) startAngle += sliceAngle else: itemHeight = QFontMetrics(self.viewOptions().font).height() listItem = int((wy - self.margin) / itemHeight) validRow = 0 for row in range(self.model().rowCount(self.rootIndex())): index = self.model().index(row, 1, self.rootIndex()) if self.model().data(index) > 0.0: if listItem == validRow: return self.model().index(row, 0, self.rootIndex()) # Update the list index that corresponds to the next valid # row. validRow += 1 return QModelIndex() def isIndexHidden(self, index): return False def itemRect(self, index): if not index.isValid(): return QRect() # Check whether the index's row is in the list of rows represented # by slices. if index.column() != 1: valueIndex = self.model().index(index.row(), 1, self.rootIndex()) else: valueIndex = index if self.model().data(valueIndex) > 0.0: listItem = 0 for row in range(index.row()-1, -1, -1): if self.model().data(self.model().index(row, 1, self.rootIndex())) > 0.0: listItem += 1 if index.column() == 0: itemHeight = QFontMetrics(self.viewOptions().font).height() return QRect(self.totalSize, int(self.margin + listItem*itemHeight), self.totalSize - self.margin, int(itemHeight)) elif index.column() == 1: return self.viewport().rect() return QRect() def itemRegion(self, index): if not index.isValid(): return QRegion() if index.column() != 1: return QRegion(self.itemRect(index)) if self.model().data(index) <= 0.0: return QRegion() startAngle = 0.0 for row in range(self.model().rowCount(self.rootIndex())): sliceIndex = self.model().index(row, 1, self.rootIndex()) value = self.model().data(sliceIndex) if value > 0.0: angle = 360*value/self.totalValue if sliceIndex == index: slicePath = QPainterPath() slicePath.moveTo(self.totalSize/2, self.totalSize/2) slicePath.arcTo(self.margin, self.margin, self.margin+self.pieSize, self.margin+self.pieSize, startAngle, angle) slicePath.closeSubpath() return QRegion(slicePath.toFillPolygon().toPolygon()) startAngle += angle return QRegion() def horizontalOffset(self): return self.horizontalScrollBar().value() def mousePressEvent(self, event): super(PieView, self).mousePressEvent(event) self.origin = event.pos() if not self.rubberBand: self.rubberBand = QRubberBand(QRubberBand.Rectangle, self) self.rubberBand.setGeometry(QRect(self.origin, QSize())) self.rubberBand.show() def mouseMoveEvent(self, event): if self.rubberBand: self.rubberBand.setGeometry(QRect(self.origin, event.pos()).normalized()) super(PieView, self).mouseMoveEvent(event) def mouseReleaseEvent(self, event): super(PieView, self).mouseReleaseEvent(event) if self.rubberBand: self.rubberBand.hide() self.viewport().update() def moveCursor(self, cursorAction, modifiers): current = self.currentIndex() if cursorAction in (QAbstractItemView.MoveLeft, QAbstractItemView.MoveUp): if current.row() > 0: current = self.model().index(current.row() - 1, current.column(), self.rootIndex()) else: current = self.model().index(0, current.column(), self.rootIndex()) elif cursorAction in (QAbstractItemView.MoveRight, QAbstractItemView.MoveDown): if current.row() < self.rows(current) - 1: current = self.model().index(current.row() + 1, current.column(), self.rootIndex()) else: current = self.model().index(self.rows(current) - 1, current.column(), self.rootIndex()) self.viewport().update() return current def paintEvent(self, event): selections = self.selectionModel() option = self.viewOptions() state = option.state background = option.palette.base() foreground = QPen(option.palette.color(QPalette.WindowText)) textPen = QPen(option.palette.color(QPalette.Text)) highlightedPen = QPen(option.palette.color(QPalette.HighlightedText)) painter = QPainter(self.viewport()) painter.setRenderHint(QPainter.Antialiasing) painter.fillRect(event.rect(), background) painter.setPen(foreground) # Viewport rectangles pieRect = QRect(self.margin, self.margin, self.pieSize, self.pieSize) keyPoint = QPoint(self.totalSize - self.horizontalScrollBar().value(), self.margin - self.verticalScrollBar().value()) if self.validItems > 0: painter.save() painter.translate(pieRect.x() - self.horizontalScrollBar().value(), pieRect.y() - self.verticalScrollBar().value()) painter.drawEllipse(0, 0, self.pieSize, self.pieSize) startAngle = 0.0 for row in range(self.model().rowCount(self.rootIndex())): index = self.model().index(row, 1, self.rootIndex()) value = self.model().data(index) if value > 0.0: angle = 360*value/self.totalValue colorIndex = self.model().index(row, 0, self.rootIndex()) color = self.model().data(colorIndex, Qt.DecorationRole) if self.currentIndex() == index: painter.setBrush(QBrush(color, Qt.Dense4Pattern)) elif selections.isSelected(index): painter.setBrush(QBrush(color, Qt.Dense3Pattern)) else: painter.setBrush(QBrush(color)) painter.drawPie(0, 0, self.pieSize, self.pieSize, int(startAngle*16), int(angle*16)) startAngle += angle painter.restore() keyNumber = 0 for row in range(self.model().rowCount(self.rootIndex())): index = self.model().index(row, 1, self.rootIndex()) value = self.model().data(index) if value > 0.0: labelIndex = self.model().index(row, 0, self.rootIndex()) option = self.viewOptions() option.rect = self.visualRect(labelIndex) if selections.isSelected(labelIndex): option.state |= QStyle.State_Selected if self.currentIndex() == labelIndex: option.state |= QStyle.State_HasFocus self.itemDelegate().paint(painter, option, labelIndex) keyNumber += 1 def resizeEvent(self, event): self.updateGeometries() def rows(self, index): return self.model().rowCount(self.model().parent(index)) def rowsInserted(self, parent, start, end): for row in range(start, end + 1): index = self.model().index(row, 1, self.rootIndex()) value = self.model().data(index) if value is not None and value > 0.0: self.totalValue += value self.validItems += 1 super(PieView, self).rowsInserted(parent, start, end) def rowsAboutToBeRemoved(self, parent, start, end): for row in range(start, end + 1): index = self.model().index(row, 1, self.rootIndex()) value = self.model().data(index) if value is not None and value > 0.0: self.totalValue -= value self.validItems -= 1 super(PieView, self).rowsAboutToBeRemoved(parent, start, end) def scrollContentsBy(self, dx, dy): self.viewport().scroll(dx, dy) def scrollTo(self, index, ScrollHint): area = self.viewport().rect() rect = self.visualRect(index) if rect.left() < area.left(): self.horizontalScrollBar().setValue( self.horizontalScrollBar().value() + rect.left() - area.left()) elif rect.right() > area.right(): self.horizontalScrollBar().setValue( self.horizontalScrollBar().value() + min( rect.right() - area.right(), rect.left() - area.left())) if rect.top() < area.top(): self.verticalScrollBar().setValue( self.verticalScrollBar().value() + rect.top() - area.top()) elif rect.bottom() > area.bottom(): self.verticalScrollBar().setValue( self.verticalScrollBar().value() + min( rect.bottom() - area.bottom(), rect.top() - area.top())) def setSelection(self, rect, command): # Use content widget coordinates because we will use the itemRegion() # function to check for intersections. contentsRect = rect.translated(self.horizontalScrollBar().value(), self.verticalScrollBar().value()).normalized() rows = self.model().rowCount(self.rootIndex()) columns = self.model().columnCount(self.rootIndex()) indexes = [] for row in range(rows): for column in range(columns): index = self.model().index(row, column, self.rootIndex()) region = self.itemRegion(index) if not region.intersect(QRegion(contentsRect)).isEmpty(): indexes.append(index) if len(indexes) > 0: firstRow = indexes[0].row() lastRow = indexes[0].row() firstColumn = indexes[0].column() lastColumn = indexes[0].column() for i in range(1, len(indexes)): firstRow = min(firstRow, indexes[i].row()) lastRow = max(lastRow, indexes[i].row()) firstColumn = min(firstColumn, indexes[i].column()) lastColumn = max(lastColumn, indexes[i].column()) selection = QItemSelection( self.model().index(firstRow, firstColumn, self.rootIndex()), self.model().index(lastRow, lastColumn, self.rootIndex())) self.selectionModel().select(selection, command) else: noIndex = QModelIndex() selection = QItemSelection(noIndex, noIndex) self.selectionModel().select(selection, command) self.update() def updateGeometries(self): self.horizontalScrollBar().setPageStep(self.viewport().width()) self.horizontalScrollBar().setRange(0, max(0, 2*self.totalSize - self.viewport().width())) self.verticalScrollBar().setPageStep(self.viewport().height()) self.verticalScrollBar().setRange(0, max(0, self.totalSize - self.viewport().height())) def verticalOffset(self): return self.verticalScrollBar().value() def visualRect(self, index): rect = self.itemRect(index) if rect.isValid(): return QRect(rect.left() - self.horizontalScrollBar().value(), rect.top() - self.verticalScrollBar().value(), rect.width(), rect.height()) else: return rect def visualRegionForSelection(self, selection): region = QRegion() for span in selection: for row in range(span.top(), span.bottom() + 1): for col in range(span.left(), span.right() + 1): index = self.model().index(row, col, self.rootIndex()) region += self.visualRect(index) return region
class DropCanvas(QWidget): def __init__(self, parent): super().__init__() self.dots = parent self.scene = QGraphicsScene(self) self.view = ControlView(self) self.control = '' ## shared self.pathMakerOn = False ## shared self.openPlayFile = '' ## shared self.pathList = [] ## used by animations, updated here self.pathMaker = PathMaker(self) self.mapper = InitMap(self) self.sideCar = SideCar(self) self.animation = Animation(self) self.sideShow = SideShow(self) self.initBkg = InitBkg(self) self.scene.setSceneRect(0, 0, common["ViewW"], common["ViewH"]) self.setFixedSize(common["ViewW"] + 2, common["ViewH"] + 2) self.key = '' self.pixCount = 0 self.origin = QPoint(0, 0) self.rubberBand = QRubberBand(QRubberBand.Rectangle, self) self.setMouseTracking(True) self.view.viewport().installEventFilter(self) self.view.keysSignal[str].connect(self.setKeys) ### -------------------------------------------------------- @pyqtSlot(str) def setKeys(self, key): self.key = key if self.pathMakerOn and self.key == 'C' and \ len(self.scene.items()) == 0: self.clear() return if not self.pathMakerOn: ## canvas if self.key in CanvasStr or self.key == '': if self.key in Loops: ## canvas hotkeys self.sideShow.keysInPlay(self.key) elif self.key == 'C': self.clear() else: self.sendPixKeys() elif self.key in PathStr: ## pathMaker self.pathMaker.pathKeys(self.key) # pub.sendMessage('setKeys', key=key) ## pypubsub solution # if used - requires some work to coordinate with mapper # if self.mapper.mapSet: ## already in sendPixKeys # self.mapper.updateMap() ## redraw mapper ### -------------------------------------------------------- ''' Problem: Starting a new path drawing session, typing 'N' in pathMaker, will immeadiately start drawing without having to register a mousePress event if entered after displaying pointItems or if started after either running play or pixtest. The mouse events used by the newPath functions no-longer respond - it won't stop drawing. Annoying but not fatal. I'm pretty sure the problem is with the eventFilter though scene ownership may also be involved. I just moved the event filter to pathMaker and it seems to work thought not a fix. The one thing the three funtions, running an animation, pixtext, and displaying pointItems have in common is they all add graphicsitems to the scene and delete them from the scene. Hope that helps. Any help would be appreciated. I may want to add additional pathMaker like classes later and how to share the canvas would be useful. Thanks in advance ..''' def eventFilter(self, source, e): if not self.pathMakerOn: if e.type() == QEvent.MouseButtonPress: self.origin = QPoint(e.pos()) self.mapper.clearTagGroup() ## chks if set if self.key == 'cmd': ## only used by eventFilter self.mapper.clearMap() ## set rubberband if mapset self.unSelect() elif self.hasHiddenPix() and self.mapper.selections: if self.control not in PlayKeys: self.mapper.updatePixItemPos() elif e.type() == QEvent.MouseMove: if self.key == 'cmd' and self.origin != QPoint(0, 0): if self.mapper.mapSet: self.mapper.removeMap() self.rubberBand.show() self.rubberBand.setGeometry( QRect(self.origin, e.pos()).normalized()) elif self.mapper.mapSet and not self.scene.selectedItems(): self.mapper.removeMap() elif self.control not in PlayKeys: ## animations running self.mapper.updatePixItemPos() ## costly but necessary elif e.type() == QEvent.MouseButtonRelease: if self.mapper.mapSet == False: self.rubberBand.hide() ## supposes something is selected self.mapper.addSelectionsFromCanvas() if self.mapper.mapSet and self.key == 'cmd': self.setKeys('') if self.hasHiddenPix() and self.key != 'cmd' or \ self.mapper.mapSet and not self.scene.selectedItems(): self.mapper.removeMap() elif e.type() == QEvent.MouseButtonDblClick and self.key != 'cmd': ## to preseve selections dblclk on an selection otherwise it ## will unselect all - possibly a default as it works the ## same as single click outside the map area if self.mapper.selections or self.hasHiddenPix(): if self.mapper.mapSet: self.mapper.removeMap() self.setKeys('noMap') return QWidget.eventFilter(self, source, e) ### -------------------------------------------------------- ## set in drag/drop for id and in pixitem to clone itself def addPixItem(self, imgFile, x, y, clone, mirror): self.pixCount += 1 pix = PixItem(imgFile, self.pixCount, x, y, self, mirror) if clone != None: ## clone it self.sideCar.transFormPixItem( pix, clone.rotation, clone.scale * random.randrange(95, 105) / 100.0) else: if 'frame' in pix.fileName: ## pin it on dnd pix.setPos(0, 0) pix.setFlag(QGraphicsPixmapItem.ItemIsMovable, False) self.scene.addItem(pix) def sendPixKeys(self): ## update pixitems thru setPixKeys for pix in self.scene.items(): if pix.type == 'pix': pix.setPixKeys(self.key) elif pix.zValue() <= common["pathZ"]: break if self.mapper.mapSet: self.mapper.updateMap() def exit(self): self.clear() self.dots.close() def clear(self): if self.pathMakerOn: self.pathMaker.pathMakerOff() self.pathMaker.pathChooserOff() self.sideShow.stop('clear') self.initBkg.disableBkgBtns() self.dots.statusBar.clearMessage() self.mapper.clearMap() self.dots.btnAddBkg.setEnabled(True) self.pixCount = 0 self.sideCar.gridSet = False self.openPlayFile = '' self.scene.clear() def loadSprites(self): self.sideShow.enablePlay() self.dots.scrollpanel.loadSprites() def selectAll(self): for pix in self.scene.items(): if pix.type == 'pix': pix.setSelected(True) pix.isHidden = False elif pix.zValue() <= common["pathZ"]: break def unSelect(self): self.mapper.clearMap() for pix in self.scene.items(): if pix.type == 'pix': pix.isHidden = False pix.setSelected(False) elif pix.zValue() <= common["pathZ"]: break ### -------------------------------------------------------- def deleteSelected(self): # self.pathMakerOn equals false self.mapper.clearMap() self.mapper.clearTagGroup() for pix in self.scene.selectedItems(): if pix.anime != None and \ pix.anime.state() == QAbstractAnimation.Running: pix.anime.stop() pix.deletePix() self.anySprites() def anySprites(self): for pix in self.scene.items(): if pix.type == 'pix': ## still some left break elif pix.zValue() <= common["pathZ"]: self.sideShow.enablePlay() def flopSelected(self): if not self.pathMakerOn: for pix in self.scene.items(): if pix.type == 'pix': if pix.isSelected() or pix.isHidden: if pix.flopped: pix.setMirrored(False) else: pix.setMirrored(True) elif pix.zValue() <= common["pathZ"]: break def hasHiddenPix(self): for pix in self.scene.items(): if pix.type == 'pix': if pix.isHidden: return True ## found one elif pix.zValue() <= common["pathZ"]: break return False ## added dlbclk if hidden to re-select ## def hideSelected(self): # if self.mapper.mapSet and self.hasHiddenPix(): self.mapper.removeMap() ## also updates pix.pos() for pix in self.scene.items(): if pix.type == 'pix': if pix.isSelected(): pix.setSelected(False) pix.isHidden = True elif pix.isHidden: pix.setSelected(True) pix.isHidden = False elif pix.zValue() <= common["pathZ"]: break def ZDump(self): for pix in self.scene.items(): print(pix.zValue()) print("bkg: " + str(self.initBkg.hasBackGround())) ### -------------------------------------------------------- def contextMenuEvent(self, e): if not self.scene.selectedItems(): return menu = QMenu(self) menu.setStyleSheet("QMenu {\n" "font-size: 14px;\n" "border: 1px solid rgb(125,125,125);\n" "}") alst = sorted(AnimeList) ## basing pathlist on what's in the directory self.pathList = getPathList(True) ## names only rlst = sorted(self.pathList) alst.extend(["Random"]) for anime in alst: action = menu.addAction(anime) action.triggered.connect( lambda chk, anime=anime: self.setAnimationTag(anime)) menu.addSeparator() for anime in rlst: action = menu.addAction(anime) action.triggered.connect( lambda chk, anime=anime: self.setAnimationTag(anime)) menu.addSeparator() anime = "Clear Tags" action = menu.addAction(anime) action.triggered.connect( lambda chk, anime=anime: self.setAnimationTag(anime)) menu.exec_(e.globalPos()) def setAnimationTag(self, tag): if self.mapper.tagSet and tag == "Clear Tags": self.mapper.clearTagGroup() for pix in self.scene.selectedItems(): if tag == "Clear Tags": pix.tag = '' else: pix.tag = tag pix.anime = None ## set by play pix.setSelected(False) ## when tagged if self.mapper.mapSet: self.mapper.removeMap()
class ImageViewer(QGraphicsView, QObject): points_selection_sgn = pyqtSignal(list) key_press_sgn = pyqtSignal(QtGui.QKeyEvent) def __init__(self, parent=None): super(ImageViewer, self).__init__(parent) self.setDragMode(QGraphicsView.ScrollHandDrag) self.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform) self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) self.setResizeAnchor(QGraphicsView.AnchorUnderMouse) self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) self._scene = ImageViewerScene(self) self.setScene(self._scene) self._image = None self._image_original = None self._pixmap = None self._img_contrast = 1.0 self._img_brightness = 50.0 self._img_gamma = 1.0 self._create_grid() self._channels = [] self._current_tool = SELECTION_TOOL.POINTER self._dataset = None # create grid lines pen_color = QColor(255, 255, 255, 255) pen = QPen(pen_color) pen.setWidth(2) pen.setStyle(QtCore.Qt.DotLine) self.vline = QGraphicsLineItem() self.vline.setVisible(False) self.vline.setPen(pen) self.hline = QGraphicsLineItem() self.hline.setVisible(False) self.hline.setPen(pen) self._scene.addItem(self.vline) self._scene.addItem(self.hline) self._current_label = None # rectangle selection tool self._rectangle_tool_origin = QPoint() self._rectangle_tool_picker = QRubberBand(QRubberBand.Rectangle, self) # polygon selection tool app = QApplication.instance() color = app.palette().color(QPalette.Highlight) self._polygon_guide_line_pen = QPen(color) self._polygon_guide_line_pen.setWidth(3) self._polygon_guide_line_pen.setStyle(QtCore.Qt.DotLine) self._polygon_guide_line = QGraphicsLineItem() self._polygon_guide_line.setVisible(False) self._polygon_guide_line.setPen(self._polygon_guide_line_pen) self._scene.addItem(self._polygon_guide_line) self._current_polygon = None # circle self._current_ellipse = None # free selection tool self._current_free_path = None self._is_drawing = False self._last_point_drawn = QPoint() self._last_click_point = None self._free_Path_pen = QPen(color) self._free_Path_pen.setWidth(10) self._extreme_points = Queue(maxsize=4) @property def current_label(self): return self._current_label @current_label.setter def current_label(self, value): self._current_label = value if self._current_label: color = QColor(self._current_label.color) self._free_Path_pen.setColor(color) self._polygon_guide_line_pen.setColor(color) self._polygon_guide_line.setPen(self._polygon_guide_line_pen) @property def dataset(self): return self._dataset @dataset.setter def dataset(self, value): self._dataset = value @property def img_contrast(self): return self._img_contrast @img_contrast.setter def img_contrast(self, value): self._img_contrast = value @property def img_gamma(self): return self._img_gamma @img_gamma.setter def img_gamma(self, value): self._img_gamma = value @property def img_brightness(self): return self._img_brightness @img_brightness.setter def img_brightness(self, value): self._img_brightness = value @property def image(self): return self._image @image.setter def image(self, value): self._image = value self._image_original = value.copy() self.update_viewer() @property def pixmap(self) -> ImagePixmap: return self._pixmap @gui_exception def update_viewer(self, fit_image=True): rgb = cv2.cvtColor(self._image, cv2.COLOR_BGR2RGB) rgb = ImageUtilities.adjust_image(rgb, self._img_contrast, self._img_brightness) rgb = ImageUtilities.adjust_gamma(rgb, self._img_gamma) pil_image = Image.fromarray(rgb) qppixmap_image = pil_image.toqpixmap() x, y = -qppixmap_image.width() / 2, -qppixmap_image.height() / 2 if self._pixmap: self._pixmap.resetTransform() self._pixmap.setPixmap(qppixmap_image) self._pixmap.setOffset(x, y) else: self._pixmap = ImagePixmap() self._pixmap.setPixmap(qppixmap_image) self._pixmap.setOffset(x, y) self._scene.addItem(self._pixmap) self._pixmap.signals.hoverEnterEventSgn.connect( self.pixmap_hoverEnterEvent_slot) self._pixmap.signals.hoverLeaveEventSgn.connect( self.pixmap_hoverLeaveEvent_slot) self._pixmap.signals.hoverMoveEventSgn.connect( self.pixmap_hoverMoveEvent_slot) self._hide_guide_lines() if fit_image: self.fit_to_window() @gui_exception def reset_viewer(self): self._img_contrast = 1.0 self._img_brightness = 50.0 self._img_gamma = 1.0 self._image = self._image_original.copy() @gui_exception def equalize_histogram(self): self._image = ImageUtilities.histogram_equalization(self._image) @gui_exception def correct_lightness(self): self._image = ImageUtilities.correct_lightness(self._image) def clusterize(self, k): self._image = ImageUtilities.kmeans(self._image.copy(), k) @property def current_tool(self): return self._current_tool @current_tool.setter def current_tool(self, value): self._polygon_guide_line.hide() self._current_polygon = None self._current_free_path = None self._current_ellipse = None self._is_drawing = value == SELECTION_TOOL.FREE self._current_tool = value self.clear_extreme_points() if value == SELECTION_TOOL.POINTER: self.enable_items(True) else: self.enable_items(False) def fit_to_window(self): if not self._pixmap or not self._pixmap.pixmap(): return self.resetTransform() self.setTransform(QtGui.QTransform()) self.fitInView(self._pixmap, QtCore.Qt.KeepAspectRatio) def _create_grid(self, gridSize=15): app: QApplication = QApplication.instance() curr_theme = "dark" if app: curr_theme = app.property("theme") if curr_theme == "light": color1 = QtGui.QColor("white") color2 = QtGui.QColor(237, 237, 237) else: color1 = QtGui.QColor(20, 20, 20) color2 = QtGui.QColor(0, 0, 0) backgroundPixmap = QtGui.QPixmap(gridSize * 2, gridSize * 2) backgroundPixmap.fill(color1) painter = QtGui.QPainter(backgroundPixmap) painter.fillRect(0, 0, gridSize, gridSize, color2) painter.fillRect(gridSize, gridSize, gridSize, gridSize, color2) painter.end() self._scene.setBackgroundBrush(QtGui.QBrush(backgroundPixmap)) def wheelEvent(self, event: QWheelEvent): adj = (event.angleDelta().y() / 120) * 0.1 self.scale(1 + adj, 1 + adj) @gui_exception def keyPressEvent(self, event: QKeyEvent): if event.key() == QtCore.Qt.Key_Space: image_rect: QRectF = self._pixmap.sceneBoundingRect() if self.current_tool == SELECTION_TOOL.POLYGON and self._current_polygon: points = self._current_polygon.points self._polygon_guide_line.hide() self.setDragMode(QGraphicsView.ScrollHandDrag) if len(points) <= 2: self._current_polygon.delete_item() self.current_tool = SELECTION_TOOL.POINTER elif self.current_tool == SELECTION_TOOL.EXTREME_POINTS and \ self._extreme_points.full(): points = [] image_offset = QPointF(image_rect.width() / 2, image_rect.height() / 2) for pt in self._extreme_points.queue: pt: EditablePolygonPoint center = pt.sceneBoundingRect().center() x = math.floor(center.x() + image_offset.x()) y = math.floor(center.y() + image_offset.y()) points.append([x, y]) self.points_selection_sgn.emit(points) self.current_tool = SELECTION_TOOL.POINTER else: event.ignore() # guide lines events def _show_guide_lines(self): if self.hline and self.vline: self.hline.show() self.vline.show() def _hide_guide_lines(self): if self.hline and self.vline: self.hline.hide() self.vline.hide() def _update_guide_lines(self, x, y): bbox: QRect = self._pixmap.boundingRect() offset = QPointF(bbox.width() / 2, bbox.height() / 2) self.vline.setLine(x, -offset.y(), x, bbox.height() - offset.y()) self.vline.setZValue(1) self.hline.setLine(-offset.x(), y, bbox.width() - offset.x(), y) self.hline.setZValue(1) def pixmap_hoverMoveEvent_slot(self, evt: QGraphicsSceneHoverEvent, x, y): self._update_guide_lines(x, y) def pixmap_hoverEnterEvent_slot(self): self._show_guide_lines() def pixmap_hoverLeaveEvent_slot(self): self._hide_guide_lines() def delete_polygon_slot(self, polygon: EditablePolygon): self._current_polygon = None self.current_tool = SELECTION_TOOL.POINTER self._polygon_guide_line.hide() @gui_exception def mousePressEvent(self, evt: QtGui.QMouseEvent) -> None: image_rect: QRectF = self._pixmap.boundingRect() mouse_pos = self.mapToScene(evt.pos()) if evt.buttons() == QtCore.Qt.LeftButton: if self.current_tool == SELECTION_TOOL.BOX: # create rectangle self.setDragMode(QGraphicsView.NoDrag) self._rectangle_tool_origin = evt.pos() geometry = QRect(self._rectangle_tool_origin, QSize()) self._rectangle_tool_picker.setGeometry(geometry) self._rectangle_tool_picker.show() elif self.current_tool == SELECTION_TOOL.POLYGON: if image_rect.contains(mouse_pos): if self._current_polygon is None: self._current_polygon = EditablePolygon() self._current_polygon.label = self._current_label self._current_polygon.tag = self._dataset self._current_polygon.signals.deleted.connect( self.delete_polygon_slot) self._scene.addItem(self._current_polygon) self._current_polygon.addPoint(mouse_pos) else: self._current_polygon.addPoint(mouse_pos) elif self.current_tool == SELECTION_TOOL.ELLIPSE: if image_rect.contains(mouse_pos): self.setDragMode(QGraphicsView.NoDrag) ellipse_rec = QtCore.QRectF(mouse_pos.x(), mouse_pos.y(), 0, 0) self._current_ellipse = EditableEllipse() self._current_ellipse.tag = self.dataset self._current_ellipse.label = self._current_label self._current_ellipse.setRect(ellipse_rec) self._scene.addItem(self._current_ellipse) elif self.current_tool == SELECTION_TOOL.FREE: # consider only the points into the image if image_rect.contains(mouse_pos): self.setDragMode(QGraphicsView.NoDrag) self._last_point_drawn = mouse_pos self._current_free_path = QGraphicsPathItem() self._current_free_path.setOpacity(0.6) self._current_free_path.setPen(self._free_Path_pen) painter = QPainterPath() painter.moveTo(self._last_point_drawn) self._current_free_path.setPath(painter) self._scene.addItem(self._current_free_path) elif self.current_tool == SELECTION_TOOL.EXTREME_POINTS: if image_rect.contains(mouse_pos): if not self._extreme_points.full(): def delete_point(idx): del self._extreme_points.queue[idx] idx = self._extreme_points.qsize() editable_pt = EditablePolygonPoint(idx) editable_pt.signals.deleted.connect(delete_point) editable_pt.setPos(mouse_pos) self._scene.addItem(editable_pt) self._extreme_points.put(editable_pt) else: self.setDragMode(QGraphicsView.ScrollHandDrag) super(ImageViewer, self).mousePressEvent(evt) @gui_exception def mouseMoveEvent(self, evt: QtGui.QMouseEvent) -> None: mouse_pos = self.mapToScene(evt.pos()) image_rect: QRectF = self._pixmap.boundingRect() if self.current_tool == SELECTION_TOOL.BOX: if not self._rectangle_tool_origin.isNull(): geometry = QRect(self._rectangle_tool_origin, evt.pos()).normalized() self._rectangle_tool_picker.setGeometry(geometry) elif self.current_tool == SELECTION_TOOL.POLYGON: if self._current_polygon and image_rect.contains(mouse_pos): if self._current_polygon.count > 0: last_point: QPointF = self._current_polygon.last_point self._polygon_guide_line.setZValue(1) self._polygon_guide_line.show() mouse_pos = self.mapToScene(evt.pos()) self._polygon_guide_line.setLine(last_point.x(), last_point.y(), mouse_pos.x(), mouse_pos.y()) else: self._polygon_guide_line.hide() elif self.current_tool == SELECTION_TOOL.ELLIPSE: if self._current_ellipse and image_rect.contains(mouse_pos): ellipse_rect = self._current_ellipse.rect() ellipse_pos = QPointF(ellipse_rect.x(), ellipse_rect.y()) distance = math.hypot(mouse_pos.x() - ellipse_pos.x(), mouse_pos.y() - ellipse_pos.y()) ellipse_rect.setWidth(distance) ellipse_rect.setHeight(distance) self._current_ellipse.setRect(ellipse_rect) elif self.current_tool == SELECTION_TOOL.FREE and evt.buttons( ) and QtCore.Qt.LeftButton: if self._current_free_path and image_rect.contains(mouse_pos): painter: QPainterPath = self._current_free_path.path() self._last_point_drawn = self.mapToScene(evt.pos()) painter.lineTo(self._last_point_drawn) self._current_free_path.setPath(painter) super(ImageViewer, self).mouseMoveEvent(evt) @gui_exception def mouseReleaseEvent(self, evt: QtGui.QMouseEvent) -> None: image_rect: QRectF = self._pixmap.boundingRect() if self.current_tool == SELECTION_TOOL.BOX: roi: QRect = self._rectangle_tool_picker.geometry() roi: QRectF = self.mapToScene(roi).boundingRect() self._rectangle_tool_picker.hide() if image_rect == roi.united(image_rect): rect = EditableBox(roi) rect.label = self.current_label rect.tag = self._dataset self._scene.addItem(rect) self.current_tool = SELECTION_TOOL.POINTER self.setDragMode(QGraphicsView.ScrollHandDrag) elif self.current_tool == SELECTION_TOOL.ELLIPSE and self._current_ellipse: roi: QRect = self._current_ellipse.boundingRect() if image_rect == roi.united(image_rect): self.current_tool = SELECTION_TOOL.POINTER self.setDragMode(QGraphicsView.ScrollHandDrag) else: self._current_ellipse.delete_item() elif self.current_tool == SELECTION_TOOL.FREE and self._current_free_path: # create polygon self._current_free_path: QGraphicsPathItem path_rect = self._current_free_path.boundingRect() if image_rect == path_rect.united(image_rect): path = self._current_free_path.path() path_polygon = EditablePolygon() path_polygon.tag = self.dataset path_polygon.label = self.current_label self._scene.addItem(path_polygon) for i in range(0, path.elementCount(), 10): x, y = path.elementAt(i).x, path.elementAt(i).y path_polygon.addPoint(QPointF(x, y)) self._scene.removeItem(self._current_free_path) self.current_tool = SELECTION_TOOL.POINTER self.setDragMode(QGraphicsView.ScrollHandDrag) super(ImageViewer, self).mouseReleaseEvent(evt) def remove_annotations(self): for item in self._scene.items(): if isinstance(item, EditableItem): item.delete_item() def remove_annotations_by_label(self, label_name): for item in self._scene.items(): if isinstance(item, EditableItem): if item.label and item.label.name == label_name: item.delete_item() def enable_items(self, value): for item in self._scene.items(): if isinstance(item, EditableItem): item.setEnabled(value) def clear_extreme_points(self): if self._extreme_points.qsize() > 0: for pt in self._extreme_points.queue: self._scene.removeItem(pt) self._extreme_points.queue.clear()
class MyGraphicsView(CanvasBase): """ This is the used Canvas to print the graphical interface of dxf2gcode. All GUI things should be performed in the View and plotting functions in the scene """ def __init__(self, parent=None): """ Initialisation of the View Object. This is called by the gui created with the QTDesigner. @param parent: Main is passed as a pointer for reference. """ super(MyGraphicsView, self).__init__(parent) self.currentItem = None self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) self.setResizeAnchor(QGraphicsView.AnchorViewCenter) # self.setDragMode(QGraphicsView.RubberBandDrag ) self.setDragMode(QGraphicsView.NoDrag) self.parent = parent self.mppos = None self.rubberBand = QRubberBand(QRubberBand.Rectangle, self) self.prvRectRubberBand = QtCore.QRect() def tr(self, string_to_translate): """ Translate a string using the QCoreApplication translation framework @param string_to_translate: a unicode string @return: the translated unicode string if it was possible to translate """ return text_type( QtCore.QCoreApplication.translate('MyGraphicsView', string_to_translate)) def contextMenuEvent(self, event): """ Create the contextmenu. @purpose: Links the new Class of ContextMenu to Graphicsview. """ position = self.mapToGlobal(event.pos()) GVPos = self.mapToScene(event.pos()) real_pos = Point(GVPos.x(), -GVPos.y()) menu = MyDropDownMenu(self.scene(), position, real_pos) def wheelEvent(self, event): """ With Mouse Wheel the object is scaled @purpose: Scale by mouse wheel @param event: Event Parameters passed to function """ if c.PYQT5notPYQT4: delta = event.angleDelta().y() else: delta = event.delta() scale = (1000 + delta) / 1000.0 self.scale(scale, scale) def mousePressEvent(self, event): """ Right Mouse click shall have no function, Therefore pass only left click event @purpose: Change inherited mousePressEvent @param event: Event Parameters passed to function """ if self.dragMode() == 1: super(MyGraphicsView, self).mousePressEvent(event) elif event.button() == QtCore.Qt.LeftButton: self.mppos = event.pos() else: pass def mouseReleaseEvent(self, event): """ Right Mouse click shall have no function, Therefore pass only left click event @purpose: Change inherited mousePressEvent @param event: Event Parameters passed to function """ delta = 2 if self.dragMode() == 1: # if (event.key() == QtCore.Qt.Key_Shift): # self.setDragMode(QGraphicsView.NoDrag) super(MyGraphicsView, self).mouseReleaseEvent(event) # Selection only enabled for left Button elif event.button() == QtCore.Qt.LeftButton: self.currentItems = [] scene = self.scene() if not self.isMultiSelect: for item in scene.selectedItems(): item.setSelected(False, False) # If the mouse button is pressed without movement of rubberband if self.rubberBand.isHidden(): rect = QtCore.QRect(event.pos().x() - delta, event.pos().y() - delta, 2 * delta, 2 * delta) # logger.debug(rect) point = self.mapToScene(event.pos()) min_distance = float(0x7fffffff) for item in self.items(rect): itemDistance = item.contains_point(point) if itemDistance < min_distance: min_distance = itemDistance self.currentItems = item if self.currentItems: if self.currentItems.isSelected(): self.currentItems.setSelected(False, False) else: self.currentItems.setSelected(True, False) else: rect = self.rubberBand.geometry() self.currentItems = self.items(rect) self.rubberBand.hide() # logger.debug("Rubberband Selection") # All items in the selection # self.currentItems = self.items(rect) # print self.currentItems # logger.debug(rect) for item in self.currentItems: if item.isSelected(): item.setSelected(False, False) else: # print (item.flags()) item.setSelected(True, False) else: pass self.mppos = None # super(MyGraphicsView, self).mouseReleaseEvent(event) def mouseMoveEvent(self, event): """ MouseMoveEvent of the Graphiscview. May also be used for the Statusbar. @purpose: Get the MouseMoveEvent and use it for the Rubberband Selection @param event: Event Parameters passed to function """ if self.mppos is not None: Point = event.pos() - self.mppos if Point.manhattanLength() > 3: # print 'the mouse has moved more than 3 pixels since the oldPosition' # print "Mouse Pointer is currently hovering at: ", event.pos() rect = QtCore.QRect(self.mppos, event.pos()) ''' The following is needed because of PyQt5 doesn't like to switch from sign it will keep displaying last rectangle, i.e. you can end up will multiple rectangles ''' if self.prvRectRubberBand.width() > 0 and not rect.width() > 0 or rect.width() == 0 or\ self.prvRectRubberBand.height() > 0 and not rect.height() > 0 or rect.height() == 0: self.rubberBand.hide() self.rubberBand.setGeometry(rect.normalized()) self.rubberBand.show() self.prvRectRubberBand = rect scpoint = self.mapToScene(event.pos()) # self.setStatusTip('X: %3.1f; Y: %3.1f' % (scpoint.x(), -scpoint.y())) # works not as supposed to self.setToolTip('X: %3.1f; Y: %3.1f' % (scpoint.x(), -scpoint.y())) super(MyGraphicsView, self).mouseMoveEvent(event) def autoscale(self): """ Automatically zooms to the full extend of the current GraphicsScene """ scene = self.scene() width = scene.BB.Pe.x - scene.BB.Ps.x height = scene.BB.Pe.y - scene.BB.Ps.y scext = QtCore.QRectF(scene.BB.Ps.x, -scene.BB.Pe.y, width * 1.05, height * 1.05) self.fitInView(scext, QtCore.Qt.KeepAspectRatio) logger.debug(self.tr("Autoscaling to extend: %s") % scext) def setShowPathDirections(self, flag): """ This function is called by the Main Window from the Menubar. @param flag: This flag is true if all Path Direction shall be shown """ scene = self.scene() for shape in scene.shapes: shape.starrow.setallwaysshow(flag) shape.enarrow.setallwaysshow(flag) shape.stmove.setallwaysshow(flag) def resetAll(self): """ Deletes the existing GraphicsScene. """ scene = self.scene() del scene
class SlideViewer(QWidget): eventSignal = pyqtSignal(PyQt5.QtCore.QEvent) def __init__(self, parent: QWidget = None, viewer_top_else_left=True): super().__init__(parent) self.init_view() self.init_labels(word_wrap=viewer_top_else_left) self.init_layout(viewer_top_else_left) def init_view(self): self.scene = MyGraphicsScene() self.view = QGraphicsView() self.view.setScene(self.scene) self.view.setTransformationAnchor(QGraphicsView.NoAnchor) self.view.viewport().installEventFilter(self) self.rubber_band = QRubberBand(QRubberBand.Rectangle, self) self.mouse_press_view = QPoint() self.view.horizontalScrollBar().sliderMoved.connect( self.on_view_changed) self.view.verticalScrollBar().sliderMoved.connect(self.on_view_changed) self.scale_initializer_deffered_function = None self.slide_view_params = None self.slide_helper = None def init_labels(self, word_wrap): # word_wrap = True self.level_downsample_label = QLabel() self.level_downsample_label.setWordWrap(word_wrap) self.level_size_label = QLabel() self.level_size_label.setWordWrap(word_wrap) self.selected_rect_label = QLabel() self.selected_rect_label.setWordWrap(word_wrap) self.mouse_pos_scene_label = QLabel() self.mouse_pos_scene_label.setWordWrap(word_wrap) self.view_rect_scene_label = QLabel() self.view_rect_scene_label.setWordWrap(word_wrap) self.labels_layout = QVBoxLayout() self.labels_layout.setAlignment(Qt.AlignTop) self.labels_layout.addWidget(self.level_downsample_label) self.labels_layout.addWidget(self.level_size_label) self.labels_layout.addWidget(self.mouse_pos_scene_label) # self.labels_layout.addWidget(self.selected_rect_label) self.labels_layout.addWidget(self.view_rect_scene_label) def init_layout(self, viewer_top_else_left=True): main_layout = QVBoxLayout( self) if viewer_top_else_left else QHBoxLayout(self) main_layout.addWidget(self.view, ) main_layout.addLayout(self.labels_layout) # main_layout.setContentsMargins(0, 0, 0, 0) self.setLayout(main_layout) """ If you want to start view frome some point at some level, specify <level> and <level_rect> params. level_rect : rect in dimensions of slide at level=level. If None - fits the whole size of slide """ def load(self, slide_view_params: SlideViewParams, preffered_rects_count=2000, zoom_step=1.15): self.zoom_step = zoom_step self.slide_view_params = slide_view_params self.slide_helper = SlideHelper(slide_view_params.slide_path) self.slide_graphics = SlideGraphicsGroup(slide_view_params, preffered_rects_count) self.scene.clear() self.scene.addItem(self.slide_graphics) if self.slide_view_params.level == -1 or self.slide_view_params.level is None: self.slide_view_params.level = self.slide_helper.get_max_level() self.slide_graphics.update_visible_level(self.slide_view_params.level) self.scene.setSceneRect( self.slide_helper.get_rect_for_level(self.slide_view_params.level)) def scale_initializer_deffered_function(): self.view.resetTransform() # print("size when loading: ", self.view.viewport().size()) if self.slide_view_params.level_rect: # self.view.fitInView(QRectF(*self.slide_view_params.level_rect), Qt.KeepAspectRatioByExpanding) self.view.fitInView(QRectF(*self.slide_view_params.level_rect), Qt.KeepAspectRatio) # print("after fit: ", self.get_current_view_scene_rect()) else: start_margins = QMarginsF(200, 200, 200, 200) start_image_rect_ = self.slide_helper.get_rect_for_level( self.slide_view_params.level) self.view.fitInView(start_image_rect_ + start_margins, Qt.KeepAspectRatio) self.scale_initializer_deffered_function = scale_initializer_deffered_function def eventFilter(self, qobj: 'QObject', event: QEvent): self.eventSignal.emit(event) event_processed = False # print("size when event: ", event, event.type(), self.view.viewport().size()) if isinstance(event, QShowEvent): """ we need it deffered because fitInView logic depends on current viewport size. Expecting at this point widget is finally resized before being shown at first """ if self.scale_initializer_deffered_function: # TODO labels start to occupy some space after view was already fitted, and labels will reduce size of viewport # self.update_labels() self.scale_initializer_deffered_function() self.on_view_changed() self.scale_initializer_deffered_function = None elif isinstance(event, QWheelEvent): event_processed = self.process_viewport_wheel_event(event) # we handle wheel event to prevent GraphicsView interpret it as scrolling elif isinstance(event, QMouseEvent): event_processed = self.process_mouse_event(event) return event_processed def process_viewport_wheel_event(self, event: QWheelEvent): # print("size when wheeling: ", self.view.viewport().size()) zoom_in = self.zoom_step zoom_out = 1 / zoom_in zoom_ = zoom_in if event.angleDelta().y() > 0 else zoom_out self.update_scale(event.pos(), zoom_) event.accept() self.on_view_changed() return True def process_mouse_event(self, event: QMouseEvent): if self.slide_helper is None: return False if event.button() == Qt.MiddleButton: if event.type() == QEvent.MouseButtonPress: self.slide_graphics.update_grid_visibility( not self.slide_graphics.slide_view_params.grid_visible) # items=self.scene.items() # QMessageBox.information(None, "Items", str(items)) return True # self.update_scale(QPoint(), 1.15) elif event.button() == Qt.LeftButton: if event.type() == QEvent.MouseButtonPress: self.mouse_press_view = QPoint(event.pos()) self.rubber_band.setGeometry( QRect(self.mouse_press_view, QSize())) self.rubber_band.show() return True elif event.type() == QEvent.MouseButtonRelease: self.rubber_band.hide() self.remember_selected_rect_params() self.slide_graphics.update_selected_rect_0_level( self.slide_view_params.selected_rect_0_level) self.update_labels() self.scene.invalidate() return True elif event.type() == QEvent.MouseMove: self.mouse_pos_scene_label.setText( "mouse_scene: " + point_to_str(self.view.mapToScene(event.pos()))) if not self.mouse_press_view.isNull(): self.rubber_band.setGeometry( QRect(self.mouse_press_view, event.pos()).normalized()) return True return False def remember_selected_rect_params(self): pos_scene = self.view.mapToScene(self.rubber_band.pos()) rect_scene = self.view.mapToScene( self.rubber_band.rect()).boundingRect() downsample = self.slide_helper.get_downsample_for_level( self.slide_view_params.level) selected_qrectf_0_level = QRectF(pos_scene * downsample, rect_scene.size() * downsample) self.slide_view_params.selected_rect_0_level = selected_qrectf_0_level.getRect( ) def update_scale(self, mouse_pos: QPoint, zoom): old_mouse_pos_scene = self.view.mapToScene(mouse_pos) old_view_scene_rect = self.view.mapToScene( self.view.viewport().rect()).boundingRect() old_level = self.get_best_level_for_scale( self.get_current_view_scale()) old_level_downsample = self.slide_helper.get_downsample_for_level( old_level) new_level = self.get_best_level_for_scale( self.get_current_view_scale() * zoom) new_level_downsample = self.slide_helper.get_downsample_for_level( new_level) level_scale_delta = 1 / (new_level_downsample / old_level_downsample) r = old_view_scene_rect.topLeft() m = old_mouse_pos_scene new_view_scene_rect_top_left = (m - (m - r) / zoom) * level_scale_delta new_view_scene_rect = QRectF( new_view_scene_rect_top_left, old_view_scene_rect.size() * level_scale_delta / zoom) new_scale = self.get_current_view_scale( ) * zoom * new_level_downsample / old_level_downsample transform = QTransform().scale(new_scale, new_scale).translate( -new_view_scene_rect.x(), -new_view_scene_rect.y()) new_rect = self.slide_helper.get_rect_for_level(new_level) self.scene.setSceneRect(new_rect) self.slide_view_params.level = new_level self.reset_view_transform() self.view.setTransform(transform, False) self.slide_graphics.update_visible_level(new_level) self.update_labels() def get_best_level_for_scale(self, scale): scene_width = self.scene.sceneRect().size().width() candidates = [0] for level in self.slide_helper.get_levels(): w, h = self.slide_helper.get_level_size(level) if scene_width * scale <= w: candidates.append(level) best_level = max(candidates) return best_level def update_labels(self): level_downsample = self.slide_helper.get_downsample_for_level( self.slide_view_params.level) level_size = self.slide_helper.get_level_size( self.slide_view_params.level) self.level_downsample_label.setText( "level, downsample: {}, {:.0f}".format( self.slide_view_params.level, level_downsample)) self.level_size_label.setText( "level_size: ({}, {})".format(*level_size)) self.view_rect_scene_label.setText( "view_scene: ({:.0f},{:.0f},{:.0f},{:.0f})".format( *self.get_current_view_scene_rect().getRect())) if self.slide_view_params.selected_rect_0_level: self.selected_rect_label.setText( "selected rect (0-level): ({:.0f},{:.0f},{:.0f},{:.0f})". format(*self.slide_view_params.selected_rect_0_level)) def on_view_changed(self): if self.scale_initializer_deffered_function is None and self.slide_view_params: self.slide_view_params.level_rect = self.get_current_view_scene_rect( ).getRect() self.update_labels() def reset_view_transform(self): self.view.resetTransform() self.view.horizontalScrollBar().setValue(0) self.view.verticalScrollBar().setValue(0) def get_current_view_scene_rect(self): return self.view.mapToScene(self.view.viewport().rect()).boundingRect() def get_current_view_scale(self): scale = self.view.transform().m11() return scale
class Viewer(QGraphicsView): def __init__(self, gridsize_label, position_label, help_label): QGraphicsView.__init__(self) self.gridsize_label = gridsize_label self.position_label = position_label self.help_label = help_label # Create a QGraphicsScene which this view looks at self.scene = QGraphicsScene(self) self.scene.setSceneRect(QRectF()) self.setScene(self.scene) # Customize QGraphicsView self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setInteractive(False) self.scale(1, -1) # Flips around the Y axis # Use OpenGL http://ralsina.me/stories/BBS53.html # self.setViewport(QtOpenGL.QGLWidget()) self.rubberBand = QRubberBand(QRubberBand.Rectangle, self) self.pen = QPen(QtCore.Qt.black, 0) self.portpen = QPen(PORT_COLOR, 3) self.portpen.setCosmetic(True) # Makes constant width self.portfont = QtGui.QFont('Arial', pointSize=14) self.portfontcolor = PORT_COLOR self.subportpen = QPen(SUBPORT_COLOR, 3) self.subportpen.setCosmetic(True) # Makes constant width self.subportfont = QtGui.QFont('Arial', pointSize=14) self.subportfontcolor = SUBPORT_COLOR # Tracking ports # Various status variables self._mousePressed = None self._rb_origin = QPoint() self.zoom_factor_total = 1 # Grid variables self.gridpen = QPen(QtCore.Qt.black, 0) self.gridpen.setStyle(QtCore.Qt.DotLine) self.gridpen.setDashPattern([1, 4]) self.gridpen.setColor(QtGui.QColor(0, 0, 0, 125)) # self.gridpen = QPen(QtCore.Qt.black, 1) # self.gridpen.setCosmetic(True) # Makes constant width self.scene_polys = [] self.initialize() def add_polygons(self, polygons, color='#A8F22A', alpha=1): qcolor = QColor() qcolor.setNamedColor(color) qcolor.setAlphaF(alpha) for points in polygons: qpoly = QPolygonF([QPointF(p[0], p[1]) for p in points]) scene_poly = self.scene.addPolygon(qpoly) scene_poly.setBrush(qcolor) scene_poly.setPen(self.pen) self.scene_polys.append(scene_poly) # Update custom bounding box sr = scene_poly.sceneBoundingRect() if len(self.scene_polys) == 1: self.scene_xmin = sr.left() self.scene_xmax = sr.right() self.scene_ymin = sr.top() self.scene_ymax = sr.bottom() else: self.scene_xmin = min(self.scene_xmin, sr.left()) self.scene_xmax = max(self.scene_xmax, sr.right()) self.scene_ymin = min(self.scene_ymin, sr.top()) self.scene_ymax = max(self.scene_ymax, sr.bottom()) def reset_view(self): # The SceneRect controls how far you can pan, make it larger than # just the bounding box so middle-click panning works panning_rect = QRectF(self.scene_bounding_rect) panning_rect_center = panning_rect.center() panning_rect_size = max(panning_rect.width(), panning_rect.height()) * 3 panning_rect.setSize(QSizeF(panning_rect_size, panning_rect_size)) panning_rect.moveCenter(panning_rect_center) self.setSceneRect(panning_rect) self.fitInView(self.scene_bounding_rect, Qt.KeepAspectRatio) self.zoom_view(0.8) self.update_grid() def add_port(self, port, is_subport=False): if (port.width is None) or (port.width == 0): x, y = port.midpoint cs = 1 # cross size pn = QPointF(x, y + cs) ps = QPointF(x, y - cs) pe = QPointF(x + cs, y) pw = QPointF(x - cs, y) qline1 = self.scene.addLine(QLineF(pn, ps)) qline2 = self.scene.addLine(QLineF(pw, pe)) port_shapes = [qline1, qline2] else: point1, point2 = port.endpoints point1 = QPointF(point1[0], point1[1]) point2 = QPointF(point2[0], point2[1]) qline = self.scene.addLine(QLineF(point1, point2)) arrow_points = np.array([[0, 0], [10, 0], [6, 4], [6, 2], [0, 2] ]) / (40) * port.width arrow_qpoly = QPolygonF( [QPointF(p[0], p[1]) for p in arrow_points]) port_scene_poly = self.scene.addPolygon(arrow_qpoly) port_scene_poly.setRotation(port.orientation) port_scene_poly.moveBy(port.midpoint[0], port.midpoint[1]) port_shapes = [qline, port_scene_poly] qtext = self.scene.addText(str(port.name), self.portfont) port_items = port_shapes + [qtext] rad = port.orientation * np.pi / 180 x, y = port.endpoints[0] * 1 / 4 + port.endpoints[ 1] * 3 / 4 + np.array([np.cos(rad), np.sin(rad)]) * port.width / 8 # x,y = port.midpoint[0], port.midpoint[1] # x,y = x - qtext.boundingRect().width()/2, y - qtext.boundingRect().height()/2 qtext.setPos(QPointF(x, y)) qtext.setFlag(QGraphicsItem.ItemIgnoresTransformations) if not is_subport: [shape.setPen(self.portpen) for shape in port_shapes] qtext.setDefaultTextColor(self.portfontcolor) self.portitems += port_items else: [shape.setPen(self.subportpen) for shape in port_shapes] qtext.setDefaultTextColor(self.subportfontcolor) self.subportitems += port_items # self.portlabels.append(qtext) def add_aliases(self, aliases): for name, ref in aliases.items(): qtext = self.scene.addText(str(name), self.portfont) x, y = ref.center qtext.setPos(QPointF(x, y)) qtext.setFlag(QGraphicsItem.ItemIgnoresTransformations) self.aliasitems += [qtext] def set_port_visibility(self, visible=True): for item in self.portitems: item.setVisible(visible) self.ports_visible = visible def set_subport_visibility(self, visible=True): for item in self.subportitems: item.setVisible(visible) self.subports_visible = visible def set_alias_visibility(self, visible=True): for item in self.aliasitems: item.setVisible(visible) self.aliases_visible = visible def initialize(self): self.scene.clear() self.polygons = {} self.portitems = [] self.subportitems = [] self.aliasitems = [] self.aliases_visible = True self.ports_visible = True self.subports_visible = True self.mouse_position = [0, 0] self.grid_size_snapped = 0 self.setMouseTracking(True) self.scene_bounding_rect = None self.scene_polys = [] self.scene_xmin = 0 self.scene_xmax = 1 self.scene_ymin = 0 self.scene_ymax = 1 def finalize(self): self.scene_bounding_rect = QRectF( QPointF(self.scene_xmin, self.scene_ymin), QPointF(self.scene_xmax, self.scene_ymax)) # self.scene_center = [self.scene_bounding_rect.center().x(), self.scene_bounding_rect.center().y()] self.scene_size = [ self.scene_bounding_rect.width(), self.scene_bounding_rect.height() ] self.create_grid() self.update_grid() #============================================================================== # Grid creation #============================================================================== def update_grid(self): grid_pixels = 50 grid_snaps = [1, 2, 4] # Number of pixels in the viewer view_width, view_height = self.rect().width(), self.rect().height() # Rectangle of viewport in terms of scene coordinates r = self.mapToScene(self.rect()).boundingRect() width, height = r.width(), r.height() xmin, ymin, xmax, ymax = r.x(), r.y(), r.x() + width, r.y() + height grid_size = grid_pixels * (width / view_width) exponent = np.floor(np.log10(grid_size)) digits = round(grid_size / 10**(exponent), 2) digits_snapped = min(grid_snaps, key=lambda x: abs(x - digits)) grid_size_snapped = digits_snapped * 10**(exponent) # Starting coordinates for gridlines x = round((xmin - 2 * width) / grid_size_snapped) * grid_size_snapped y = round((ymin - 2 * height) / grid_size_snapped) * grid_size_snapped for gl in self.gridlinesx: gl.setLine(x, -1e10, x, 1e10) x += grid_size_snapped for gl in self.gridlinesy: gl.setLine(-1e10, y, 1e10, y) y += grid_size_snapped self.grid_size_snapped = grid_size_snapped self.update_gridsize_label() def update_gridsize_label(self): self.gridsize_label.setText('grid size = ' + str(self.grid_size_snapped)) self.gridsize_label.move(QPoint(5, self.height() - 25)) def update_mouse_position_label(self): self.position_label.setText( 'X = %0.4f / Y = %0.4f' % (self.mouse_position[0], self.mouse_position[1])) self.position_label.move(QPoint(self.width() - 250, self.height() - 25)) def update_help_label(self): self.help_label.setText('Press "?" key for help') self.help_label.move(QPoint(self.width() - 175, 0)) def create_grid(self): self.gridlinesx = [ self.scene.addLine(-10, -10, 10, 10, self.gridpen) for n in range(300) ] self.gridlinesy = [ self.scene.addLine(-10, -10, 10, 10, self.gridpen) for n in range(300) ] self.update_grid() #============================================================================== # Mousewheel zoom, taken from http://stackoverflow.com/a/29026916 #============================================================================== def wheelEvent(self, event): # Zoom Factor zoom_percentage = 1.4 # Set Anchors self.setTransformationAnchor(QGraphicsView.NoAnchor) self.setResizeAnchor(QGraphicsView.NoAnchor) # Save the scene pos oldPos = self.mapToScene(event.pos()) # Zoom mousewheel_rotation = event.angleDelta().y( ) # Typically = 120 on most mousewheels zoom_factor = zoom_percentage**(mousewheel_rotation / 120) zoom_factor = np.clip(zoom_factor, 0.5, 2.0) # Check to make sure we're not overzoomed min_width = 0.01 min_height = 0.01 window_width = self.rect().width() window_height = self.rect().height() scene_upper_left_corner = self.mapToScene(QPoint(0, 0)) scene_bottom_right_corner = self.mapToScene( QPoint(window_width, window_height)) scene_width = (scene_bottom_right_corner - scene_upper_left_corner).x() scene_height = (scene_upper_left_corner - scene_bottom_right_corner).y() max_width = self.scene_bounding_rect.width() * 3 max_height = self.scene_bounding_rect.height() * 3 if ((scene_width > max_width) and (scene_height > max_height)) and (zoom_factor < 1): pass elif ((scene_width < min_width) and (scene_height < min_height)) and (zoom_factor > 1): pass else: self.zoom_view(zoom_factor) # Get the new position and move scene to old position newPos = self.mapToScene(event.pos()) delta = newPos - oldPos self.translate(delta.x(), delta.y()) self.update_grid() def zoom_view(self, zoom_factor): old_center = self.mapToScene(self.rect().center()) self.scale(zoom_factor, zoom_factor) self.centerOn(old_center) self.zoom_factor_total *= zoom_factor def resizeEvent(self, event): super(QGraphicsView, self).resizeEvent(event) if self.scene_bounding_rect is not None: self.reset_view() self.update_gridsize_label() self.update_mouse_position_label() self.update_help_label() def mousePressEvent(self, event): super(QGraphicsView, self).mousePressEvent(event) #============================================================================== # Zoom to rectangle, from # https://wiki.python.org/moin/PyQt/Selecting%20a%20region%20of%20a%20widget #============================================================================== if event.button() == Qt.RightButton: self._mousePressed = Qt.RightButton self._rb_origin = QPoint(event.pos()) self.rubberBand.setGeometry(QRect(self._rb_origin, QSize())) self.rubberBand.show() #============================================================================== # Mouse panning, taken from # http://stackoverflow.com/a/15043279 #============================================================================== elif event.button() == Qt.MidButton: self._mousePressed = Qt.MidButton self._mousePressedPos = event.pos() self.setCursor(QtCore.Qt.ClosedHandCursor) self._dragPos = event.pos() def mouseMoveEvent(self, event): super(QGraphicsView, self).mouseMoveEvent(event) # # Useful debug # try: # self.debug_label.setText(str(itemsBoundingRect_nogrid().width())) # except: # print('Debug statement failed') # Update the X,Y label indicating where the mouse is on the geometry mouse_position = self.mapToScene(event.pos()) self.mouse_position = [mouse_position.x(), mouse_position.y()] self.update_mouse_position_label() if not self._rb_origin.isNull( ) and self._mousePressed == Qt.RightButton: self.rubberBand.setGeometry( QRect(self._rb_origin, event.pos()).normalized()) # Middle-click-to-pan if self._mousePressed == Qt.MidButton: newPos = event.pos() diff = newPos - self._dragPos self._dragPos = newPos self.horizontalScrollBar().setValue( self.horizontalScrollBar().value() - diff.x()) self.verticalScrollBar().setValue( self.verticalScrollBar().value() - diff.y()) # event.accept() def mouseReleaseEvent(self, event): if event.button() == Qt.RightButton: self.rubberBand.hide() rb_rect = QRect(self._rb_origin, event.pos()) rb_center = rb_rect.center() rb_size = rb_rect.size() if abs(rb_size.width()) > 3 and abs(rb_size.height()) > 3: viewport_size = self.viewport().geometry().size() zoom_factor_x = abs(viewport_size.width() / rb_size.width()) zoom_factor_y = abs(viewport_size.height() / rb_size.height()) new_center = self.mapToScene(rb_center) zoom_factor = min(zoom_factor_x, zoom_factor_y) self.zoom_view(zoom_factor) self.centerOn(new_center) self.update_grid() if event.button() == Qt.MidButton: self.setCursor(Qt.ArrowCursor) self._mousePressed = None self.update_grid() def keyPressEvent(self, event): if event.key() == Qt.Key_Escape: self.reset_view() if event.key() == Qt.Key_F1: self.set_alias_visibility(not self.aliases_visible) if event.key() == Qt.Key_F2: self.set_port_visibility(not self.ports_visible) if event.key() == Qt.Key_F3: self.set_subport_visibility(not self.subports_visible) if event.key() == Qt.Key_Question: help_str = """ Mouse control: Mousewheel: Zoom in and out Right-click & drag: Zoom to rectangle Middle-click & drag: Pan Keyboard shortcuts: Esc: Reset view F1: Show/hide alias names F2: Show/hide ports F3: Show/hide subports (ports in underlying references) """ msg = QMessageBox.about(self, 'PHIDL Help', help_str) msg.raise_()
class ImageView(QGraphicsView): @staticmethod def close_enough(point1, point2): d = 20 return abs(point1.x() - point2.x()) < d and abs(point1.y() - point2.y()) < d def __init__(self, parent=None): QGraphicsView.__init__(self, parent) self.go_prev_img = parent.go_prev_img self.go_next_img = parent.go_next_img pal = self.palette() pal.setColor(self.backgroundRole(), Qt.black) self.setPalette(pal) self.setFrameShape(QFrame.NoFrame) self.rband = None self.rband_state = None self.rband_corner = None # If mouse is over a corner self.rband_origin = QPoint() self.rband_endpoint = QPoint() def mousePressEvent(self, event): """Go to the next / previous image, or be able to drag the image with a hand.""" if self.rband_state == "initial": self.rband = QRubberBand(QRubberBand.Rectangle, self) self.rband_origin = event.pos() self.rband.setGeometry(QRect(self.rband_origin, QSize())) self.rband.show() self.rband_state = "set-endpoint" elif self.rband_state == "drawn": if self.rband_corner == "nw": self.rband_state = "set-origin" elif self.rband_corner == "se": self.rband_state = "set-endpoint" elif self.rband_corner == "inside": self.rband_state = "move" self.setCursor(QCursor(Qt.ClosedHandCursor)) self.rband_previous_pos = event.pos() else: if event.button() == Qt.LeftButton: x = event.x() if x < 100: self.go_prev_img() elif x > self.width() - 100: self.go_next_img() else: self.setDragMode(QGraphicsView.ScrollHandDrag) QGraphicsView.mousePressEvent(self, event) def mouseMoveEvent(self, event): if self.rband_state == "set-endpoint": self.rband.setGeometry( QRect(self.rband_origin, event.pos()).normalized()) self.rband_endpoint = event.pos() elif self.rband_state == "set-origin": self.rband.setGeometry( QRect(event.pos(), self.rband_endpoint).normalized()) self.rband_origin = event.pos() elif self.rband_state == "move": old_pos = self.rband.geometry() new_pos = old_pos.translated(event.pos() - self.rband_previous_pos) trace(0) self.rband.setGeometry(new_pos) self.rband_previous_pos = event.pos() elif self.rband_state == "drawn": if ImageView.close_enough(self.rband_origin, event.pos()): self.rband_corner = "nw" self.setCursor(QCursor(Qt.SizeFDiagCursor)) elif ImageView.close_enough(self.rband_endpoint, event.pos()): self.rband_corner = "se" self.setCursor(QCursor(Qt.SizeFDiagCursor)) elif self.rband.geometry().contains(event.pos()): self.rband_corner = "inside" self.setCursor(QCursor(Qt.OpenHandCursor)) else: self.rband_corner = None self.setCursor(QCursor(Qt.ArrowCursor)) QGraphicsView.mouseMoveEvent(self, event) def mouseReleaseEvent(self, event): #print("State %s", self.rband_state) if self.rband_state == "set-endpoint" or self.rband_state == "set-origin": self.rband_state = "drawn" elif self.rband_state == "move": self.setCursor(QCursor(Qt.ArrowCursor)) self.rband_state = "drawn" self.rband_origin = self.rband.geometry().topLeft() self.rband_endpoint = self.rband.geometry().bottomRight() else: self.setCursor(QCursor(Qt.ArrowCursor)) self.setDragMode(QGraphicsView.NoDrag) QGraphicsView.mouseReleaseEvent(self, event) def keyPressEvent(self, event): if self.rband_state == "drawn": if event.key() == Qt.Key_Escape: self.setCursor(QCursor(Qt.ArrowCursor)) self.rband.hide() self.rband_state = None self.setMouseTracking(False) elif event.key() in [Qt.Key_Return, Qt.Key_Enter, Qt.Key_Space]: self.setCursor(QCursor(Qt.ArrowCursor)) self.rband.hide() self.rband_state = None self.setMouseTracking(False) #print("CB ", self.get_coords()) self.crop_callback(self.get_coords()) event.accept() def zoom(self, zoomratio): self.scale(zoomratio, zoomratio) def wheelEvent(self, event): zoomratio = 1.1 if event.angleDelta().y() < 0: zoomratio = 1.0 / zoomratio self.scale(zoomratio, zoomratio) def crop(self, callback): self.crop_callback = callback self.rband_state = "initial" self.setMouseTracking(True) def get_coords(self): rect = self.rband.geometry() size = self.mapToScene(rect).boundingRect() x = int(size.x()) y = int(size.y()) width = int(size.width()) height = int(size.height()) return (x, y, width, height)
class MainView(QGraphicsView): """ This class implements the main view displayed in the MDI area. """ MoveRate = 40 MoveBound = 10 sgnScaled = pyqtSignal(float) def __init__(self, mainwindow, scene): """ Initialize the main scene. :type mainwindow: MainWindow :type scene: DiagramScene """ super().__init__(scene) self.setContextMenuPolicy(Qt.PreventContextMenu) self.setDragMode(QGraphicsView.NoDrag) self.setOptimizationFlags(QGraphicsView.DontAdjustForAntialiasing) self.setOptimizationFlags(QGraphicsView.DontSavePainterState) self.setViewportUpdateMode(QGraphicsView.MinimalViewportUpdate) self.mainwindow = mainwindow self.mousePressCenterPos = None self.mousePressPos = None self.moveTimer = None self.rubberBandOrigin = None self.rubberBand = QRubberBand(QRubberBand.Rectangle, self) self.rubberBand.hide() self.zoom = 1.00 #################################################################################################################### # # # SLOTS # # # #################################################################################################################### @pyqtSlot() def updateView(self): """ Update the Overview. """ viewport = self.viewport() viewport.update() @pyqtSlot(float) def zoomChanged(self, zoom): """ Executed when the zoom factor changes (triggered by the Zoom widget). :type zoom: float """ self.scaleView(zoom) #################################################################################################################### # # # DRAWING # # # #################################################################################################################### def drawBackground(self, painter, rect): """ Draw the scene background. :type painter: QPainter :type rect: QRectF """ if self.mainwindow.snapToGrid: s = DiagramScene.GridSize x = int(rect.left()) - (int(rect.left()) % s) y = int(rect.top()) - (int(rect.top()) % s) painter.setPen(DiagramScene.GridPen) painter.drawPoints(*(QPointF(i, j) for i in rangeF(x, rect.right(), s) for j in rangeF(y, rect.bottom(), s))) #################################################################################################################### # # # EVENTS # # # #################################################################################################################### def keyPressEvent(self, keyEvent): """ Executed when a combination of key is pressed. :type keyEvent: QKeyEvent """ scene = self.scene() key = keyEvent.key() modifiers = keyEvent.modifiers() if scene.mode is DiagramMode.Idle and \ modifiers & Qt.ControlModifier and \ key in {Qt.Key_Minus, Qt.Key_Plus, Qt.Key_0}: if key == Qt.Key_Minus: zoom = self.zoom - Zoom.Step elif key == Qt.Key_Plus: zoom = self.zoom + Zoom.Step else: zoom = Zoom.Default zoom = clamp(zoom, Zoom.Min, Zoom.Max) if zoom != self.zoom: self.setTransformationAnchor(QGraphicsView.NoAnchor) self.setResizeAnchor(QGraphicsView.NoAnchor) self.scaleView(zoom) self.sgnScaled.emit(zoom) else: super().keyPressEvent(keyEvent) def mousePressEvent(self, mouseEvent): """ Executed when a mouse button is clicked on the view. :type mouseEvent: QGraphicsSceneMouseEvent """ scene = self.scene() mouseButtons = mouseEvent.buttons() mousePos = mouseEvent.pos() if mouseButtons & Qt.RightButton: ############################################################################################################ # # # SCENE DRAG # # # ############################################################################################################ visibleRect = self.visibleRect() self.mousePressCenterPos = visibleRect.center() self.mousePressPos = mousePos else: if mouseButtons & Qt.LeftButton: ######################################################################################################## # # # RUBBERBAND SELECTION # # # ######################################################################################################## if scene.mode is DiagramMode.Idle and not self.itemAt(mousePos): self.rubberBandOrigin = self.mapToScene(mousePos) self.rubberBand.setGeometry(QRectF(mousePos, mousePos).toRect()) self.rubberBand.show() scene.setMode(DiagramMode.RubberBandDrag) super().mousePressEvent(mouseEvent) # noinspection PyArgumentList def mouseMoveEvent(self, mouseEvent): """ Executed when then mouse is moved on the view. :type mouseEvent: QGraphicsSceneMouseEvent """ scene = self.scene() mousePos = mouseEvent.pos() mouseButtons = mouseEvent.buttons() viewport = self.viewport() if mouseButtons & Qt.RightButton: if (mouseEvent.pos() - self.mousePressPos).manhattanLength() >= QApplication.startDragDistance(): ######################################################################################################## # # # SCENE DRAG # # # ######################################################################################################## if scene.mode is not DiagramMode.SceneDrag: scene.setMode(DiagramMode.SceneDrag) viewport.setCursor(Qt.ClosedHandCursor) mousePos /= self.zoom mousePressPos = self.mousePressPos / self.zoom self.centerOn(self.mousePressCenterPos - mousePos + mousePressPos) else: super().mouseMoveEvent(mouseEvent) if mouseButtons & Qt.LeftButton: self.stopMove() if scene.mode is DiagramMode.RubberBandDrag: #################################################################################################### # # # RUBBERBAND DRAG # # # #################################################################################################### area = QRectF(self.mapFromScene(self.rubberBandOrigin), mousePos).normalized() path = QPainterPath() path.addRect(area) scene.setSelectionArea(self.mapToScene(path)) self.rubberBand.setGeometry(area.toRect()) if scene.mode in { DiagramMode.BreakPointMove, DiagramMode.InsertEdge, DiagramMode.MoveNode, DiagramMode.ResizeNode, DiagramMode.RubberBandDrag }: #################################################################################################### # # # VIEW SCROLLING # # # #################################################################################################### R = viewport.rect() if not R.contains(mousePos): move = QPointF(0, 0) if mousePos.x() < R.left(): move.setX(mousePos.x() - R.left()) elif mousePos.x() > R.right(): move.setX(mousePos.x() - R.right()) if mousePos.y() < R.top(): move.setY(mousePos.y() - R.top()) elif mousePos.y() > R.bottom(): move.setY(mousePos.y() - R.bottom()) if move: move.setX(clamp(move.x(), -MainView.MoveBound, +MainView.MoveBound)) move.setY(clamp(move.y(), -MainView.MoveBound, +MainView.MoveBound)) self.startMove(move, MainView.MoveRate) def mouseReleaseEvent(self, mouseEvent): """ Executed when the mouse is released from the view. :type mouseEvent: QGraphicsSceneMouseEvent """ self.mousePressCenterPos = None self.mousePressPos = None self.rubberBandOrigin = None self.rubberBand.hide() self.stopMove() scene = self.scene() viewport = self.viewport() viewport.setCursor(Qt.ArrowCursor) viewport.update() super().mouseReleaseEvent(mouseEvent) if scene.mode in {DiagramMode.RubberBandDrag, DiagramMode.SceneDrag}: scene.setMode(DiagramMode.Idle) def wheelEvent(self, wheelEvent): """ Executed when the mouse wheel is moved on the scene. :type wheelEvent: QWheelEvent """ if wheelEvent.modifiers() & Qt.ControlModifier: wheelPos = wheelEvent.pos() wheelAngle = wheelEvent.angleDelta() zoom = self.zoom zoom += +Zoom.Step if wheelAngle.y() > 0 else -Zoom.Step zoom = clamp(zoom, Zoom.Min, Zoom.Max) if zoom != self.zoom: self.setTransformationAnchor(QGraphicsView.NoAnchor) self.setResizeAnchor(QGraphicsView.NoAnchor) p1 = self.mapToScene(wheelPos) self.scaleView(zoom) self.sgnScaled.emit(zoom) p2 = self.mapToScene(wheelPos) move = p2 - p1 self.translate(move.x(), move.y()) else: super().wheelEvent(wheelEvent) #################################################################################################################### # # # INTERFACE # # # #################################################################################################################### def moveBy(self, *__args): """ Move the view by the given delta. """ if len(__args) == 1: delta = __args[0] elif len(__args) == 2: delta = QPointF(__args[0], __args[1]) else: raise TypeError('too many arguments; expected {}, got {}'.format(2, len(__args))) self.centerOn(self.visibleRect().center() + delta) def scaleView(self, zoom): """ Scale the view according to the given zoom. :type zoom: float """ transform = self.transform() self.resetTransform() self.translate(transform.dx(), transform.dy()) self.scale(zoom, zoom) self.zoom = zoom def startMove(self, delta, rate): """ Start the view movement. :type delta: QPointF :type rate: float """ if self.moveTimer: self.stopMove() # move the view: this is needed before the timer so that if we keep # moving the mouse fast outside the viewport rectangle we still are able # to move the view; if we don't do this the timer may not have kicked in # and thus we remain with a non-moving view with a unfocused graphicsitem self.moveBy(delta) # setup a timer for future move, so the view keeps moving # also if we are not moving the mouse anymore but we are # holding the position outside the viewport rect self.moveTimer = QTimer() connect(self.moveTimer.timeout, self.moveBy, delta) self.moveTimer.start(rate) def stopMove(self): """ Stop the view movement by destroying the timer object causing it. """ if self.moveTimer: self.moveTimer.stop() disconnect(self.moveTimer.timeout) self.moveTimer = None def visibleRect(self): """ Returns the visible area in scene coordinates. :rtype: QRectF """ return self.mapToScene(self.viewport().rect()).boundingRect()
class QMainLabel(QLabel): def __init__(self, parentQWidget): super(QMainLabel, self).__init__(parentQWidget) self.original_image_pixmap = None self.original_mask_pixmap = None self.target_size = self.parent().main_label_size def set_size(self, size): self.target_size = size def load_pixmap(self, image, mask=None): self.original_image_pixmap = QPixmap(image) if mask: self.original_mask_pixmap = QPixmap(mask) combo = self.merge_image_mask(self.original_image_pixmap, self.original_mask_pixmap) self.setPixmap(combo) return self.setPixmap(self.original_image_pixmap) def merge_image_mask(self, image, mask): print(type(image), type(mask)) if type(image) == QPixmap and type(mask) == QPixmap: pil_image = ImageQt.fromqpixmap(image) pil_mask = ImageQt.fromqpixmap(mask) else: pil_image = image pil_mask = mask np_image = np.array(pil_image) np_mask = np.array(pil_mask) print(np_image.shape, np_mask.shape) prob = np.float32(np_mask) / 255.0 np_combo = np.uint8(np_image * (1 - prob) + np_mask * prob * np.array([[[0., 1., 0.]]])) pil_combo = Image.fromarray(np_combo) combo = ImageQt.toqpixmap(pil_combo) return combo def restore_original(self): print('Ctrl+Z') image = self.original_image_pixmap mask = self.original_mask_pixmap if image and mask: image = self.merge_image_mask(image, mask) self.setPixmap(image) def mousePressEvent(self, eventQMouseEvent): self.originQPoint = eventQMouseEvent.pos() self.currentQRubberBand = QRubberBand(QRubberBand.Rectangle, self) self.currentQRubberBand.setGeometry(QRect(self.originQPoint, QSize())) self.currentQRubberBand.show() def mouseMoveEvent(self, eventQMouseEvent): self.currentQRubberBand.setGeometry( QRect(self.originQPoint, eventQMouseEvent.pos()).normalized()) def mouseReleaseEvent(self, eventQMouseEvent): self.currentQRubberBand.hide() currentQRect = self.currentQRubberBand.geometry() self.currentQRubberBand.deleteLater() cropQPixmap = self.pixmap().copy(currentQRect) # cropQPixmap.save('output.png') crop_PIL = ImageQt.fromqpixmap(cropQPixmap) crop_PIL = pad_to_square(crop_PIL, self.target_size) cropQPixmap = ImageQt.toqpixmap(crop_PIL) self.setPixmap(cropQPixmap)
class QTrappingPattern(pg.ScatterPlotItem): """Interface between QJansenScreen GUI and CGH pipeline. Implements logic for manipulating traps. """ trapAdded = pyqtSignal(QTrap) sigCompute = pyqtSignal(object) def __init__(self, parent=None, *args, **kwargs): super(QTrappingPattern, self).__init__(*args, **kwargs) self.setParent(parent) # this is not set by ScatterPlotItem self.setPxMode(False) # scale plot symbols with window # Rubberband selection self.selection = QRubberBand(QRubberBand.Rectangle, self.parent()) self.origin = QPoint() # traps, selected trap and active group self.traps = QTrapGroup(self) self.trap = None self.group = None self._prev = None self.selected = [] self._appearanceOutdated = False self._hologramOutdated = False @property def prev(self): return self._prev @prev.setter def prev(self, prev): if self._prev is not None: self._prev.state = states.normal self._prev = prev if self.prev is not None: prev.state = states.special @pyqtSlot() def toggleAppearance(self): logger.debug('toggleAppearance') self._appearanceOutdated = True @pyqtSlot() def toggleHologram(self): logger.debug('toggleHologram') self._hologramOutdated = True @pyqtSlot(object) def refresh(self, frame): self.refreshAppearance() self.refreshHologram() @pyqtSlot() def refreshAppearance(self): """Provide a list of spots to screen for plotting. This will be called by children when their properties change. Changes can be triggered by mouse events, by interaction with property widgets, or by direct programmatic control of traps or groups. """ if self._appearanceOutdated: traps = self.traps.flatten() spots = [trap.spot for trap in traps] self.setData(spots=spots) self._appearanceOutdated = False logger.debug('refreshAppearance') @pyqtSlot() def refreshHologram(self): if self._hologramOutdated: traps = self.traps.flatten() self.sigCompute.emit(traps) self._hologramOutdated = False logger.debug('refreshHologram') def selectedPoint(self, position): points = self.pointsAt(position) index = self.points().tolist().index(points[0]) if points else None return index # Selecting traps and groups of traps def clickedTrap(self, pos): """Return the trap at the specified position """ coords = self.mapFromScene(pos) index = self.selectedPoint(coords) found = index is not None return self.traps.flatten()[index] if found else None def groupOf(self, obj): """Return the highest-level group containing the specified object. """ if obj is None: return None while obj.parent() is not self.traps: obj = obj.parent() return obj def clickedGroup(self, pos): """Return the highest-level group containing the trap at the specified position. """ self.trap = self.clickedTrap(pos) return self.groupOf(self.trap) def selectedTraps(self, region): """Return a list of traps whose groups fall entirely within the selection region. """ self.selected = [] rect = self.mapFromScene(QRectF(region)).boundingRect() for child in self.traps.children(): if child.isWithin(rect): self.selected.append(child) child.state = states.grouping else: child.state = states.normal self.refreshAppearance() # Creating and deleting traps def addTrap(self, trap): trap.setParent(self) self.traps.add(trap) trap.appearanceChanged.connect(self.toggleAppearance) trap.hologramChanged.connect(self.toggleHologram) trap.destroyed.connect(self.toggleAppearance) trap.destroyed.connect(self.toggleHologram) trap.cgh = self.parent().cgh.device self.trapAdded.emit(trap) def createTrap(self, r, state=None): trap = QTrap(r=r, state=state) self.addTrap(trap) return trap def createTraps(self, coordinates, state=None): coords = list(coordinates) if not coords: return group = QTrapGroup() self.traps.add(group) for r in coords: trap = self.createTrap(r, state=state) group.add(trap) return group def clearTrap(self, trap): """Remove specified trap from trapping pattern""" self.traps.remove(trap, delete=True) def clearTraps(self, traps=None): """Remove all traps from trapping pattern. """ traps = traps or self.traps.flatten() for trap in traps: self.clearTrap(trap) # Creating, breaking and moving groups of traps def createGroup(self): """Combine selected objects into new group""" group = QTrapGroup() for trap in self.selected: if trap.parent() is not self: trap.parent().remove(trap) group.add(trap) self.traps.add(group) self.selected = [] return group def breakGroup(self): """Break group into children and place children in the top level. """ if isinstance(self.group, QTrapGroup): for child in self.group.children(): child.state = states.grouping self.group.remove(child) self.traps.add(child) self.group = None def moveGroup(self, pos): """Move the selected group so that the selected trap is at the specified position. """ coords = self.mapFromScene(pos) dr = QVector3D(coords - self.trap.coords()) self.group.moveBy(dr) # Dispatch low-level events to actions def leftPress(self, pos, modifiers): """Selection and grouping. """ self.group = self.clickedGroup(pos) # update selection rectangle if self.group is None: self.origin = QPoint(pos) rect = QRect(self.origin, QSize()) self.selection.setGeometry(rect) self.selection.show() # break selected group elif modifiers == Qt.ControlModifier: self.breakGroup() # select group else: self.group.state = states.selected self.refreshAppearance() def rightPress(self, pos, modifiers): """Creation and destruction. """ # Shift-Right Click: Add trap if modifiers == Qt.ShiftModifier: r = self.mapFromScene(pos) self.createTrap(r, state=states.selected) # Ctrl-Right Click: Delete trap elif modifiers == Qt.ControlModifier: self.traps.remove(self.clickedGroup(pos), delete=True) # Handlers for signals emitted by QJansenScreen @pyqtSlot(QMouseEvent) def mousePress(self, event): """Event handler for mousePress events. """ button = event.button() pos = event.pos() modifiers = event.modifiers() self.prev = None if button == Qt.LeftButton: self.leftPress(pos, modifiers) elif button == Qt.RightButton: self.rightPress(pos, modifiers) @pyqtSlot(QMouseEvent) def mouseMove(self, event): """Event handler for mouseMove events. """ pos = event.pos() # Move traps if self.group is not None: self.moveGroup(pos) # Update selection box elif self.selection.isVisible(): region = QRect(self.origin, QPoint(pos)).normalized() self.selection.setGeometry(region) self.selectedTraps(region) @pyqtSlot(QMouseEvent) def mouseRelease(self, event): """Event handler for mouseRelease events. """ if self.selected: self.createGroup() for child in self.traps.children(): child.state = states.normal self.prev = self.group self.group = None self.selection.hide() self.refreshAppearance() @pyqtSlot(QWheelEvent) def mouseWheel(self, event): """Event handler for mouse wheel events. """ pos = event.pos() group = self.clickedGroup(pos) if group is not None: group.state = states.selected dz = event.angleDelta().y() / 120. group.moveBy(QVector3D(0, 0, dz)) # group.state = states.normal self.group = None