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 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 __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 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 __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)
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)
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 __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)
class ImageView(QGraphicsView): 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) def mousePressEvent(self, event): """Go to the next / previous image, or be able to drag the image with a hand.""" 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 mouseReleaseEvent(self, event): self.setDragMode(QGraphicsView.NoDrag) QGraphicsView.mouseReleaseEvent(self, event) 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 setup_crop(self, width, height): self.rband = QRubberBand(QRubberBand.Rectangle, self) coords = self.mapFromScene(0, 0, width, height) self.rband.setGeometry(QRect(coords.boundingRect())) self.rband.show() def crop_draw(self, x, y, width, height): coords = self.mapFromScene(x, y, width, height) self.rband.setGeometry(QRect(coords.boundingRect())) 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)
def __init__(self, parent): super(FrameLess, self).__init__(parent) self._parent = parent self._cursorChanged = False self._leftButtonPressed = False self._mouseMove = Edge.NoEdge self._mousePress = Edge.NoEdge self._borderWidth = 5 self._rubberBand = QRubberBand(QRubberBand.Rectangle) self._parent.setWindowFlags(Qt.FramelessWindowHint | Qt.CustomizeWindowHint | Qt.Window) self._parent.setAttribute(Qt.WA_Hover) self._parent.setMouseTracking(True) self._parent.installEventFilter(self)
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
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()
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 __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
def __init__(self, parent=None): super(ResizableRubberBandWithGrip, self).__init__(parent) self.setAttribute(Qt.WA_DeleteOnClose) self.setWindowTitle("Defter: Screenshot") self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) self.setAttribute(Qt.WA_TranslucentBackground) self.setAttribute(Qt.WA_NoSystemBackground) # self.setAttribute(Qt.WA_PaintOnScreen) # self.setAttribute(Qt.WA_OpaquePaintEvent, False) # self.setStyleSheet("QWidget{background:transparent;}") # self.setStyleSheet("QWidget{background-color:none;}") # self.setStyleSheet("background-color: rgba(255, 255, 255, 40%);") # self.setWindowOpacity(0.7) layout = QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) grip1 = MySizeGrip(self) # grip2 = QSizeGrip(self) # grip3 = QSizeGrip(self) grip4 = MySizeGrip(self) layout.addWidget(grip1, 0, Qt.AlignLeft | Qt.AlignTop) # layout.addWidget(grip3, 0, Qt.AlignRight | Qt.AlignTop) # layout.addWidget(grip2, 0, Qt.AlignLeft | Qt.AlignBottom) layout.addWidget(grip4, 0, Qt.AlignRight | Qt.AlignBottom) self.rubberband = QRubberBand(QRubberBand.Rectangle, self) self.rubberband.move(0, 0) self.rubberband.show() self.show()
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 ResizableRubberBandWithGrip(QWidget): # containerResized = Signal(QSize) enter_or_return_key_pressed = pyqtSignal(QRect) esc_key_pressed = pyqtSignal() # --------------------------------------------------------------------- def __init__(self, parent=None): super(ResizableRubberBandWithGrip, self).__init__(parent) self.setAttribute(Qt.WA_DeleteOnClose) self.setWindowTitle("Defter: Screenshot") self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) self.setAttribute(Qt.WA_TranslucentBackground) self.setAttribute(Qt.WA_NoSystemBackground) # self.setAttribute(Qt.WA_PaintOnScreen) # self.setAttribute(Qt.WA_OpaquePaintEvent, False) # self.setStyleSheet("QWidget{background:transparent;}") # self.setStyleSheet("QWidget{background-color:none;}") # self.setStyleSheet("background-color: rgba(255, 255, 255, 40%);") # self.setWindowOpacity(0.7) layout = QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) grip1 = MySizeGrip(self) # grip2 = QSizeGrip(self) # grip3 = QSizeGrip(self) grip4 = MySizeGrip(self) layout.addWidget(grip1, 0, Qt.AlignLeft | Qt.AlignTop) # layout.addWidget(grip3, 0, Qt.AlignRight | Qt.AlignTop) # layout.addWidget(grip2, 0, Qt.AlignLeft | Qt.AlignBottom) layout.addWidget(grip4, 0, Qt.AlignRight | Qt.AlignBottom) self.rubberband = QRubberBand(QRubberBand.Rectangle, self) self.rubberband.move(0, 0) self.rubberband.show() self.show() def mousePressEvent(self, event): self._mousePressPos = None self._mouseMovePos = None if event.button() == Qt.LeftButton: self._mousePressPos = event.globalPos() self._mouseMovePos = event.globalPos() super(ResizableRubberBandWithGrip, self).mousePressEvent(event) def mouseMoveEvent(self, event): if event.buttons() == Qt.LeftButton: # adjust offset from clicked point to origin of widget currPos = self.mapToGlobal(self.pos()) globalPos = event.globalPos() diff = globalPos - self._mouseMovePos newPos = self.mapFromGlobal(currPos + diff) self.move(newPos) self._mouseMovePos = globalPos super(ResizableRubberBandWithGrip, self).mouseMoveEvent(event) def mouseReleaseEvent(self, event): if self._mousePressPos: moved = event.globalPos() - self._mousePressPos if moved.manhattanLength() > 3: event.ignore() return super(ResizableRubberBandWithGrip, self).mouseReleaseEvent(event) # --------------------------------------------------------------------- def resizeEvent(self, event): # self.containerResized.emit((self.size())) self.rubberband.resize(self.size()) super(ResizableRubberBandWithGrip, self).resizeEvent(event) # --------------------------------------------------------------------- def mouseDoubleClickEvent(self, event): self.hide() self.enter_or_return_key_pressed.emit(self.geometry()) # self.close() # self.deleteLater() # --------------------------------------------------------------------- def keyPressEvent(self, event): if event.key() == Qt.Key_Escape: self.esc_key_pressed.emit() elif event.key() == Qt.Key_Enter or Qt.Key_Return: self.hide() self.enter_or_return_key_pressed.emit(self.geometry()) # self.close() # self.deleteLater() # return ResizableRubberBandWithGripForImageResizing.keyPressEvent(self, event) super(ResizableRubberBandWithGrip, self).keyPressEvent(event)
def __init__(self, parent=None): self._parent_class = _parent_class QLabel.__init__(self, parent) self.selection = QRubberBand(QRubberBand.Rectangle, self)
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 value = self.model().data(valueIndex) if value is not None and value > 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 region.intersects(QRegion(contentsRect)): 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
def __init__(self, parent=None): super().__init__(parent=parent) self.initialClick = QPoint() self.selectionBand = QRubberBand(QRubberBand.Rectangle, self) self.selecting = 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 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: try: self._prev.state = states.normal except: logger.debug( 'warning: cannot set _prev.state (trapgroup may have been deleted)' ) 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): '''Index of trap at position of mouse click''' points = self.pointsAt(position) if len(points) == 0: return None return self.points().tolist().index(points[0]) # 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
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()
class IMUChartView(QChartView, BaseGraph): def __init__(self, parent=None): super().__init__(parent=parent) self.cursor = QGraphicsLineItem() self.scene().addItem(self.cursor) self.xvalues = {} self.chart = QChart() self.setChart(self.chart) self.chart.legend().setVisible(True) self.chart.legend().setAlignment(Qt.AlignTop) self.ncurves = 0 self.setRenderHint(QPainter.Antialiasing) # Selection features # self.setRubberBand(QChartView.HorizontalRubberBand) self.selectionBand = QRubberBand(QRubberBand.Rectangle, self) self.selecting = False self.initialClick = QPoint() # Track mouse self.setMouseTracking(True) self.labelValue = QLabel(self) self.labelValue.setStyleSheet( "background-color: rgba(255,255,255,75%); color: black;") self.labelValue.setAlignment(Qt.AlignCenter) self.labelValue.setMargin(5) self.labelValue.setVisible(False) self.build_style() def build_style(self): self.setStyleSheet("QLabel{color:blue;}") self.chart.setTheme(QChart.ChartThemeBlueCerulean) self.setBackgroundBrush(QBrush(Qt.darkGray)) self.chart.setPlotAreaBackgroundBrush(QBrush(Qt.black)) self.chart.setPlotAreaBackgroundVisible(True) def save_as_png(self, file_path): pixmap = self.grab() child = self.findChild(QOpenGLWidget) painter = QPainter(pixmap) if child is not None: d = child.mapToGlobal(QPoint()) - self.mapToGlobal(QPoint()) painter.setCompositionMode(QPainter.CompositionMode_SourceAtop) painter.drawImage(d, child.grabFramebuffer()) painter.end() pixmap.save(file_path, 'PNG') # def closeEvent(self, event): # self.aboutToClose.emit(self) @staticmethod def decimate(xdata, ydata): # assert(len(xdata) == len(ydata)) # Decimate only if we have too much data decimate_factor = len(xdata) / 100000.0 # decimate_factor = 1.0 if decimate_factor > 1.0: decimate_factor = int(np.floor(decimate_factor)) # print('decimate factor', decimate_factor) x = np.ndarray(int(len(xdata) / decimate_factor), dtype=np.float64) y = np.ndarray(int(len(ydata) / decimate_factor), dtype=np.float64) # for i in range(len(x)): for i, _ in enumerate(x): index = i * decimate_factor # assert(index < len(xdata)) x[i] = xdata[index] y[i] = ydata[index] if x[i] < x[0]: print('timestamp error', x[i], x[0]) return x, y else: return xdata, ydata @pyqtSlot(float, float) def axis_range_changed(self, min_value, max_value): # print('axis_range_changed', min, max) for axis in self.chart.axes(): axis.applyNiceNumbers() def update_axes(self): # Get and remove all axes for axis in self.chart.axes(): self.chart.removeAxis(axis) # Create new axes # Create axis X axisx = QDateTimeAxis() axisx.setTickCount(5) axisx.setFormat("dd MMM yyyy hh:mm:ss") axisx.setTitleText("Date") self.chart.addAxis(axisx, Qt.AlignBottom) # Create axis Y axisY = QValueAxis() axisY.setTickCount(5) axisY.setLabelFormat("%.3f") axisY.setTitleText("Values") self.chart.addAxis(axisY, Qt.AlignLeft) # axisY.rangeChanged.connect(self.axis_range_changed) ymin = None ymax = None # Attach axes to series, find min-max for series in self.chart.series(): series.attachAxis(axisx) series.attachAxis(axisY) vect = series.pointsVector() # for i in range(len(vect)): for i, _ in enumerate(vect): if ymin is None: ymin = vect[i].y() ymax = vect[i].y() else: ymin = min(ymin, vect[i].y()) ymax = max(ymax, vect[i].y()) # Update range # print('min max', ymin, ymax) if ymin is not None: axisY.setRange(ymin, ymax) # Make the X,Y axis more readable # axisx.applyNiceNumbers() # axisY.applyNiceNumbers() def add_data(self, xdata, ydata, color=None, legend_text=None): curve = QLineSeries() pen = curve.pen() if color is not None: pen.setColor(color) pen.setWidthF(1.5) curve.setPen(pen) # curve.setPointsVisible(True) # curve.setUseOpenGL(True) self.total_samples = max(self.total_samples, len(xdata)) # Decimate xdecimated, ydecimated = self.decimate(xdata, ydata) # Data must be in ms since epoch # curve.append(self.series_to_polyline(xdecimated * 1000.0, ydecimated)) # self.reftime = datetime.datetime.fromtimestamp(xdecimated[0]) # if len(xdecimated) > 0: # xdecimated = xdecimated - xdecimated[0] xdecimated *= 1000 # No decimal expected points = [] for i, _ in enumerate(xdecimated): # TODO hack # curve.append(QPointF(xdecimated[i], ydecimated[i])) points.append(QPointF(xdecimated[i], ydecimated[i])) curve.replace(points) points.clear() if legend_text is not None: curve.setName(legend_text) # Needed for mouse events on series self.chart.setAcceptHoverEvents(True) self.xvalues[self.ncurves] = np.array(xdecimated) # connect signals / slots # curve.clicked.connect(self.lineseries_clicked) # curve.hovered.connect(self.lineseries_hovered) # Add series self.chart.addSeries(curve) self.ncurves += 1 self.update_axes() def update_data(self, xdata, ydata, series_id): if series_id < len(self.chart.series()): # Find start time to replace data current_series = self.chart.series()[series_id] current_points = current_series.pointsVector() # Find start and end indexes start_index = -1 try: start_index = current_points.index( QPointF(xdata[0] * 1000, ydata[0])) # Right on the value! # print("update_data: start_index found exact match.") except ValueError: # print("update_data: start_index no exact match - scanning deeper...") for i, value in enumerate(current_points): # print(str(current_points[i].x()) + " == " + str(xdata[0]*1000)) if current_points[i].x() == xdata[0] * 1000 or ( i > 0 and current_points[i - 1].x() < xdata[0] * 1000 < current_points[i].x()): start_index = i # print("update_data: start_index found approximative match.") break end_index = -1 try: end_index = current_points.index( QPointF(xdata[len(xdata) - 1] * 1000, ydata[len(ydata) - 1])) # Right on! # print("update_data: start_index found exact match.") except ValueError: # print("update_data: start_index no exact match - scanning deeper...") for i, value in enumerate(current_points): # print(str(current_points[i].x()) + " == " + str(xdata[0]*1000)) if current_points[i].x( ) == xdata[len(xdata) - 1] * 1000 or ( i > 0 and current_points[i - 1].x() < xdata[len(xdata) - 1] * 1000 < current_points[i].x()): end_index = i # print("update_data: start_index found approximative match.") break if start_index < 0 or end_index < 0: return # Decimate, if needed xdata, ydata = self.decimate(xdata, ydata) # Check if we have the same number of points for that range. If not, remove and replace! target_points = current_points[start_index:end_index] if len(target_points) != len(xdata): points = [] for i, value in enumerate(xdata): # TODO improve points.append(QPointF(value * 1000, ydata[i])) new_points = current_points[0:start_index] + points[0:len(points)-1] + \ current_points[end_index:len(current_points)-1] current_series.replace(new_points) new_points.clear() # self.xvalues[series_id] = np.array(xdata) return @classmethod def set_title(cls, title): # print('Setting title: ', title) # self.chart.setTitle(title) return @staticmethod def series_to_polyline(xdata, ydata): """Convert series data to QPolygon(F) polyline This code is derived from PythonQwt's function named `qwt.plot_curve.series_to_polyline`""" # print('series_to_polyline types:', type(xdata[0]), type(ydata[0])) size = len(xdata) polyline = QPolygonF(size) # for i in range(0, len(xdata)): # polyline[i] = QPointF(xdata[i] - xdata[0], ydata[i]) for i, data in enumerate(xdata): polyline[i] = QPointF(data - xdata[0], ydata[i]) # pointer = polyline.data() # dtype, tinfo = np.float, np.finfo # integers: = np.int, np.iinfo # pointer.setsize(2*polyline.size()*tinfo(dtype).dtype.itemsize) # memory = np.frombuffer(pointer, dtype) # memory[:(size-1)*2+1:2] = xdata # memory[1:(size-1)*2+2:2] = ydata return polyline def add_test_data(self): # 100Hz, one day accelerometer values npoints = 1000 * 60 * 24 xdata = np.linspace(0., 10., npoints) self.add_data(xdata, np.sin(xdata), color=Qt.red, legend_text='Acc. X') # self.add_data(xdata, np.cos(xdata), color=Qt.green, legend_text='Acc. Y') # self.add_data(xdata, np.cos(2 * xdata), color=Qt.blue, legend_text='Acc. Z') self.set_title( "Simple example with %d curves of %d points (OpenGL Accelerated Series)" % (self.ncurves, npoints)) def mouseMoveEvent(self, e: QMouseEvent): if self.selecting: current_pos = e.pos() if self.interaction_mode == GraphInteractionMode.SELECT: 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, self.chart.plotArea().y(), width, self.chart.plotArea().height())) if self.interaction_mode == GraphInteractionMode.MOVE: new_pos = current_pos - self.initialClick self.chart.scroll(-new_pos.x(), new_pos.y()) self.initialClick = current_pos def mousePressEvent(self, e: QMouseEvent): # Handling rubberbands # super().mousePressEvent(e) self.selecting = True self.initialClick = e.pos() if self.interaction_mode == GraphInteractionMode.SELECT: self.selectionBand.setGeometry( QRect(self.initialClick.x(), self.chart.plotArea().y(), 1, self.chart.plotArea().height())) self.selectionBand.show() if self.interaction_mode == GraphInteractionMode.MOVE: QGuiApplication.setOverrideCursor(Qt.ClosedHandCursor) self.labelValue.setVisible(False) def mouseReleaseEvent(self, e: QMouseEvent): # Handling rubberbands clicked_x = self.mapToScene(e.pos()).x() if self.interaction_mode == GraphInteractionMode.SELECT: self.selectionBand.hide() if clicked_x == self.mapToScene(self.initialClick).x(): self.setCursorPosition(clicked_x, True) else: mapped_x = self.mapToScene(self.initialClick).x() if self.initialClick.x() < clicked_x: self.setSelectionArea(mapped_x, clicked_x, True) else: self.setSelectionArea(clicked_x, mapped_x, True) if self.interaction_mode == GraphInteractionMode.MOVE: QGuiApplication.restoreOverrideCursor() self.selecting = False def clearSelectionArea(self, emit_signal=False): self.scene().removeItem(self.selection_rec) self.selection_rec = None if emit_signal: self.clearedSelectionArea.emit() def setSelectionArea(self, start_pos, end_pos, emit_signal=False): selection_brush = QBrush(QColor(153, 204, 255, 128)) selection_pen = QPen(Qt.transparent) self.scene().removeItem(self.selection_rec) self.selection_rec = self.scene().addRect( start_pos, self.chart.plotArea().y(), end_pos - start_pos, self.chart.plotArea().height(), selection_pen, selection_brush) if emit_signal: self.selectedAreaChanged.emit( self.chart.mapToValue(QPointF(start_pos, 0)).x(), self.chart.mapToValue(QPointF(end_pos, 0)).x()) def setSelectionAreaFromTime(self, start_time, end_time, emit_signal=False): # Convert times to x values if isinstance(start_time, datetime.datetime): start_time = start_time.timestamp() * 1000 if isinstance(end_time, datetime.datetime): end_time = end_time.timestamp() * 1000 start_pos = self.chart.mapToPosition(QPointF(start_time, 0)).x() end_pos = self.chart.mapToPosition(QPointF(end_time, 0)).x() self.setSelectionArea(start_pos, end_pos) def setCursorPosition(self, pos, emit_signal=False): # print (pos) pen = self.cursor.pen() pen.setColor(Qt.cyan) pen.setWidthF(1.0) self.cursor.setPen(pen) # On Top self.cursor.setZValue(100.0) area = self.chart.plotArea() x = pos y1 = area.y() y2 = area.y() + area.height() # self.cursor.set self.cursor.setLine(x, y1, x, y2) self.cursor.show() xmap_initial = self.chart.mapToValue(QPointF(pos, 0)).x() display = '' # '<i>' + (datetime.datetime.fromtimestamp(xmap + self.reftime.timestamp())).strftime('%d-%m-%Y %H:%M:%S') + # '</i><br />' ypos = 10 last_val = None for i in range(self.ncurves): # Find nearest point idx = (np.abs(self.xvalues[i] - xmap_initial)).argmin() ymap = self.chart.series()[i].at(idx).y() xmap = self.chart.series()[i].at(idx).x() if i == 0: display += "<i>" + (datetime.datetime.fromtimestamp(xmap_initial/1000)).strftime('%d-%m-%Y %H:%M:%S:%f') + \ "</i>" # Compute where to display label if last_val is None or ymap > last_val: last_val = ymap ypos = self.chart.mapToPosition(QPointF(xmap, ymap)).y() if display != '': display += '<br />' display += self.chart.series()[i].name( ) + ': <b>' + '%.3f' % ymap + '</b>' self.labelValue.setText(display) self.labelValue.setGeometry(pos, ypos, 100, 100) self.labelValue.adjustSize() self.labelValue.setVisible(True) if emit_signal: self.cursorMoved.emit(xmap_initial) self.update() def setCursorPositionFromTime(self, timestamp, emit_signal=False): # Find nearest point if isinstance(timestamp, datetime.datetime): timestamp = timestamp.timestamp() pos = self.get_pos_from_time(timestamp) self.setCursorPosition(pos, emit_signal) def get_pos_from_time(self, timestamp): px = timestamp idx1 = (np.abs(self.xvalues[0] - px)).argmin() x1 = self.chart.series()[0].at(idx1).x() pos1 = self.chart.mapToPosition(QPointF(x1, 0)).x() idx2 = idx1 + 1 if idx2 < len(self.chart.series()[0]): x2 = self.chart.series()[0].at(idx2).x() if x2 != x1: pos2 = self.chart.mapToPosition(QPointF(x2, 0)).x() x2 /= 1000 x1 /= 1000 pos = (((px - x1) / (x2 - x1)) * (pos2 - pos1)) + pos1 else: pos = pos1 else: pos = pos1 return pos def resizeEvent(self, e: QResizeEvent): super().resizeEvent(e) # Update cursor height area = self.chart.plotArea() line = self.cursor.line() self.cursor.setLine(line.x1(), area.y(), line.x2(), area.y() + area.height()) def zoom_in(self): self.chart.zoomIn() self.update_axes() def zoom_out(self): self.chart.zoomOut() self.update_axes() def zoom_area(self): if self.selection_rec: zoom_rec = self.selection_rec.rect() zoom_rec.setY(0) zoom_rec.setHeight(self.chart.plotArea().height()) self.chart.zoomIn(zoom_rec) self.clearSelectionArea(True) self.update_axes() def zoom_reset(self): self.chart.zoomReset() self.update_axes() def get_displayed_start_time(self): min_x = self.chart.mapToScene(self.chart.plotArea()).boundingRect().x() xmap = self.chart.mapToValue(QPointF(min_x, 0)).x() return datetime.datetime.fromtimestamp(xmap / 1000) def get_displayed_end_time(self): max_x = self.chart.mapToScene(self.chart.plotArea()).boundingRect().x() max_x += self.chart.mapToScene( self.chart.plotArea()).boundingRect().width() xmap = self.chart.mapToValue(QPointF(max_x, 0)).x() return datetime.datetime.fromtimestamp(xmap / 1000) @property def is_zoomed(self): return self.chart.isZoomed()
class QToolWindowManager ( QWidget ): toolWindowVisibilityChanged = pyqtSignal ( QWidget, bool ) layoutChanged = pyqtSignal () updateTrackingTooltip = pyqtSignal ( str, QPoint ) def __init__ ( self, parent, config, factory = None ): super ( QToolWindowManager, self ).__init__ ( parent ) self.factory = factory if factory != None else QToolWindowManagerClassFactory () self.dragHandler = None self.config = config self.closingWindow = 0 self.areas = [] self.wrappers = [] self.toolWindows = [] self.toolWindowsTypes = {} self.draggedToolWindows = [] self.layoutChangeNotifyLocks = 0 if self.factory.parent () == None: self.factory.setParent ( self ) self.mainWrapper = QToolWindowWrapper ( self ) self.mainWrapper.getWidget ().setObjectName ( 'mainWrapper' ) self.setLayout ( QVBoxLayout ( self ) ) self.layout ().setContentsMargins ( 2, 2, 2, 2 ) self.layout ().setSpacing ( 0 ) self.layout ().addWidget ( self.mainWrapper.getWidget () ) self.lastArea = self.createArea () self.draggedWrapper = None self.resizedWrapper = None self.mainWrapper.setContents ( self.lastArea.getWidget () ) self.dragHandler = self.createDragHandler () self.preview = QRubberBand ( QRubberBand.Rectangle ) self.preview.hide () self.raiseTimer = QTimer ( self ) self.raiseTimer.timeout.connect ( self.raiseCurrentArea ) self.setSizePolicy ( QSizePolicy.Expanding, QSizePolicy.Expanding ) qApp.installEventFilter ( self ) def __del__ ( self ): self.suspendLayoutNotifications () while len ( self.areas ) != 0: a = self.areas[ 0 ] a.setParent ( None ) self.areas.remove ( a ) while len ( self.wrappers ) != 0: w = self.wrappers[ 0 ] w.setParent ( None ) self.areas.remove ( w ) del self.dragHandler del self.mainWrapper def empty ( self ) -> bool: return len ( self.areas ) == 0 def removeArea ( self, area ): """Summary line. Args: area (QToolWindowArea): Description of param1 Returns: QToolWindowArea: Description of return value """ # print ( "[QToolWindowManager] removeArea %s" % area ) if area in self.areas: self.areas.remove ( area ) if self.lastArea == area: self.lastArea = None def removeWrapper ( self, wrapper ): """Summary line. Args: wrapper (QToolWindowWrapper): Description of param1 """ if self.ownsWrapper ( wrapper ): self.wrappers.remove ( wrapper ) def ownsArea ( self, area ) -> bool: """Summary line. Args: area (QToolWindowArea): Description of param1 Returns: bool: Description of return value """ return area in self.areas def ownsWrapper ( self, wrapper ) -> bool: """Summary line. Args: wrapper (QToolWindowWrapper): Description of param1 Returns: bool: Description of return value """ return wrapper in self.wrappers def ownsToolWindow ( self, toolWindow ) -> bool: """Summary line. Args: wrapper (QToolWindowWrapper): Description of param1 Returns: bool: Description of return value """ return toolWindow in self.toolWindows def startDrag ( self, toolWindows, area ): if len ( toolWindows ) == 0: return # print ( "[QToolWindowManager] startDrag", toolWindows ) self.dragHandler.startDrag () self.draggedToolWindows = toolWindows floatingGeometry = QRect ( QCursor.pos (), area.size () ) self.moveToolWindows ( toolWindows, area, QToolWindowAreaReference.Drag, -1, floatingGeometry ) def startDragWrapper ( self, wrapper ): self.dragHandler.startDrag () self.draggedWrapper = wrapper self.lastArea = None self.updateDragPosition () def startResize ( self, wrapper ): self.resizedWrapper = wrapper def addToolWindowTarget ( self, toolWindow, target, toolType = QTWMToolType.ttStandard ): self.insertToToolTypes ( toolWindow, toolType ) self.addToolWindow ( toolWindow, target.area, target.reference, target.index, target.geometry ) def addToolWindow ( self, toolWindow, area = None, reference = QToolWindowAreaReference.Combine, index = -1, geometry = QRect () ): self.addToolWindows ( [ toolWindow ], area, reference, index, geometry ) def addToolWindowsTarget ( self, toolWindows, target, toolType ): for toolWindow in toolWindows: self.insertToToolTypes ( toolWindow, toolType ) self.addToolWindows ( toolWindows, target.area, target.reference, target.index, target.geometry ) def addToolWindows ( self, toolWindows, area = None, reference = QToolWindowAreaReference.Combine, index = -1, geometry = QRect () ): for toolWindow in toolWindows: if not self.ownsToolWindow ( toolWindow ): toolWindow.hide () toolWindow.setParent ( None ) toolWindow.installEventFilter ( self ) self.insertToToolTypes ( toolWindow, QTWMToolType.ttStandard ) self.toolWindows.append ( toolWindow ) # from qt.controls.QToolWindowManager.QToolTabManager import QTabPane # # if isinstance ( toolWindow, QTabPane ): # print ( "[QToolWindowManager] addToolWindow: QTabPane %s" % toolWindow.pane ) # else: # print ( "[QToolWindowManager] addToolWindow: %s" % toolWindow ) self.moveToolWindows ( toolWindows, area, reference, index, geometry ) def moveToolWindowTarget ( self, toolWindow, target, toolType = QTWMToolType.ttStandard ): self.insertToToolTypes ( toolWindow, toolType ) self.addToolWindow ( toolWindow, target.area, target.reference, target.index, target.geometry ) def moveToolWindow ( self, toolWindow, area = None, reference = QToolWindowAreaReference.Combine, index = -1, geometry = QRect () ): self.moveToolWindows ( [ toolWindow ], area, reference, index, geometry ) def moveToolWindowsTarget ( self, toolWindows, target, toolType = QTWMToolType.ttStandard ): for toolWindow in toolWindows: self.insertToToolTypes ( toolWindow, toolType ) self.addToolWindows ( toolWindows, target.area, target.reference, target.index, target.geometry ) def moveToolWindows ( self, toolWindows, area = None, reference = QToolWindowAreaReference.Combine, index = -1, geometry = None ): # qWarning ( "moveToolWindows %s" % toolWindows ) # If no area find one if area == None: if self.lastArea != None: area = self.lastArea elif len ( self.areas ) != 0: area = self.areas[ 0 ] else: qWarning ( "lastArea is None, and areas is 0, self.createArea ()" ) area = self.createArea () self.lastArea = area self.mainWrapper.setContents ( self.lastArea.getWidget () ) dragOffset = QPoint () # Get the current mouse position and offset from the area before we remove the tool windows from it if area != None and reference == QToolWindowAreaReference.Drag: widgetPos = area.mapToGlobal ( area.rect ().topLeft () ) dragOffset = widgetPos - QCursor.pos () wrapper = None currentAreaIsSimple = True for toolWindow in toolWindows: # when iterating over the tool windows, we will figure out if the current one is actually roll-ups and not tabs currentArea = findClosestParent ( toolWindow, [ QToolWindowArea, QToolWindowRollupBarArea ] ) if currentAreaIsSimple and currentArea != None and currentArea.areaType () == QTWMWrapperAreaType.watTabs: currentAreaIsSimple = False self.releaseToolWindow ( toolWindow, False ) if reference == QToolWindowAreaReference.Top or \ reference == QToolWindowAreaReference.Bottom or \ reference == QToolWindowAreaReference.Left or \ reference == QToolWindowAreaReference.Right: area = cast ( self.splitArea ( self.getFurthestParentArea ( area.getWidget () ).getWidget (), reference ), [ QToolWindowArea, QToolWindowRollupBarArea ] ) elif reference == QToolWindowAreaReference.HSplitTop or \ reference == QToolWindowAreaReference.HSplitBottom or \ reference == QToolWindowAreaReference.VSplitLeft or \ reference == QToolWindowAreaReference.VSplitRight: area = cast ( self.splitArea ( area.getWidget (), reference ), [ QToolWindowArea, QToolWindowRollupBarArea ] ) elif reference == QToolWindowAreaReference.Floating or \ reference == QToolWindowAreaReference.Drag: # when dragging we will try to determine target are type, from window types. areaType = QTWMWrapperAreaType.watTabs if len ( toolWindows ) > 1: if currentAreaIsSimple: areaType = QTWMWrapperAreaType.watRollups elif self.toolWindowsTypes[ toolWindows[ 0 ] ] == QTWMToolType.ttSimple: areaType = QTWMWrapperAreaType.watRollups # create new window area = self.createArea ( areaType ) wrapper = self.createWrapper () wrapper.setContents ( area.getWidget () ) # wrapper.move ( QCursor.pos () ) wrapper.getWidget ().show () # wrapper.getWidget ().grabMouse () # qWarning ( "[QToolWindowManager] moveToolWindows create new window reference: %s area's wrapper: %s" % ( # self.getAreaReferenceString ( reference ), area.wrapper ()) )\\\ if geometry != None: # we have geometry, apply the mouse offset # If we have a title bar we want to move the mouse to half the height of it titleBar = wrapper.getWidget ().findChild ( QCustomTitleBar ) if titleBar: dragOffset.setY ( -titleBar.height () / 2 ) # apply the mouse offset to the current rect geometry.moveTopLeft ( geometry.topLeft () + dragOffset ) wrapper.getWidget ().setGeometry ( geometry ) wrapper.getWidget ().move ( QCursor.pos () ) if titleBar: currentTitle = titleBar.caption.text () if len ( toolWindows ) > 0: titleBar.caption.setText ( toolWindows[ 0 ].windowTitle () ) wrapper.getWidget ().move ( - ( titleBar.caption.mapToGlobal ( titleBar.caption.rect ().topLeft () ) - wrapper.mapToGlobal ( wrapper.rect ().topLeft () ) ) + QPoint ( -titleBar.caption.fontMetrics ().boundingRect ( titleBar.caption.text () ).width () / 2, -titleBar.caption.height () / 2 ) + wrapper.pos () ) titleBar.caption.setText ( currentTitle ) else: # with no present geometry we just create a new one wrapper.getWidget ().setGeometry ( QRect ( QPoint ( 0, 0 ), toolWindows[ 0 ].sizeHint () ) ) wrapper.getWidget ().move ( QCursor.pos () ) # when create new wrapper, we should transfer mouse-event to wrapper's titleBar for we can continue move. if reference == QToolWindowAreaReference.Drag: titleBar = wrapper.getWidget ().findChild ( QCustomTitleBar ) if titleBar: # set flag for titleBar releaseMouse titleBar.createFromDraging = True startDragEvent = QMouseEvent ( QEvent.MouseButtonPress, QCursor.pos (), QtCore.Qt.LeftButton, QtCore.Qt.LeftButton, QtCore.Qt.NoModifier ) qApp.sendEvent ( titleBar, startDragEvent ) titleBar.grabMouse () if reference != QToolWindowAreaReference.Hidden: area.addToolWindows ( toolWindows, index ) self.lastArea = area # This will remove the previous area the tool windows where attached to self.simplifyLayout () for toolWindow in toolWindows: self.toolWindowVisibilityChanged.emit ( toolWindow, toolWindow.parent () != None ) self.notifyLayoutChange () if reference == QToolWindowAreaReference.Drag and wrapper != None: self.draggedWrapper = wrapper # start the drag on the new wrapper, will end up calling QToolWindowManager::startDrag(IToolWindowWrapper* wrapper) wrapper.startDrag () def getAreaReferenceString ( self, reference ): # Top = 0 # Bottom = 1 # Left = 2 # Right = 3 # HSplitTop = 4 # HSplitBottom = 5 # VSplitLeft = 6 # VSplitRight = 7 # Combine = 8 # Floating = 9 # Drag = 10 # Hidden = 11 if reference == QToolWindowAreaReference.Top: return "Top" elif reference == QToolWindowAreaReference.Bottom: return "Bottom" elif reference == QToolWindowAreaReference.Left: return "Left" elif reference == QToolWindowAreaReference.Right: return "Right" elif reference == QToolWindowAreaReference.HSplitTop: return "HSplitTop" elif reference == QToolWindowAreaReference.HSplitBottom: return "HSplitBottom" elif reference == QToolWindowAreaReference.VSplitLeft: return "VSplitLeft" elif reference == QToolWindowAreaReference.VSplitRight: return "VSplitRight" elif reference == QToolWindowAreaReference.Combine: return "Combine" elif reference == QToolWindowAreaReference.Floating: return "Floating" elif reference == QToolWindowAreaReference.Drag: return "Drag" elif reference == QToolWindowAreaReference.Hidden: return "Hidden" def releaseToolWindow ( self, toolWindow, allowClose = False ) -> bool: # No parent, so can't possibly be inside an IToolWindowArea if toolWindow.parentWidget () == None: # qWarning ( "[QToolWindowManager] releaseToolWindow %s, but toolWindow.parentWidget () == None" % toolWindow ) return False if not self.ownsToolWindow ( toolWindow ): qWarning ( "Unknown tool window %s" % toolWindow ) return False previousTabWidget = findClosestParent ( toolWindow, [ QToolWindowArea, QToolWindowRollupBarArea ] ) if previousTabWidget is None: qWarning ( "[QToolWindowManager] cannot find area for tool window %s" % toolWindow ) return False if allowClose: releasePolicy = self.config.setdefault ( QTWM_RELEASE_POLICY, QTWMReleaseCachingPolicy.rcpWidget ) if releasePolicy == QTWMReleaseCachingPolicy.rcpKeep: self.moveToolWindow ( toolWindow, None, QToolWindowAreaReference.Hidden ) if self.config.setdefault ( QTWM_ALWAYS_CLOSE_WIDGETS, True ) and not self.tryCloseToolWindow ( toolWindow ): return False elif releasePolicy == QTWMReleaseCachingPolicy.rcpWidget: if not toolWindow.testAttribute ( QtCore.Qt.WA_DeleteOnClose ): if self.config.setdefault ( QTWM_ALWAYS_CLOSE_WIDGETS, True ) and not self.tryCloseToolWindow ( toolWindow ): return False self.moveToolWindow ( toolWindow, None, QToolWindowAreaReference.Hidden ) elif releasePolicy == QTWMReleaseCachingPolicy.rcpForget or releasePolicy == QTWMReleaseCachingPolicy.rcpDelete: if not self.tryCloseToolWindow ( toolWindow ): return False self.moveToolWindow ( toolWindow, None, QToolWindowAreaReference.Hidden ) self.toolWindows.remove ( toolWindow ) if releasePolicy == QTWMReleaseCachingPolicy.rcpDelete: toolWindow.deleteLater () return True previousTabWidget.removeToolWindow ( toolWindow ) if allowClose: self.simplifyLayout () else: previousTabWidget.adjustDragVisuals () toolWindow.hide () toolWindow.setParent ( None ) return True def releaseToolWindows ( self, toolWindows, allowClose = False ) -> bool: # print ( "[QToolWindowManager] releaseToolWindows", toolWindows ) result = True for i in range ( len ( toolWindows ) - 1, -1, -1 ): # for toolWindow in toolWindows: # if i >= 0 and i < len ( toolWindows ): result &= self.releaseToolWindow ( toolWindows[ i ], allowClose ) # while len ( toolWindows ) != 0: # result &= self.releaseToolWindow ( toolWindows[ 0 ], allowClose ) return result def areaOf ( self, toolWindow ) -> QToolWindowArea: area = findClosestParent ( toolWindow, [ QToolWindowArea, QToolWindowRollupBarArea ] ) # print ( "[QToolWindowManager] areaOf %s is %s" % ( toolWindow, area ) ) return area def swapAreaType ( self, oldArea, areaType = QTWMWrapperAreaType.watTabs ): from QToolWindowManager.QToolWindowWrapper import QToolWindowWrapper from QToolWindowManager.QToolWindowCustomWrapper import QToolWindowCustomWrapper targetWrapper = findClosestParent ( oldArea.getWidget (), [ QToolWindowWrapper, QToolWindowCustomWrapper ] ) parentSplitter = cast ( oldArea.parentWidget (), QSplitter ) newArea = self.createArea ( areaType ) if parentSplitter is None and targetWrapper is None: qWarning ( "[QToolWindowManager] Could not determine area parent" ) return newArea.addToolWindow ( oldArea.toolWindows () ) if parentSplitter != None: targetIndex = parentSplitter.indexOf ( oldArea.getWidget () ) parentSplitter.insertWidget ( targetIndex, newArea.getWidget () ) else: targetWrapper.setContents ( newArea.getWidget () ) if self.lastArea == oldArea: self.lastArea = newArea oldAreaIndex = self.areas.index ( oldArea ) self.areas.remove ( oldArea ) self.areas.insert ( oldAreaIndex, newArea ) oldArea.getWidget ().setParent ( None ) newArea.adjustDragVisuals () def isWrapper ( self, w ) -> bool: if w is None: return False from QToolWindowManager.QToolWindowWrapper import QToolWindowWrapper from QToolWindowManager.QToolWindowCustomWrapper import QToolWindowCustomWrapper return cast ( w, [ QToolWindowWrapper, QToolWindowCustomWrapper ] ) or ( not cast ( w, QSplitter ) and cast ( w.parentWidget (), [ QToolWindowWrapper, QToolWindowCustomWrapper ] ) ) def isMainWrapper ( self, w ) -> bool: from QToolWindowManager.QToolWindowWrapper import QToolWindowWrapper from QToolWindowManager.QToolWindowCustomWrapper import QToolWindowCustomWrapper return self.isWrapper ( w ) and ( cast ( w, [ QToolWindowWrapper, QToolWindowCustomWrapper ] ) == self.mainWrapper or cast ( w.parentWidget (), [ QToolWindowWrapper, QToolWindowCustomWrapper ] ) == self.mainWrapper ) def isFloatingWrapper ( self, w ) -> bool: if not self.isWrapper ( w ): return False from QToolWindowManager.QToolWindowWrapper import QToolWindowWrapper from QToolWindowManager.QToolWindowCustomWrapper import QToolWindowCustomWrapper if cast ( w, [ QToolWindowWrapper, QToolWindowCustomWrapper ] ) and cast ( w, [ QToolWindowWrapper, QToolWindowCustomWrapper ] ) != self.mainWrapper: return True return not cast ( w, QSplitter ) and cast ( w.parentWidget (), [ QToolWindowWrapper, QToolWindowCustomWrapper ] ) and cast ( w.parentWidget (), [ QToolWindowWrapper, QToolWindowCustomWrapper ] ) != self.mainWrapper def saveState ( self ): result = { } result[ "toolWindowManagerStateFormat" ] = 2 if self.mainWrapper.getContents () and self.mainWrapper.getContents ().metaObject (): result[ "mainWrapper" ] = self.saveWrapperState ( self.mainWrapper ) floatingWindowsData = [ ] for wrapper in self.wrappers: if wrapper.getWidget ().isWindow () and wrapper.getContents () and wrapper.getContents ().metaObject (): m = self.saveWrapperState ( wrapper ) if len ( m ) != 0: floatingWindowsData.append ( m ) result[ "floatingWindows" ] = floatingWindowsData return result def restoreState ( self, data ): if data is None: return stateFormat = data[ "toolWindowManagerStateFormat" ] if stateFormat != 1 and stateFormat != 2: qWarning ( "state format is not recognized" ) return self.suspendLayoutNotifications () self.mainWrapper.getWidget ().hide () for wrapper in self.wrappers: wrapper.getWidget ().hide () self.moveToolWindow ( self.toolWindows, None, QToolWindowAreaReference.Hidden ) self.simplifyLayout ( True ) self.mainWrapper.setContents ( None ) self.restoreWrapperState ( data[ "mainWrapper" ], stateFormat, self.mainWrapper ) for windowData in data[ "floatingWindows" ]: self.restoreWrapperState ( windowData, stateFormat ) self.simplifyLayout () for toolWindow in self.toolWindows: self.toolWindowVisibilityChanged.emit ( toolWindow, toolWindow.parentWidget () != None ) self.resumeLayoutNotifications () self.notifyLayoutChange () def isAnyWindowActive ( self ) -> bool: for tool in self.toolWindows: if tool.isAnyWindowActive (): return True return False def updateDragPosition ( self ): if self.draggedWrapper == None: return # print ( "[QToolWindowManager] updateDragPosition" ) self.draggedWrapper.getWidget ().raise_ () hoveredWindow = windowBelow ( self.draggedWrapper.getWidget () ) if hoveredWindow != None and hoveredWindow != self.draggedWrapper.window (): handlerWidget = hoveredWindow.childAt ( hoveredWindow.mapFromGlobal ( QCursor.pos () ) ) if handlerWidget != None and not self.dragHandler.isHandlerWidget ( handlerWidget ): area = self.getClosestParentArea ( handlerWidget ) if area != None and self.lastArea != area: self.dragHandler.switchedArea ( self.lastArea, area ) self.lastArea = area delayTime = self.config.setdefault ( QTWM_RAISE_DELAY, 500 ) self.raiseTimer.stop () if delayTime > 0: self.raiseTimer.start ( delayTime ) target = self.dragHandler.getTargetFromPosition ( self.lastArea ) if self.lastArea != None and target.reference != QToolWindowAreaReference.Floating: if QToolWindowAreaReference.isOuter ( target.reference ): from QToolWindowManager.QToolWindowCustomWrapper import QToolWindowCustomWrapper previewArea = findClosestParent ( self.lastArea.getWidget (), [ QToolWindowWrapper, QToolWindowCustomWrapper ] ) previewAreaContents = previewArea.getContents () self.preview.setParent ( previewArea.getWidget () ) self.preview.setGeometry ( self.dragHandler.getRectFromCursorPos ( previewAreaContents, self.lastArea ).translated ( previewAreaContents.pos () ) ) else: previewArea = self.lastArea.getWidget () self.preview.setParent ( previewArea ) self.preview.setGeometry ( self.dragHandler.getRectFromCursorPos ( previewArea, self.lastArea ) ) self.preview.show () else: self.preview.hide () self.updateTrackingTooltip.emit ( self.textForPosition ( target.reference ), QCursor.pos () + self.config.setdefault ( QTWM_TOOLTIP_OFFSET, QPoint ( 1, 20 ) ) ) def finishWrapperDrag ( self ): target = self.dragHandler.finishDrag ( self.draggedToolWindows, None, self.lastArea ) self.lastArea = None wrapper = self.draggedWrapper self.draggedWrapper = None self.raiseTimer.stop () self.preview.setParent ( None ) self.preview.hide () if wrapper is None: qWarning ( "[QToolWindowManager] finishWrapperDrag wrapper == None." ) return # print ( "[QToolWindowManager] finishWrapperDrag %s %s" % ( self.draggedWrapper, self.draggedToolWindows ) ) contents = wrapper.getContents () toolWindows = [] contentsWidgets = contents.findChildren ( QWidget ) contentsWidgets.append ( contents ) for w in contentsWidgets: area = cast ( w, [ QToolWindowArea, QToolWindowRollupBarArea ] ) if area != None and self.ownsArea ( area ): toolWindows += area.toolWindows () if target.reference != QToolWindowAreaReference.Floating: contents = wrapper.getContents () wrapper.setContents ( None ) if target.reference == QToolWindowAreaReference.Top or \ target.reference == QToolWindowAreaReference.Bottom or \ target.reference == QToolWindowAreaReference.Left or \ target.reference == QToolWindowAreaReference.Right: self.splitArea ( self.getFurthestParentArea ( target.area.getWidget () ).getWidget (), target.reference, contents ) elif target.reference == QToolWindowAreaReference.HSplitTop or \ target.reference == QToolWindowAreaReference.HSplitBottom or \ target.reference == QToolWindowAreaReference.VSplitLeft or \ target.reference == QToolWindowAreaReference.VSplitRight: self.splitArea ( target.area.getWidget (), target.reference, contents ) elif target.reference == QToolWindowAreaReference.Combine: self.moveToolWindowsTarget ( toolWindows, target ) wrapper.getWidget ().close () self.simplifyLayout () if target.reference != QToolWindowAreaReference.Combine: for w in toolWindows: self.toolWindowVisibilityChanged.emit ( w, True ) if target.reference != QToolWindowAreaReference.Floating: self.notifyLayoutChange () self.updateTrackingTooltip.emit ( "", QPoint () ) def finishWrapperResize ( self ): toolWindows = [] contents = self.resizedWrapper.getContents () contentsWidgets = contents.findChildren ( QWidget ) contentsWidgets.append ( contents ) for w in contentsWidgets: area = cast ( w, [ QToolWindowArea, QToolWindowRollupBarArea ] ) if area and self.ownsArea ( area ): toolWindows.append ( area.toolWindows () ) for w in toolWindows: self.toolWindowVisibilityChanged.emit ( w, True ) self.resizedWrapper = None def saveWrapperState ( self, wrapper ): if wrapper.getContents () == None: qWarning ( "[QToolWindowManager] saveWrapperState Empty top level wrapper." ) return { } result = { } result[ "geometry" ] = wrapper.getWidget ().saveGeometry ().toBase64 () result[ "name" ] = wrapper.getWidget ().objectName () obj = wrapper.getContents () if cast ( obj, QSplitter ): result[ "splitter" ] = self.saveSplitterState ( obj ) return result if isinstance ( obj, QToolWindowArea ): result[ "area" ] = obj.saveState () return result qWarning ( "[QToolWindowManager] saveWrapperState Couldn't find valid child widget" ) return { } def restoreWrapperState ( self, data, stateFormat, wrapper = None ): newContents = None if wrapper and data[ "name" ]: wrapper.getWidget ().setObjectName ( data[ "name" ] ) if data[ "splitter" ]: newContents = self.restoreSplitterState ( data[ "splitter" ], stateFormat ) elif data[ "area" ]: areaType = QTWMWrapperAreaType.watTabs if data[ "area" ][ "type" ] and data[ "area" ][ "type" ] == "rollup": areaType = QTWMWrapperAreaType.watRollups area = self.createArea ( areaType ) area.restoreState ( data[ "area" ], stateFormat ) if area.count () > 0: newContents = area.getWidget () else: area.deleteLater () if not wrapper: if newContents: wrapper = self.createWrapper () if data[ "name" ]: wrapper.getWidget ().setObjectName ( data[ "name" ] ) else: return None wrapper.setContents ( newContents ) if stateFormat == 1: if data[ "geometry" ]: if not wrapper.getWidget ().restoreGeometry ( data[ "geometry" ] ): print ( "Failed to restore wrapper geometry" ) elif stateFormat == 2: if data[ "geometry" ]: if not wrapper.getWidget ().restoreGeometry ( QByteArray.fromBase64 ( data[ "geometry" ] ) ): print ( "Failed to restore wrapper geometry" ) else: print ( "Unknown state format" ) if data[ "geometry" ]: wrapper.getWidget ().show () return wrapper def saveSplitterState ( self, splitter ): result = { } result[ "state" ] = splitter.saveState ().toBase64 () result[ "type" ] = "splitter" items = [ ] for i in range ( splitter.count () ): item = splitter.widget ( i ) itemValue = { } area = item if area != None: itemValue = area.saveState () else: childSplitter = item if childSplitter: itemValue = self.saveSplitterState ( childSplitter ) else: qWarning ( "[QToolWindowManager] saveSplitterState Unknown splitter item" ) items.append ( itemValue ) result[ "items" ] = items return result def restoreSplitterState ( self, data, stateFormat ): if len ( data[ "items" ] ) < 2: print ( "Invalid splitter encountered" ) if len ( data[ "items" ] ) == 0: return None splitter = self.createSplitter () for itemData in data[ "items" ]: itemValue = itemData itemType = itemValue[ "type" ] if itemType == "splitter": w = self.restoreSplitterState ( itemValue, stateFormat ) if w: splitter.addWidget ( w ) elif itemType == "area": area = self.createArea () area.restoreState ( itemValue, stateFormat ) splitter.addWidget ( area.getWidget () ) elif itemType == "rollup": area = self.createArea () area.restoreState ( itemValue, stateFormat ) splitter.addWidget ( area.getWidget () ) else: print ( "Unknown item type" ) if stateFormat == 1: if data[ "state" ]: if not splitter.restoreState ( data[ "state" ] ): print ( "Failed to restore splitter state" ) elif stateFormat == 2: if data[ "state" ]: if not splitter.restoreState ( QByteArray.fromBase64 ( data[ "state" ] ) ): print ( "Failed to restore splitter state" ) else: print ( "Unknown state format" ) return splitter def resizeSplitter ( self, widget, sizes ): s = cast ( widget, QSplitter ) if s == None: s = findClosestParent ( widget, QSplitter ) if s == None: qWarning ( "Could not find a matching splitter!" ) return scaleFactor = s.width () if s.orientation () == QtCore.Qt.Horizontal else s.height () for i in range ( 0, len ( sizes ) ): sizes[ i ] *= scaleFactor s.setSizes ( sizes ) def createArea ( self, areaType = QTWMWrapperAreaType.watTabs ): # qWarning ( "[QToolWindowManager] createArea %s" % areaType ) a = self.factory.createArea ( self, None, areaType ) self.lastArea = a self.areas.append ( a ) return a def createWrapper ( self ): w = self.factory.createWrapper ( self ) name = None while True: i = QtCore.qrand () name = "wrapper#%s" % i for w2 in self.wrappers: if name == w2.getWidget ().objectName (): continue break w.getWidget ().setObjectName ( name ) self.wrappers.append ( w ) return w def getNotifyLock ( self, allowNotify = True ): return QTWMNotifyLock ( self, allowNotify ) def hide ( self ): self.mainWrapper.getWidget ().hide () def show ( self ): self.mainWrapper.getWidget ().show () def clear ( self ): self.releaseToolWindows ( self.toolWindows, True ) if len ( self.areas ) != 0: self.lastArea = self.areas[ 0 ] else: self.lastArea = None def bringAllToFront ( self ): # TODO: Win64 / OSX from QToolWindowManager.QToolWindowCustomWrapper import QToolWindowCustomWrapper list = qApp.topLevelWidgets () for w in list: if w != None and not w.windowState ().testFlag ( QtCore.Qt.WindowMinimized ) and ( isinstance ( w, QToolWindowWrapper ) or isinstance ( w, QToolWindowCustomWrapper ) or isinstance ( w, QCustomWindowFrame ) ): w.show () w.raise_ () def bringToFront ( self, toolWindow ): area = self.areaOf ( toolWindow ) if not area: return while area.indexOf ( toolWindow ) == -1: toolWindow = toolWindow.parentWidget () area.setCurrentWidget ( toolWindow ) window = area.getWidget ().window () window.setWindowState ( window.windowState () & ~QtCore.Qt.WindowMinimized ) window.show () window.raise_ () window.activateWindow () toolWindow.setFocus () def getToolPath ( self, toolWindow ): w = toolWindow result = '' pw = w.parentWidget () while pw: if isinstance ( pw, QSplitter ): orientation = None if pw.orientation () == QtCore.Qt.Horizontal: orientation = 'h' else: orientation = 'v' result += "/%c/%d" % (orientation, pw.indexOf ( w )) elif isinstance ( pw, QToolWindowWrapper ): result += toolWindow.window ().objectName () break w = pw pw = w.parentWidget () return result def targetFromPath ( self, toolPath ) -> QToolWindowAreaTarget: for w in self.areas: if w.count () < 0 and self.getToolPath ( w.widget ( 0 ) ) == toolPath: return QToolWindowAreaTarget.createByArea ( w, QToolWindowAreaReference.Combine ) return QToolWindowAreaTarget ( QToolWindowAreaReference.Floating ) def eventFilter ( self, o, e ): """Summary line. Args: o (QObject): Description of param1 e (QEvent): Description of param2 """ if o == qApp: if e.type () == QEvent.ApplicationActivate and ( self.config.setdefault ( QTWM_WRAPPERS_ARE_CHILDREN, False )) and ( self.config.setdefault ( QTWM_BRING_ALL_TO_FRONT, True )): self.bringAllToFront () return False if e.type () == 16: # event Destroy w = cast ( o, QWidget ) if not self.closingWindow and self.ownsToolWindow ( w ) and w.isVisible (): self.releaseToolWindow ( w, True ) return False # if self.draggedWrapper and o == self.draggedWrapper: # if e.type () == QEvent.MouseMove: # qWarning ( "Manager eventFilter: send MouseMove" ) # qApp.sendEvent ( self.draggedWrapper, e ) # print ( "QToolWindowManager::eventFilter", type ( o ), EventTypes ().as_string ( e.type () ) ) # if self.draggedWrapper and o == self.draggedWrapper: # if e.type () == QEvent.MouseButtonRelease: # qWarning ( f"Manager eventFilter: send MouseButtonRelease {self.draggedWrapper}" ) # if e.type () == Qt.QEvent.Destroy: # if not self.closingWindow and self.ownsToolWindow ( w ) and w.isVisible (): # self.releaseToolWindow ( w, True ) # return False return super ().eventFilter ( o, e ) def event ( self, e ): if e.type () == QEvent.StyleChange: if self.parentWidget () and self.parentWidget ().styleSheet () != self.styleSheet (): self.setStyleSheet ( self.parentWidget ().styleSheet () ) return super ().event ( e ) def tryCloseToolWindow ( self, toolWindow ): self.closingWindow += 1 result = True if not toolWindow.close (): qWarning ( "Widget could not be closed" ) result = False self.closingWindow -= 1 return result def createSplitter ( self ) -> QSplitter: return self.factory.createSplitter ( self ) def splitArea ( self, area, reference, insertWidget = None ): from QToolWindowManager.QToolWindowWrapper import QToolWindowWrapper from QToolWindowManager.QToolWindowCustomWrapper import QToolWindowCustomWrapper residingWidget = insertWidget if residingWidget == None: residingWidget = self.createArea ().getWidget () if not QToolWindowAreaReference.requiresSplit ( reference ): qWarning ( "Invalid reference for area split" ) return None forceOuter = QToolWindowAreaReference.isOuter ( reference ) reference = reference & 0x3 parentSplitter = None targetWrapper = findClosestParent ( area, [ QToolWindowWrapper, QToolWindowCustomWrapper ] ) if not forceOuter: parentSplitter = cast ( area.parentWidget (), QSplitter ) if parentSplitter == None and targetWrapper == None: qWarning ( "Could not determine area parent" ) return None useParentSplitter = False targetIndex = 0 parentSizes = [ QSize () ] if parentSplitter != None: parentSizes = parentSplitter.sizes () targetIndex += parentSplitter.indexOf ( area ) useParentSplitter = parentSplitter.orientation () == QToolWindowAreaReference.splitOrientation ( reference ) if useParentSplitter: origIndex = targetIndex targetIndex += reference & 0x1 newSizes = self.dragHandler.getSplitSizes ( parentSizes[ origIndex ] ) parentSizes[ origIndex ] = newSizes.oldSize parentSizes.insert ( targetIndex, newSizes.newSize ) parentSplitter.insertWidget ( targetIndex, residingWidget ) parentSplitter.setSizes ( parentSizes ) return residingWidget splitter = self.createSplitter () splitter.setOrientation ( QToolWindowAreaReference.splitOrientation ( reference ) ) if forceOuter or area == targetWrapper.getContents (): firstChild = targetWrapper.getContents () targetWrapper.setContents ( None ) splitter.addWidget ( firstChild ) else: area.hide () area.setParent ( None ) splitter.addWidget ( area ) area.show () splitter.insertWidget ( reference & 0x1, residingWidget ) if parentSplitter != None: parentSplitter.insertWidget ( targetIndex, splitter ) parentSplitter.setSizes ( parentSizes ) else: targetWrapper.setContents ( splitter ) sizes = [ ] baseSize = splitter.height () if splitter.orientation () == QtCore.Qt.Vertical else splitter.width () newSizes = self.dragHandler.getSplitSizes ( baseSize ) sizes.append ( newSizes.oldSize ) sizes.insert ( reference & 0x1, newSizes.newSize ) splitter.setSizes ( sizes ) contentsWidgets = residingWidget.findChildren ( QWidget ) for w in contentsWidgets: qApp.sendEvent ( w, QEvent ( QEvent.ParentChange ) ) return residingWidget def getClosestParentArea ( self, widget ): area = findClosestParent ( widget, [ QToolWindowArea, QToolWindowRollupBarArea ] ) while area != None and not self.ownsArea ( area ): area = findClosestParent ( area.getWidget ().parentWidget (), [ QToolWindowArea, QToolWindowRollupBarArea ] ) # qWarning ( "[QToolWindowManager] getClosestParentArea %s is %s" % ( widget, area ) ) return area def getFurthestParentArea ( self, widget ): area = findClosestParent ( widget, [ QToolWindowArea, QToolWindowRollupBarArea ] ) previousArea = area while area != None and not self.ownsArea ( area ): area = findClosestParent ( area.getWidget ().parentWidget (), [ QToolWindowArea, QToolWindowRollupBarArea ] ) if area == None: return previousArea else: previousArea = area # qWarning ( "getFurthestParentArea %s is %s" % ( widget, area ) ) return area def textForPosition ( self, reference ): texts = [ "Place at top of window", "Place at bottom of window", "Place on left side of window", "Place on right side of window", "Split horizontally, place above", "Split horizontally, place below", "Split vertically, place left", "Split vertically, place right", "Add to tab list", "" ] return texts[ reference ] def simplifyLayout ( self, clearMain = False ): from QToolWindowManager.QToolWindowWrapper import QToolWindowWrapper from QToolWindowManager.QToolWindowCustomWrapper import QToolWindowCustomWrapper self.suspendLayoutNotifications () madeChanges = True # Some layout changes may require multiple iterations to fully simplify. while madeChanges: madeChanges = False areasToRemove = [ ] for area in self.areas: # remove empty areas (if this is only area in main wrapper, only remove it when we explicitly ask for that) if area.count () == 0 and ( clearMain or cast ( area.parent (), [ QToolWindowWrapper, QToolWindowCustomWrapper ] ) != self.mainWrapper ): areasToRemove.append ( area ) madeChanges = True s = cast ( area.parentWidget (), QSplitter ) while s != None and s.parentWidget () != None: sp_s = cast ( s.parentWidget (), QSplitter ) sp_w = cast ( s.parentWidget (), [ QToolWindowWrapper, QToolWindowCustomWrapper ] ) sw = findClosestParent ( s, [ QToolWindowWrapper, QToolWindowCustomWrapper ] ) # If splitter only contains one object, replace the splitter with the contained object if s.count () == 1: if sp_s != None: index = sp_s.indexOf ( s ) sizes = sp_s.sizes () sp_s.insertWidget ( index, s.widget ( 0 ) ) s.hide () s.setParent ( None ) s.deleteLater () sp_s.setSizes ( sizes ) madeChanges = True elif sp_w != None: sp_w.setContents ( s.widget ( 0 ) ) s.setParent ( None ) s.deleteLater () madeChanges = True else: qWarning ( "Unexpected splitter parent" ) # If splitter's parent is also a splitter, and both have same orientation, replace splitter with contents elif sp_s != None and s.orientation () == sp_s.orientation (): index = sp_s.indexOf ( s ) newSizes = sp_s.sizes () oldSizes = s.sizes () newSum = newSizes[ index ] oldSum = 0 for i in oldSizes: oldSum += i for i in range ( len ( oldSizes ) - 1, -1, -1 ): sp_s.insertWidget ( index, s.widget ( i ) ) newSizes.insert ( index, oldSizes[ i ] / oldSum * newSum ) s.hide () s.setParent ( None ) s.deleteLater () sp_s.setSizes ( newSizes ) madeChanges = True s = sp_s for area in areasToRemove: area.hide () aw = cast ( area.parentWidget (), [ QToolWindowWrapper, QToolWindowCustomWrapper ] ) if aw != None: aw.setContents ( None ) area.setParent ( None ) self.areas.remove ( area ) area.deleteLater () wrappersToRemove = [ ] for wrapper in self.wrappers: if wrapper.getWidget ().isWindow () and wrapper.getContents () == None: wrappersToRemove.append ( wrapper ) for wrapper in wrappersToRemove: self.wrappers.remove ( wrapper ) wrapper.hide () wrapper.deferDeletion () for area in self.areas: area.adjustDragVisuals () self.resumeLayoutNotifications () self.notifyLayoutChange () def createDragHandler ( self ): if self.dragHandler: del self.dragHandler return self.factory.createDragHandler ( self ) def suspendLayoutNotifications ( self ): self.layoutChangeNotifyLocks += 1 def resumeLayoutNotifications ( self ): if self.layoutChangeNotifyLocks > 0: self.layoutChangeNotifyLocks -= 1 def notifyLayoutChange ( self ): if self.layoutChangeNotifyLocks == 0: QtCore.QMetaObject.invokeMethod ( self, 'layoutChanged', QtCore.Qt.QueuedConnection ) def insertToToolTypes ( self, tool, toolType ): if tool not in self.toolWindowsTypes: self.toolWindowsTypes[ tool ] = toolType def raiseCurrentArea ( self ): if self.lastArea: w = self.lastArea.getWidget () while w.parentWidget () and not w.isWindow (): w = w.parentWidget () w.raise_ () self.updateDragPosition () self.raiseTimer.stop ()
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 PictureBox(QLabel): changedNumberRegions = pyqtSignal(int) def __init__(self, image, regions, parent=None): QLabel.__init__(self, parent) self.rubberBand = QRubberBand(QRubberBand.Rectangle, self) self.origin = QPoint() self.regions = None self.image = image def region_to_rect(region): y1, x1, y2, x2 = region.bbox return QRect(x1, y1, x2 - x1, y2 - y1) self.regions = list(map(region_to_rect, regions)) self._update_picture() def mousePressEvent(self, event): pal = QPalette() if event.button() == Qt.LeftButton: pal.setBrush(QPalette.Highlight, QBrush(Qt.blue)) elif event.button() == Qt.RightButton: pal.setBrush(QPalette.Highlight, QBrush(Qt.green)) self.rubberBand.setPalette(pal) 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: drawnRect = QRect(self.origin, event.pos()).normalized() for region in self.getIntersectedRegions(drawnRect): self.regions.remove(region) elif event.button() == Qt.RightButton: drawnRect = QRect(self.origin, event.pos()).normalized() meanArea = np.mean([r.width() * r.height() for r in self.regions]) meanHeight = np.mean([r.height() for r in self.regions]) meanWidth = np.mean([r.width() for r in self.regions]) if drawnRect.width() * drawnRect.height() * 2 < meanArea: rect = QRect(self.origin.x() - meanWidth / 2, self.origin.y() - meanHeight / 2, meanWidth, meanHeight) self.regions.append(rect) else: self.regions.append(drawnRect) self.rubberBand.hide() self._update_picture() def getIntersectedRegions(self, drawnRect): result = [] for region in self.regions: if region.intersects(drawnRect): result.append(region) return result def _update_regions(self): pixmap = self._get_pixmap() paint = QPainter(pixmap) paint.setPen(QColor("green")) for region in self.regions: paint.drawRect(region) # rect = mpatches.Rectangle((minc, minr), maxc - minc, maxr - minr, # fill=False, edgecolor='green', linewidth=2) del paint return pixmap def savePicture(self, fname): pixmap = self._update_regions() return pixmap.save(fname) def _update_picture(self): pixmap = self._update_regions() self.setAlignment(Qt.AlignLeft | Qt.AlignTop) self.setPixmap(pixmap) self.changedNumberRegions.emit(len(self.regions)) def _get_pixmap(self): # Convert image to QImage qimg = qimage2ndarray.array2qimage(self.image, True) return QPixmap(qimg)
def __init__(self, parent=None, main=None): QLabel.__init__(self, parent) self.selection = QRubberBand(QRubberBand.Rectangle, self) self.main = main
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 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 ViewerWidget(QAbstractScrollArea): """ The main ViewerWidget class. Should be embeddable in other applications. See the open() function for loading images. """ # signals geolinkMove = pyqtSignal(GeolinkInfo, name='geolinkMove') geolinkQueryPoint = pyqtSignal(GeolinkInfo, name='geolinkQueryPoint') # can't use ViewerWidget - use base class instead layerAdded = pyqtSignal(QAbstractScrollArea, name='layerAdded') showStatusMessage = pyqtSignal('QString', name='showStatusMessage') activeToolChanged = pyqtSignal(ActiveToolChangedInfo, name='activeToolChanged') polygonCollected = pyqtSignal(PolygonToolInfo, name='polygonCollected') polylineCollected = pyqtSignal(PolylineToolInfo, name='polylineCollected') vectorLocationSelected = pyqtSignal(list, viewerlayers.ViewerVectorLayer, name='vectorLocationSelected') locationSelected = pyqtSignal(QueryInfo, name='locationSelected') def __init__(self, parent): QAbstractScrollArea.__init__(self, parent) self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn) # tracking works awfully badly under X # turn off until we work out a workaround self.verticalScrollBar().setTracking(False) self.horizontalScrollBar().setTracking(False) self.layers = viewerlayers.LayerManager() self.paintPoint = QPoint() # normally 0,0 unless we are panning # when moving the scroll bars # events get fired that we wish to ignore self.suppressscrollevent = False # set the background color to black so window # is black when nothing loaded and when panning # new areas are initially black. self.setBackgroundColor(Qt.black) # to do with tools self.rubberBand = None self.panCursor = None self.panGrabCursor = None self.zoomInCursor = None self.zoomOutCursor = None self.queryCursor = None self.vectorQueryCursor = None self.polygonCursor = None self.activeTool = VIEWER_TOOL_NONE self.panOrigin = None self.toolPoints = None # for line and polygon tools - list of points self.toolPointsFinished = True # True if we finished collecting # with line and poly tools self.toolPen = QPen() # for drawing the toolPoints self.toolPen.setWidth(1) self.toolPen.setColor(Qt.yellow) self.toolPen.setDashPattern([5, 5, 5, 5]) # Define the scroll wheel behaviour self.mouseWheelZoom = True # do we follow extent when geolinking? self.geolinkFollowExtent = True # to we query all layers or only displayed? self.queryOnlyDisplayed = False def updateScrollBars(self): """ Update the scroll bars to accurately show where we are relative to the full extent """ fullextent = self.layers.getFullExtent() setbars = False self.suppressscrollevent = True if fullextent is not None: (fullleft, fulltop, fullright, fullbottom) = fullextent layer = self.layers.getTopLayer() if layer is not None: (left, top, right, bottom) = layer.coordmgr.getWorldExtent() (wldX, wldY) = layer.coordmgr.getWorldCenter() verticalBar = self.verticalScrollBar() horizontalBar = self.horizontalScrollBar() # always set range to 0 - 1000 and calculate # everything as fraction of that verticalBar.setRange(0, 1000) horizontalBar.setRange(0, 1000) # to pagestep which is also the slider size fullxsize = float(fullright - fullleft) if fullxsize == 0: fullxsize = 100 hpagestep = (float(right - left) / fullxsize) * 1000 horizontalBar.setPageStep(int(hpagestep)) fullysize = float(fulltop - fullbottom) if fullysize == 0: fullysize = 100 vpagestep = (float(top - bottom) / fullysize) * 1000 verticalBar.setPageStep(int(vpagestep)) # position of the slider relative to the center of the image hpos = (float(wldX - fullleft) / fullxsize) * 1000 horizontalBar.setSliderPosition(int(hpos)) vpos = (float(fulltop - wldY) / fullysize) * 1000 verticalBar.setSliderPosition(int(vpos)) setbars = True if not setbars: # something went wrong - disable self.horizontalScrollBar().setRange(0, 0) self.verticalScrollBar().setRange(0, 0) self.suppressscrollevent = False def addRasterLayer(self, gdalDataset, stretch, lut=None, ignoreProjectionMismatch=False, quiet=False): """ Add the given dataset to the stack of images being displayed as a raster layer """ size = self.viewport().size() self.layers.addRasterLayer(gdalDataset, size.width(), size.height(), stretch, lut, ignoreProjectionMismatch, quiet) # get rid off tool points self.toolPoints = None self.toolPointsFinished = True self.viewport().update() self.updateScrollBars() self.layerAdded.emit(self) def addVectorLayer(self, ogrDataSource, ogrLayer, color=None, resultSet=False, origSQL=None, quiet=False): """ Add the vector given by the ogrDataSource and its dependent ogrLayer to the stack of images. """ size = self.viewport().size() if color is None: color = viewerlayers.DEFAULT_VECTOR_COLOR self.layers.addVectorLayer(ogrDataSource, ogrLayer, size.width(), size.height(), color, resultSet, origSQL, quiet) self.viewport().update() self.updateScrollBars() self.layerAdded.emit(self) def addVectorFeatureLayer(self, ogrDataSource, ogrLayer, ogrFeature, color=None, quiet=None): """ Just a single feature vector """ size = self.viewport().size() if color is None: color = viewerlayers.DEFAULT_VECTOR_COLOR self.layers.addVectorFeatureLayer(ogrDataSource, ogrLayer, ogrFeature, size.width(), size.height(), color, quiet) self.viewport().update() self.updateScrollBars() self.layerAdded.emit(self) def addLayersFromJSONFile(self, fileobj, nlayers): """ Get the layer manager to read all the layer descriptions from fileobj and load these layers into this widget. """ size = self.viewport().size() self.layers.fromFile(fileobj, nlayers, size.width(), size.height()) self.viewport().update() self.updateScrollBars() self.layerAdded.emit(self) def removeLayer(self): """ Removes the top later """ self.layers.removeTopLayer() # get rid off tool points self.toolPoints = None self.toolPointsFinished = True self.viewport().update() self.updateScrollBars() # query point functions def setQueryPoint(self, senderid, easting, northing, color, size=None, cursor=None): """ Sets/Updates query point keyed on the id() of the sender """ self.layers.queryPointLayer.setQueryPoint(senderid, easting, northing, color, size, cursor) self.layers.queryPointLayer.getImage() self.viewport().update() def removeQueryPoint(self, senderid): """ Removes a query point. keyed on the id() of the sender """ self.layers.queryPointLayer.removeQueryPoint(senderid) self.layers.queryPointLayer.getImage() self.viewport().update() def highlightValues(self, color, selectionArray=None): """ Applies a QColor to the LUT where selectionArray == True to the top layer and redraws. Pass None to reset """ layer = self.layers.getTopRasterLayer() if layer is not None: if len(layer.stretch.bands) != 1: msg = 'can only highlight values on single band images' raise viewererrors.InvalidDataset(msg) layer.highlightRows(color, selectionArray) # force repaint self.viewport().update() def setColorTableLookup(self, lookupArray=None, colName=None, surrogateLUT=None, surrogateName=None): """ Uses the supplied lookupArray to look up image data before indexing into color table in the top layer and redraws. Pass None to reset. """ layer = self.layers.getTopRasterLayer() if layer is not None: if len(layer.stretch.bands) != 1: msg = 'can only highlight values on single band images' raise viewererrors.InvalidDataset(msg) layer.setColorTableLookup(lookupArray, colName, surrogateLUT, surrogateName) # force repaint self.viewport().update() def zoomNativeResolution(self): """ Sets the zoom to native resolution wherever the current viewport is centered """ self.layers.zoomNativeResolution() # force repaint self.viewport().update() self.updateScrollBars() # geolink self.emitGeolinkMoved() def zoomFullExtent(self): """ Resets the zoom to full extent - should be the same as when file was opened. """ self.layers.zoomFullExtent() # force repaint self.viewport().update() self.updateScrollBars() # geolink self.emitGeolinkMoved() def setActiveTool(self, tool, senderid): """ Set active tool (one of VIEWER_TOOL_*). pass VIEWER_TOOL_NONE to disable pass the id() of the calling object. This is passed around in the activeToolChanged signal so GUI elements can recognise who asked for the change """ # if the tool was line or polygon # now is the time to remove the outline from the widget if (self.activeTool == VIEWER_TOOL_POLYGON or self.activeTool == VIEWER_TOOL_POLYLINE): self.toolPoints = None self.toolPointsFinished = True # force repaint self.viewport().update() self.activeTool = tool if tool == VIEWER_TOOL_ZOOMIN: if self.zoomInCursor is None: # create if needed. self.zoomInCursor = QCursor( QPixmap([ "16 16 3 1", ". c None", "a c #000000", "# c #ffffff", ".....#####......", "...##aaaaa##....", "..#.a.....a.#...", ".#.a...a...a.#..", ".#a....a....a#..", "#a.....a.....a#.", "#a.....a.....a#.", "#a.aaaa#aaaa.a#.", "#a.....a.....a#.", "#a.....a.....a#.", ".#a....a....a#..", ".#.a...a...aaa#.", "..#.a.....a#aaa#", "...##aaaaa###aa#", ".....#####...###", "..............#." ])) self.viewport().setCursor(self.zoomInCursor) elif tool == VIEWER_TOOL_ZOOMOUT: if self.zoomOutCursor is None: # create if needed self.zoomOutCursor = QCursor( QPixmap([ "16 16 4 1", "b c None", ". c None", "a c #000000", "# c #ffffff", ".....#####......", "...##aaaaa##....", "..#.a.....a.#...", ".#.a.......a.#..", ".#a.........a#..", "#a...........a#.", "#a...........a#.", "#a.aaaa#aaaa.a#.", "#a...........a#.", "#a...........a#.", ".#a.........a#..", ".#.a.......aaa#.", "..#.a.....a#aaa#", "...##aaaaa###aa#", ".....#####...###", "..............#." ])) self.viewport().setCursor(self.zoomOutCursor) elif tool == VIEWER_TOOL_PAN: if self.panCursor is None: # both these used for pan operations self.panCursor = QCursor(Qt.OpenHandCursor) self.panGrabCursor = QCursor(Qt.ClosedHandCursor) self.viewport().setCursor(self.panCursor) elif tool == VIEWER_TOOL_QUERY: if self.queryCursor is None: self.queryCursor = QCursor(Qt.CrossCursor) self.viewport().setCursor(self.queryCursor) elif tool == VIEWER_TOOL_VECTORQUERY: if self.vectorQueryCursor is None: self.vectorQueryCursor = QCursor( QPixmap([ "16 16 3 1", "# c None", "a c #000000", ". c #ffffff", "######aaaa######", "######a..a######", "######a..a######", "######a..a######", "######a..a######", "######aaaa######", "aaaaa######aaaaa", "a...a######a...a", "a...a######a...a", "aaaaa######aaaaa", "######aaaa######", "######a..a###a.a", "######a..a###aaa", "######a..a###a.a", "######a..a###a.a", "######aaaa###aaa" ])) self.viewport().setCursor(self.vectorQueryCursor) elif tool == VIEWER_TOOL_POLYGON or tool == VIEWER_TOOL_POLYLINE: if self.polygonCursor is None: self.polygonCursor = QCursor( QPixmap([ "16 16 3 1", " c None", ". c #000000", "+ c #FFFFFF", " ", " +.+ ", " ++.++ ", " +.....+ ", " +. .+ ", " +. . .+ ", " +. . .+ ", " ++. . .++", " ... ...+... ...", " ++. . .++", " +. . .+ ", " +. . .+ ", " ++. .+ ", " ++.....+ ", " ++.++ ", " +.+ " ])) self.viewport().setCursor(self.polygonCursor) msg = ('Left click adds a point, middle to remove last,' + ' right click to end') self.showStatusMessage.emit(msg) elif tool == VIEWER_TOOL_NONE: # change back self.viewport().setCursor(Qt.ArrowCursor) obj = ActiveToolChangedInfo(self.activeTool, senderid) self.activeToolChanged.emit(obj) def setNewStretch(self, newstretch, layer, local=False): """ Change the stretch being applied to the current data """ layer.setNewStretch(newstretch, local) self.viewport().update() def timeseriesBackward(self): """ Assume images are a stacked timeseries oldest to newest. Turn on the previous one to the current topmost displayed """ self.layers.timeseriesBackward() self.viewport().update() def timeseriesForward(self): """ Assume images are a stacked timeseries oldest to newest. Turn off the current topmost displayed """ self.layers.timeseriesForward() self.viewport().update() def setMouseScrollWheelAction(self, scrollZoom): "Set the action for a mouse wheen event (scroll/zoom)" self.mouseWheelZoom = scrollZoom def setBackgroundColor(self, color): "Sets the background color for the widget" widget = self.viewport() palette = widget.palette() palette.setColor(widget.backgroundRole(), color) widget.setPalette(palette) def setGeolinkFollowExtentAction(self, followExtent): "Set whether we are following geolink extent of just center" self.geolinkFollowExtent = followExtent def setQueryOnlyDisplayed(self, queryOnlyDisplayed): """ set whether we are only querying displayed layers (True) or all (False) """ self.queryOnlyDisplayed = queryOnlyDisplayed def flicker(self): """ Call to change the flicker state (ie draw the top raster image or not). """ state = False layer = self.layers.getTopLayer() if layer is not None: state = not layer.displayed self.layers.setDisplayedState(layer, state) self.viewport().update() return state def scrollContentsBy(self, dx, dy): """ Handle the user moving the scroll bars """ if not self.suppressscrollevent: layer = self.layers.getTopLayer() if layer is not None: (left, top, right, bottom) = layer.coordmgr.getWorldExtent() hpagestep = float(self.horizontalScrollBar().pageStep()) xamount = -(float(dx) / hpagestep) * (right - left) vpagestep = float(self.verticalScrollBar().pageStep()) yamount = (float(dy) / vpagestep) * (top - bottom) wldX, wldY = layer.coordmgr.getWorldCenter() layer.coordmgr.setWorldCenter(wldX + xamount, wldY + yamount) # not sure why we need this but get black strips # around otherwise layer.coordmgr.recalcBottomRight() self.layers.makeLayersConsistent(layer) self.layers.updateImages() self.viewport().update() self.updateScrollBars() # geolink self.emitGeolinkMoved() def wheelEvent(self, event): """ User has used mouse wheel to zoom in/out or pan depending on defined preference """ layer = self.layers.getTopRasterLayer() if layer is not None: delta = event.angleDelta().y() # Shift scrolling will move you forward and backwards through the time series if QApplication.keyboardModifiers() == Qt.ShiftModifier: if delta > 0: self.timeseriesBackward() elif delta < 0: self.timeseriesForward() elif self.mouseWheelZoom: (wldX, wldY) = layer.coordmgr.getWorldCenter() impixperwinpix = layer.coordmgr.imgPixPerWinPix if delta > 0: impixperwinpix *= 1.0 - VIEWER_ZOOM_WHEEL_FRACTION elif delta < 0: impixperwinpix *= 1.0 + VIEWER_ZOOM_WHEEL_FRACTION layer.coordmgr.setZoomFactor(impixperwinpix) layer.coordmgr.setWorldCenter(wldX, wldY) # not sure why we need this but get black strips # around otherwise layer.coordmgr.recalcBottomRight() self.layers.makeLayersConsistent(layer) self.layers.updateImages() self.updateScrollBars() self.viewport().update() else: dx = event.angleDelta().x() dy = delta self.scrollContentsBy(dx, dy) # geolink self.emitGeolinkMoved() def resizeEvent(self, event): """ Window has been resized - get new data """ size = event.size() self.layers.setDisplaySize(size.width(), size.height()) self.updateScrollBars() def paintEvent(self, event): """ Viewport needs to be redrawn. Assume that each layer's image is current (as created by getImage()) we can just draw it with QPainter """ paint = QPainter(self.viewport()) for layer in self.layers.layers: if layer.displayed: paint.drawImage(self.paintPoint, layer.image) # draw any query points on top of image paint.drawImage(self.paintPoint, self.layers.queryPointLayer.image) # now any tool points if self.toolPoints is not None: path = QPainterPath() firstpt = self.toolPoints[0] path.moveTo(firstpt.x(), firstpt.y()) for pt in self.toolPoints[1:]: path.lineTo(pt.x(), pt.y()) paint.setPen(self.toolPen) paint.drawPath(path) paint.end() def mousePressEvent(self, event): """ Mouse has been clicked down if we are in zoom/pan mode we need to start doing stuff here """ QAbstractScrollArea.mousePressEvent(self, event) pos = event.pos() if (self.activeTool == VIEWER_TOOL_ZOOMIN or self.activeTool == VIEWER_TOOL_ZOOMOUT): if self.rubberBand is None: self.rubberBand = QRubberBand(QRubberBand.Rectangle, self) self.rubberBand.setGeometry(QRect(pos, QSize())) self.rubberBand.show() self.rubberBand.origin = pos elif self.activeTool == VIEWER_TOOL_PAN: # remember pos self.panOrigin = pos # change cursor self.viewport().setCursor(self.panGrabCursor) elif self.activeTool == VIEWER_TOOL_QUERY: modifiers = event.modifiers() (dspX, dspY) = (pos.x(), pos.y()) self.newQueryPoint(dspX=dspX, dspY=dspY, modifiers=modifiers) elif self.activeTool == VIEWER_TOOL_VECTORQUERY: modifiers = event.modifiers() (dspX, dspY) = (pos.x(), pos.y()) self.newVectorQueryPoint(dspX, dspY, modifiers=modifiers) elif self.activeTool == VIEWER_TOOL_POLYGON: button = event.button() if button == Qt.LeftButton: # adding points if self.toolPoints is None or self.toolPointsFinished: # first point - starts and ends at same pos self.toolPoints = [pos, pos] self.toolPointsFinished = False else: # last point same as first - insert before last self.toolPoints.insert(-1, pos) elif button == Qt.MiddleButton and self.toolPoints is not None: # delete last point if len(self.toolPoints) > 2: del self.toolPoints[-2] elif button == Qt.RightButton and self.toolPoints is not None: # finished # create object for signal layer = self.layers.getTopRasterLayer() modifiers = event.modifiers() obj = PolygonToolInfo(self.toolPoints, layer, modifiers) self.polygonCollected.emit(obj) self.toolPointsFinished = True # done, but still display # redraw so paint() gets called self.viewport().update() elif self.activeTool == VIEWER_TOOL_POLYLINE: button = event.button() if button == Qt.LeftButton: # adding points if self.toolPoints is None or self.toolPointsFinished: # first point self.toolPoints = [pos] self.toolPointsFinished = False else: # add to list self.toolPoints.append(pos) elif button == Qt.MiddleButton and self.toolPoints is not None: # delete last point if len(self.toolPoints) > 1: self.toolPoints.pop() elif button == Qt.RightButton and self.toolPoints is not None: # finished # create object for signal if self.queryOnlyDisplayed: layer = self.layers.getTopDisplayedRasterLayer() else: layer = self.layers.getTopRasterLayer() modifiers = event.modifiers() obj = PolylineToolInfo(self.toolPoints, layer, modifiers) self.polylineCollected.emit(obj) self.toolPointsFinished = True # done, but still display # redraw so paint() gets called self.viewport().update() def mouseReleaseEvent(self, event): """ Mouse has been released, if we are in zoom/pan mode we do stuff here. """ QAbstractScrollArea.mouseReleaseEvent(self, event) if self.rubberBand is not None and self.rubberBand.isVisible(): # get the information about the rect they have drawn # note this is on self, rather than viewport() selection = self.rubberBand.geometry() geom = self.viewport().geometry() selectionsize = float(selection.width() * selection.height()) geomsize = float(geom.width() * geom.height()) self.rubberBand.hide() layer = self.layers.getTopRasterLayer() if layer is not None: if selectionsize < MIN_SELECTION_SIZE_PX: # this is practically a '0' size selection on a 4K screen and # an improbably small selection on an HD screen so assume user # has just clicked the image and set fraction to 0.5 fraction = 0.5 else: fraction = numpy.sqrt(selectionsize / geomsize) if self.activeTool == VIEWER_TOOL_ZOOMIN: if selectionsize < MIN_SELECTION_SIZE_PX: # user 'just clicked' (see if statement above). # NOTE: this fixes issues with 0 or negative dimensions # inside else: below that used ot hard-crash tuiview wldX, wldY = layer.coordmgr.display2world( selection.left(), selection.top()) layer.coordmgr.setZoomFactor( layer.coordmgr.imgPixPerWinPix * fraction) layer.coordmgr.setWorldCenter(wldX, wldY) # not sure why we need this but get black strips # around otherwise layer.coordmgr.recalcBottomRight() else: # I don't think anything needs to be added here dspTop = selection.top() dspLeft = selection.left() dspBottom = selection.bottom() dspRight = selection.right() (rastLeft, rastTop) = layer.coordmgr.display2pixel( dspLeft, dspTop) (rastRight, rastBottom) = layer.coordmgr.display2pixel( dspRight, dspBottom) #print layer.coordmgr layer.coordmgr.setTopLeftPixel(rastLeft, rastTop) layer.coordmgr.calcZoomFactor(rastRight, rastBottom) # not sure why we need this but get black strips # around otherwise layer.coordmgr.recalcBottomRight() #print layer.coordmgr elif self.activeTool == VIEWER_TOOL_ZOOMOUT: # the smaller the area the larger the zoom center = selection.center() wldX, wldY = layer.coordmgr.display2world( center.x(), center.y()) layer.coordmgr.setZoomFactor( layer.coordmgr.imgPixPerWinPix / fraction) layer.coordmgr.setWorldCenter(wldX, wldY) # not sure why we need this but get black strips # around otherwise layer.coordmgr.recalcBottomRight() # redraw self.layers.makeLayersConsistent(layer) self.layers.updateImages() self.viewport().update() self.updateScrollBars() # geolink self.emitGeolinkMoved() elif self.activeTool == VIEWER_TOOL_PAN: # change cursor back self.viewport().setCursor(self.panCursor) layer = self.layers.getTopRasterLayer() if layer is not None: # stop panning and move viewport dspXmove = -self.paintPoint.x() dspYmove = -self.paintPoint.y() (pixNewX, pixNewY) = layer.coordmgr.display2pixel(dspXmove, dspYmove) #print 'panning' #print layer.coordmgr layer.coordmgr.setTopLeftPixel(pixNewX, pixNewY) layer.coordmgr.recalcBottomRight() #print layer.coordmgr # reset self.paintPoint.setX(0) self.paintPoint.setY(0) # redraw self.layers.makeLayersConsistent(layer) self.layers.updateImages() self.viewport().update() self.updateScrollBars() # geolink self.emitGeolinkMoved() def mouseMoveEvent(self, event): """ Mouse has been moved while dragging. If in zoom/pan mode we need to do something here. """ QAbstractScrollArea.mouseMoveEvent(self, event) if self.rubberBand is not None and self.rubberBand.isVisible(): # must be doing zoom in/out. extend rect rect = QRect(self.rubberBand.origin, event.pos()).normalized() self.rubberBand.setGeometry(rect) elif self.activeTool == VIEWER_TOOL_PAN: # panning. Work out the offset from where we # starting panning and draw the current image # at an offset pos = event.pos() xamount = pos.x() - self.panOrigin.x() yamount = pos.y() - self.panOrigin.y() self.paintPoint.setX(xamount) self.paintPoint.setY(yamount) # force repaint - self.paintPoint used by paintEvent() self.viewport().update() self.updateScrollBars() # query point routines def newQueryPoint(self, easting=None, northing=None, dspY=None, dspX=None, column=None, row=None, modifiers=None): """ This viewer has recorded a new query point. Or user has entered new coords in querywindow. Calls updateQueryPoint and emits the geolinkQueryPoint signal pass either [easting and northing] or [dspX,dspY] or [column, row] """ if self.queryOnlyDisplayed: layer = self.layers.getTopDisplayedRasterLayer() else: layer = self.layers.getTopRasterLayer() if layer is None: return if ((easting is None or northing is None) and (dspX is None or dspY is None) and (column is None or row is None)): msg = ("must provide one of [easting,northing] or [dspX,dspY] " + "or [column, row]") raise ValueError(msg) if dspX is not None and dspY is not None: (column, row) = layer.coordmgr.display2pixel(dspX, dspY) (easting, northing) = layer.coordmgr.pixel2world(column, row) elif easting is not None and northing is not None: (column, row) = layer.coordmgr.world2pixel(easting, northing) elif column is not None and row is not None: (easting, northing) = layer.coordmgr.pixel2world(column, row) # update the point self.updateQueryPoint(easting, northing, column, row, modifiers) # emit the geolinked query point signal obj = GeolinkInfo(id(self), easting, northing) self.geolinkQueryPoint.emit(obj) def newVectorQueryPoint(self, dspX, dspY, modifiers=None): """ New vector query point. Does the spatial query and emits the vectorLocationSelected signal with the results """ if self.queryOnlyDisplayed: layer = self.layers.getTopDisplayedVectorLayer() else: layer = self.layers.getTopVectorLayer() if layer is None: return (easting, northing) = layer.coordmgr.display2world(dspX, dspY) tolerance = layer.coordmgr.metersperpix * 3 # maybe should be a pref? # show hourglass while query running oldCursor = self.cursor() self.setCursor(Qt.WaitCursor) results = layer.getAttributesAtPoint(easting, northing, tolerance) self.setCursor(oldCursor) self.vectorLocationSelected.emit(results, layer) def updateQueryPoint(self, easting, northing, column, row, modifiers): """ Map has been clicked, get the value and emit a locationSelected signal. Called by newQueryPoint or when a geolinkQueryPoint signal has been received. """ # read the data out of the dataset if self.queryOnlyDisplayed: layer = self.layers.getTopDisplayedRasterLayer() else: layer = self.layers.getTopRasterLayer() if (layer is not None and column >= 0 and column < layer.gdalDataset.RasterXSize and row >= 0 and row < layer.gdalDataset.RasterYSize): data = layer.gdalDataset.ReadAsArray(int(column), int(row), 1, 1) if data is not None: # we just want the single 'drill down' of data as a 1d array data = data[..., 0, 0] # if single band GDAL gives us a single value - # convert back to array # to make life easier if data.size == 1: data = numpy.array([data]) qi = QueryInfo(easting, northing, column, row, data, layer, modifiers) # emit the signal - handled by the QueryDockWidget self.locationSelected.emit(qi) def doGeolinkQueryPoint(self, easting, northing): """ Call this when the widget query point has been moved in another viewer and should be updated in this one if the query tool is active. """ if self.activeTool == VIEWER_TOOL_QUERY: if self.queryOnlyDisplayed: layer = self.layers.getTopDisplayedRasterLayer() else: layer = self.layers.getTopRasterLayer() if layer is not None: (col, row) = layer.coordmgr.world2pixel(easting, northing) self.updateQueryPoint(easting, northing, col, row, None) # geolinking routines def doGeolinkMove(self, easting, northing, metresperwinpix): """ Call this when widget needs to be moved because of geolinking event. """ layer = self.layers.getTopRasterLayer() if layer is not None: if self.geolinkFollowExtent and metresperwinpix != 0: imgpixperwinpix = metresperwinpix / layer.coordmgr.geotransform[ 1] layer.coordmgr.setZoomFactor(imgpixperwinpix) layer.coordmgr.setWorldCenter(easting, northing) self.layers.makeLayersConsistent(layer) self.layers.updateImages() self.updateScrollBars() self.viewport().update() def getGeolinkInfo(self): """ Called by emitGeolinkMoved and anything else that needs the current GeolinkInfo """ info = None # get the coords of the current centre layer = self.layers.getTopRasterLayer() if layer is not None: easting, northing = layer.coordmgr.getWorldCenter() metresperwinpix = (layer.coordmgr.imgPixPerWinPix * layer.coordmgr.geotransform[1]) info = GeolinkInfo(id(self), easting, northing, metresperwinpix) return info def emitGeolinkMoved(self): """ Call this on each zoom/pan to emit the appropriate signal. """ info = self.getGeolinkInfo() if info is not None: # emit the signal self.geolinkMove.emit(info)
def mousePressEvent(self, 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 mousePressEvent(self, event): """ Mouse has been clicked down if we are in zoom/pan mode we need to start doing stuff here """ QAbstractScrollArea.mousePressEvent(self, event) pos = event.pos() if (self.activeTool == VIEWER_TOOL_ZOOMIN or self.activeTool == VIEWER_TOOL_ZOOMOUT): if self.rubberBand is None: self.rubberBand = QRubberBand(QRubberBand.Rectangle, self) self.rubberBand.setGeometry(QRect(pos, QSize())) self.rubberBand.show() self.rubberBand.origin = pos elif self.activeTool == VIEWER_TOOL_PAN: # remember pos self.panOrigin = pos # change cursor self.viewport().setCursor(self.panGrabCursor) elif self.activeTool == VIEWER_TOOL_QUERY: modifiers = event.modifiers() (dspX, dspY) = (pos.x(), pos.y()) self.newQueryPoint(dspX=dspX, dspY=dspY, modifiers=modifiers) elif self.activeTool == VIEWER_TOOL_VECTORQUERY: modifiers = event.modifiers() (dspX, dspY) = (pos.x(), pos.y()) self.newVectorQueryPoint(dspX, dspY, modifiers=modifiers) elif self.activeTool == VIEWER_TOOL_POLYGON: button = event.button() if button == Qt.LeftButton: # adding points if self.toolPoints is None or self.toolPointsFinished: # first point - starts and ends at same pos self.toolPoints = [pos, pos] self.toolPointsFinished = False else: # last point same as first - insert before last self.toolPoints.insert(-1, pos) elif button == Qt.MiddleButton and self.toolPoints is not None: # delete last point if len(self.toolPoints) > 2: del self.toolPoints[-2] elif button == Qt.RightButton and self.toolPoints is not None: # finished # create object for signal layer = self.layers.getTopRasterLayer() modifiers = event.modifiers() obj = PolygonToolInfo(self.toolPoints, layer, modifiers) self.polygonCollected.emit(obj) self.toolPointsFinished = True # done, but still display # redraw so paint() gets called self.viewport().update() elif self.activeTool == VIEWER_TOOL_POLYLINE: button = event.button() if button == Qt.LeftButton: # adding points if self.toolPoints is None or self.toolPointsFinished: # first point self.toolPoints = [pos] self.toolPointsFinished = False else: # add to list self.toolPoints.append(pos) elif button == Qt.MiddleButton and self.toolPoints is not None: # delete last point if len(self.toolPoints) > 1: self.toolPoints.pop() elif button == Qt.RightButton and self.toolPoints is not None: # finished # create object for signal if self.queryOnlyDisplayed: layer = self.layers.getTopDisplayedRasterLayer() else: layer = self.layers.getTopRasterLayer() modifiers = event.modifiers() obj = PolylineToolInfo(self.toolPoints, layer, modifiers) self.polylineCollected.emit(obj) self.toolPointsFinished = True # done, but still display # redraw so paint() gets called self.viewport().update()
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
def __init__(self,*args,**kwargs): QRubberBand.__init__(self,*args,**kwargs)
class CaptureImage(QLabel): sequence = [] bag = None imageLabel = ImageLabel() def __init__(self, timeline, topic, msg, stamp): super(CaptureImage, self).__init__() self._timeline = timeline self._topic = topic #INISIALIZING MESSAGE AND COMMON ATTRIBUTES self._msg = msg self.imageLabel.header = self._msg.header self.imageLabel.imageTopic = self._topic #ORIGINAL TIME FROM THE ORIGINAL BAG SO THAT WE SAVE THAT SAME TIME IN THE NEW BAG self.stamp = stamp #WE HAVE TO DO BECAUSE RECT ARE RELATED WITH THE WIDGET WHICH CREATED THEM #SO IN ORDER TO SEE AND DELETE THEM FROM THE NEW WIDGET WE HAVE TO RELATE THEM WITH IT #***NOTE***: OBJECTS STORED IN SEQUENCE ARE OF TYPE QRUBBERBAND WHEREAS IN IMAGELABEL ARE #OF TYPE RECT if len(self.sequence) > 0: aux_sequence = [] for rectangle in self.sequence: self.currentQRubberBand = QRubberBand(QRubberBand.Rectangle, self) self.currentQRubberBand.setGeometry(rectangle.x(), rectangle.y(), rectangle.width(), rectangle.height()) aux_sequence.append(self.currentQRubberBand) self.currentQRubberBand.show() del self.sequence[:] for rectangle in aux_sequence: self.sequence.append(rectangle) def mousePressEvent(self, eventQMouseEvent): if eventQMouseEvent.buttons() == Qt.LeftButton: self.originQPoint = eventQMouseEvent.pos() self.currentQRubberBand = QRubberBand(QRubberBand.Rectangle, self) self.sequence.append(self.currentQRubberBand) r = randint(0, 255) g = randint(0, 255) b = randint(0, 255) palette = QPalette() palette.setColor(self.currentQRubberBand.foregroundRole(), QColor(r, g, b)) self.currentQRubberBand.setPalette(palette) self.currentQRubberBand.setGeometry( QRect(self.originQPoint, QSize())) self.currentQRubberBand.show() elif eventQMouseEvent.buttons() == Qt.RightButton: found = False for rect in self.sequence: if (rect.geometry().contains(eventQMouseEvent.pos())): self.menu = MenuRectangle(self._timeline, eventQMouseEvent, rect, self.imageLabel, self.sequence) found = True if not found: self.menuglobalimage = MenuGlobalImage( self._timeline, eventQMouseEvent, self._topic, self.sequence, self.imageLabel, self.stamp) def mouseMoveEvent(self, eventQMouseEvent): self.currentQRubberBand.setGeometry( QRect(self.originQPoint, eventQMouseEvent.pos()).normalized())
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()
def __init__(self, *args, **kwargs): QRubberBand.__init__(self, *args, **kwargs)
def setupUi(self, MainWindow): MainWindow.showMaximized() # Default font font = QFont() font.setFamily("Bahnschrift Light") font.setPointSize(10) font.setBold(False) font.setWeight(50) self.centralwidget = QWidget(MainWindow) self.verticalLayoutWidget = QWidget(self.centralwidget) self.verticalLayoutWidget.setFont(font) self.verticalLayoutWidget.setGeometry(QRect(20, 60, 1100, 891)) self.verticalLayout = QVBoxLayout(self.verticalLayoutWidget) self.verticalLayout.setSizeConstraint(QLayout.SetMaximumSize) self.verticalLayout.setSpacing(8) # for spacing UI spacerItem = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.verticalLayout.addItem(spacerItem) self.outputLabel = QLabel(self.verticalLayoutWidget) self.verticalLayout.addWidget(self.outputLabel) self.printer = QPrinter() self.scaleFactor = 0.0 # To select area subclasses = self._subclass_container(self.verticalLayoutWidget) self.imageLabel = subclasses["rubberband"] # Select From Language self.fromLabel = QLabel(self.verticalLayoutWidget) self.fromLabel.setText("From:") self.fromLabel.setFont(font) self.fromLabel.setMaximumSize(QSize(16777215, 30)) self.verticalLayout.addWidget(self.fromLabel) self.comboBox = QComboBox(self.verticalLayoutWidget) self.comboBox.addItem("English") self.comboBox.addItem("Korean") self.comboBox.setFont(font) self.comboBox.currentIndexChanged.connect(self.selectionchange) self.verticalLayout.addWidget(self.comboBox) # Select To Language self.toLabel = QLabel(self.verticalLayoutWidget) self.toLabel.setText("To:") self.toLabel.setFont(font) self.toLabel.setMaximumSize(QSize(16777215, 30)) self.verticalLayout.addWidget(self.toLabel) self.comboBox_2 = QComboBox(self.verticalLayoutWidget) self.comboBox_2.addItem("English") self.comboBox_2.addItem("Korean") self.comboBox_2.setFont(font) self.comboBox_2.currentIndexChanged.connect(self.selectionchange2) self.verticalLayout.addWidget(self.comboBox_2) # Translate Button self.translateButton = QPushButton(self.verticalLayoutWidget) self.translateButton.setText("Translate") self.translateButton.setFont(font) self.translateButton.clicked.connect(self.changeImage) self.verticalLayout.addWidget(self.translateButton) spacerItem1 = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Minimum) self.verticalLayout.addItem(spacerItem1) # Instruction text label self.instructutionLabel = QLabel(self.centralwidget) self.instructutionLabel.setGeometry(QRect(60, 20, 651, 16)) self.instructutionLabel.setFont(font) self.instructutionLabel.setText("*Warning: Please select individual scene only, not the speech bubble!*") MainWindow.setCentralWidget(self.centralwidget) self.rubberBand = QRubberBand(QRubberBand.Rectangle, self.imageLabel) self.imageLabel.setMouseTracking(True) self.origin = QPoint() self.verticalLayoutWidget_2 = QWidget(self.centralwidget) self.verticalLayoutWidget_2.setGeometry(QRect(1200, 60, 591, 900)) self.verticalLayout_2 = QVBoxLayout(self.verticalLayoutWidget_2) # Original image text label self.originalLabel = QLabel(self.verticalLayoutWidget_2) self.originalLabel.setMaximumSize(QSize(16777215, 30)) self.originalLabel.setFont(font) self.originalLabel.setText("Original:") self.verticalLayout_2.addWidget(self.originalLabel) # Original image label self.originalImageLabel = QLabel(self.verticalLayoutWidget_2) self.originalImageLabel.setObjectName("originalImageLabel") self.originalImageLabel.setMinimumSize(QSize(0, 300)) self.verticalLayout_2.addWidget(self.originalImageLabel) # Translated image text label self.translatedLabel = QLabel(self.verticalLayoutWidget_2) self.translatedLabel.setMaximumSize(QSize(16777215, 30)) self.translatedLabel.setText("Translated:") self.translatedLabel.setFont(font) self.verticalLayout_2.addWidget(self.translatedLabel) # Translated image label self.translatedImageLabel = QLabel(self.verticalLayoutWidget_2) self.translatedImageLabel.setMinimumSize(QSize(0, 300)) self.verticalLayout_2.addWidget(self.translatedImageLabel) #Menu self.createActions() self.createMenus() MainWindow.setWindowTitle("Cartoon Translator") QMetaObject.connectSlotsByName(MainWindow)
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
def __init__(self, parent): super().__init__() self.rubberband = QRubberBand(QRubberBand.Rectangle, self) self.setMouseTracking(True) self.parent = parent
class RubberbandEnhancedLabel(QLabel): def __init__(self, parent=None): self._parent_class = _parent_class QLabel.__init__(self, parent) self.selection = QRubberBand(QRubberBand.Rectangle, self) def mousePressEvent(self, event): ''' Mouse is pressed. If selection is visible either set dragging mode (if close to border) or hide selection. If selection is not visible make it visible and start at this point. ''' # if event.button() == Qt.LeftButton: position = QPoint(event.pos()) if self.selection.isVisible(): # visible selection if (self.upper_left - position).manhattanLength() < 20: # close to upper left corner, drag it self.mode = "drag_upper_left" elif (self.lower_right - position).manhattanLength() < 20: # close to lower right corner, drag it self.mode = "drag_lower_right" else: # clicked somewhere else, hide selection self.selection.hide() else: # no visible selection, start new selection self.upper_left = position self.lower_right = position self.mode = "drag_lower_right" self.selection.show() def mouseMoveEvent(self, event): ''' Mouse moved. If selection is visible, drag it according to drag mode. ''' if self.selection.isVisible(): # visible selection if self.mode == "drag_lower_right": self.lower_right = QPoint(event.pos()) elif self.mode == "drag_upper_left": self.upper_left = QPoint(event.pos()) # update geometry self.selection.setGeometry(QRect(self.upper_left, self.lower_right).normalized()) def mouseReleaseEvent(self, event): if self.selection.isVisible(): currentQRect = self.selection.geometry() cropQPixmap = self.pixmap().copy(currentQRect) cropQPixmap.save('output.png') image = QImage("output.png") if image.isNull(): QMessageBox.information(self.centralwidget, "Image Viewer", "Cannot load %s." % self.fileName) return pm = QPixmap.fromImage(image) h = pm.height() w = pm.width() if (h > w): self._parent_class.originalImageLabel.setPixmap(pm.scaledToHeight(400)) else: self._parent_class.originalImageLabel.setPixmap(pm.scaledToWidth(400)) self.selection.hide()
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() 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
def initUI(self): ''' UI initialization ''' #set size and center self.setFixedSize(370, 350) self.center() #create all butons for menu and assign them functions self.btn1 = QPushButton('Load PDF', self) self.btn1.clicked.connect(self.getFilePath) self.btn1.setToolTip('Choose pdf file to work with') self.btn1.resize(100, 50) self.btn1.move(25, 25) self.btn2 = QPushButton('Set Config', self) self.btn2.clicked.connect(self.initConfigForm) self.btn2.setToolTip('Configure settings') self.btn2.resize(100, 50) self.btn2.move(25, 80) self.btn3 = QPushButton('Create\nBounding Box', self) self.btn3.clicked.connect(self.createBoundingBox) self.btn3.setToolTip('Create box limiting area of change') self.btn3.resize(100, 50) self.btn3.move(135, 25) self.btn4 = QPushButton('Generate Average', self) self.btn4.clicked.connect(self.getAverageEstimateWrapper) self.btn4.setToolTip('Estimate watermark by averaging over pages') self.btn4.resize(100, 50) self.btn4.move(135, 80) self.btn5 = QPushButton('Flat Elimination', self) self.btn5.clicked.connect(self.convertFlatWrapper) self.btn5.setToolTip('Replace all RGB values between set values and replace them with color') self.btn5.resize(100, 50) self.btn5.move(245, 25) self.btn6 = QPushButton('Average\nElimination', self) self.btn6.clicked.connect(self.convertAverageWrapper) self.btn6.setToolTip('Replace all RGB values that are similar enough to averaged sample') self.btn6.resize(100, 50) self.btn6.move(245, 80) #hide certain buttons untill a pdf is choosen self.btn3.hide() self.btn4.hide() self.btn5.hide() self.btn6.hide() #set name, logo, etc. for progress bar self.bar = QWidget() self.bar.setWindowIcon(QIcon('graphics//logo.png')) self.bar.setWindowTitle('Progress bar') self.progress = QProgressBar(self.bar) self.bar.move(QDesktopWidget().availableGeometry().center() - QPoint(150, 15)) self.progress.setGeometry(0, 0, 300, 30) #slector for bounding box self.selector = QWidget() self.pic = QLabel(self.selector) #assign events to the correct window self.selector.mousePressEvent = lambda event : self.MPE(event) self.selector.mouseMoveEvent = lambda event : self.MME(event) self.selector.mouseReleaseEvent = lambda event : self.MRE(event) #set title and logo self.selector.setWindowTitle('Select Bounding Box') self.selector.setWindowIcon(QIcon('graphics//logo.png')) #create rubber band for selector self.selection = QRubberBand(QRubberBand.Rectangle, self.pic) #set project logo in the menu logo = QLabel(self) logo.resize(200, 200) logo.setPixmap(QPixmap("graphics//logo.png")) logo.move(85, 130) #set title and logo for main windows and finish initialization self.setWindowIcon(QIcon('graphics//logo.png')) self.setWindowTitle('PDF Purifico') self.show() return
def setup_crop(self, width, height): self.rband = QRubberBand(QRubberBand.Rectangle, self) coords = self.mapFromScene(0, 0, width, height) self.rband.setGeometry(QRect(coords.boundingRect())) self.rband.show()
class GUI(QMainWindow): ''' Main GUI class ''' def __init__(self): ''' Initializes most basic object and sets default values for parameters ''' super().__init__() self.initUI() #path to pdf self.path = None #masks for flat elimination algorithm self.conditionLower = np.array([255, 255, 255]) self.conditionUpper = np.array([100, 100, 100]) #batch size self.batchSize = 5 #pages to work on -> unfinished so set to always work on full file self.pages = [0] #color to use when replacing pixels self.color = np.array([255, 255, 255]) #default output name self.outName = "temporary" #not existing bounding box self.boundingBox = [0] self.DEBUG = True def initUI(self): ''' UI initialization ''' #set size and center self.setFixedSize(370, 350) self.center() #create all butons for menu and assign them functions self.btn1 = QPushButton('Load PDF', self) self.btn1.clicked.connect(self.getFilePath) self.btn1.setToolTip('Choose pdf file to work with') self.btn1.resize(100, 50) self.btn1.move(25, 25) self.btn2 = QPushButton('Set Config', self) self.btn2.clicked.connect(self.initConfigForm) self.btn2.setToolTip('Configure settings') self.btn2.resize(100, 50) self.btn2.move(25, 80) self.btn3 = QPushButton('Create\nBounding Box', self) self.btn3.clicked.connect(self.createBoundingBox) self.btn3.setToolTip('Create box limiting area of change') self.btn3.resize(100, 50) self.btn3.move(135, 25) self.btn4 = QPushButton('Generate Average', self) self.btn4.clicked.connect(self.getAverageEstimateWrapper) self.btn4.setToolTip('Estimate watermark by averaging over pages') self.btn4.resize(100, 50) self.btn4.move(135, 80) self.btn5 = QPushButton('Flat Elimination', self) self.btn5.clicked.connect(self.convertFlatWrapper) self.btn5.setToolTip('Replace all RGB values between set values and replace them with color') self.btn5.resize(100, 50) self.btn5.move(245, 25) self.btn6 = QPushButton('Average\nElimination', self) self.btn6.clicked.connect(self.convertAverageWrapper) self.btn6.setToolTip('Replace all RGB values that are similar enough to averaged sample') self.btn6.resize(100, 50) self.btn6.move(245, 80) #hide certain buttons untill a pdf is choosen self.btn3.hide() self.btn4.hide() self.btn5.hide() self.btn6.hide() #set name, logo, etc. for progress bar self.bar = QWidget() self.bar.setWindowIcon(QIcon('graphics//logo.png')) self.bar.setWindowTitle('Progress bar') self.progress = QProgressBar(self.bar) self.bar.move(QDesktopWidget().availableGeometry().center() - QPoint(150, 15)) self.progress.setGeometry(0, 0, 300, 30) #slector for bounding box self.selector = QWidget() self.pic = QLabel(self.selector) #assign events to the correct window self.selector.mousePressEvent = lambda event : self.MPE(event) self.selector.mouseMoveEvent = lambda event : self.MME(event) self.selector.mouseReleaseEvent = lambda event : self.MRE(event) #set title and logo self.selector.setWindowTitle('Select Bounding Box') self.selector.setWindowIcon(QIcon('graphics//logo.png')) #create rubber band for selector self.selection = QRubberBand(QRubberBand.Rectangle, self.pic) #set project logo in the menu logo = QLabel(self) logo.resize(200, 200) logo.setPixmap(QPixmap("graphics//logo.png")) logo.move(85, 130) #set title and logo for main windows and finish initialization self.setWindowIcon(QIcon('graphics//logo.png')) self.setWindowTitle('PDF Purifico') self.show() return def pageForm(self): ''' Unfinished form for retrieving arbitrary start and finish pages ''' #setup widget self.pageForm = QWidget() self.pageForm.resize(600, 100) #set label label = QLabel(self.pageForm) label.setText("Set first and last page") label.move(15, 15) #set input fields self.firstP = QLineEdit(self.pageForm) self.firstP.move(180, 15) self.lastP = QLineEdit(self.pageForm) self.lastP.move(330, 15) #create send button self.formPageButton = QPushButton('Set pages', self.pageForm) self.formPageButton.clicked.connect(self.setPages) self.formPageButton.move(480, 15) self.pageForm.show() return def setPages(self): ''' validate input and save it ''' firstP = int(self.firstP.text()) lastP = int(self.lastP.text()) #validate if (firstP > 0) and (lastP > 0) and (lastP >= firstP): self.pages = [firstP, lastP] self.waitForPages = False #hide form once done self.pageForm.hide() return def initConfigForm(self): ''' Create form forconfiguration changes ''' #create base widget self.form = QWidget() self.form.resize(500, 350) #Creates labels and input fields for all the data label = QLabel(self.form) label.setText("R value from RGB (min - max)") label.move(15, 15) label = QLabel(self.form) label.setText("G value from RGB (min - max)") label.move(15, 45) label = QLabel(self.form) label.setText("B value from RGB (min - max)") label.move(15, 75) self.minR = QLineEdit(self.form) self.minR.move(180, 15) self.maxR = QLineEdit(self.form) self.maxR.move(330, 15) self.minG = QLineEdit(self.form) self.minG.move(180, 45) self.maxG = QLineEdit(self.form) self.maxG.move(330, 45) self.minB = QLineEdit(self.form) self.minB.move(180, 75) self.maxB = QLineEdit(self.form) self.maxB.move(330, 75) label = QLabel(self.form) label.setText("R value from RGB to set") label.move(15, 105) label = QLabel(self.form) label.setText("G value from RGB to set") label.move(15, 135) label = QLabel(self.form) label.setText("B value from RGB to set") label.move(15, 165) self.colorR = QLineEdit(self.form) self.colorR.move(180, 105) self.colorG = QLineEdit(self.form) self.colorG.move(180, 135) self.colorB = QLineEdit(self.form) self.colorB.move(180, 165) label = QLabel(self.form) label.setText("Size of batches to read to RAM") label.move(15, 195) self.batchS = QLineEdit(self.form) self.batchS.move(180, 195) label = QLabel(self.form) label.setText("Name for new file") label.move(15, 225) self.name = QLineEdit(self.form) self.name.move(180, 225) #button to send self.formButton = QPushButton('Save Config', self.form) self.formButton.clicked.connect(self.setConfig) self.formButton.move(330, 135) self.formButton.resize(135, 50) #set name and logo self.form.setWindowTitle("Config") self.form.setWindowIcon(QIcon('graphics//logo.png')) self.form.show() return def setConfig(self): ''' validate and set data from config form ''' try: #retrieve all the data and cast to correct type minR = int(self.minR.text()) minG = int(self.minG.text()) minB = int(self.minB.text()) maxR = int(self.maxR.text()) maxG = int(self.maxG.text()) maxB = int(self.maxB.text()) colorR = int(self.colorR.text()) colorG = int(self.colorG.text()) colorB = int(self.colorB.text()) batchS = int(self.batchS.text()) self.outName = self.name.text() #validate and save if (0 <= minR <= 255) and (0 <= minG <= 255) and (0 <= minB <= 255): self.conditionUpper = np.array([minR, minG, minB]) if (0 <= maxR <= 255) and (0 <= maxG <= 255) and (0 <= maxB <= 255): self.conditionUpper = np.array([maxR, maxG, maxB]) if (0 <= colorR <= 255) and (0 <= colorG <= 255) and (0 <= colorB <= 255): self.conditionUpper = np.array([colorR, colorG, colorB]) if (batchS > 0): self.batchSize = batchS self.form.hide() except: #in case of error QMessageBox.question(self, 'Error', "Something broke", QMessageBox.Yes) self.form.hide() return def onCountChanged(self, value): ''' For updating progress bar ''' self.progress.setValue(value) return def getFilePath(self): ''' Get path to pdf file ''' w = QWidget() filename = QFileDialog.getOpenFileName(w, 'Select PDF file') self.path = filename[0] #since we got a pdf now we can enable the other buttons self.btn3.show() self.btn4.show() self.btn5.show() self.btn6.show() return def convertFlatWrapper(self): ''' Wrapper for convertFlat function ''' #set pages correctly if (len(self.pages) == 2): pagesToClean = self.pages[1] - self.pages[0] + 1 pageOffset = self.pages[0] - 1 elif (len(self.pages) == 1): pagesToClean = self.pages[0] pageOffset = 0 else: QMessageBox.about(self,"Error", "Pages set incorrectly") return #prepare update bar send data to thread and start it self.bar.show() self.calc = External() self.calc.setFunction(convertFlat, (self.path, self.conditionLower, self.conditionUpper, self.batchSize, self.outName, pagesToClean, pageOffset, self.color, self.boundingBox)) self.calc.countChanged.connect(self.onCountChanged) self.calc.start() return def getAverageEstimateWrapper(self): ''' Wrapper for getAverageEstimate ''' #set pages correctly if (len(self.pages) == 2): pagesToClean = self.pages[1] - self.pages[0] + 1 pageOffset = self.pages[0] - 1 elif (len(self.pages) == 1): pagesToClean = self.pages[0] pageOffset = 0 else: QMessageBox.about(self,"Error", "Pages set incorrectly") return #prepare update bar send data to thread and start it self.bar.show() self.calc = External() self.calc.setFunction(getAverageEstimate, (self.path, self.batchSize, self.outName, pagesToClean, pageOffset)) self.calc.countChanged.connect(self.onCountChanged) self.calc.start() return def convertAverageWrapper(self): ''' Wrapper for converAverage ''' if (len(self.pages) == 2): pagesToClean = self.pages[1] - self.pages[0] + 1 pageOffset = self.pages[0] - 1 elif (len(self.pages) == 1): pagesToClean = self.pages[0] pageOffset = 0 else: QMessageBox.about(self,"Error", "Pages set incorrectly") #slect averaged file touse as filter for pdf w = QWidget() filename = QFileDialog.getOpenFileName(w, 'Select averaged file to filter pdf') #prepare update bar send data to thread and start it self.bar.show() self.calc = External() self.calc.setFunction(convertAverage, (self.path, filename[0], self.batchSize, self.outName, pagesToClean, pageOffset, self.color, self.boundingBox)) self.calc.countChanged.connect(self.onCountChanged) self.calc.start() return def center(self): ''' method for centering ''' qr = self.frameGeometry() cp = QDesktopWidget().availableGeometry().center() qr.moveCenter(cp) self.move(qr.topLeft()) return def closeEvent(self, event): ''' Confirmation when closing software ''' reply = QMessageBox.question(self, 'Message', "Are you sure?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: event.accept() else: event.ignore() return def MPE(self, event): ''' Mouse is pressed. If selection is visible either set dragging mode (if close to border) or hide selection. If selection is not visible make it visible and start at this point. ''' if event.button() == Qt.LeftButton: position = QPoint(event.pos()) if (self.DEBUG): print("Start coords:", position.x(), position.y()) #set coords for later self.start_x = position.x() self.start_y = position.y() if self.selection.isVisible(): # visible selection if (self.upper_left - position).manhattanLength() < 20: # close to upper left corner, drag it self.mode = "drag_upper_left" elif (self.lower_right - position).manhattanLength() < 20: # close to lower right corner, drag it self.mode = "drag_lower_right" else: # clicked somewhere else, hide selection self.selection.hide() else: # no visible selection, start new selection self.upper_left = position self.lower_right = position self.mode = "drag_lower_right" self.selection.show() return def MME(self, event): ''' Mouse moved. If selection is visible, drag it according to drag mode. ''' if self.selection.isVisible(): # visible selection if self.mode is "drag_lower_right": self.lower_right = QPoint(event.pos()) elif self.mode is "drag_upper_left": self.upper_left = QPoint(event.pos()) # update geometry self.selection.setGeometry(QRect(self.upper_left, self.lower_right).normalized()) return def MRE(self, event): ''' Mouse is released. Hide selector, selection and save propertly transformed coords ''' if event.button() == Qt.LeftButton: position = QPoint(event.pos()) if (self.DEBUG): print("Stop coords:", position.x(), position.y()) #save final position self.stop_x = position.x() self.stop_y = position.y() if self.selection.isVisible(): self.selection.hide() self.selector.hide() im = convert_from_path(self.path, fmt='jpeg', first_page=1, last_page=1)[0] width, height = im.size if (self.DEBUG): orig = np.array(im) color = np.array([100, 100, 200]) pts = np.zeros((2,2)) #set coordinates for bounding box pts[0][0] = int(self.start_x / self.scaled_im_width * width) pts[0][1] = int(self.start_y / self.scaled_im_height * height) pts[1][0] = int(self.stop_x / self.scaled_im_width * width) pts[1][1] = int(self.stop_y / self.scaled_im_height * height) self.boundingBox = pts if(self.DEBUG): drawLines(orig, pts.astype(int), color, 3) im = Image.fromarray(orig) im.save('temp\\sample.jpeg') im.show() return def createBoundingBox(self): ''' Creates bounding box to be used in one of the enxt algorithms ''' im = convert_from_path(self.path, fmt='jpeg', first_page=1, last_page=1)[0] im.save("temp\\temp.jpg") #get original sizes width, height = im.size im.close() geo = QDesktopWidget().availableGeometry() #transform for convinience if (geo.height() < height): width *= (geo.height()/height) * 0.9 height = geo.height() * 0.9 #save for reversing later self.scaled_im_width = int(width) self.scaled_im_height = int(height) self.pic.setGeometry(0 , 0, width, height) self.selector.setFixedSize(width, height) #load picture self.pic.setPixmap(QPixmap("temp\\temp.jpg").scaledToHeight(height)) qr = self.selector.frameGeometry() cp = QDesktopWidget().availableGeometry().center() qr.moveCenter(cp) self.selector.move(qr.topLeft()) self.selector.show() return
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()
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) """ QMessageBox.about(self, 'PHIDL Help', help_str)
class VideoWidget(QVideoWidget): def __init__(self, parent=None): super(VideoWidget, self).__init__(parent) self.rubberBand = QRubberBand(QRubberBand.Rectangle, self) pal = QPalette() pal.setBrush(QPalette.Highlight, QBrush(QColor(Qt.green))) self.rubberBand.setPalette(pal) self.setUpdatesEnabled(True) self.setMouseTracking(True) self.origin = QPoint() self.changeRubberBand = False self.parent = parent.parent() self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) self.setAttribute(Qt.WA_NoSystemBackground, True) self.setAttribute(Qt.WA_PaintOnScreen, True) palette = self.palette() palette.setColor(QPalette.Background, Qt.black) self.setPalette(palette) self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) self.surface = VideoWidgetSurface(self) self.setAttribute(Qt.WA_OpaquePaintEvent) self.gt = None self.pressed = False self.snapped = False self.zoomed = False self.zoomedRect = False self.offset = QPoint() self.pressPos = QPoint() self.dragPos = QPoint() self.tapTimer = QBasicTimer() self.zoomPixmap = QPixmap() self.maskPixmap = QPixmap() def keyPressEvent(self, event): if event.key() == Qt.Key_Escape and self.isFullScreen(): self.setFullScreen(False) event.accept() elif event.key() == Qt.Key_Enter and event.modifiers() & Qt.Key_Alt: self.setFullScreen(not self.isFullScreen()) event.accept() else: super(VideoWidget, self).keyPressEvent(event) def mouseDoubleClickEvent(self, event): """ :type event: QMouseEvent :param event: :return: """ self.setFullScreen(not self.isFullScreen()) event.accept() def videoSurface(self): return self.surface def UpdateSurface(self): self.surface.widget.update() def sizeHint(self): return self.surface.surfaceFormat().sizeHint() def GetCurrentFrame(self): return self.surface.image def SetInvertColor(self, value): global invertColorFilter invertColorFilter = value def SetGray(self, value): global grayColorFilter grayColorFilter = value def SetEdgeDetection(self, value): global edgeDetectionFilter edgeDetectionFilter = value def SetAutoContrastFilter(self, value): global contrastFilter contrastFilter = value def SetMonoFilter(self, value): global monoFilter monoFilter = value def RestoreFilters(self): global invertColorFilter, grayColorFilter, edgeDetectionFilter, monoFilter, contrastFilter invertColorFilter = grayColorFilter = edgeDetectionFilter = monoFilter = contrastFilter = False def GetXBlackZone(self): x = 0.0 normalizedWidth = self.surface.widget.height() * (GetImageWidth() / GetImageHeight()) if (self.surface.widget.width() / self.surface.widget.height()) > ( GetImageWidth() / GetImageHeight()): x = (self.surface.widget.width() - (normalizedWidth)) / 2.0 return x def GetYBlackZone(self): y = 0.0 normalizedHeight = self.surface.widget.width() / (GetImageWidth() / GetImageHeight()) if (self.surface.widget.width() / self.surface.widget.height()) < ( GetImageWidth() / GetImageHeight()): y = (self.surface.widget.height() - (normalizedHeight)) / 2.0 return y # determines if a clicked point lands on the image (False if lands on the # black borders or outside) def IsPointOnScreen(self, x, y): res = True normalizedWidth = self.surface.widget.height() * (GetImageWidth() / GetImageHeight()) normalizedHeight = self.surface.widget.width() / (GetImageWidth() / GetImageHeight()) if x > (normalizedWidth + self.GetXBlackZone()) or x < self.GetXBlackZone(): res = False if y > (normalizedHeight + self.GetYBlackZone()) or y < self.GetYBlackZone(): res = False return res # ratio between event.x() and real image width on screen. def GetXRatio(self): return GetImageWidth() / (self.surface.widget.width() - (2 * self.GetXBlackZone())) # ratio between event.y() and real image height on screen. def GetYRatio(self): return GetImageHeight() / (self.surface.widget.height() - (2 * self.GetYBlackZone())) def paintEvent(self, event): self.gt = GetGCPGeoTransform() painter = QPainter(self) if (self.surface.isActive()): videoRect = self.surface.videoRect() if not videoRect.contains(event.rect()): region = event.region() region.subtracted(QRegion(videoRect)) brush = self.palette().window() for rect in region.rects(): painter.fillRect(rect, brush) try: self.surface.paint(painter) except Exception: None else: painter.fillRect(event.rect(), self.palette().window()) try: SetImageSize(self.surface.currentFrame.width(), self.surface.currentFrame.height()) except: None # Magnifier Glass if self.zoomed and magnifier: dim = min(self.width(), self.height()) magnifierSize = min(MAX_MAGNIFIER, dim * 2 / 3) radius = magnifierSize / 2 ring = radius - 15 box = QSize(magnifierSize, magnifierSize) # reupdate our mask if self.maskPixmap.size() != box: self.maskPixmap = QPixmap(box) self.maskPixmap.fill(Qt.transparent) g = QRadialGradient() g.setCenter(radius, radius) g.setFocalPoint(radius, radius) g.setRadius(radius) g.setColorAt(1.0, QColor(64, 64, 64, 0)) g.setColorAt(0.5, QColor(0, 0, 0, 255)) mask = QPainter(self.maskPixmap) mask.setRenderHint(QPainter.Antialiasing) mask.setCompositionMode(QPainter.CompositionMode_Source) mask.setBrush(g) mask.setPen(Qt.NoPen) mask.drawRect(self.maskPixmap.rect()) mask.setBrush(QColor(Qt.transparent)) mask.drawEllipse(g.center(), ring, ring) mask.end() center = self.dragPos - QPoint(0, radius) center += QPoint(0, radius / 2) corner = center - QPoint(radius, radius) xy = center * 2 - QPoint(radius, radius) # only set the dimension to the magnified portion if self.zoomPixmap.size() != box: self.zoomPixmap = QPixmap(box) self.zoomPixmap.fill(Qt.lightGray) if True: painter = QPainter(self.zoomPixmap) painter.translate(-xy) self.largePixmap = QPixmap.fromImage(self.surface.image) painter.drawPixmap(self.offset, self.largePixmap) painter.end() clipPath = QPainterPath() clipPath.addEllipse(QPointF(center), ring, ring) painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) painter.setClipPath(clipPath) painter.drawPixmap(corner, self.zoomPixmap) painter.drawPixmap(corner, self.maskPixmap) painter.setPen(Qt.gray) painter.drawPath(clipPath) return def resizeEvent(self, event): """ :type event: QMouseEvent :param event: :return: """ QWidget.resizeEvent(self, event) self.zoomed = False if zoomRect and self.surface.zoomedrect is not None: self.surface.updatetest() return self.surface.updateVideoRect() def mouseMoveEvent(self, event): """ :type event: QMouseEvent :param event: :return: """ # Cursor Coordinates if self.gt is not None: # check if the point is on picture (not in black borders) if (not self.IsPointOnScreen(event.x(), event.y())): return transf = self.gt([ (event.x() - self.GetXBlackZone()) * self.GetXRatio(), (event.y() - self.GetYBlackZone()) * self.GetYRatio() ]) Longitude = transf[1] Latitude = transf[0] Altitude = 0.0 self.parent.lb_cursor_coord.setText( "<span style='font-size:10pt; font-weight:bold;'>Lon :</span>" + "<span style='font-size:9pt; font-weight:normal;'>" + ("%.3f" % Longitude) + "</span>" + "<span style='font-size:10pt; font-weight:bold;'> Lat :</span>" + "<span style='font-size:9pt; font-weight:normal;'>" + ("%.3f" % Latitude) + "</span>") else: self.parent.lb_cursor_coord.setText( "<span style='font-size:10pt; font-weight:bold;'>Lon :</span>" + "<span style='font-size:9pt; font-weight:normal;'>Null</span>" + "<span style='font-size:10pt; font-weight:bold;'> Lat :</span>" + "<span style='font-size:9pt; font-weight:normal;'>Null</span>") if not event.buttons(): return if self.changeRubberBand: self.rubberBand.setGeometry( QRect(self.origin, event.pos()).normalized()) if self.zoomed is True: self.rubberBand.hide() self.zoomedRect = False if not self.zoomed: if not self.pressed or not self.snapped: delta = event.pos() - self.pressPos self.pressPos = event.pos() self.pan(delta) return else: threshold = 10 delta = event.pos() - self.pressPos if self.snapped: self.snapped &= delta.x() < threshold self.snapped &= delta.y() < threshold self.snapped &= delta.x() > -threshold self.snapped &= delta.y() > -threshold if not self.snapped: self.tapTimer.stop() else: self.dragPos = event.pos() self.surface.updateVideoRect() # TODO: MAKE PAINT GEOMETRY ACTION AND CREATE SHAPES def pan(self, delta): """ Pan Action """ self.offset += delta self.surface.updateVideoRect() def timerEvent(self, _): """ Time Event """ if not self.zoomed: self.activateMagnifier() self.surface.updateVideoRect() def mousePressEvent(self, event): """ :type event: QMouseEvent :param event: :return: """ if event.button() == Qt.LeftButton: self.pressed = self.snapped = True self.pressPos = self.dragPos = event.pos() self.tapTimer.stop() self.tapTimer.start(HOLD_TIME, self) if zoomRect and event.button() == Qt.LeftButton: self.origin = event.pos() self.rubberBand.setGeometry(QRect(self.origin, QSize())) self.rubberBand.show() self.changeRubberBand = True def activateMagnifier(self): """ Activate Magnifier Glass """ self.zoomed = True self.tapTimer.stop() self.surface.updateVideoRect() def SetMagnifier(self, value): """ Set Magnifier Glass """ global magnifier magnifier = value def SetZoomRect(self, value): """ Set Zoom Rectangle """ global zoomRect zoomRect = value def mouseReleaseEvent(self, _): """ :type event: QMouseEvent :param event: :return: """ self.changeRubberBand = False if self.zoomed is True: return self.zoomed = False if not zoomRect: self.surface.updateVideoRect() else: self.rubberBand.hide() self.zoomedRect = True # TODO : ACTUALIZAR LA IMAGEN selRect = self.rubberBand.geometry() orig2widgScale = self.surface.widget.contentsRect().width() / \ self.surface.image.width() X1 = selRect.topLeft().x() / orig2widgScale Y1 = selRect.topLeft().y() / orig2widgScale X2 = selRect.bottomRight().x() / self.surface.widget.contentsRect().bottomRight().x() * \ self.surface.image.width() Y2 = selRect.bottomRight().y() / self.surface.widget.contentsRect().bottomRight().y() * \ self.surface.image.height() self.surface.image.width() self.surface.image.height() wid2origRect = QRect(X1, Y1, X2, Y2) zoom_img = self.surface.image.copy(wid2origRect) # zoom_img.save('D:\\test.png') # n_img = self.surface.image.copy(selRect) # n_img.save('D:\\test.png') zoom_img.scaled(1920, 1080) self.surface.currentFrame = QVideoFrame(zoom_img) # self.surface.targetRect=selRect self.surface.currentFrame.unmap() self.UpdateSurface() def leaveEvent(self, _): self.parent.lb_cursor_coord.setText("")
class VideoWidget(QVideoWidget): def __init__(self, parent=None): ''' Constructor ''' super(VideoWidget, self).__init__(parent) self.surface = VideoWidgetSurface(self) self.Tracking_RubberBand = QRubberBand(QRubberBand.Rectangle, self) self.Censure_RubberBand = QRubberBand(QRubberBand.Rectangle, self) pal = QPalette() pal.setBrush(QPalette.Highlight, QBrush(QColor(Qt.blue))) self.Tracking_RubberBand.setPalette(pal) pal = QPalette() pal.setBrush(QPalette.Highlight, QBrush(QColor(Qt.black))) self.Censure_RubberBand.setPalette(pal) self.var_currentMouseMoveEvent = None self._interaction = InteractionState() self._filterSatate = FilterState() self.setUpdatesEnabled(True) self.snapped = False self.zoomed = False self._isinit = False self.gt = None self.drawCesure = [] self.poly_coordinates, self.drawPtPos, self.drawLines, self.drawRuler, self.drawPolygon = [], [], [], [], [] self.poly_RubberBand = QgsRubberBand(iface.mapCanvas(), True) # Polygon type # set rubber band style color = QColor(176, 255, 128) self.poly_RubberBand.setColor(color) color.setAlpha(190) self.poly_RubberBand.setStrokeColor(color) self.poly_RubberBand.setWidth(3) self.parent = parent.parent() self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) self.setAttribute(Qt.WA_NoSystemBackground) self.setAttribute(Qt.WA_PaintOnScreen) self.setAttribute(Qt.WA_OpaquePaintEvent) self.setAttribute(Qt.WA_DeleteOnClose) palette = self.palette() palette.setColor(QPalette.Background, Qt.black) self.setPalette(palette) self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) self.offset, self.origin, self.pressPos, self.dragPos = QPoint( ), QPoint(), QPoint(), QPoint() self.tapTimer = QBasicTimer() self.zoomPixmap, self.maskPixmap = QPixmap(), QPixmap() def removeLastLine(self): ''' Remove Last Line Objects ''' if len(self.drawLines) > 0: for pt in range(len(self.drawLines) - 1, -1, -1): del self.drawLines[pt] try: if self.drawLines[pt - 1][0] is None: break except Exception: None self.UpdateSurface() AddDrawLineOnMap(self.drawLines) return def removeLastSegmentLine(self): ''' Remove Last Segment Line Objects ''' if len(self.drawLines) > 0: del self.drawLines[-1] self.UpdateSurface() AddDrawLineOnMap(self.drawLines) return def removeAllLines(self): ''' Resets Line List ''' self.drawLines = [] self.UpdateSurface() # Clear all Layer RemoveAllDrawLineOnMap() def ResetDrawRuler(self): ''' Resets Ruler List ''' self.drawRuler = [] def removeAllCensure(self): ''' Remove All Censure Objects ''' self.drawCesure = [] def removeLastCensured(self): ''' Remove Last Censure Objects ''' if len(self.drawCesure) > 0: del self.drawCesure[-1] def removeLastPoint(self): ''' Remove All Point Drawer Objects ''' if len(self.drawPtPos) > 0: del self.drawPtPos[-1] self.UpdateSurface() RemoveLastDrawPointOnMap() return def removeAllPoint(self): ''' Remove All Point Drawer Objects ''' self.drawPtPos = [] self.UpdateSurface() # Clear all Layer RemoveAllDrawPointOnMap() return def removeAllPolygon(self): ''' Remove All Polygon Drawer Objects ''' self.drawPolygon = [] self.UpdateSurface() # Clear all Layer RemoveAllDrawPolygonOnMap() def removeLastPolygon(self): ''' Remove Last Polygon Drawer Objects ''' if len(self.drawPolygon) > 0: for pt in range(len(self.drawPolygon) - 1, -1, -1): del self.drawPolygon[pt] try: if self.drawPolygon[pt - 1][0] is None: break except Exception: None self.UpdateSurface() # remove last index layer RemoveLastDrawPolygonOnMap() def currentMouseMoveEvent(self, event): self.var_currentMouseMoveEvent = event def keyPressEvent(self, event): ''' Exit fullscreen ''' if event.key() == Qt.Key_Escape and self.isFullScreen(): self.setFullScreen(False) event.accept() elif event.key() == Qt.Key_Enter and event.modifiers() & Qt.Key_Alt: self.setFullScreen(not self.isFullScreen()) event.accept() else: super(VideoWidget, self).keyPressEvent(event) def mouseDoubleClickEvent(self, event): """ :type event: QMouseEvent :param event: :return: """ if GetImageHeight() == 0: return if (not vut.IsPointOnScreen(event.x(), event.y(), self.surface)): return if self.gt is not None and self._interaction.lineDrawer: self.drawLines.append([None, None, None]) self.UpdateSurface() return if self.gt is not None and self._interaction.ruler: self.drawRuler.append([None, None, None]) self.UpdateSurface() return if self.gt is not None and self._interaction.polygonDrawer: self.drawPolygon.append([None, None, None]) AddDrawPolygonOnMap(self.poly_coordinates) # Empty RubberBand for _ in range(self.poly_RubberBand.numberOfVertices()): self.poly_RubberBand.removeLastPoint() # Empty List self.poly_coordinates = [] self.UpdateSurface() return self.setFullScreen(not self.isFullScreen()) event.accept() def videoSurface(self): ''' Return video Surface ''' return self.surface def UpdateSurface(self): ''' Update Video Surface ''' self.surface.widget.update() def sizeHint(self): ''' This property holds the recommended size for the widget ''' return self.surface.surfaceFormat().sizeHint() def GetCurrentFrame(self): ''' Return current frame QImage ''' return self.surface.image def SetInvertColor(self, value): ''' Set Invert color filter ''' self._filterSatate.invertColorFilter = value def SetObjectTracking(self, value): ''' Set Object Tracking ''' self._interaction.objectTracking = value def SetRuler(self, value): ''' Set Ruler ''' self._interaction.ruler = value def SetHandDraw(self, value): ''' Set Hand Draw ''' self._interaction.HandDraw = value def SetCensure(self, value): ''' Set Censure Video Parts ''' self._interaction.censure = value def SetGray(self, value): ''' Set gray scale ''' self._filterSatate.grayColorFilter = value def SetMirrorH(self, value): ''' Set Horizontal Mirror ''' self._filterSatate.MirroredHFilter = value def SetEdgeDetection(self, value): ''' Set Canny Edge filter ''' self._filterSatate.edgeDetectionFilter = value def SetAutoContrastFilter(self, value): ''' Set Automatic Contrast filter ''' self._filterSatate.contrastFilter = value def SetMonoFilter(self, value): ''' Set mono filter ''' self._filterSatate.monoFilter = value def RestoreFilters(self): ''' Remove and restore all video filters ''' self._filterSatate.clear() def RestoreDrawer(self): ''' Remove and restore all Drawer Options ''' self._interaction.clear() def paintEvent(self, event): ''' Paint Event ''' self.gt = GetGCPGeoTransform() self.painter = QPainter(self) self.painter.setRenderHint(QPainter.HighQualityAntialiasing) if (self.surface.isActive()): videoRect = self.surface.videoRect() if not videoRect.contains(event.rect()): region = event.region() region.subtracted(QRegion(videoRect)) brush = self.palette().window() for rect in region.rects(): self.painter.fillRect(rect, brush) try: self.painter = self.surface.paint(self.painter) except Exception: None else: self.painter.fillRect(event.rect(), self.palette().window()) try: SetImageSize(self.surface.currentFrame.width(), self.surface.currentFrame.height()) except Exception: None # Draw On Video draw.drawOnVideo(self.drawPtPos, self.drawLines, self.drawPolygon, self.drawRuler, self.drawCesure, self.painter, self.surface, self.gt) # Magnifier Glass if self.zoomed and self._interaction.magnifier: draw.drawMagnifierOnVideo(self.width(), self.height(), self.maskPixmap, self.dragPos, self.zoomPixmap, self.surface, self.painter, self.offset) self.painter.end() return def resizeEvent(self, event): """ :type event: QMouseEvent :param event: :return: """ QWidget.resizeEvent(self, event) self.zoomed = False self.surface.updateVideoRect() def mouseMoveEvent(self, event): """ :type event: QMouseEvent :param event: :return: """ if GetImageHeight() == 0: return # check if the point is on picture (not in black borders) if (not vut.IsPointOnScreen(event.x(), event.y(), self.surface)): return if self._interaction.pointDrawer or self._interaction.polygonDrawer or self._interaction.lineDrawer or self._interaction.ruler: self.setCursor(QCursor(Qt.CrossCursor)) # Cursor Coordinates if self.gt is not None: Longitude, Latitude, Altitude = vut.GetPointCommonCoords( event, self.surface) txt = "<span style='font-size:10pt; font-weight:bold;'>Lon :</span>" txt += "<span style='font-size:9pt; font-weight:normal;'>" + \ ("%.3f" % Longitude) + "</span>" txt += "<span style='font-size:10pt; font-weight:bold;'> Lat :</span>" txt += "<span style='font-size:9pt; font-weight:normal;'>" + \ ("%.3f" % Latitude) + "</span>" if hasElevationModel(): txt += "<span style='font-size:10pt; font-weight:bold;'> Alt :</span>" txt += "<span style='font-size:9pt; font-weight:normal;'>" + \ ("%.0f" % Altitude) + "</span>" else: txt += "<span style='font-size:10pt; font-weight:bold;'> Alt :</span>" txt += "<span style='font-size:9pt; font-weight:normal;'>-</span>" self.parent.lb_cursor_coord.setText(txt) else: self.parent.lb_cursor_coord.setText( "<span style='font-size:10pt; font-weight:bold;'>Lon :</span>" + "<span style='font-size:9pt; font-weight:normal;'>-</span>" + "<span style='font-size:10pt; font-weight:bold;'> Lat :</span>" + "<span style='font-size:9pt; font-weight:normal;'>-</span>" + "<span style='font-size:10pt; font-weight:bold;'> Alt :</span>" + "<span style='font-size:9pt; font-weight:normal;'>-</span>") if not event.buttons(): return if not self.Tracking_RubberBand.isHidden(): self.Tracking_RubberBand.setGeometry( QRect(self.origin, event.pos()).normalized()) if not self.Censure_RubberBand.isHidden(): self.Censure_RubberBand.setGeometry( QRect(self.origin, event.pos()).normalized()) if not self.zoomed: delta = event.pos() - self.pressPos if not self.snapped: self.pressPos = event.pos() self.pan(delta) self.tapTimer.stop() return else: threshold = 10 self.snapped &= delta.x() < threshold self.snapped &= delta.y() < threshold self.snapped &= delta.x() > -threshold self.snapped &= delta.y() > -threshold else: self.dragPos = event.pos() self.surface.updateVideoRect() def pan(self, delta): """ Pan Action (Magnifier method)""" self.offset += delta self.surface.updateVideoRect() def timerEvent(self, _): """ Time Event (Magnifier method)""" if not self.zoomed: self.activateMagnifier() self.surface.updateVideoRect() def mousePressEvent(self, event): """ :type event: QMouseEvent :param event: :return: """ if GetImageHeight() == 0: return if event.button() == Qt.LeftButton: self.snapped = True self.pressPos = self.dragPos = event.pos() self.tapTimer.stop() self.tapTimer.start(100, self) if (not vut.IsPointOnScreen(event.x(), event.y(), self.surface)): self.UpdateSurface() return # point drawer if self.gt is not None and self._interaction.pointDrawer: Longitude, Latitude, Altitude = vut.GetPointCommonCoords( event, self.surface) pointIndex = len(self.drawPtPos) + 1 AddDrawPointOnMap(pointIndex, Longitude, Latitude, Altitude) self.drawPtPos.append([Longitude, Latitude, Altitude]) # polygon drawer if self.gt is not None and self._interaction.polygonDrawer: Longitude, Latitude, Altitude = vut.GetPointCommonCoords( event, self.surface) self.poly_RubberBand.addPoint(QgsPointXY(Longitude, Latitude)) self.poly_coordinates.extend(QgsPointXY(Longitude, Latitude)) self.drawPolygon.append([Longitude, Latitude, Altitude]) # line drawer if self.gt is not None and self._interaction.lineDrawer: Longitude, Latitude, Altitude = vut.GetPointCommonCoords( event, self.surface) self.drawLines.append([Longitude, Latitude, Altitude]) AddDrawLineOnMap(self.drawLines) if self._interaction.objectTracking: self.origin = event.pos() self.Tracking_RubberBand.setGeometry( QRect(self.origin, QSize())) self.Tracking_RubberBand.show() if self._interaction.censure: self.origin = event.pos() self.Censure_RubberBand.setGeometry(QRect( self.origin, QSize())) self.Censure_RubberBand.show() # Ruler drawer if self.gt is not None and self._interaction.ruler: Longitude, Latitude, Altitude = vut.GetPointCommonCoords( event, self.surface) self.drawRuler.append([Longitude, Latitude, Altitude]) # if not called, the paint event is not triggered. self.UpdateSurface() def activateMagnifier(self): """ Activate Magnifier Glass """ self.zoomed = True self.tapTimer.stop() self.surface.updateVideoRect() def SetMagnifier(self, value): """ Set Magnifier Glass """ self._interaction.magnifier = value def SetPointDrawer(self, value): """ Set Point Drawer """ self._interaction.pointDrawer = value def SetLineDrawer(self, value): """ Set Line Drawer """ self._interaction.lineDrawer = value def SetPolygonDrawer(self, value): """ Set Polygon Drawer """ self._interaction.polygonDrawer = value def mouseReleaseEvent(self, _): """ :type event: QMouseEvent :param event: :return: """ if self._interaction.censure: geom = self.Tracking_RubberBand.geometry() self.Censure_RubberBand.hide() self.drawCesure.append([geom]) if self._interaction.objectTracking: geom = self.Tracking_RubberBand.geometry() bbox = (geom.x(), geom.y(), geom.width(), geom.height()) frame = convertQImageToMat(self.GetCurrentFrame()) self.Tracking_RubberBand.hide() self.tracker = cv2.TrackerBoosting_create() self.tracker.clear() ok = self.tracker.init(frame, bbox) if ok: self._isinit = True else: self._isinit = False def leaveEvent(self, _): self.parent.lb_cursor_coord.setText("") self.setCursor(QCursor(Qt.ArrowCursor))
class App(QWidget): #几个传递值的信号 ExposuretimeChangedValue = pyqtSignal(int) GainChangedValue = pyqtSignal(int) plotBlood = pyqtSignal(int, int, int, int) #绘图 laser_computation = pyqtSignal(QImage) camera_height = 2748 camera_width = 3664 laserimage_height = 2748 laserimage_width = 3664 win = pg.GraphicsWindow(title="v distribution") win.resize(500, 300) win.setWindowTitle('blood perfusion') p = win.addPlot(title='blood perfusion') p.setLabel(axis='left', text='v') p.setLabel(axis='bottom', text='number') p.setRange(yRange=[0, 150], padding=0) curve = p.plot(pen='y') def __init__(self): super(App, self).__init__() self.ui = Ui_widget() self.ui.setupUi(self) self.title = 'PyQt5 Video' self.initUI() self.origin = QPoint() self.rubberBand = QRubberBand(QRubberBand.Rectangle, self) self.originX = 0 self.originY = 0 self.endX = 0 self.endY = 0 self.plotFlag = 0 self.plotdata = np.empty(0) self.plotdata_v1 = np.empty(0) self.plotdata_v1_flag = 0 def initUI(self): #相机线程 th = camera_thread.Thread(self) #计算线程 computation_th = computation_thread.Thread(self) self.laser_computation.connect( lambda p: computation_th.receive_image(p)) # 测试用 computation_th.laser_computation_finish.connect( lambda p, x, y: self.setLaserImage(p, x, y)) th.changePixmap.connect( lambda p: self.setPixMap(p)) # 将changePixmap 函数与setPixMap 函数 联系起来 th.laser_posess.connect(lambda p: self.get_under_posess_image(p)) # #显示初始值 self.ui.lineEdit_gain.setText(str(th.Gain)) self.ui.lineEdit_exposuretime.setText(str(th.Exposuretime)) #发送图片 #截取图片 self.ui.label_CaptureFrame.clicked.connect(th.capturePicture) #滑动条修改曝光时间 self.ui.horizontalSlider_SetExposure.setTickPosition( QSlider.TicksBelow) self.ui.horizontalSlider_SetExposure.valueChanged.connect( self.on_changed_ExposureTime) self.ui.horizontalSlider_SetExposure.valueChanged.connect( th.CameraSettingChanged) self.ExposuretimeChangedValue.connect( lambda value: th.CameraSettingExposuretime(value)) #滑动条修改帧率 self.ui.horizontalSlider_Setfps.valueChanged.connect( th.CameraSettingChanged) #滑动条修改增益 self.ui.horizontalSlider_SetGain.setTickPosition(QSlider.TicksBelow) self.ui.horizontalSlider_SetGain.valueChanged.connect( th.CameraSettingChanged) self.ui.horizontalSlider_SetGain.valueChanged.connect( self.on_changed_Gain) self.GainChangedValue.connect( lambda value: th.CameraSettingExposuretime(value)) self.ui.label_OpenCamera.setMouseTracking(True) #plot按钮功能 self.ui.label_PlotBloodFlow.clicked.connect(self.PlotBloodFlow) #不同流速血流计算功能 self.ui.pushButton_v1.setCheckable(True) self.ui.pushButton_v1.toggled.connect(self.changeV1Flag) #还差保持路径、截取间隔、还有截取时间 #开启线程 #画图信号 self.plotBlood.connect( lambda ox, oy, ex, ey: computation_th.getPlotEdge(ox, oy, ex, ey)) th.start() computation_th.start() print("init gui finish") #修改相机曝光时间 def on_changed_ExposureTime(self, value): Exposuretime = self.ui.horizontalSlider_SetExposure.value() self.ui.lineEdit_exposuretime.setText(str(Exposuretime)) print(Exposuretime) # self.plotdata = np.append(self.plotdata, Exposuretime) # print(self.plotdata) # self.curve.setData(self.plotdata) self.ExposuretimeChangedValue.emit(Exposuretime) #修改相机增益 def on_changed_Gain(self, value): Gain = self.ui.horizontalSlider_SetGain.value() self.ui.lineEdit_gain.setText(str(Gain)) print(Gain) self.GainChangedValue.emit(Gain) #显示拍摄图片 def setPixMap(self, p): p = QPixmap.fromImage(p) p = p.scaled(640, 480, Qt.KeepAspectRatio) self.ui.label_VideoDisplay.setPixmap(p) #显示激光散斑图像 def get_under_posess_image(self, p): print("get under posess image") under_posess_image = p print(type(p)) self.laser_computation.emit(under_posess_image) print("send image to thread to possess") # 鼠标响应事件,用来画圈求均值 def mousePressEvent(self, event): if event.button() == Qt.LeftButton: self.origin = QPoint(event.pos()) self.originX = event.x() self.originY = event.y() print(self.originX, self.originY) 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.endX = event.x() self.endY = event.y() print(self.endX, self.endY) # self.rubberBand.hide() #画完之后,获取区域,开始画图 print("change the plotflag") labelOrignX = self.originX - self.ui.label_laserdisplay.x() print(labelOrignX) labelOrignY = self.originY - self.ui.label_laserdisplay.y() labelEndX = self.endX - self.ui.label_laserdisplay.x() labelEndY = self.endY - self.ui.label_laserdisplay.y() print(labelOrignX, labelOrignY, labelEndX, labelEndY) self.plotBlood.emit(labelOrignX, labelOrignY, labelEndX, labelEndY) self.plotFlag = 1 print("change the plotflag") # def PlotBloodFlow(self): # labelOrignX = self.originX - self.ui.label_laserdisplay.x() # labelOrignY = self.originY - self.ui.label_laserdisplay.y() # labelEndX = self.endX - self.ui.label_laserdisplay.x() # labelEndY = self.endY - self.ui.label_laserdisplay.y() def PlotBloodFlow(self): self.plotFlag = 0 self.plotdata = np.empty(0) def setLaserImage(self, p, x, y): #变回mat格式 print('start set laser image') setLaserImage_start = datetime.datetime.now() yVals = y / (640 * 480) p = QPixmap.fromImage(p) p = p.scaled(640, 480, Qt.KeepAspectRatio) # # p = self.cmap2pixmap(laser,'Reds', 50) # # # 再转化为opencv里的图片格式,求完激光散斑再转Qimage显示 plot_start = datetime.datetime.now() self.ui.label_laserdisplay.setPixmap(p) plot_end = datetime.datetime.now() print("plot cost time:", plot_end - plot_start) if (self.plotFlag == 1): if (self.plotdata_v1_flag): self.plotdata_v1 = np.append(self.plotdata_v1, y) # self.plotdata = np.append(self.plotdata, y) self.curve.setData(y) # self.ui.graphicsView.plotItem(y) # plotWidget = pg.plot(title="Three plot curves") # plotWidget.plot(x, yVals) setLaserImage_end = datetime.datetime.now() print('laser image display successfully and time = ', setLaserImage_end - setLaserImage_start) #修改截取时间 def convertQImageToMat(self, p): ''' Converts a QImage into an opencv MAT format ''' convert_start = datetime.datetime.now() p = p.convertToFormat(3) ptr = p.bits() ptr.setsize(p.byteCount()) arr = np.array(ptr).reshape(p.height(), p.width(), 1) convert_end = datetime.datetime.now() print("convertQImageToMat Finished and time =", convert_end - convert_start) return arr ##激光散斑处理 def laser_posess(self, imarray, wsize): posess_start = datetime.datetime.now() # imarray = cv2.cvtColor(imarray, cv2.COLOR_BGR2GRAY) # print(imarray.shape) print('start posess') imarray1 = imarray imarray1 = imarray.reshape(self.laserimage_height, self.laserimage_width) imarray1 = np.array(imarray1).astype(float) # print(imarray1.shape) # print(imarray1.dtype) # 图像类型 #JISUAN immean = ndimage.uniform_filter(imarray1, size=wsize) im2mean = ndimage.uniform_filter(np.square(imarray1), size=wsize) imcontrast = np.sqrt( abs(im2mean - np.square(immean)) / np.square(immean)) temp = 2 * np.square(imcontrast) result = np.real((1 + np.sqrt(1 - temp)) / temp) T1 = 0.0015 v = 1000 * ((780 * 1e-9) / (2 * np.pi * T1)) * result print('speckle computation finished') imcontrast_show = v #把(0,1)to (0,255) # print(np.max(imcontrast_show)) # print(np.min(imcontrast_show)) # print(imcontrast_show.shape) # print(imcontrast_show.dtype) # 图像类型 imcontrast_show = np.array(imcontrast_show).astype(int) imcontrast_show = imcontrast_show.reshape(self.laserimage_height, self.laserimage_width, 1) # imcontrast_show = np.transpose(imcontrast_show, (1, 0, 2)).copy() posess_end = datetime.datetime.now() print('laser_posess finished and time=', posess_end - posess_start) return imcontrast_show def changeV1Flag(self, checked): if checked: self.plotdata_v1_flag = 1 self.plotdata_v1 = np.arange(5) else: print(self.plotdata_v1) file = open('log.txt', 'a') for fp in self.plotdata_v1: file.write(str(fp)) file.write(' ') file.write( '\n-------------------------------------我是分割线-----------------------------------------\n' ) file.close() self.plotdata_v1_flag = 0 self.plotdata_v1 = np.empty(0)
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 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
def showBand(self, *geom): if not self.__band: self.__band = QRubberBand(QRubberBand.Rectangle, parent=self) self.__band.setGeometry(*geom) self.__band.show()
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): rect = self.parent().mapToScene(self.parent().rect()).boundingRect() if not rect.contains(self.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 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 Viewer(QGraphicsView): def __init__(self): QGraphicsView.__init__(self) self.setGeometry(QRect(100, 100, 800, 600)) self.setWindowTitle("PIHDL Graphics Window") # 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.gridlinesx = [self.scene.addLine(-10,-10,10,10, self.gridpen) for n in range(100)] # self.gridlinesy = [self.scene.addLine(-10,-10,10,10, self.gridpen) for n in range(100)] self.initialize() def itemsBoundingRect_nogrid(self): self.remove_grid() r = self.scene.itemsBoundingRect() self.create_grid() return r 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) sr = self.itemsBoundingRect_nogrid() ymax = sr.top() xmin = sr.left() width = sr.width() height = sr.height() self.scene.setSceneRect( QRectF(xmin - 2 * width, ymax - 2 * height, width * 5, height * 5)) def reset_view(self): self.fitInView(self.itemsBoundingRect_nogrid(), Qt.KeepAspectRatio) 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] # x,y = port.midpoint[0], port.midpoint[1] # x,y = x - qtext.boundingRect().width()/2, y - qtext.boundingRect().height()/2 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.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 # print('\n xmin = %s, xmax = %s, ymin = %s, ymax = %s' % (xmin, xmax, ymin, ymax)) # print('Starting at x = %s' % x) # print('Starting at y = %s' % y) 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 def create_grid(self): self.gridlinesx = [ self.scene.addLine(-10, -10, 10, 10, self.gridpen) for n in range(200) ] self.gridlinesy = [ self.scene.addLine(-10, -10, 10, 10, self.gridpen) for n in range(200) ] self.update_grid() def remove_grid(self): for gl in self.gridlinesx + self.gridlinesy: self.scene.removeItem(gl) self.gridlinesx == [] self.gridlinesy == [] #============================================================================== # 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 actual_rect = self.mapToScene(self.rect()) bbox_size = actual_rect[0] - actual_rect[2] actual_width = abs(bbox_size.x()) actual_height = abs(bbox_size.y()) max_width = abs(self.scene.sceneRect().x() * 3) max_height = abs(self.scene.sceneRect().y() * 3) min_width = 1 min_height = 1 if ((actual_width > max_width) or (actual_height > max_height)) and (zoom_factor < 1): pass elif ((actual_width < min_width) or (actual_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): self.scale(zoom_factor, zoom_factor) self.zoom_factor_total *= zoom_factor def mousePressEvent(self, 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): if not self._rb_origin.isNull( ) and self._mousePressed == Qt.RightButton: self.rubberBand.setGeometry( QRect(self._rb_origin, event.pos()).normalized()) 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)