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 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 CaptureImage(QLabel): sequence = [] bag = None imageLabel = ImageLabel() def __init__(self, timeline, topic, msg, stamp): super(CaptureImage, self).__init__() self._timeline = timeline self._topic = topic #INISIALIZING MESSAGE AND COMMON ATTRIBUTES self._msg = msg self.imageLabel.header = self._msg.header self.imageLabel.imageTopic = self._topic #ORIGINAL TIME FROM THE ORIGINAL BAG SO THAT WE SAVE THAT SAME TIME IN THE NEW BAG self.stamp = stamp #WE HAVE TO DO BECAUSE RECT ARE RELATED WITH THE WIDGET WHICH CREATED THEM #SO IN ORDER TO SEE AND DELETE THEM FROM THE NEW WIDGET WE HAVE TO RELATE THEM WITH IT #***NOTE***: OBJECTS STORED IN SEQUENCE ARE OF TYPE QRUBBERBAND WHEREAS IN IMAGELABEL ARE #OF TYPE RECT if len(self.sequence) > 0: aux_sequence = [] for rectangle in self.sequence: self.currentQRubberBand = QRubberBand(QRubberBand.Rectangle, self) self.currentQRubberBand.setGeometry(rectangle.x(), rectangle.y(), rectangle.width(), rectangle.height()) aux_sequence.append(self.currentQRubberBand) self.currentQRubberBand.show() del self.sequence[:] for rectangle in aux_sequence: self.sequence.append(rectangle) def mousePressEvent(self, eventQMouseEvent): if eventQMouseEvent.buttons() == Qt.LeftButton: self.originQPoint = eventQMouseEvent.pos() self.currentQRubberBand = QRubberBand(QRubberBand.Rectangle, self) self.sequence.append(self.currentQRubberBand) r = randint(0, 255) g = randint(0, 255) b = randint(0, 255) palette = QPalette() palette.setColor(self.currentQRubberBand.foregroundRole(), QColor(r, g, b)) self.currentQRubberBand.setPalette(palette) self.currentQRubberBand.setGeometry( QRect(self.originQPoint, QSize())) self.currentQRubberBand.show() elif eventQMouseEvent.buttons() == Qt.RightButton: found = False for rect in self.sequence: if (rect.geometry().contains(eventQMouseEvent.pos())): self.menu = MenuRectangle(self._timeline, eventQMouseEvent, rect, self.imageLabel, self.sequence) found = True if not found: self.menuglobalimage = MenuGlobalImage( self._timeline, eventQMouseEvent, self._topic, self.sequence, self.imageLabel, self.stamp) def mouseMoveEvent(self, eventQMouseEvent): self.currentQRubberBand.setGeometry( QRect(self.originQPoint, eventQMouseEvent.pos()).normalized())
class PictureBox(QLabel): changedNumberRegions = pyqtSignal(int) def __init__(self, image, regions, parent=None): QLabel.__init__(self, parent) self.rubberBand = QRubberBand(QRubberBand.Rectangle, self) self.origin = QPoint() self.regions = None self.image = image def region_to_rect(region): y1, x1, y2, x2 = region.bbox return QRect(x1, y1, x2 - x1, y2 - y1) self.regions = list(map(region_to_rect, regions)) self._update_picture() def mousePressEvent(self, event): pal = QPalette() if event.button() == Qt.LeftButton: pal.setBrush(QPalette.Highlight, QBrush(Qt.blue)) elif event.button() == Qt.RightButton: pal.setBrush(QPalette.Highlight, QBrush(Qt.green)) self.rubberBand.setPalette(pal) self.origin = QPoint(event.pos()) self.rubberBand.setGeometry(QRect(self.origin, QSize())) self.rubberBand.show() def mouseMoveEvent(self, event): if not self.origin.isNull(): self.rubberBand.setGeometry( QRect(self.origin, event.pos()).normalized()) def mouseReleaseEvent(self, event): if event.button() == Qt.LeftButton: drawnRect = QRect(self.origin, event.pos()).normalized() for region in self.getIntersectedRegions(drawnRect): self.regions.remove(region) elif event.button() == Qt.RightButton: drawnRect = QRect(self.origin, event.pos()).normalized() meanArea = np.mean([r.width() * r.height() for r in self.regions]) meanHeight = np.mean([r.height() for r in self.regions]) meanWidth = np.mean([r.width() for r in self.regions]) if drawnRect.width() * drawnRect.height() * 2 < meanArea: rect = QRect(self.origin.x() - meanWidth / 2, self.origin.y() - meanHeight / 2, meanWidth, meanHeight) self.regions.append(rect) else: self.regions.append(drawnRect) self.rubberBand.hide() self._update_picture() def getIntersectedRegions(self, drawnRect): result = [] for region in self.regions: if region.intersects(drawnRect): result.append(region) return result def _update_regions(self): pixmap = self._get_pixmap() paint = QPainter(pixmap) paint.setPen(QColor("green")) for region in self.regions: paint.drawRect(region) # rect = mpatches.Rectangle((minc, minr), maxc - minc, maxr - minr, # fill=False, edgecolor='green', linewidth=2) del paint return pixmap def savePicture(self, fname): pixmap = self._update_regions() return pixmap.save(fname) def _update_picture(self): pixmap = self._update_regions() self.setAlignment(Qt.AlignLeft | Qt.AlignTop) self.setPixmap(pixmap) self.changedNumberRegions.emit(len(self.regions)) def _get_pixmap(self): # Convert image to QImage qimg = qimage2ndarray.array2qimage(self.image, True) return QPixmap(qimg)
class VideoWidget(QVideoWidget): def __init__(self, parent=None): super(VideoWidget, self).__init__(parent) self.rubberBand = QRubberBand(QRubberBand.Rectangle, self) pal = QPalette() pal.setBrush(QPalette.Highlight, QBrush(QColor(Qt.green))) self.rubberBand.setPalette(pal) self.setUpdatesEnabled(True) self.setMouseTracking(True) self.origin = QPoint() self.changeRubberBand = False self.parent = parent.parent() self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) self.setAttribute(Qt.WA_NoSystemBackground, True) self.setAttribute(Qt.WA_PaintOnScreen, True) palette = self.palette() palette.setColor(QPalette.Background, Qt.black) self.setPalette(palette) self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) self.surface = VideoWidgetSurface(self) self.setAttribute(Qt.WA_OpaquePaintEvent) self.gt = None self.pressed = False self.snapped = False self.zoomed = False self.zoomedRect = False self.offset = QPoint() self.pressPos = QPoint() self.dragPos = QPoint() self.tapTimer = QBasicTimer() self.zoomPixmap = QPixmap() self.maskPixmap = QPixmap() def keyPressEvent(self, event): if event.key() == Qt.Key_Escape and self.isFullScreen(): self.setFullScreen(False) event.accept() elif event.key() == Qt.Key_Enter and event.modifiers() & Qt.Key_Alt: self.setFullScreen(not self.isFullScreen()) event.accept() else: super(VideoWidget, self).keyPressEvent(event) def mouseDoubleClickEvent(self, event): """ :type event: QMouseEvent :param event: :return: """ self.setFullScreen(not self.isFullScreen()) event.accept() def videoSurface(self): return self.surface def UpdateSurface(self): self.surface.widget.update() def sizeHint(self): return self.surface.surfaceFormat().sizeHint() def GetCurrentFrame(self): return self.surface.image def SetInvertColor(self, value): global invertColorFilter invertColorFilter = value def SetGray(self, value): global grayColorFilter grayColorFilter = value def SetEdgeDetection(self, value): global edgeDetectionFilter edgeDetectionFilter = value def SetAutoContrastFilter(self, value): global contrastFilter contrastFilter = value def SetMonoFilter(self, value): global monoFilter monoFilter = value def RestoreFilters(self): global invertColorFilter, grayColorFilter, edgeDetectionFilter, monoFilter, contrastFilter invertColorFilter = grayColorFilter = edgeDetectionFilter = monoFilter = contrastFilter = False def GetXBlackZone(self): x = 0.0 normalizedWidth = self.surface.widget.height() * (GetImageWidth() / GetImageHeight()) if (self.surface.widget.width() / self.surface.widget.height()) > ( GetImageWidth() / GetImageHeight()): x = (self.surface.widget.width() - (normalizedWidth)) / 2.0 return x def GetYBlackZone(self): y = 0.0 normalizedHeight = self.surface.widget.width() / (GetImageWidth() / GetImageHeight()) if (self.surface.widget.width() / self.surface.widget.height()) < ( GetImageWidth() / GetImageHeight()): y = (self.surface.widget.height() - (normalizedHeight)) / 2.0 return y # determines if a clicked point lands on the image (False if lands on the # black borders or outside) def IsPointOnScreen(self, x, y): res = True normalizedWidth = self.surface.widget.height() * (GetImageWidth() / GetImageHeight()) normalizedHeight = self.surface.widget.width() / (GetImageWidth() / GetImageHeight()) if x > (normalizedWidth + self.GetXBlackZone()) or x < self.GetXBlackZone(): res = False if y > (normalizedHeight + self.GetYBlackZone()) or y < self.GetYBlackZone(): res = False return res # ratio between event.x() and real image width on screen. def GetXRatio(self): return GetImageWidth() / (self.surface.widget.width() - (2 * self.GetXBlackZone())) # ratio between event.y() and real image height on screen. def GetYRatio(self): return GetImageHeight() / (self.surface.widget.height() - (2 * self.GetYBlackZone())) def paintEvent(self, event): self.gt = GetGCPGeoTransform() painter = QPainter(self) if (self.surface.isActive()): videoRect = self.surface.videoRect() if not videoRect.contains(event.rect()): region = event.region() region.subtracted(QRegion(videoRect)) brush = self.palette().window() for rect in region.rects(): painter.fillRect(rect, brush) try: self.surface.paint(painter) except Exception: None else: painter.fillRect(event.rect(), self.palette().window()) try: SetImageSize(self.surface.currentFrame.width(), self.surface.currentFrame.height()) except: None # Magnifier Glass if self.zoomed and magnifier: dim = min(self.width(), self.height()) magnifierSize = min(MAX_MAGNIFIER, dim * 2 / 3) radius = magnifierSize / 2 ring = radius - 15 box = QSize(magnifierSize, magnifierSize) # reupdate our mask if self.maskPixmap.size() != box: self.maskPixmap = QPixmap(box) self.maskPixmap.fill(Qt.transparent) g = QRadialGradient() g.setCenter(radius, radius) g.setFocalPoint(radius, radius) g.setRadius(radius) g.setColorAt(1.0, QColor(64, 64, 64, 0)) g.setColorAt(0.5, QColor(0, 0, 0, 255)) mask = QPainter(self.maskPixmap) mask.setRenderHint(QPainter.Antialiasing) mask.setCompositionMode(QPainter.CompositionMode_Source) mask.setBrush(g) mask.setPen(Qt.NoPen) mask.drawRect(self.maskPixmap.rect()) mask.setBrush(QColor(Qt.transparent)) mask.drawEllipse(g.center(), ring, ring) mask.end() center = self.dragPos - QPoint(0, radius) center += QPoint(0, radius / 2) corner = center - QPoint(radius, radius) xy = center * 2 - QPoint(radius, radius) # only set the dimension to the magnified portion if self.zoomPixmap.size() != box: self.zoomPixmap = QPixmap(box) self.zoomPixmap.fill(Qt.lightGray) if True: painter = QPainter(self.zoomPixmap) painter.translate(-xy) self.largePixmap = QPixmap.fromImage(self.surface.image) painter.drawPixmap(self.offset, self.largePixmap) painter.end() clipPath = QPainterPath() clipPath.addEllipse(QPointF(center), ring, ring) painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) painter.setClipPath(clipPath) painter.drawPixmap(corner, self.zoomPixmap) painter.drawPixmap(corner, self.maskPixmap) painter.setPen(Qt.gray) painter.drawPath(clipPath) return def resizeEvent(self, event): """ :type event: QMouseEvent :param event: :return: """ QWidget.resizeEvent(self, event) self.zoomed = False if zoomRect and self.surface.zoomedrect is not None: self.surface.updatetest() return self.surface.updateVideoRect() def mouseMoveEvent(self, event): """ :type event: QMouseEvent :param event: :return: """ # Cursor Coordinates if self.gt is not None: # check if the point is on picture (not in black borders) if (not self.IsPointOnScreen(event.x(), event.y())): return transf = self.gt([ (event.x() - self.GetXBlackZone()) * self.GetXRatio(), (event.y() - self.GetYBlackZone()) * self.GetYRatio() ]) Longitude = transf[1] Latitude = transf[0] Altitude = 0.0 self.parent.lb_cursor_coord.setText( "<span style='font-size:10pt; font-weight:bold;'>Lon :</span>" + "<span style='font-size:9pt; font-weight:normal;'>" + ("%.3f" % Longitude) + "</span>" + "<span style='font-size:10pt; font-weight:bold;'> Lat :</span>" + "<span style='font-size:9pt; font-weight:normal;'>" + ("%.3f" % Latitude) + "</span>") else: self.parent.lb_cursor_coord.setText( "<span style='font-size:10pt; font-weight:bold;'>Lon :</span>" + "<span style='font-size:9pt; font-weight:normal;'>Null</span>" + "<span style='font-size:10pt; font-weight:bold;'> Lat :</span>" + "<span style='font-size:9pt; font-weight:normal;'>Null</span>") if not event.buttons(): return if self.changeRubberBand: self.rubberBand.setGeometry( QRect(self.origin, event.pos()).normalized()) if self.zoomed is True: self.rubberBand.hide() self.zoomedRect = False if not self.zoomed: if not self.pressed or not self.snapped: delta = event.pos() - self.pressPos self.pressPos = event.pos() self.pan(delta) return else: threshold = 10 delta = event.pos() - self.pressPos if self.snapped: self.snapped &= delta.x() < threshold self.snapped &= delta.y() < threshold self.snapped &= delta.x() > -threshold self.snapped &= delta.y() > -threshold if not self.snapped: self.tapTimer.stop() else: self.dragPos = event.pos() self.surface.updateVideoRect() # TODO: MAKE PAINT GEOMETRY ACTION AND CREATE SHAPES def pan(self, delta): """ Pan Action """ self.offset += delta self.surface.updateVideoRect() def timerEvent(self, _): """ Time Event """ if not self.zoomed: self.activateMagnifier() self.surface.updateVideoRect() def mousePressEvent(self, event): """ :type event: QMouseEvent :param event: :return: """ if event.button() == Qt.LeftButton: self.pressed = self.snapped = True self.pressPos = self.dragPos = event.pos() self.tapTimer.stop() self.tapTimer.start(HOLD_TIME, self) if zoomRect and event.button() == Qt.LeftButton: self.origin = event.pos() self.rubberBand.setGeometry(QRect(self.origin, QSize())) self.rubberBand.show() self.changeRubberBand = True def activateMagnifier(self): """ Activate Magnifier Glass """ self.zoomed = True self.tapTimer.stop() self.surface.updateVideoRect() def SetMagnifier(self, value): """ Set Magnifier Glass """ global magnifier magnifier = value def SetZoomRect(self, value): """ Set Zoom Rectangle """ global zoomRect zoomRect = value def mouseReleaseEvent(self, _): """ :type event: QMouseEvent :param event: :return: """ self.changeRubberBand = False if self.zoomed is True: return self.zoomed = False if not zoomRect: self.surface.updateVideoRect() else: self.rubberBand.hide() self.zoomedRect = True # TODO : ACTUALIZAR LA IMAGEN selRect = self.rubberBand.geometry() orig2widgScale = self.surface.widget.contentsRect().width() / \ self.surface.image.width() X1 = selRect.topLeft().x() / orig2widgScale Y1 = selRect.topLeft().y() / orig2widgScale X2 = selRect.bottomRight().x() / self.surface.widget.contentsRect().bottomRight().x() * \ self.surface.image.width() Y2 = selRect.bottomRight().y() / self.surface.widget.contentsRect().bottomRight().y() * \ self.surface.image.height() self.surface.image.width() self.surface.image.height() wid2origRect = QRect(X1, Y1, X2, Y2) zoom_img = self.surface.image.copy(wid2origRect) # zoom_img.save('D:\\test.png') # n_img = self.surface.image.copy(selRect) # n_img.save('D:\\test.png') zoom_img.scaled(1920, 1080) self.surface.currentFrame = QVideoFrame(zoom_img) # self.surface.targetRect=selRect self.surface.currentFrame.unmap() self.UpdateSurface() def leaveEvent(self, _): self.parent.lb_cursor_coord.setText("")
class VideoWidget(QVideoWidget): def __init__(self, parent=None): 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 ToolWindowManager ( QWidget ): '''docstring for ToolWindowManager''' #! The area tool windows has been added to most recently. LastUsedArea = 1 #! New area in a detached window. NewFloatingArea = 2 #! Area inside the manager widget (only available when there is no tool windows in it). EmptySpace = 3 #! Tool window is hidden. NoArea = 4 #! Existing area specified in AreaReference argument. AddTo = 5 #! New area to the left of the area specified in AreaReference argument. LeftOf = 6 #! New area to the right of the area specified in AreaReference argument. RightOf = 7 #! New area to the top of the area specified in AreaReference argument. TopOf = 8 #! New area to the bottom of the area specified in AreaReference argument. BottomOf = 9 #signal toolWindowVisibilityChanged = pyqtSignal ( QWidget, bool ) def __init__ ( self, parent = None ): super ( ToolWindowManager, self ).__init__ ( parent ) self.lastUsedArea = None self.suggestions = [] self.wrappers = [] self.areas = [] self.toolWindowList = [] self.draggedToolWindows = [] #---- self.borderSensitivity = 12 testSplitter = QSplitter () self.rubberBandLineWidth = testSplitter.handleWidth () self.dragIndicator = QtWidgets.QLabel ( None, Qt.ToolTip) # 使用QLabel来显示拖拽的提示 self.dragIndicator.setAttribute ( Qt.WA_ShowWithoutActivating) mainLayout = QtWidgets.QVBoxLayout ( self ) mainLayout.setContentsMargins ( 0, 0, 0, 0 ) wrapper = ToolWindowManagerWrapper ( self ) wrapper.setWindowFlags ( wrapper.windowFlags () & ~Qt.Tool ) mainLayout.addWidget ( wrapper ) self.dropSuggestionSwitchTimer = QtCore.QTimer ( self ) self.dropSuggestionSwitchTimer.timeout.connect ( self.showNextDropSuggestion ) self.dropSuggestionSwitchTimer.setInterval ( 800 ) self.dropCurrentSuggestionIndex = 0 palette = QtGui.QPalette () color = QtGui.QColor ( Qt.blue ) color.setAlpha ( 80 ) palette.setBrush ( QtGui.QPalette.Highlight, QtGui.QBrush ( color ) ) self.rectRubberBand = QRubberBand ( QRubberBand.Rectangle, self ) self.lineRubberBand = QRubberBand ( QRubberBand.Line, self ) self.rectRubberBand.setPalette ( palette ) self.lineRubberBand.setPalette ( palette ) def hideToolWindow ( self, toolWindow ): self.moveToolWindow ( toolWindow, ToolWindowManager.NoArea ) def toolWindows ( self ): return self.toolWindowList def hasToolWindow ( self, toolWindow ): return toolWindow in self.toolWindowList def addToolWindow ( self, toolWindow, area ): return self.addToolWindows ( [toolWindow], area ) def addToolWindows ( self, toolWindows, area ): for toolWindow in toolWindows: if self.hasToolWindow ( toolWindow ): continue toolWindow.hide () toolWindow.setParent ( None ) self.toolWindowList.append ( toolWindow ) self.moveToolWindows ( toolWindows, area ) def areaOf ( self, toolWindow ) -> ToolWindowManagerArea: ''' 查找toolWindow所属的area ''' return findClosestParent ( toolWindow, ToolWindowManagerArea ) def moveToolWindow ( self, toolWindow, area ): self.moveToolWindows ( [ toolWindow ], area ) def moveToolWindows ( self, toolWindows, area ): if type ( area ) == int: area = AreaReference ( area ) for toolWindow in toolWindows: if not self.hasToolWindow ( toolWindow ): return if toolWindow.parentWidget (): self.releaseToolWindow ( toolWindow ) areaType = area.type if areaType == ToolWindowManager.LastUsedArea and not self.lastUsedArea: foundArea = self.findChild ( ToolWindowManagerArea ) if foundArea: area = AreaReference ( ToolWindowManager.AddTo, foundArea ) else: area = ToolWindowManager.EmptySpace if areaType == ToolWindowManager.NoArea: #do nothing # qWarning ( '[moveToolWindows] areaType = NoArea' ) pass elif areaType == ToolWindowManager.NewFloatingArea: area = self.createArea () area.addToolWindows ( toolWindows ) wrapper = ToolWindowManagerWrapper ( self ) wrapper.layout ().addWidget ( area ) wrapper.move ( QCursor.pos () ) wrapper.show () # qWarning ( '[moveToolWindows] areaType = NewFloatingArea' ) elif areaType == ToolWindowManager.AddTo: area.area ().addToolWindows ( toolWindows ) # qWarning ( '[moveToolWindows] areaType = AddTo' ) elif areaType in ( ToolWindowManager.LeftOf, ToolWindowManager.RightOf, ToolWindowManager.TopOf, ToolWindowManager.BottomOf ): parentSplitter = cast ( area.widget.parentWidget (), QSplitter ) wrapper = cast ( area.widget.parentWidget (), ToolWindowManagerWrapper ) if not ( parentSplitter or wrapper ): qWarning ( 'unknown parent type' ) return # import pudb; pu.db useParentSplitter = False indexInParentSplitter = 0 if parentSplitter: indexInParentSplitter = parentSplitter.indexOf ( area.widget ) if parentSplitter.orientation () == Qt.Vertical: useParentSplitter = areaType in ( ToolWindowManager.TopOf, ToolWindowManager.BottomOf ) else: useParentSplitter = areaType in ( ToolWindowManager.LeftOf, ToolWindowManager.RightOf ) if useParentSplitter: if areaType in ( ToolWindowManager.BottomOf , ToolWindowManager.RightOf ): indexInParentSplitter += 1 newArea = self.createArea () newArea.addToolWindows ( toolWindows ) parentSplitter.insertWidget ( indexInParentSplitter, newArea ) else: area.widget.hide () area.widget.setParent ( None ) splitter = self.createSplitter () if areaType in ( ToolWindowManager.TopOf, ToolWindowManager.BottomOf ): splitter.setOrientation (Qt.Vertical) else: splitter.setOrientation (Qt.Horizontal) splitter.addWidget ( area.widget ) area.widget.show () newArea = self.createArea () if areaType in ( ToolWindowManager.TopOf, ToolWindowManager.LeftOf ): splitter.insertWidget ( 0, newArea ) else: splitter.addWidget ( newArea ) if parentSplitter: parentSplitter.insertWidget ( indexInParentSplitter, splitter ) else: wrapper.layout ().addWidget ( splitter ) newArea.addToolWindows ( toolWindows ) # qWarning ( '[moveToolWindows] areaType = LeftOf/RightOf/TopOf/BottomOf' ) elif areaType == ToolWindowManager.EmptySpace: wrapper = self.findChild ( ToolWindowManagerWrapper ) if wrapper.isOccupied (): self.lastUsedArea.addToolWindows ( toolWindows ) else: newArea = self.createArea () wrapper.layout ().addWidget ( newArea ) newArea.addToolWindows ( toolWindows ) # qWarning ( '[moveToolWindows] areaType = EmptySpace' ) elif areaType == ToolWindowManager.LastUsedArea: self.lastUsedArea.addToolWindows ( toolWindows ) # qWarning ( '[moveToolWindows] areaType = areaType == ToolWindowManager.LastUsedArea' ) else: qWarning ( 'invalid type' ) self.simplifyLayout () for toolWindow in toolWindows: self.toolWindowVisibilityChanged.emit ( toolWindow, toolWindow.parent () != None ) def removeToolWindow ( self, toolWindow ): if toolWindow not in self.toolWindowList: qWarning ( 'unknown tool window' ) return self.moveToolWindow ( toolWindow, ToolWindowManager.NoArea ) self.toolWindowList.removeOne (toolWindow) def setSuggestionSwitchInterval ( self, msec ): self.dropSuggestionSwitchTimer.setInterval (msec) def suggestionSwitchInterval ( self ): return self.dropSuggestionSwitchTimer.interval () def setBorderSensitivity ( self, pixels ): self.borderSensitivity = pixels def setRubberBandLineWidth ( self, pixels ): self.rubberBandLineWidth = pixels def saveState ( self ): result = {} result[ 'toolWindowManagerStateFormat' ] = 1 mainWrapper = self.findChild ( ToolWindowManagerWrapper ) if mainWrapper == None: qWarning ( 'can not find main wrapper' ) return {} result[ 'mainWrapper' ] = mainWrapper.saveState () floatingWindowsData = [] for wrapper in self.wrappers: if not wrapper.isWindow (): continue floatingWindowsData.append ( wrapper.saveState () ) result[ 'floatingWindows' ] = floatingWindowsData return result def restoreState ( self, data ): if not isinstance ( data, dict ): return if data[ 'toolWindowManagerStateFormat' ] != 1: qWarning ( 'state format is not recognized' ) return self.moveToolWindows ( self.toolWindowList, ToolWindowManager.NoArea ) mainWrapper = self.findChild ( ToolWindowManagerWrapper ) if not mainWrapper: qWarning ( 'can not find main wrapper' ) mainWrapper.restoreState ( data['mainWrapper'] ) for windowData in data['floatingWindows']: wrapper = ToolWindowManagerWrapper ( self ) wrapper.restoreState ( windowData ) wrapper.show () self.simplifyLayout () for toolWindow in self.toolWindowList: self.toolWindowVisibilityChanged.emit ( toolWindow, toolWindow.parentWidget () != None ) def createArea ( self ) -> ToolWindowManagerArea: ''' 创建area并监听area的tabClose事件 ''' area = ToolWindowManagerArea ( self, None ) area.tabCloseRequested.connect ( self.tabCloseRequested ) return area def handleNoSuggestions ( self ): self.rectRubberBand.hide () self.lineRubberBand.hide () self.lineRubberBand.setParent (self) self.rectRubberBand.setParent (self) self.suggestions = [] self.dropCurrentSuggestionIndex = 0 if self.dropSuggestionSwitchTimer.isActive (): self.dropSuggestionSwitchTimer.stop () def releaseToolWindow ( self, toolWindow ): previousTabWidget = findClosestParent ( toolWindow, ToolWindowManagerArea ) if not previousTabWidget: qWarning ( 'cannot find tab widget for tool window' ) return previousTabWidget.removeTab ( previousTabWidget.indexOf (toolWindow) ) toolWindow.hide () toolWindow.setParent ( None ) def removeArea ( self, area ): area.manager = None area.deleteLater () def removeWrapper ( self, wrapper ): wrapper.deleteLater () self.wrappers.remove ( wrapper ) def simplifyLayout ( self ): newAreas = [] currentAreas = self.areas for area in currentAreas: if area.parentWidget () is None: if area.count () == 0: if area == self.lastUsedArea: self.lastUsedArea = None self.removeArea ( area ) continue splitter = cast ( area.parentWidget (), QSplitter ) validSplitter = None # least top level splitter that should remain invalidSplitter = None #most top level splitter that should be deleted while ( splitter ): if splitter.count () > 1: validSplitter = splitter break else: invalidSplitter = splitter splitter = cast ( splitter.parentWidget (), QSplitter ) if not validSplitter: wrapper = findClosestParent ( area, ToolWindowManagerWrapper ) if not wrapper: qWarning ( 'can not find wrapper' ) print ( findClosestParent ( area, ToolWindowManagerWrapper ) ) print ( type ( area.parentWidget () ) == ToolWindowManagerWrapper ) return if area.count () == 0 and wrapper.isWindow (): wrapper.hide () wrapper.setParent ( None ) # can not deleteLater immediately (strange MacOS bug) self.removeWrapper ( wrapper ) elif area.parent () != wrapper: wrapper.layout ().addWidget ( area ) else: if area.count () > 0: if validSplitter and area.parent () != validSplitter: index = validSplitter.indexOf ( invalidSplitter ) validSplitter.insertWidget ( index, area ) if not invalidSplitter is None: invalidSplitter.hide () invalidSplitter.setParent ( None ) invalidSplitter.deleteLater () if area.count () == 0: area.hide () area.setParent ( None ) if area == self.lastUsedArea: self.lastUsedArea = None self.removeArea ( area ) continue newAreas.append ( area ) #keep self.areas = newAreas def dragInProgress ( self ): ''' 指示当前是否正处于拖拽状态 ''' return len ( self.draggedToolWindows ) > 0 def startDrag ( self, toolWindows ): ''' 开始拖拽,在指针位置显示拖拽toolWindows的简略快照 ''' if self.dragInProgress (): qWarning ( 'ToolWindowManager::execDrag: drag is already in progress' ) return if not toolWindows: return self.draggedToolWindows = toolWindows self.dragIndicator.setPixmap ( self.generateDragPixmap ( toolWindows ) ) self.updateDragPosition () self.dragIndicator.show () def saveSplitterState ( self, splitter ): result = {} result['state'] = splitter.saveState () result['type'] = 'splitter' items = [] for i in range ( splitter.count () ): item = splitter.widget ( i ) area = cast ( item, ToolWindowManagerArea ) if area: items.append ( area.saveState () ) else: childSplitter = cast ( item, QSplitter ) if childSplitter: items.append ( self. saveSplitterState ( childSplitter ) ) else: qWarning ( 'unknown splitter item' ) result[ 'items' ] = items return result def restoreSplitterState ( self, data ): if len ( data[ 'items' ] )< 2: qWarning ( 'invalid splitter encountered' ) splitter = self.createSplitter () for itemData in data[ 'items' ]: itemType = itemData['type'] if itemType == 'splitter': splitter.addWidget ( self.restoreSplitterState ( itemData ) ) elif itemType == 'area': area = self.createArea () area.restoreState ( itemData ) splitter.addWidget ( area ) else: qWarning ( 'unknown item type' ) splitter.restoreState ( data['state'] ) return splitter def generateDragPixmap ( self, toolWindows ): ''' 生成一个QTabBar的快照 ''' widget = QTabBar () widget.setDocumentMode (True) for toolWindow in toolWindows: widget.addTab (toolWindow.windowIcon (), toolWindow.windowTitle ()) #if QT_VERSION >= 0x050000 # Qt5 return widget.grab () #else #Qt4 # return QtGui.QPixmap.grabWidget ( widget ) #endif def showNextDropSuggestion ( self ): if len ( self.suggestions ) == 0: qWarning ( 'showNextDropSuggestion called but no suggestions' ) return self.dropCurrentSuggestionIndex += 1 if self.dropCurrentSuggestionIndex >= len ( self.suggestions ): self.dropCurrentSuggestionIndex = 0 suggestion = self.suggestions[ self.dropCurrentSuggestionIndex ] if suggestion.type in ( ToolWindowManager.AddTo , ToolWindowManager.EmptySpace ): if suggestion.type == ToolWindowManager.EmptySpace: widget = self.findChild ( ToolWindowManagerWrapper ) else: widget = suggestion.widget if widget.window () == self.window (): placeHolderParent = self else: placeHolderParent = widget.window () placeHolderGeometry = widget.rect () placeHolderGeometry.moveTopLeft ( widget.mapTo ( placeHolderParent, placeHolderGeometry.topLeft () ) ) self.rectRubberBand.setGeometry ( placeHolderGeometry ) self.rectRubberBand.setParent ( placeHolderParent ) self.rectRubberBand.show () self.lineRubberBand.hide () elif suggestion.type in ( ToolWindowManager.LeftOf , ToolWindowManager.RightOf, ToolWindowManager.TopOf , ToolWindowManager.BottomOf ): if suggestion.widget.window () == self.window (): placeHolderParent = self else: placeHolderParent = suggestion.widget.window () placeHolderGeometry = self.sidePlaceHolderRect ( suggestion.widget, suggestion.type ) placeHolderGeometry.moveTopLeft ( suggestion.widget.mapTo ( placeHolderParent, placeHolderGeometry.topLeft () ) ) self.lineRubberBand.setGeometry (placeHolderGeometry) self.lineRubberBand.setParent (placeHolderParent) self.lineRubberBand.show () self.rectRubberBand.hide () else: qWarning ( 'unsupported suggestion type' ) def findSuggestions ( self, wrapper ): self.suggestions = [] self.dropCurrentSuggestionIndex = -1 globalPos = QCursor.pos () candidates = [] for splitter in wrapper.findChildren ( QSplitter ): candidates.append ( splitter ) for area in self.areas: if area.window () == wrapper.window (): candidates.append ( area ) for widget in candidates: splitter = cast ( widget, QSplitter ) area = cast ( widget, ToolWindowManagerArea ) if not ( splitter or area ): qWarning ( 'unexpected widget type' ) continue parentSplitter = cast ( widget.parentWidget (), QSplitter ) lastInSplitter = parentSplitter and \ parentSplitter.indexOf (widget) == parentSplitter.count () - 1 allowedSides = [] if not splitter or splitter.orientation () == Qt.Vertical: allowedSides.append ( ToolWindowManager.LeftOf ) if not splitter or splitter.orientation () == Qt.Horizontal: allowedSides.append ( ToolWindowManager.TopOf ) if not parentSplitter or parentSplitter.orientation () == Qt.Vertical or lastInSplitter: if not splitter or splitter.orientation () == Qt.Vertical: allowedSides.append ( ToolWindowManager.RightOf ) if not parentSplitter or parentSplitter.orientation () == Qt.Horizontal or lastInSplitter: if not splitter or splitter.orientation () == Qt.Horizontal: allowedSides.append ( ToolWindowManager.BottomOf ) for side in allowedSides: rect = self.sideSensitiveArea ( widget, side ) pos = widget.mapFromGlobal ( globalPos ) if rect.contains ( pos ): self.suggestions.append ( AreaReference ( side, widget ) ) if area: rect = area.rect () pos = area.mapFromGlobal ( globalPos ) if rect.contains ( pos ): self.suggestions.append ( AreaReference ( ToolWindowManager.AddTo, area ) ) #end of for if not candidates: self.suggestions.append ( AreaReference ( ToolWindowManager.EmptySpace ) ) if len ( self.suggestions ) == 0: self.handleNoSuggestions () else: self.showNextDropSuggestion () def sideSensitiveArea ( self, widget, side ): widgetRect = widget.rect () if side == ToolWindowManager.TopOf: return QRect (QPoint (widgetRect.left (), widgetRect.top () - self.borderSensitivity), QSize (widgetRect.width (), self.borderSensitivity * 2)) elif side == ToolWindowManager.LeftOf: return QRect (QPoint (widgetRect.left () - self.borderSensitivity, widgetRect.top ()), QSize (self.borderSensitivity * 2, widgetRect.height ())) elif side == ToolWindowManager.BottomOf: return QRect (QPoint (widgetRect.left (), widgetRect.top () + widgetRect.height () - self.borderSensitivity), QSize (widgetRect.width (), self.borderSensitivity * 2)) elif side == ToolWindowManager.RightOf: return QRect (QPoint (widgetRect.left () + widgetRect.width () - self.borderSensitivity, widgetRect.top ()), QSize (self.borderSensitivity * 2, widgetRect.height ())) else: qWarning ( 'invalid side' ) return QRect () def sidePlaceHolderRect ( self, widget, side ): widgetRect = widget.rect () parentSplitter = cast ( widget.parentWidget (), QSplitter ) if parentSplitter and parentSplitter.indexOf (widget) > 0: delta = parentSplitter.handleWidth () / 2 + self.rubberBandLineWidth / 2 if side == ToolWindowManager.TopOf and parentSplitter.orientation () == Qt.Vertical: return QRect (QPoint ( widgetRect.left (), widgetRect.top () - int (delta) ), QSize ( widgetRect.width (), self.rubberBandLineWidth ) ) elif side == ToolWindowManager.LeftOf and parentSplitter.orientation () == Qt.Horizontal: return QRect (QPoint (widgetRect.left () - int (delta), widgetRect.top ()), QSize (self.rubberBandLineWidth, widgetRect.height ())) if side == ToolWindowManager.TopOf: return QRect (QPoint (widgetRect.left (), widgetRect.top ()), QSize (widgetRect.width (), self.rubberBandLineWidth)) elif side == ToolWindowManager.LeftOf: return QRect (QPoint (widgetRect.left (), widgetRect.top ()), QSize (self.rubberBandLineWidth, widgetRect.height ())) elif side == ToolWindowManager.BottomOf: return QRect (QPoint (widgetRect.left (), widgetRect.top () + widgetRect.height () - self.rubberBandLineWidth), QSize (widgetRect.width (), self.rubberBandLineWidth)) elif side == ToolWindowManager.RightOf: return QRect (QPoint (widgetRect.left () + widgetRect.width () - self.rubberBandLineWidth, widgetRect.top ()), QSize (self.rubberBandLineWidth, widgetRect.height ())) else: qWarning ( 'invalid side' ) return QRect () def updateDragPosition ( self ): if not self.dragInProgress (): return if not QApplication.mouseButtons () & Qt.LeftButton : self.finishDrag () return pos = QCursor.pos () self.dragIndicator.move ( pos + QPoint (1, 1) ) foundWrapper = False window = QApplication.topLevelAt ( pos ) for wrapper in self.wrappers: if wrapper.window () == window: if wrapper.rect ().contains ( wrapper.mapFromGlobal (pos) ): self.findSuggestions ( wrapper ) if len ( self.suggestions ) > 0: #starting or restarting timer if self.dropSuggestionSwitchTimer.isActive (): self.dropSuggestionSwitchTimer.stop () self.dropSuggestionSwitchTimer.start () foundWrapper = True break if not foundWrapper: self.handleNoSuggestions () def finishDrag ( self ): if not self.dragInProgress (): qWarning ( 'unexpected finishDrag' ) return # 如果suggestions数组中没有建议位置,则创建一个Wrapper窗口 # 反之,则移动toolWindow到建议位置 if len ( self.suggestions ) == 0: self.moveToolWindows ( self.draggedToolWindows, ToolWindowManager.NewFloatingArea ) else: if self.dropCurrentSuggestionIndex >= len ( self.suggestions ): qWarning ( 'invalid self.dropCurrentSuggestionIndex' ) return suggestion = self.suggestions[ self.dropCurrentSuggestionIndex ] self.handleNoSuggestions () self.moveToolWindows ( self.draggedToolWindows, suggestion ) self.dragIndicator.hide () self.draggedToolWindows = [] def tabCloseRequested ( self, index ): ''' 处理area控件的子tab关闭事件 ''' if not isinstance ( self.sender (), ToolWindowManagerArea ): qWarning ( 'sender is not a ToolWindowManagerArea' ) return area = self.sender () toolWindow = area.widget ( index ) if not self.hasToolWindow ( toolWindow ): qWarning ( 'unknown tab in tab widget' ) return self.hideToolWindow ( toolWindow ) def createSplitter ( self ): splitter = QSplitter () splitter.setChildrenCollapsible ( False ) return splitter
def _create_rubber_band(self): rubber_band = QRubberBand(QRubberBand.Rectangle, self.label_image) pal = QPalette() pal.setBrush(QPalette.Highlight, QBrush(Qt.red)) rubber_band.setPalette(pal) return rubber_band