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 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)
class DragWidget(QWidget): spacerX = 16 spacerY = 16 clipicon = None new_window_signal = pyqtSignal(str) query = pyqtSignal() src_dragwidget = None src_selected = [] def __init__(self, path, parent=None): super(DragWidget, self).__init__(parent) self.setMinimumSize(400, 200) self.setAcceptDrops(True) self.parent = parent # self.parent.menu.connect(self.delete_icon) self.modifier = False self.rubberband = QRubberBand(QRubberBand.Rectangle, self) self.path = path self.icons = [] self.icon_offsetx = 0 self.icon_offsety = 0 # self.clipicon = None # self.moving_icons = [] self.read_drawer() self.clean_up() # print(type(IconWidget.icon)) # print(self.findChildren(ClickableIcon)) def read_drawer(self): # self.icons.clear() for item in os.scandir(self.path): if item.is_dir(): icon_widget = IconWidget(parent=self, name=item.name, path=self.path, dir=True) else: icon_widget = IconWidget(parent=self, name=item.name, path=self.path, dir=False) icon_widget.new_window.connect(self.new_window_signal.emit) icon_widget.clipboard.connect(self.on_clipboard) self.icons.append(icon_widget) self.icons[-1].setAttribute(Qt.WA_DeleteOnClose) # self.update() def updateScrollArea(self): """ set the dimension of the widget """ iconx = [] icony = [] if len(self.icons) > 0: for item in self.icons: iconx.append(item.x()) icony.append(item.y()) self.setMinimumWidth(max(iconx)+75) self.setMinimumHeight(max(icony)+75) def dragEnterEvent(self, event): event.accept() def dragMoveEvent(self, event): event.accept() def get_dnd_list(self, event): icon_list = [] icon_offsetx = None icon_offsety = None if len(DragWidget.src_selected) > 0: for item in DragWidget.src_selected: icon_list.append(item) else: icon_list.append(event.source()) return icon_list def create_icon(self, name, drawer): if drawer: icon_widget = IconWidget(self, name=name, path=self.path, dir=True) else: icon_widget = IconWidget(self, name=name, path=self.path, dir=False) icon_widget.new_window.connect(self.new_window_signal.emit) self.icons.append(icon_widget) def place_icon(self, x, y): self.icons[-1].move(x, y) self.icons[-1].show() def dropEvent(self, event): event.accept() icon_list = self.get_dnd_list(event) icon_offsetx = event.pos().x() icon_offsety = event.pos().y() for item in icon_list: name = item.name drawer = item.drawer src_path = item.path + "/" + name dst_path = self.path + "/" if event.mimeData().hasFormat("application/x-icon"): self.create_icon(name, drawer) self.move_data(src_path, dst_path) self.place_icon(icon_offsetx, icon_offsety) icon_offsetx += 100 if icon_offsetx > self.window().width(): icon_offsetx = event.pos().x() icon_offsety += 75 icon_offsetx = None icon_offsety = None self.updateScrollArea() def clear_dnd(self): DragWidget.src_dragwidget = None DragWidget.src_selected.clear() def get_modifier(self): return self.parent.modifier def mousePressEvent(self, event): if event.buttons() == Qt.LeftButton: for item in self.icons: item.icon.deselect_icon() self.clear_dnd() self.origin = event.pos() self.rubberband.setGeometry(QRect(self.origin, QSize())) self.rubberband.show() def mouseMoveEvent(self, event): if self.rubberband.isVisible(): self.rubberband.setGeometry( QRect(self.origin, event.pos()).normalized()) # QWidget.mouseMoveEvent(self, event) def mouseReleaseEvent(self, event): self.clear_dnd() if self.rubberband.isVisible(): self.rubberband.hide() rect = self.rubberband.geometry() for child in self.findChildren(IconWidget): if rect.intersects(child.geometry()): child.icon.select_icon() DragWidget.src_selected.append(child) if DragWidget.src_dragwidget is not self: DragWidget.src_dragwidget = self def mouseDoubleClickEvent(self, event): print(BLU, "Double Click", END) self.query.emit() def create_file(self): new_file = self.path + "/" + "newfile.txt" open(new_file, 'w').close() icon_widget = IconWidget(self, name="newfile.txt", path=self.path, dir=False) icon_widget.new_window.connect(self.new_window_signal.emit) icon_widget.show() self.icons.append(icon_widget) def create_drawer(self): print("creating new drawer") new_drawer = self.path + "/" + "NewDrawer" os.makedirs(new_drawer) icon_widget = IconWidget(self, name="NewDrawer", path=self.path, dir=True) icon_widget.new_window.connect(self.new_window_signal.emit) icon_widget.show() self.icons.append(icon_widget) def rename_file(self): print("renaming file") def clean_up(self): for item in self.icons: item.move(DragWidget.spacerX, DragWidget.spacerY) # initial icon placement DragWidget.spacerX += 100 if DragWidget.spacerX + 100 > self.window().width(): DragWidget.spacerY += 75 DragWidget.spacerX = 16 # reset placement values DragWidget.spacerX = 16 DragWidget.spacerY = 16 self.updateScrollArea() def move_data(self, source, dest): srce_path = source.rsplit('/', 1)[0] dest_path = dest.rsplit('/', 1)[0] if srce_path != dest_path: try: shutil.move(source, dest) except Exception as err: print(err) def copy_icon(self, source, dest): pass def delete_icon(self): dest = os.path.expanduser("~") + "/.Trash/" error_string = "" for item in self.icons: if item.icon.selected: source = item.path + "/" + item.name if source is not "": try: shutil.move(source, dest) except Exception as err: error_string += str(err) + "\n" + "\n" else: self.icons.remove(item) item.deleteLater() if error_string is not "": QMessageBox.information(self, 'Info', error_string, QMessageBox.Ok) def paste_icon(self): print("---") print("srce=", self.clipicon.path + "/" + self.clipicon.name) # print("res=", self.clipicon.path + "/" + self.clipicon.name) print("dest=", self.path + "/") # if os.path.isdir(os.path.join(self.clipicon.path, self.clipicon.name)): SRCE = self.clipicon.path + "/" + self.clipicon.name DEST = self.path+"/" + self.clipicon.name shutil.copytree(SRCE, DEST) def on_clipboard(self, icon): print("realpath", self.path) print("clip_icon_name=", icon.name) DragWidget.clipicon = icon
class 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 ImageView(QGraphicsView): imageChanged = pyqtSignal() def __init__(self, *args, **kwargs): QGraphicsView.__init__(self, *args, **kwargs) scene = QGraphicsScene(self) self.graphics_pixmap = QGraphicsPixmapItem() scene.addItem(self.graphics_pixmap) self.zoom_factor = 1.5 self.setScene(scene) self.start_drag = QPoint() self.rubberBand = None self.panning = False self.first_show_occured = False self.last_scene_roi = None @property def pixmap(self): return self.graphics_pixmap.pixmap() @pixmap.setter def pixmap(self, image, image_format=None): pixmap = None if have_numpy and isinstance(image, np.ndarray): if image.ndim == 3: if image.shape[2] == 3: if image_format is None: image_format = QImage.Format_RGB888 q_image = QImage(image.data, image.shape[1], image.shape[0], image_format) pixmap = QPixmap.fromImage( q_image ) #note this copies the data from the QImage referencing image original data elif image.shape[2] == 4: if image_format is None: image_format = QImage.Format_RGB32 q_image = QImage(image.data, image.shape[1], image.shape[0], image_format) pixmap = QPixmap.fromImage( q_image ) #note this copies the data from the QImage referencing image original data else: raise TypeError(image) elif image.ndim == 2: image_rgb = np.dstack((image, image, image)) if image_format is None: image_format = QImage.Format_RGB888 q_image = QImage(image.data, image.shape[1], image.shape[0], image_format) pixmap = QPixmap.fromImage( q_image ) #note this copies the data from the QImage referencing original image else: raise ValueError(image) elif isinstance(image, QImage): pixmap = QPixmap.fromImage(image) elif isinstance(image, QPixmap): pixmap = image else: raise TypeError(image) self.graphics_pixmap.setPixmap(pixmap) self.setSceneDims() #self.fitInView() self.graphics_pixmap.update() self.imageChanged.emit() #image property alias @property def image(self): return self.pixmap @image.setter def image(self, image): self.pixmap = image def setSceneDims(self): pixmap = self.pixmap self.setSceneRect( QRectF( #-QPointF(pixmap.width(), pixmap.height())/2, 1.5*QPointF(pixmap.width(), pixmap.height()) QPointF(0, 0), QPointF(pixmap.width(), pixmap.height()))) @property def image_scene_rect(self): return QRectF(self.graphics_pixmap.pos(), QSizeF(self.pixmap.size())) def resizeEvent(self, event): QGraphicsView.resizeEvent(self, event) self.setSceneDims() event.accept() #self.reset() #self.fitInView(roi, Qt.KeepAspectRatio) self.fitInView(self.last_scene_roi, Qt.KeepAspectRatio) self.update() def zoomROICentered(self, p, zoom_level_delta): pixmap = self.graphics_pixmap.pixmap() roi = self.current_scene_ROI roi_dims = QPointF(roi.width(), roi.height()) roi_scalef = 1 if zoom_level_delta > 0: roi_scalef = 1 / self.zoom_factor elif zoom_level_delta < 0: roi_scalef = self.zoom_factor nroi_dims = roi_dims * roi_scalef nroi_dims.setX(max(nroi_dims.x(), 1)) nroi_dims.setY(max(nroi_dims.y(), 1)) if nroi_dims.x() > self.pixmap.size().width() or nroi_dims.y( ) > self.pixmap.size().height(): self.reset() else: nroi_center = p nroi_dimsh = nroi_dims / 2 nroi_topleft = nroi_center - nroi_dimsh nroi = QRectF(nroi_topleft.x(), nroi_topleft.y(), nroi_dims.x(), nroi_dims.y()) self.fitInView(nroi, Qt.KeepAspectRatio) self.update() def zoomROITo(self, p, zoom_level_delta): pixmap = self.graphics_pixmap.pixmap() roi = self.current_scene_ROI roi_dims = QPointF(roi.width(), roi.height()) roi_topleft = roi.topLeft() roi_scalef = 1 if zoom_level_delta > 0: roi_scalef = 1 / self.zoom_factor elif zoom_level_delta < 0: roi_scalef = self.zoom_factor nroi_dims = roi_dims * roi_scalef nroi_dims.setX(max(nroi_dims.x(), 1)) nroi_dims.setY(max(nroi_dims.y(), 1)) if nroi_dims.x() > self.pixmap.size().width() or nroi_dims.y( ) > self.pixmap.size().height(): self.reset() else: prel_scaled_x = (p.x() - roi_topleft.x()) / roi_dims.x() prel_scaled_y = (p.y() - roi_topleft.y()) / roi_dims.y() nroi_topleft_x = p.x() - prel_scaled_x * nroi_dims.x() nroi_topleft_y = p.y() - prel_scaled_y * nroi_dims.y() nroi = QRectF(nroi_topleft_x, nroi_topleft_y, nroi_dims.x(), nroi_dims.y()) self.fitInView(nroi, Qt.KeepAspectRatio) self.update() def _scene_ROI(self, geometry): return QRectF(self.mapToScene(geometry.topLeft()), self.mapToScene(geometry.bottomRight())) @property def current_scene_ROI(self): return self.last_scene_roi #return self._scene_ROI(self.viewport().geometry()) def mousePressEvent(self, event): QGraphicsView.mousePressEvent(self, event) button = event.button() modifier = event.modifiers() #pan if modifier == Qt.ControlModifier and button == Qt.LeftButton: self.start_drag = event.pos() self.panning = True #initiate/show ROI selection if modifier == Qt.ShiftModifier and button == Qt.LeftButton: self.start_drag = event.pos() if self.rubberBand is None: self.rubberBand = QRubberBand(QRubberBand.Rectangle, self.viewport()) self.rubberBand.setGeometry(QRect(self.start_drag, QSize())) self.rubberBand.show() def mouseMoveEvent(self, event): QGraphicsView.mouseMoveEvent(self, event) #update selection display if self.rubberBand is not None: self.rubberBand.setGeometry( QRect(self.start_drag, event.pos()).normalized()) if self.panning: scene_end_drag = self.mapToScene(event.pos()) end_drag = event.pos() pan_vector = end_drag - self.start_drag scene2view = self.transform() #skip shear sx = scene2view.m11() sy = scene2view.m22() dx = scene2view.dx() dy = scene2view.dy() scene_pan_vector = QPointF(pan_vector.x() / sx, pan_vector.y() / sy) roi = self.current_scene_ROI top_left = roi.topLeft() new_top_left = top_left - scene_pan_vector scene_rect = self.sceneRect() new_top_left.setX( clamp(new_top_left.x(), scene_rect.left(), scene_rect.right())) new_top_left.setY( clamp(new_top_left.y(), scene_rect.top(), scene_rect.bottom())) nroi = QRectF(new_top_left, roi.size()) self.fitInView(nroi, Qt.KeepAspectRatio) self.start_drag = end_drag self.update() def mouseReleaseEvent(self, event): QGraphicsView.mouseReleaseEvent(self, event) #consume rubber band selection if self.rubberBand is not None: self.rubberBand.hide() #set view to ROI rect = self.rubberBand.geometry().normalized() if rect.width() > 5 and rect.height() > 5: roi = QRectF(self.mapToScene(rect.topLeft()), self.mapToScene(rect.bottomRight())) self.fitInView(roi, Qt.KeepAspectRatio) self.rubberBand = None if self.panning: self.panning = False self.update() def wheelEvent(self, event): dy = event.angleDelta().y() update = False #adjust zoom if abs(dy) > 0: scene_pos = self.mapToScene(event.pos()) sign = 1 if dy >= 0 else -1 self.zoomROITo(scene_pos, sign) def keyPressEvent(self, event): pass def showEvent(self, event): QGraphicsView.showEvent(self, event) if event.spontaneous(): return if not self.first_show_occured: self.first_show_occured = True self.reset() def reset(self): self.setSceneDims() self.fitInView(self.image_scene_rect, Qt.KeepAspectRatio) self.update() #override arbitrary and unwanted margins: https://bugreports.qt.io/browse/QTBUG-42331 - based on QT sources def fitInView(self, rect, flags=Qt.IgnoreAspectRatio): if self.scene() is None or rect.isNull(): return self.last_scene_roi = rect unity = self.transform().mapRect(QRectF(0, 0, 1, 1)) self.scale(1 / unity.width(), 1 / unity.height()) viewRect = self.viewport().rect() sceneRect = self.transform().mapRect(rect) xratio = viewRect.width() / sceneRect.width() yratio = viewRect.height() / sceneRect.height() if flags == Qt.KeepAspectRatio: xratio = yratio = min(xratio, yratio) elif flags == Qt.KeepAspectRatioByExpanding: xratio = yratio = max(xratio, yratio) self.scale(xratio, yratio) self.centerOn(rect.center())
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)
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 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
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 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 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 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.drawPtPos = [] self.drawLines = [] 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 def GetXRatio(self): ''' ratio between event.x() and real image width on screen. ''' return GetImageWidth() / (self.surface.widget.width() - (2 * self.GetXBlackZone())) def GetYRatio(self): ''' ratio between event.y() and real image height on screen. ''' 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 #Draw clicked points on video for pt in self.drawPtPos: #adds a mark on the video self.drawPointOnVideo(pt) #Draw clicked lines on video for pt in self.drawLines: #adds a mark on the video self.drawLinesOnVideo(pt) # 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 GetInverseMatrix(self, x, y): ''' inverse matrix transformation (lon-lat to video units x,y) ''' transf = (~self.gt)([x, y]) scr_x = (transf[0] / self.GetXRatio()) + self.GetXBlackZone() scr_y = (transf[1] / self.GetYRatio()) + self.GetYBlackZone() return scr_x, scr_y def drawLinesOnVideo(self, pt): ''' Draw Lines on Video ''' scr_x, scr_y = self.GetInverseMatrix(pt[1], pt[0]) radius = 3 center = QPoint(scr_x, scr_y) pen = QPen(Qt.yellow) pen.setWidth(radius) pen.setCapStyle(Qt.RoundCap) pen.setDashPattern([1, 4, 5, 4]) painter_p = QPainter(self) painter_p.setPen(pen) painter_p.setRenderHint(QPainter.HighQualityAntialiasing, True) painter_p.drawPoint(center) if len(self.drawLines) > 1: try: idx = self.drawLines.index(pt) scr_x, scr_y = self.GetInverseMatrix( self.drawLines[idx + 1][1], self.drawLines[idx + 1][0]) end = QPoint(scr_x, scr_y) painter_p.drawLine(center, end) except: None return def drawPointOnVideo(self, pt): scr_x, scr_y = self.GetInverseMatrix(pt[1], pt[0]) radius = 10 center = QPoint(scr_x, scr_y) pen = QPen(Qt.red) pen.setWidth(radius) pen.setCapStyle(Qt.RoundCap) painter_p = QPainter(self) painter_p.setPen(pen) painter_p.setRenderHint(QPainter.HighQualityAntialiasing, True) painter_p.drawPoint(center) 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: """ # check if the point is on picture (not in black borders) if (not self.IsPointOnScreen(event.x(), event.y())): return # # Draw Line on the fly # if self.gt is not None and lineDrawer: # if len(self.drawLines) > 0: # scr_x, scr_y = self.GetInverseMatrix(self.drawLines[-1][1],self.drawLines[-1][1]) # # radius = 3 # center = QPoint(scr_x, scr_y) # # pen = QPen(Qt.yellow) # pen.setWidth(radius) # pen.setCapStyle(Qt.RoundCap) # pen.setDashPattern([1, 4, 5, 4]) # painter_p = QPainter(self) # painter_p.setPen(pen) # painter_p.setRenderHint(QPainter.HighQualityAntialiasing, True) # painter_p.drawPoint(center) # # try: # transf = self.gt([(event.x() - self.GetXBlackZone()) * self.GetXRatio(), (event.y() - self.GetYBlackZone()) * self.GetYRatio()]) # scr_x, scr_y = self.GetInverseMatrix(float(round(transf[1], 4)) , float(round(transf[0], 4))) # end = QPoint(scr_x, scr_y) # painter_p.drawLine(center, end) # self.UpdateSurface() # except: # None # Cursor Coordinates if self.gt is not None: 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() 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) #point drawer if self.gt is not None and pointDrawer: if (not self.IsPointOnScreen(event.x(), event.y())): return transf = self.gt([ (event.x() - self.GetXBlackZone()) * self.GetXRatio(), (event.y() - self.GetYBlackZone()) * self.GetYRatio() ]) #targetAlt = GetFrameCenter()[2] Longitude = float(round(transf[1], 4)) Latitude = float(round(transf[0], 4)) #Altitude = targetAlt Altitude = 0.0 #add pin point on the map pointLyr = qgsu.selectLayerByName(Point_lyr) pointLyr.startEditing() feature = QgsFeature() feature.setAttributes([Longitude, Latitude, Altitude]) p = QgsPointXY() p.set(Longitude, Latitude) geom = QgsGeometry.fromPointXY(p) feature.setGeometry(geom) pointLyr.addFeatures([feature]) CommonLayer(pointLyr) self.drawPtPos.append([Longitude, Latitude]) #if not called, the paint event is not triggered. self.UpdateSurface() #line drawer if self.gt is not None and lineDrawer: if (not self.IsPointOnScreen(event.x(), event.y())): return transf = self.gt([ (event.x() - self.GetXBlackZone()) * self.GetXRatio(), (event.y() - self.GetYBlackZone()) * self.GetYRatio() ]) #targetAlt = GetFrameCenter()[2] Longitude = float(round(transf[1], 4)) Latitude = float(round(transf[0], 4)) #Altitude = targetAlt Altitude = 0.0 #add pin point on the map linelyr = qgsu.selectLayerByName(Line_lyr) linelyr.startEditing() feature = QgsFeature() f = QgsFeature() if linelyr.featureCount() == 0: f.setAttributes([Longitude, Latitude, Altitude]) surface = QgsGeometry.fromPolylineXY([ QgsPointXY(Longitude, Latitude), QgsPointXY(Longitude, Latitude) ]) f.setGeometry(surface) linelyr.addFeatures([f]) else: f_last = linelyr.getFeature(linelyr.featureCount()) f.setAttributes([Longitude, Latitude, Altitude]) surface = QgsGeometry.fromPolylineXY([ QgsPointXY(Longitude, Latitude), QgsPointXY(f_last.attribute(0), f_last.attribute(1)) ]) f.setGeometry(surface) linelyr.addFeatures([f]) CommonLayer(linelyr) self.drawLines.append([Longitude, Latitude]) #if not called, the paint event is not triggered. self.UpdateSurface() 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 SetPointDrawer(self, value): """ Set Point Drawer """ global pointDrawer pointDrawer = value def SetLineDrawer(self, value): """ Set Line Drawer """ global lineDrawer lineDrawer = 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 : make zoom rectangle functionality 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 View(QGraphicsView): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.rubberBandActive = False self.rubberBand = QRubberBand(QRubberBand.Rectangle, self) self.baseRect = QRect() self.clearSelection() self.setStyleSheet("border: 1px solid black") # Returns a QRect that covers the scene tile the coords are over def _getBox(self, pos): scenePos = self.mapToScene(pos) newX = scenePos.x() // 8 newY = scenePos.y() // 8 topLeft = QPointF(8*newX, 8*newY) bottomRight = topLeft+QPointF(8.0,8.0) return QRect(self.mapFromScene(topLeft), self.mapFromScene(bottomRight)) def setRect(self): self.setTransform(QTransform()) self.scale(self.viewport().width() / max(1, self.sceneRect().width()), self.viewport().height() / max(1, self.sceneRect().height())) def toggleBand(self): self.rubberBandActive = not self.rubberBandActive if self.rubberBandActive: self.scene().cursor.setVisible(False) else: self.clearSelection() def clearSelection(self): self.rubberBand.setGeometry(QRect()) def selection(self): return self.rubberBand.geometry() def sceneSelection(self): topLeft = self.rubberBand.geometry().topLeft() topLeft = self.mapToScene(topLeft) bottomRight = self.rubberBand.geometry().bottomRight() bottomRight = self.mapToScene(bottomRight) return QRectF(topLeft, bottomRight) def mousePressEvent(self, e): if self.rubberBandActive: if e.button() == Qt.LeftButton: if self.rubberBand.geometry() == QRect(): self.baseRect = self._getBox(e.pos()) self.rubberBand.setGeometry(self.baseRect) self.rubberBand.show() else: self.clearSelection() else: super().mousePressEvent(e) print(e.pos()) print(self.mapToScene(e.pos())) def mouseMoveEvent(self, e): if self.rubberBandActive: if int(e.buttons()) & Qt.LeftButton and self.rubberBand.geometry() != QRect(): newRect = self._getBox(e.pos()) self.rubberBand.setGeometry(self.baseRect.united(newRect)) else: super().mouseMoveEvent(e) def mouseReleaseEvent(self, e): super().mouseReleaseEvent(e) def mouseDoubleClickEvent(self, e): if not self.rubberBandActive: super().mouseDoubleClickEvent(e) # self.clearSelection() def paintEvent(self, e): super().paintEvent(e) painter = QPainter(self.viewport()) painter.end()
class ImageViewer(QGraphicsView, QObject): points_selection_sgn = pyqtSignal(list) key_press_sgn = pyqtSignal(QtGui.QKeyEvent) def __init__(self, parent=None): super(ImageViewer, self).__init__(parent) self.setDragMode(QGraphicsView.ScrollHandDrag) self.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform) self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) self.setResizeAnchor(QGraphicsView.AnchorUnderMouse) self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) self._scene = ImageViewerScene(self) self.setScene(self._scene) self._image = None self._image_original = None self._pixmap = None self._img_contrast = 1.0 self._img_brightness = 50.0 self._img_gamma = 1.0 self._create_grid() self._channels = [] self._current_tool = SELECTION_TOOL.POINTER self._dataset = None # create grid lines pen_color = QColor(255, 255, 255, 255) pen = QPen(pen_color) pen.setWidth(2) pen.setStyle(QtCore.Qt.DotLine) self.vline = QGraphicsLineItem() self.vline.setVisible(False) self.vline.setPen(pen) self.hline = QGraphicsLineItem() self.hline.setVisible(False) self.hline.setPen(pen) self._scene.addItem(self.vline) self._scene.addItem(self.hline) self._current_label = None # rectangle selection tool self._rectangle_tool_origin = QPoint() self._rectangle_tool_picker = QRubberBand(QRubberBand.Rectangle, self) # polygon selection tool app = QApplication.instance() color = app.palette().color(QPalette.Highlight) self._polygon_guide_line_pen = QPen(color) self._polygon_guide_line_pen.setWidth(3) self._polygon_guide_line_pen.setStyle(QtCore.Qt.DotLine) self._polygon_guide_line = QGraphicsLineItem() self._polygon_guide_line.setVisible(False) self._polygon_guide_line.setPen(self._polygon_guide_line_pen) self._scene.addItem(self._polygon_guide_line) self._current_polygon = None # circle self._current_ellipse = None # free selection tool self._current_free_path = None self._is_drawing = False self._last_point_drawn = QPoint() self._last_click_point = None self._free_Path_pen = QPen(color) self._free_Path_pen.setWidth(10) self._extreme_points = Queue(maxsize=4) @property def current_label(self): return self._current_label @current_label.setter def current_label(self, value): self._current_label = value if self._current_label: color = QColor(self._current_label.color) self._free_Path_pen.setColor(color) self._polygon_guide_line_pen.setColor(color) self._polygon_guide_line.setPen(self._polygon_guide_line_pen) @property def dataset(self): return self._dataset @dataset.setter def dataset(self, value): self._dataset = value @property def img_contrast(self): return self._img_contrast @img_contrast.setter def img_contrast(self, value): self._img_contrast = value @property def img_gamma(self): return self._img_gamma @img_gamma.setter def img_gamma(self, value): self._img_gamma = value @property def img_brightness(self): return self._img_brightness @img_brightness.setter def img_brightness(self, value): self._img_brightness = value @property def image(self): return self._image @image.setter def image(self, value): self._image = value self._image_original = value.copy() self.update_viewer() @property def pixmap(self) -> ImagePixmap: return self._pixmap @gui_exception def update_viewer(self, fit_image=True): rgb = cv2.cvtColor(self._image, cv2.COLOR_BGR2RGB) rgb = ImageUtilities.adjust_image(rgb, self._img_contrast, self._img_brightness) rgb = ImageUtilities.adjust_gamma(rgb, self._img_gamma) pil_image = Image.fromarray(rgb) qppixmap_image = pil_image.toqpixmap() x, y = -qppixmap_image.width() / 2, -qppixmap_image.height() / 2 if self._pixmap: self._pixmap.resetTransform() self._pixmap.setPixmap(qppixmap_image) self._pixmap.setOffset(x, y) else: self._pixmap = ImagePixmap() self._pixmap.setPixmap(qppixmap_image) self._pixmap.setOffset(x, y) self._scene.addItem(self._pixmap) self._pixmap.signals.hoverEnterEventSgn.connect( self.pixmap_hoverEnterEvent_slot) self._pixmap.signals.hoverLeaveEventSgn.connect( self.pixmap_hoverLeaveEvent_slot) self._pixmap.signals.hoverMoveEventSgn.connect( self.pixmap_hoverMoveEvent_slot) self._hide_guide_lines() if fit_image: self.fit_to_window() @gui_exception def reset_viewer(self): self._img_contrast = 1.0 self._img_brightness = 50.0 self._img_gamma = 1.0 self._image = self._image_original.copy() @gui_exception def equalize_histogram(self): self._image = ImageUtilities.histogram_equalization(self._image) @gui_exception def correct_lightness(self): self._image = ImageUtilities.correct_lightness(self._image) def clusterize(self, k): self._image = ImageUtilities.kmeans(self._image.copy(), k) @property def current_tool(self): return self._current_tool @current_tool.setter def current_tool(self, value): self._polygon_guide_line.hide() self._current_polygon = None self._current_free_path = None self._current_ellipse = None self._is_drawing = value == SELECTION_TOOL.FREE self._current_tool = value self.clear_extreme_points() if value == SELECTION_TOOL.POINTER: self.enable_items(True) else: self.enable_items(False) def fit_to_window(self): if not self._pixmap or not self._pixmap.pixmap(): return self.resetTransform() self.setTransform(QtGui.QTransform()) self.fitInView(self._pixmap, QtCore.Qt.KeepAspectRatio) def _create_grid(self, gridSize=15): app: QApplication = QApplication.instance() curr_theme = "dark" if app: curr_theme = app.property("theme") if curr_theme == "light": color1 = QtGui.QColor("white") color2 = QtGui.QColor(237, 237, 237) else: color1 = QtGui.QColor(20, 20, 20) color2 = QtGui.QColor(0, 0, 0) backgroundPixmap = QtGui.QPixmap(gridSize * 2, gridSize * 2) backgroundPixmap.fill(color1) painter = QtGui.QPainter(backgroundPixmap) painter.fillRect(0, 0, gridSize, gridSize, color2) painter.fillRect(gridSize, gridSize, gridSize, gridSize, color2) painter.end() self._scene.setBackgroundBrush(QtGui.QBrush(backgroundPixmap)) def wheelEvent(self, event: QWheelEvent): adj = (event.angleDelta().y() / 120) * 0.1 self.scale(1 + adj, 1 + adj) @gui_exception def keyPressEvent(self, event: QKeyEvent): if event.key() == QtCore.Qt.Key_Space: image_rect: QRectF = self._pixmap.sceneBoundingRect() if self.current_tool == SELECTION_TOOL.POLYGON and self._current_polygon: points = self._current_polygon.points self._polygon_guide_line.hide() self.setDragMode(QGraphicsView.ScrollHandDrag) if len(points) <= 2: self._current_polygon.delete_item() self.current_tool = SELECTION_TOOL.POINTER elif self.current_tool == SELECTION_TOOL.EXTREME_POINTS and \ self._extreme_points.full(): points = [] image_offset = QPointF(image_rect.width() / 2, image_rect.height() / 2) for pt in self._extreme_points.queue: pt: EditablePolygonPoint center = pt.sceneBoundingRect().center() x = math.floor(center.x() + image_offset.x()) y = math.floor(center.y() + image_offset.y()) points.append([x, y]) self.points_selection_sgn.emit(points) self.current_tool = SELECTION_TOOL.POINTER else: event.ignore() # guide lines events def _show_guide_lines(self): if self.hline and self.vline: self.hline.show() self.vline.show() def _hide_guide_lines(self): if self.hline and self.vline: self.hline.hide() self.vline.hide() def _update_guide_lines(self, x, y): bbox: QRect = self._pixmap.boundingRect() offset = QPointF(bbox.width() / 2, bbox.height() / 2) self.vline.setLine(x, -offset.y(), x, bbox.height() - offset.y()) self.vline.setZValue(1) self.hline.setLine(-offset.x(), y, bbox.width() - offset.x(), y) self.hline.setZValue(1) def pixmap_hoverMoveEvent_slot(self, evt: QGraphicsSceneHoverEvent, x, y): self._update_guide_lines(x, y) def pixmap_hoverEnterEvent_slot(self): self._show_guide_lines() def pixmap_hoverLeaveEvent_slot(self): self._hide_guide_lines() def delete_polygon_slot(self, polygon: EditablePolygon): self._current_polygon = None self.current_tool = SELECTION_TOOL.POINTER self._polygon_guide_line.hide() @gui_exception def mousePressEvent(self, evt: QtGui.QMouseEvent) -> None: image_rect: QRectF = self._pixmap.boundingRect() mouse_pos = self.mapToScene(evt.pos()) if evt.buttons() == QtCore.Qt.LeftButton: if self.current_tool == SELECTION_TOOL.BOX: # create rectangle self.setDragMode(QGraphicsView.NoDrag) self._rectangle_tool_origin = evt.pos() geometry = QRect(self._rectangle_tool_origin, QSize()) self._rectangle_tool_picker.setGeometry(geometry) self._rectangle_tool_picker.show() elif self.current_tool == SELECTION_TOOL.POLYGON: if image_rect.contains(mouse_pos): if self._current_polygon is None: self._current_polygon = EditablePolygon() self._current_polygon.label = self._current_label self._current_polygon.tag = self._dataset self._current_polygon.signals.deleted.connect( self.delete_polygon_slot) self._scene.addItem(self._current_polygon) self._current_polygon.addPoint(mouse_pos) else: self._current_polygon.addPoint(mouse_pos) elif self.current_tool == SELECTION_TOOL.ELLIPSE: if image_rect.contains(mouse_pos): self.setDragMode(QGraphicsView.NoDrag) ellipse_rec = QtCore.QRectF(mouse_pos.x(), mouse_pos.y(), 0, 0) self._current_ellipse = EditableEllipse() self._current_ellipse.tag = self.dataset self._current_ellipse.label = self._current_label self._current_ellipse.setRect(ellipse_rec) self._scene.addItem(self._current_ellipse) elif self.current_tool == SELECTION_TOOL.FREE: # consider only the points into the image if image_rect.contains(mouse_pos): self.setDragMode(QGraphicsView.NoDrag) self._last_point_drawn = mouse_pos self._current_free_path = QGraphicsPathItem() self._current_free_path.setOpacity(0.6) self._current_free_path.setPen(self._free_Path_pen) painter = QPainterPath() painter.moveTo(self._last_point_drawn) self._current_free_path.setPath(painter) self._scene.addItem(self._current_free_path) elif self.current_tool == SELECTION_TOOL.EXTREME_POINTS: if image_rect.contains(mouse_pos): if not self._extreme_points.full(): def delete_point(idx): del self._extreme_points.queue[idx] idx = self._extreme_points.qsize() editable_pt = EditablePolygonPoint(idx) editable_pt.signals.deleted.connect(delete_point) editable_pt.setPos(mouse_pos) self._scene.addItem(editable_pt) self._extreme_points.put(editable_pt) else: self.setDragMode(QGraphicsView.ScrollHandDrag) super(ImageViewer, self).mousePressEvent(evt) @gui_exception def mouseMoveEvent(self, evt: QtGui.QMouseEvent) -> None: mouse_pos = self.mapToScene(evt.pos()) image_rect: QRectF = self._pixmap.boundingRect() if self.current_tool == SELECTION_TOOL.BOX: if not self._rectangle_tool_origin.isNull(): geometry = QRect(self._rectangle_tool_origin, evt.pos()).normalized() self._rectangle_tool_picker.setGeometry(geometry) elif self.current_tool == SELECTION_TOOL.POLYGON: if self._current_polygon and image_rect.contains(mouse_pos): if self._current_polygon.count > 0: last_point: QPointF = self._current_polygon.last_point self._polygon_guide_line.setZValue(1) self._polygon_guide_line.show() mouse_pos = self.mapToScene(evt.pos()) self._polygon_guide_line.setLine(last_point.x(), last_point.y(), mouse_pos.x(), mouse_pos.y()) else: self._polygon_guide_line.hide() elif self.current_tool == SELECTION_TOOL.ELLIPSE: if self._current_ellipse and image_rect.contains(mouse_pos): ellipse_rect = self._current_ellipse.rect() ellipse_pos = QPointF(ellipse_rect.x(), ellipse_rect.y()) distance = math.hypot(mouse_pos.x() - ellipse_pos.x(), mouse_pos.y() - ellipse_pos.y()) ellipse_rect.setWidth(distance) ellipse_rect.setHeight(distance) self._current_ellipse.setRect(ellipse_rect) elif self.current_tool == SELECTION_TOOL.FREE and evt.buttons( ) and QtCore.Qt.LeftButton: if self._current_free_path and image_rect.contains(mouse_pos): painter: QPainterPath = self._current_free_path.path() self._last_point_drawn = self.mapToScene(evt.pos()) painter.lineTo(self._last_point_drawn) self._current_free_path.setPath(painter) super(ImageViewer, self).mouseMoveEvent(evt) @gui_exception def mouseReleaseEvent(self, evt: QtGui.QMouseEvent) -> None: image_rect: QRectF = self._pixmap.boundingRect() if self.current_tool == SELECTION_TOOL.BOX: roi: QRect = self._rectangle_tool_picker.geometry() roi: QRectF = self.mapToScene(roi).boundingRect() self._rectangle_tool_picker.hide() if image_rect == roi.united(image_rect): rect = EditableBox(roi) rect.label = self.current_label rect.tag = self._dataset self._scene.addItem(rect) self.current_tool = SELECTION_TOOL.POINTER self.setDragMode(QGraphicsView.ScrollHandDrag) elif self.current_tool == SELECTION_TOOL.ELLIPSE and self._current_ellipse: roi: QRect = self._current_ellipse.boundingRect() if image_rect == roi.united(image_rect): self.current_tool = SELECTION_TOOL.POINTER self.setDragMode(QGraphicsView.ScrollHandDrag) else: self._current_ellipse.delete_item() elif self.current_tool == SELECTION_TOOL.FREE and self._current_free_path: # create polygon self._current_free_path: QGraphicsPathItem path_rect = self._current_free_path.boundingRect() if image_rect == path_rect.united(image_rect): path = self._current_free_path.path() path_polygon = EditablePolygon() path_polygon.tag = self.dataset path_polygon.label = self.current_label self._scene.addItem(path_polygon) for i in range(0, path.elementCount(), 10): x, y = path.elementAt(i).x, path.elementAt(i).y path_polygon.addPoint(QPointF(x, y)) self._scene.removeItem(self._current_free_path) self.current_tool = SELECTION_TOOL.POINTER self.setDragMode(QGraphicsView.ScrollHandDrag) super(ImageViewer, self).mouseReleaseEvent(evt) def remove_annotations(self): for item in self._scene.items(): if isinstance(item, EditableItem): item.delete_item() def remove_annotations_by_label(self, label_name): for item in self._scene.items(): if isinstance(item, EditableItem): if item.label and item.label.name == label_name: item.delete_item() def enable_items(self, value): for item in self._scene.items(): if isinstance(item, EditableItem): item.setEnabled(value) def clear_extreme_points(self): if self._extreme_points.qsize() > 0: for pt in self._extreme_points.queue: self._scene.removeItem(pt) self._extreme_points.queue.clear()
class ImageViewer(QGraphicsView, QObject): def __init__(self, parent=None): super(ImageViewer, self).__init__(parent) self.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform) #self.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop) self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) self.setDragMode(QGraphicsView.ScrollHandDrag) #self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) #self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) self._scene = ImageViewerScene(self) self.setScene(self._scene) self._create_grid() self._create_grid_lines() self._pixmap = None self._selection_mode = SELECTION_MODE.NONE # polygon selection _polygon_guide_line_pen = QPen(QtGui.QColor(235, 72, 40)) _polygon_guide_line_pen.setWidth(2) _polygon_guide_line_pen.setStyle(QtCore.Qt.DotLine) self._polygon_guide_line = QGraphicsLineItem() self._polygon_guide_line.setVisible(False) self._polygon_guide_line.setPen(_polygon_guide_line_pen) self._scene.addItem(self._polygon_guide_line) self._current_polygon = None # rectangle selection self._box_origin = QPoint() self._box_picker = QRubberBand(QRubberBand.Rectangle, self) # free selection self._current_free_path = None self._is_drawing = False self._last_point_drawn = QPoint() self._current_label = None @property def current_label(self): return self._current_label @current_label.setter def current_label(self, value): self._current_label = value @property def pixmap(self) -> ImagePixmap: return self._pixmap @pixmap.setter def pixmap(self, value: QPixmap): self.selection_mode = SELECTION_MODE.NONE self.resetTransform() if self.pixmap: self._scene.removeItem(self._pixmap) self.remove_annotations() self._pixmap = ImagePixmap() self._pixmap.setPixmap(value) self._pixmap.setOffset(-value.width() / 2, -value.height() / 2) self._pixmap.setTransformationMode(QtCore.Qt.SmoothTransformation) self._pixmap.signals.hoverEnterEventSgn.connect( self.pixmap_hoverEnterEvent_slot) self._pixmap.signals.hoverLeaveEventSgn.connect( self.pixmap_hoverLeaveEvent_slot) self._pixmap.signals.hoverMoveEventSgn.connect( self.pixmap_hoverMoveEvent_slot) self._scene.addItem(self._pixmap) # rect=self._scene.addRect(QtCore.QRectF(0,0,100,100), QtGui.QPen(QtGui.QColor("red"))) # rect.setZValue(1.0) self.fit_to_window() @property def selection_mode(self): return self._selection_mode @selection_mode.setter def selection_mode(self, value): self._polygon_guide_line.hide() self._current_polygon = None self._current_free_path = None self._is_drawing = value == SELECTION_MODE.FREE if value == SELECTION_MODE.NONE: self.enable_items(True) else: self.enable_items(False) self._selection_mode = value def remove_annotations(self): for item in self._scene.items(): if isinstance(item, EditableBox): self._scene.removeItem(item) elif isinstance(item, EditablePolygon): item.delete_polygon() def remove_annotations_by_label(self, label_name): for item in self._scene.items(): if isinstance(item, EditableBox): if item.label and item.label.name == label_name: self._scene.removeItem(item) elif isinstance(item, EditablePolygon): if item.label and item.label.name == label_name: item.delete_polygon() def enable_items(self, value): for item in self._scene.items(): if isinstance(item, EditablePolygon) or isinstance( item, EditableBox): item.setEnabled(value) def _create_grid(self): gridSize = 15 backgroundPixmap = QtGui.QPixmap(gridSize * 2, gridSize * 2) #backgroundPixmap.fill(QtGui.QColor("white")) backgroundPixmap.fill(QtGui.QColor(20, 20, 20)) #backgroundPixmap.fill(QtGui.QColor("powderblue")) painter = QtGui.QPainter(backgroundPixmap) #backgroundColor=QtGui.QColor("palegoldenrod") #backgroundColor=QtGui.QColor(237,237,237) backgroundColor = QtGui.QColor(0, 0, 0) painter.fillRect(0, 0, gridSize, gridSize, backgroundColor) painter.fillRect(gridSize, gridSize, gridSize, gridSize, backgroundColor) painter.end() self._scene.setBackgroundBrush(QtGui.QBrush(backgroundPixmap)) def _create_grid_lines(self): pen_color = QColor(255, 255, 255, 255) pen = QPen(pen_color) pen.setWidth(2) pen.setStyle(QtCore.Qt.DotLine) self.vline = QGraphicsLineItem() self.vline.setVisible(False) self.vline.setPen(pen) self.hline = QGraphicsLineItem() self.hline.setVisible(False) self.hline.setPen(pen) self._scene.addItem(self.vline) self._scene.addItem(self.hline) def wheelEvent(self, event: QWheelEvent): adj = (event.angleDelta().y() / 120) * 0.1 self.scale(1 + adj, 1 + adj) def fit_to_window(self): """Fit image within view.""" if not self.pixmap or not self._pixmap.pixmap(): return #self._pixmap.setTransformationMode(QtCore.Qt.SmoothTransformation) self.fitInView(self._pixmap, QtCore.Qt.KeepAspectRatio) def show_guide_lines(self): if self.hline and self.vline: self.hline.show() self.vline.show() def hide_guide_lines(self): if self.hline and self.vline: self.hline.hide() self.vline.hide() def pixmap_hoverEnterEvent_slot(self): self.show_guide_lines() def pixmap_hoverLeaveEvent_slot(self): self.hide_guide_lines() def pixmap_hoverMoveEvent_slot(self, evt: QGraphicsSceneHoverEvent, x, y): bbox: QRect = self._pixmap.boundingRect() offset = QPointF(bbox.width() / 2, bbox.height() / 2) self.vline.setLine(x, -offset.y(), x, bbox.height() - offset.y()) self.vline.setZValue(1) self.hline.setLine(-offset.x(), y, bbox.width() - offset.x(), y) self.hline.setZValue(1) def mouseMoveEvent(self, evt: QtGui.QMouseEvent) -> None: if self.selection_mode == SELECTION_MODE.BOX: if not self._box_origin.isNull(): self._box_picker.setGeometry( QRect(self._box_origin, evt.pos()).normalized()) elif self.selection_mode == SELECTION_MODE.POLYGON: if self._current_polygon: if self._current_polygon.count > 0: last_point: QPointF = self._current_polygon.last_point self._polygon_guide_line.setZValue(1) self._polygon_guide_line.show() mouse_pos = self.mapToScene(evt.pos()) self._polygon_guide_line.setLine(last_point.x(), last_point.y(), mouse_pos.x(), mouse_pos.y()) else: self._polygon_guide_line.hide() elif self.selection_mode == SELECTION_MODE.FREE and evt.buttons( ) and QtCore.Qt.LeftButton: if self._current_free_path: painter: QPainterPath = self._current_free_path.path() self._last_point_drawn = self.mapToScene(evt.pos()) painter.lineTo(self._last_point_drawn) self._current_free_path.setPath(painter) super(ImageViewer, self).mouseMoveEvent(evt) def mousePressEvent(self, evt: QtGui.QMouseEvent) -> None: if evt.buttons() == QtCore.Qt.LeftButton: if self.selection_mode == SELECTION_MODE.BOX: self.setDragMode(QGraphicsView.NoDrag) self._box_origin = evt.pos() self._box_picker.setGeometry(QRect(self._box_origin, QSize())) self._box_picker.show() elif self._selection_mode == SELECTION_MODE.POLYGON: pixmap_rect: QRectF = self._pixmap.boundingRect() new_point = self.mapToScene(evt.pos()) # consider only the points intothe image if pixmap_rect.contains(new_point): if self._current_polygon is None: self._current_polygon = EditablePolygon() self._current_polygon.signals.deleted.connect( self.delete_polygon_slot) self._scene.addItem(self._current_polygon) self._current_polygon.addPoint(new_point) else: self._current_polygon.addPoint(new_point) elif self._selection_mode == SELECTION_MODE.FREE: # start drawing new_point = self.mapToScene(evt.pos()) pixmap_rect: QRectF = self._pixmap.boundingRect() # consider only the points intothe image if pixmap_rect.contains(new_point): self.setDragMode(QGraphicsView.NoDrag) pen = QPen(QtGui.QColor(235, 72, 40)) pen.setWidth(10) self._last_point_drawn = new_point self._current_free_path = QGraphicsPathItem() self._current_free_path.setOpacity(0.6) self._current_free_path.setPen(pen) painter = QPainterPath() painter.moveTo(self._last_point_drawn) self._current_free_path.setPath(painter) self._scene.addItem(self._current_free_path) else: self.setDragMode(QGraphicsView.ScrollHandDrag) super(ImageViewer, self).mousePressEvent(evt) def mouseReleaseEvent(self, evt: QtGui.QMouseEvent) -> None: if evt.button() == QtCore.Qt.LeftButton: if self.selection_mode == SELECTION_MODE.BOX: roi: QRect = self._box_picker.geometry() roi: QRectF = self.mapToScene(roi).boundingRect() pixmap_rect = self._pixmap.boundingRect() self._box_picker.hide() if pixmap_rect == roi.united(pixmap_rect): rect = EditableBox(roi) rect.label = self.current_label self._scene.addItem(rect) self.selection_mode = SELECTION_MODE.NONE self.setDragMode(QGraphicsView.ScrollHandDrag) elif self.selection_mode == SELECTION_MODE.FREE and self._current_free_path: # create polygon self._current_free_path: QGraphicsPathItem path_rect = self._current_free_path.boundingRect() pixmap_rect = self._pixmap.boundingRect() if pixmap_rect == path_rect.united(pixmap_rect): path = self._current_free_path.path() path_polygon = EditablePolygon() path_polygon.label = self.current_label self._scene.addItem(path_polygon) for i in range(0, path.elementCount(), 10): x, y = path.elementAt(i).x, path.elementAt(i).y path_polygon.addPoint(QPointF(x, y)) self._scene.removeItem(self._current_free_path) self.selection_mode = SELECTION_MODE.NONE self.setDragMode(QGraphicsView.ScrollHandDrag) super(ImageViewer, self).mouseReleaseEvent(evt) def keyPressEvent(self, event: QtGui.QKeyEvent) -> None: if self._current_polygon and event.key() == QtCore.Qt.Key_Space: points = self._current_polygon.points self._current_polygon.label = self.current_label self._current_polygon = None self.selection_mode = SELECTION_MODE.NONE self._polygon_guide_line.hide() self.setDragMode(QGraphicsView.ScrollHandDrag) super(ImageViewer, self).keyPressEvent(event) def delete_polygon_slot(self, polygon: EditablePolygon): self._current_polygon = None self.selection_mode = SELECTION_MODE.NONE self._polygon_guide_line.hide()
class MainScene(QGraphicsScene): COLORS = { 'black': QColor(Qt.black), 'white': QColor(Qt.white), 'red': QColor(Qt.red), 'green': QColor(Qt.green), 'blue': QColor(Qt.blue), 'bark_red': QColor(Qt.darkRed), 'dark_green': QColor(Qt.darkGreen), 'dark_blue': QColor(Qt.darkBlue), 'cyan': QColor(Qt.cyan), 'magenta': QColor(Qt.magenta), 'yellow': QColor(Qt.yellow), 'gray': QColor(Qt.gray), 'dark_cyan': QColor(Qt.darkCyan), 'dark_magenta': QColor(Qt.darkMagenta), 'dark_yellow': QColor(Qt.darkYellow), 'dark_gray': QColor(Qt.darkGray), 'light_gray': QColor(Qt.lightGray) } def __init__(self, parent): super().__init__() self.parent = parent # Variables self.graph_to_display = None self.default_graph = None self.clustering_algorithm = None self.graph_center = None self.scale_factor = None self.scene_graph_rect = None self.points = [] self.lines = [] self.vertex_to_display = [] self.edge_to_display = [] self._move = False self.highlighted_item = None self.selected_item = None self.rb_selected_points = SelectionList() self.rb_origin = None self.rubber_band = None self.init_variables() def add_link(self, edge): point_a = self.points[edge.source] point_b = self.points[edge.target] line_pen = QPen(self.COLORS[edge['edge_color']]) line_pen.setWidth(edge['edge_width']) line = MainEdge(edge, point_a, point_b, line_pen, self) self.addItem(line) self.lines.append(line) def add_node(self, vertex): point_pen = QPen(self.COLORS['black']) point_pen.setWidth(self.parent.SETTINGS['point_border_width']) d = self.parent.SETTINGS['point_diameter'] point = MainVertex(vertex, d, point_pen, vertex['color'], self) self.addItem(point) self.points.append(point) def init_variables(self): self.graph_to_display = self.parent.main_window.graph self.default_graph = self.graph_to_display.copy() self.clustering_algorithm = self.parent.main_window.clustering_algorithm graph_rect = QRectF( QPointF(min(self.graph_to_display.vs['x']), min(self.graph_to_display.vs['y'])), QPointF(max(self.graph_to_display.vs['x']), max(self.graph_to_display.vs['y']))) self.graph_center = QPointF(graph_rect.center()) self.scale_factor = scale_factor_hint(self.parent.geometry(), graph_rect, 1.05) self.init_edge_color_to_default() self.set_background_color() def init_edge_color_to_default(self, ): for edge in self.graph_to_display.es: edge['edge_width'] = self.parent.SETTINGS['edge_width'] edge['edge_color'] = self.parent.SETTINGS['edge_color'] def set_background_color(self): background_color = QBrush( QColor(self.COLORS[self.parent.SETTINGS['background_color']])) self.setBackgroundBrush(background_color) def display(self): def availability_color_to_vertex(): for v in self.graph_to_display.vs: if v['availability']: v['color'] = QBrush(self.COLORS['green']) else: v['color'] = QBrush(self.COLORS['red']) def assign_color_to_vertex(): colors = list(self.COLORS.keys()) for v in self.graph_to_display.vs: color_index = v['cluster'] % len(colors) if colors[color_index] == self.parent.SETTINGS[ 'highlight_color']: # highlighted items will stand out color_index = (color_index + 1) % len(colors) q_color = self.COLORS[colors[color_index]] cluster_color = QBrush(q_color) v['color'] = cluster_color def assign_vertex_to_cluster(): for v in self.graph_to_display.vs: for cluster in clusters: # If elements of clusters are not lists, then clusters had been a VertexDendrogram object, which # gives a "list" of igraph.Graph objects when subgraphed. The VertexClustering object, on the other # hand, gives a list of lists of vertex ids when subgraphed if isinstance(cluster, Graph) and v['id'] in cluster.vs['id']: v['cluster'] = clusters.index(cluster) break elif isinstance(cluster, list) and v.index in cluster: v['cluster'] = clusters.index(cluster) break clusters = getattr(self.graph_to_display, self.clustering_algorithm)() # Some clustering algorithms return VertexDendrogram, some return VertexClustering, and VertexDendrogram object # has to be transformed into VertexClustering (as_clustering()) before it can be subgraphed if isinstance(clusters, VertexDendrogram): clusters = clusters.as_clustering() clusters = clusters.subgraphs() assign_vertex_to_cluster() assign_color_to_vertex() # based on the cluster it belongs to if self.parent.availability: availability_color_to_vertex() self.display_vertices() self.display_edges() def display_vertices(self): point_pen = QPen(self.COLORS['black']) point_pen.setWidth(self.parent.SETTINGS['point_border_width']) d = self.parent.SETTINGS['point_diameter'] for vertex in self.graph_to_display.vs: x, y = dilate(vertex['x'], vertex['y'], self.graph_center, self.scale_factor) vertex['pos'] = {'x': x, 'y': y} point = MainVertex(vertex, d, point_pen, vertex['color'], self) self.addItem(point) self.points.append(point) def display_edges(self): for edge in self.graph_to_display.es: point_a = self.points[edge.source] point_b = self.points[edge.target] line_pen = QPen(self.COLORS[edge['edge_color']]) line_pen.setWidth(edge['edge_width']) line = MainEdge(edge, point_a, point_b, line_pen, self) self.addItem(line) self.lines.append(line) def scalling(self): bandwidth = [] attribute = self.parent.main_window.DEFAULT_ATTRIBUTE for edge in self.graph_to_display.es: bandwidth.append(edge[attribute]) max_value = max(bandwidth) min_value = min(bandwidth) max_min = max_value - min_value for i in range(len(bandwidth)): bandwidth[i] = (bandwidth[i] - min_value) / max_min return bandwidth # This is a more complete way of showing gradient in the edge def display_edges_by_gradient(self): if not self.parent.main_window.contains_attribute(): return bandwidth = self.scalling() n = 0 # set the thickness of QPen according to the attribute value for edge in self.graph_to_display.es: line = self.lines[edge.index] line.edge['edge_color'] = QColor(255 - bandwidth[n] * 255, 0, bandwidth[n] * 255) line_pen = QPen(line.edge['edge_color']) line_pen.setWidthF(line.edge['edge_width']) line.setPen(line_pen) line._pen = line_pen n += 1 def display_edges_by_thickness(self): if not self.parent.main_window.contains_attribute(): return bandwidth = self.scalling() n = 0 # set the thickness of QPen according to the attribute value for edge in self.graph_to_display.es: line = self.lines[edge.index] line.edge['edge_width'] = self.parent.SETTINGS[ 'edge_width'] * bandwidth[n] * 2 line_pen = QPen(QColor('black')) line_pen.setWidthF(line.edge['edge_width']) line.setPen(line_pen) line._pen = line_pen n += 1 def change_color_all_links(self, the_color): for edge in self.graph_to_display.es: line = self.lines[edge.index] line.edge['edge_color'] = the_color line_pen = QPen(line.edge['edge_color']) line_pen.setWidthF(line.edge['edge_width']) line.setPen(line_pen) line._pen = line_pen def highlight_edges(self, edge_path): for edge_id in edge_path: line = self.lines[edge_id] line.highlight_self() line.setHighlighted(True) def unhighlight_edges(self, edge_path): for edge_id in edge_path: line = self.lines[edge_id] line.unhighlight_self() line.setHighlighted(False) def highlight_vertices(self, vertex_path): for vertex_id in vertex_path: point = self.points[vertex_id] point.highlight_self() point.setHighlighted(True) def unhighlight_vertices(self, vertex_path): for vertex_id in vertex_path: point = self.points[vertex_id] point.unhighlight_self() point.setHighlighted(False) def update_vertex(self, point): dilated_x, dilated_y = point.x(), point.y() original_x, original_y = undilate(dilated_x, dilated_y, self.graph_center, self.scale_factor) point.vertex.update_attributes(x=original_x, y=original_y, pos={ 'x': dilated_x, 'y': dilated_y }) def crop(self): if self.rb_selected_points.is_empty(): return lines_to_keep = [] for point in self.rb_selected_points: for line in point.lines: if line not in lines_to_keep and \ (self.rb_selected_points.contains(line.point_a) and self.rb_selected_points.contains( line.point_b)): lines_to_keep.append(line) for item in self.items(): if item not in lines_to_keep and not self.rb_selected_points.contains( item): self.removeItem(item) self.save_cropped() def reverse_crop(self): if self.rb_selected_points.is_empty(): return for point in self.rb_selected_points: if point in self.items(): for line in point.lines: if line in self.items(): self.removeItem(line) self.removeItem(point) self.save_cropped() def save_cropped(self): graph = Graph() items = self.items() points = [] point_index = 0 for point in self.points: if point in items: create_vertices(graph, 1) created_vertex = graph.vs[point_index] created_vertex.update_attributes(point.vertex.attributes()) points.append(point) point_index += 1 self.points = points lines = [] line_index = 0 for line in self.lines: if line in items: st_tuple = [0, 0] for vertex in graph.vs: if vertex['id'] == line.point_a.vertex['id']: st_tuple[0] = vertex.index break for vertex in graph.vs: if vertex['id'] == line.point_b.vertex['id']: st_tuple[1] = vertex.index break st_tuple = [tuple(st_tuple)] create_edges(graph, st_tuple) created_edge = graph.es[line_index] created_edge.update_attributes(line.edge.attributes()) lines.append(line) line_index += 1 self.lines = lines self.rb_selected_points.clear() self.parent.main_window.graph = graph self.graph_to_display = self.parent.main_window.graph def revert_to_default(self): items = self.items() for point in self.points: if point not in items: self.addItem(point) for line in self.lines: if line not in items: self.addItem(line) self.rb_selected_points.clear() self.graph_to_display = self.default_graph.copy() self.parent.main_window.graph = self.graph_to_display def remove_point(self, point): self.removeItem(point) for line in point.lines: self.removeItem(line) self.save_cropped() def remove_line(self, line): self.removeItem(line) self.save_cropped() # For add vertex def mouseDoubleClickEvent(self, event): if self.parent.main_window.MODE_ADD_NODE: cursor_pos = event.scenePos().toPoint() item_under_cursor = self.itemAt(cursor_pos, QTransform()) if item_under_cursor is None: self.parent.main_window.add_node(event, self.graph_center, self.scale_factor) # For add edge def real_add_edge(self, vertex): if len(self.parent.main_window.SOURCE_TARGET) == 0: self.parent.main_window.SOURCE_TARGET.append(vertex) elif len(self.parent.main_window.SOURCE_TARGET) == 1: self.parent.main_window.SOURCE_TARGET.append(vertex) self.parent.main_window.graph = create_edges( self.parent.main_window.graph, [(int(self.parent.main_window.SOURCE_TARGET[0].index), int(self.parent.main_window.SOURCE_TARGET[1].index))]) # Set default value for new edge self.parent.main_window.graph.es[ self.parent.main_window.graph.ecount() - 1]['edge_color'] = "" self.parent.main_window.graph.es[ self.parent.main_window.graph.ecount() - 1]['weight'] = 1000.0 self.parent.main_window.graph.es[ self.parent.main_window.graph.ecount() - 1]['LinkType'] = "" self.parent.main_window.graph.es[ self.parent.main_window.graph.ecount() - 1]['LinkNote'] = "" self.parent.main_window.graph.es[ self.parent.main_window.graph.ecount() - 1]['LinkSpeedUnits'] = "G" self.parent.main_window.graph.es[ self.parent.main_window.graph.ecount() - 1]['label'] = "1000.0 MBit/s" self.parent.main_window.graph.es[ self.parent.main_window.graph.ecount() - 1]['LinkLabel'] = "" self.parent.main_window.graph.es[ self.parent.main_window.graph.ecount() - 1]['edge_width'] = 1 self.parent.main_window.graph.es[ self.parent.main_window.graph.ecount() - 1]['LinkSpeedRaw'] = 1000000000.0 self.parent.main_window.graph.es[ self.parent.main_window.graph.ecount() - 1]['key'] = 0.0 self.parent.main_window.graph.es[ self.parent.main_window.graph.ecount() - 1]['zorder'] = 1.0 # Check for attribute delay, if it exists set default value for new edge dictionary = self.parent.main_window.graph.es[0].attributes() for key, value in dictionary.items(): if str(key) == "delay": self.parent.main_window.graph.es[ self.parent.main_window.graph.ecount() - 1]['delay'] = 50.0 self.edge_to_display.append(self.parent.main_window.graph.es[ self.parent.main_window.graph.ecount() - 1]) point_a = self.points[self.edge_to_display[-1].source] point_b = self.points[self.edge_to_display[-1].target] line_pen = QPen(self.COLORS['black']) line_pen.setWidth(1) line = MainEdge(self.edge_to_display[-1], point_a, point_b, line_pen, self) self.addItem(line) self.lines.append(line) self.parent.main_window.SOURCE_TARGET = [] self.parent.main_window.ADD_EDGE_STATE = False # self.parent.main_window.button_add_edge.setToolTip("Add Edge") def mousePressEvent(self, event): self.rb_selected_points.clear() cursor_pos = event.scenePos().toPoint() item_under_cursor = self.itemAt(cursor_pos, QTransform()) if item_under_cursor is not None: if self.highlighted_item is not None and \ not self.highlighted_item.isHighlighted() and \ not self.highlighted_item.isPersistent(): self.highlighted_item.unhighlight_self() if self.selected_item is not None and \ not self.selected_item.isHighlighted() and \ not self.selected_item.isPersistent(): self.selected_item.unhighlight_self() self.selected_item = item_under_cursor self.selected_item.mousePressEvent(event) if not self.parent.main_window.MODE_RECOLOR_NODE: self.highlighted_item = item_under_cursor self.highlighted_item.highlight_self() self._move = True else: # Clicking else where resets any selection previously made if self.highlighted_item is not None: if not self.highlighted_item.isHighlighted( ) and not self.highlighted_item.isPersistent(): self.highlighted_item.unhighlight_self() if self.selected_item is not None: if not self.selected_item.isHighlighted( ) and not self.selected_item.isPersistent(): self.selected_item.unhighlight_self() self.highlighted_item = None self.selected_item = None if self.parent.main_window.MODE_RUBBER_BAND: # Initializing rubber band self.rb_origin = self.parent.mapFromScene(cursor_pos) self.rubber_band = QRubberBand(QRubberBand.Rectangle, self.parent) self.rubber_band.setGeometry(QRect(self.rb_origin, QSize())) self.rubber_band.show() def mouseMoveEvent(self, event): cursor_pos = event.scenePos().toPoint() if self.selected_item is not None and self._move: self.selected_item.highlight_self() self.selected_item.mouseMoveEvent(event) elif self.parent.main_window.MODE_RUBBER_BAND and self.rb_origin is not None: if self.rb_origin is not None: adjusted_cursor_pos = self.parent.mapFromScene(cursor_pos) self.rubber_band.setGeometry( QRect(self.rb_origin, adjusted_cursor_pos).normalized()) elif not self.parent.main_window.MODE_RUBBER_BAND and not self.parent.main_window.MODE_RECOLOR_NODE: item_under_cursor = self.itemAt(cursor_pos, QTransform()) if item_under_cursor is None and self.highlighted_item is not None: # When mouse moves out of an item into background self.parent.setDragMode(self.parent.drag_mode_hint()) if self.highlighted_item != self.selected_item and not self.highlighted_item.isHighlighted( ): self.highlighted_item.unhighlight_self() self.highlighted_item = item_under_cursor elif item_under_cursor is not None and self.highlighted_item is None: # When mouse moves from background into an item self.parent.setDragMode(QGraphicsView.NoDrag) self.highlighted_item = item_under_cursor self.highlighted_item.highlight_self() elif item_under_cursor is not None and self.highlighted_item is not None: # When mouse moves out of one item into another item if self.highlighted_item != self.selected_item and not self.highlighted_item.isHighlighted( ): self.highlighted_item.unhighlight_self() self.highlighted_item = item_under_cursor self.highlighted_item.highlight_self() def mouseReleaseEvent(self, event): if self.selected_item is not None: self._move = False elif self.parent.main_window.MODE_RUBBER_BAND and self.rb_origin is not None: self.rubber_band.hide() rect = self.rubber_band.geometry() rect_scene = self.parent.mapToScene(rect).boundingRect() selected = self.items(rect_scene) for item in selected: if isinstance(item, MainVertex): self.rb_selected_points.append(item) self.rb_origin = None self.rubber_band = None