コード例 #1
0
class Window(QLabel):
    def __init__(self, parent=None):

        QLabel.__init__(self, parent)
        self.rubberBand = ResizableRubberBand(self)
        self.origin = QPoint()

    def mousePressEvent(self, event):

        if event.button() == Qt.LeftButton:

            self.origin = QPoint(event.pos())
            self.rubberBand.setGeometry(QRect(self.origin, QSize()))
            self.rubberBand.show()

    def mouseMoveEvent(self, event):

        if not self.origin.isNull():
            self.rubberBand.setGeometry(
                QRect(self.origin, event.pos()).normalized())

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Enter:
            print(str(self.rubberBand.pos()))
            print(str(self.rubberBand.size()))
            self.rubberBand.hide()
コード例 #2
0
class AreaEditWidget(TransparentWidget):

    # add a signal that emits the area selected
    areaSelected = Signal(QRect)
    areaRemoved = Signal(QPoint)

    def __init__(self, parent=None):
        super().__init__(opacity=0.25)
        # select area
        self.rubberband = QRubberBand(QRubberBand.Rectangle, self)
        # coords of mouse click
        self.origin = QPoint()

    def mousePressEvent(self, event):
        # left click starts the rubber band
        if event.button() == Qt.LeftButton:
            self.origin = QPoint(event.pos())
            self.rubberband.setGeometry(QRect(self.origin, QSize()))
            self.rubberband.show()
        # right click on a selected area to remove it
        if event.button() == Qt.RightButton:
            self.areaRemoved.emit(event.pos())

    def mouseMoveEvent(self, event):
        if not self.origin.isNull():
            self.rubberband.setGeometry(
                QRect(self.origin, event.pos()).normalized())

    def mouseReleaseEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.rubberband.hide()
            area_selected = self.rubberband.geometry()
            self.areaSelected.emit(area_selected)
コード例 #3
0
class MandelbrotWidget(QWidget):
    def __init__(self, parent=None):
        super(MandelbrotWidget, self).__init__(parent)

        self.thread = RenderThread()
        self.pixmap = QPixmap()
        self.pixmapOffset = QPoint()
        self.lastDragPos = QPoint()

        self.centerX = DefaultCenterX
        self.centerY = DefaultCenterY
        self.pixmapScale = DefaultScale
        self.curScale = DefaultScale

        self.thread.renderedImage.connect(self.updatePixmap)

        self.setWindowTitle("Mandelbrot")
        self.setCursor(Qt.CrossCursor)
        self.resize(550, 400)

    def paintEvent(self, event):
        painter = QPainter(self)
        painter.fillRect(self.rect(), Qt.black)

        if self.pixmap.isNull():
            painter.setPen(Qt.white)
            painter.drawText(self.rect(), Qt.AlignCenter,
                             "Rendering initial image, please wait...")
            return

        if self.curScale == self.pixmapScale:
            painter.drawPixmap(self.pixmapOffset, self.pixmap)
        else:
            scaleFactor = self.pixmapScale / self.curScale
            newWidth = int(self.pixmap.width() * scaleFactor)
            newHeight = int(self.pixmap.height() * scaleFactor)
            newX = self.pixmapOffset.x() + (self.pixmap.width() - newWidth) / 2
            newY = self.pixmapOffset.y() + (self.pixmap.height() -
                                            newHeight) / 2

            painter.save()
            painter.translate(newX, newY)
            painter.scale(scaleFactor, scaleFactor)
            exposed, _ = painter.matrix().inverted()
            exposed = exposed.mapRect(self.rect()).adjusted(-1, -1, 1, 1)
            painter.drawPixmap(exposed, self.pixmap, exposed)
            painter.restore()

        text = "Use mouse wheel or the '+' and '-' keys to zoom. Press and " \
                "hold left mouse button to scroll."
        metrics = painter.fontMetrics()
        textWidth = metrics.width(text)

        painter.setPen(Qt.NoPen)
        painter.setBrush(QColor(0, 0, 0, 127))
        painter.drawRect((self.width() - textWidth) / 2 - 5, 0, textWidth + 10,
                         metrics.lineSpacing() + 5)
        painter.setPen(Qt.white)
        painter.drawText((self.width() - textWidth) / 2,
                         metrics.leading() + metrics.ascent(), text)

    def resizeEvent(self, event):
        self.thread.render(self.centerX, self.centerY, self.curScale,
                           self.size())

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Plus:
            self.zoom(ZoomInFactor)
        elif event.key() == Qt.Key_Minus:
            self.zoom(ZoomOutFactor)
        elif event.key() == Qt.Key_Left:
            self.scroll(-ScrollStep, 0)
        elif event.key() == Qt.Key_Right:
            self.scroll(+ScrollStep, 0)
        elif event.key() == Qt.Key_Down:
            self.scroll(0, -ScrollStep)
        elif event.key() == Qt.Key_Up:
            self.scroll(0, +ScrollStep)
        else:
            super(MandelbrotWidget, self).keyPressEvent(event)

    def wheelEvent(self, event):
        numDegrees = event.angleDelta().y() / 8
        numSteps = numDegrees / 15.0
        self.zoom(pow(ZoomInFactor, numSteps))

    def mousePressEvent(self, event):
        if event.buttons() == Qt.LeftButton:
            self.lastDragPos = QPoint(event.pos())

    def mouseMoveEvent(self, event):
        if event.buttons() & Qt.LeftButton:
            self.pixmapOffset += event.pos() - self.lastDragPos
            self.lastDragPos = QPoint(event.pos())
            self.update()

    def mouseReleaseEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.pixmapOffset += event.pos() - self.lastDragPos
            self.lastDragPos = QPoint()

            deltaX = (self.width() -
                      self.pixmap.width()) / 2 - self.pixmapOffset.x()
            deltaY = (self.height() -
                      self.pixmap.height()) / 2 - self.pixmapOffset.y()
            self.scroll(deltaX, deltaY)

    def updatePixmap(self, image, scaleFactor):
        if not self.lastDragPos.isNull():
            return

        self.pixmap = QPixmap.fromImage(image)
        self.pixmapOffset = QPoint()
        self.lastDragPosition = QPoint()
        self.pixmapScale = scaleFactor
        self.update()

    def zoom(self, zoomFactor):
        self.curScale *= zoomFactor
        self.update()
        self.thread.render(self.centerX, self.centerY, self.curScale,
                           self.size())

    def scroll(self, deltaX, deltaY):
        self.centerX += deltaX * self.curScale
        self.centerY += deltaY * self.curScale
        self.update()
        self.thread.render(self.centerX, self.centerY, self.curScale,
                           self.size())
コード例 #4
0
class CameraPreview(QOpenGLWidget):
    """ 
    widget to display camera feed and overlay droplet approximations from opencv
    """
    def __init__(self, parent=None):
        super(CameraPreview, self).__init__(parent)
        self.roi_origin = QPoint(0, 0)
        self._pixmap: QPixmap = QPixmap(480, 360)
        self._double_buffer: QImage = None
        self._raw_image: np.ndarray = None
        self._image_size = (1, 1)
        self._image_size_invalid = True
        self._roi_rubber_band = ResizableRubberBand(self)
        self._needle_mask = DynamicNeedleMask(self)
        self._needle_mask.update_mask_signal.connect(self.update_mask)
        self._baseline = Baseline(self)
        self._droplet = Droplet()
        self._mask = None
        logging.debug("initialized camera preview")

    def prepare(self):
        """ preset the baseline to 250 which is roughly base of the test image droplet """
        self._baseline.y_level = self.mapFromImage(y=250)

    def paintEvent(self, event: QPaintEvent):
        """
        custom paint event to 
        draw camera stream and droplet approximation if available

        uses double buffering to avoid flicker
        """
        # completely override super.paintEvent() to use double buffering
        painter = QPainter(self)

        buf = self.doubleBufferPaint(self._double_buffer)
        # painting the buffer pixmap to screen
        painter.drawImage(0, 0, buf)
        painter.end()

    def doubleBufferPaint(self, buffer=None):
        self.blockSignals(True)
        #self.drawFrame(painter)
        if buffer is None:
            buffer = QImage(self.width(), self.height(), QImage.Format_RGB888)
        buffer.fill(Qt.black)
        # calculate offset and scale of droplet image pixmap
        scale_x, scale_y, offset_x, offset_y = self.get_from_image_transform()

        db_painter = QPainter(buffer)
        db_painter.setRenderHints(QPainter.Antialiasing
                                  | QPainter.NonCosmeticDefaultPen)
        db_painter.setBackground(QBrush(Qt.black))
        db_painter.setPen(QPen(Qt.black, 0))
        db_painter.drawPixmap(offset_x, offset_y, self._pixmap)
        pen = QPen(Qt.magenta, 1)
        pen_fine = QPen(Qt.blue, 1)
        pen.setCosmetic(True)
        db_painter.setPen(pen)
        # draw droplet outline and tangent only if evaluate_droplet was successful
        if self._droplet.is_valid:
            try:
                # transforming true image coordinates to scaled pixmap coordinates
                db_painter.translate(offset_x, offset_y)
                db_painter.scale(scale_x, scale_y)

                # drawing tangents and baseline
                db_painter.drawLine(*self._droplet.line_l)
                db_painter.drawLine(*self._droplet.line_r)
                db_painter.drawLine(*self._droplet.int_l, *self._droplet.int_r)

                # move origin to ellipse origin
                db_painter.translate(*self._droplet.center)

                # draw diagnostics
                # db_painter.setPen(pen_fine)
                # #  lines parallel to coordinate axes
                # db_painter.drawLine(0,0,20*scale_x,0)
                # db_painter.drawLine(0,0,0,20*scale_y)
                # # angle arc
                # db_painter.drawArc(-5*scale_x, -5*scale_y, 10*scale_x, 10*scale_y, 0, -self._droplet.tilt_deg*16)

                # rotate coordinates to ellipse tilt
                db_painter.rotate(self._droplet.tilt_deg)

                # draw ellipse
                # db_painter.setPen(pen)
                db_painter.drawEllipse(-self._droplet.maj / 2,
                                       -self._droplet.min / 2,
                                       self._droplet.maj, self._droplet.min)

                # # major and minor axis for diagnostics
                # db_painter.drawLine(0, 0, self._droplet.maj/2, 0)
                # db_painter.drawLine(0, 0, 0, self._droplet.min/2)
            except Exception as ex:
                logging.error(ex)
        db_painter.end()
        self.blockSignals(False)
        return buffer

    def mousePressEvent(self, event):
        """
        mouse pressed handler
        
        creates ROI rubberband rectangle
        """
        if event.button() == Qt.LeftButton:
            # create new rubberband rectangle
            self.roi_origin = QPoint(event.pos())
            self._roi_rubber_band.hide()
            self._roi_rubber_band.setGeometry(QRect(self.roi_origin, QSize()))
            self._roi_rubber_band.show()

    def mouseMoveEvent(self, event):
        """
        mouse moved handler

        resizes the ROI rubberband rectangle if left mouse button is pressed
        """
        if event.buttons() == Qt.NoButton:
            pass
        elif event.buttons() == Qt.LeftButton:
            # resize rubberband while mouse is moving
            if not self.roi_origin.isNull():
                self._roi_rubber_band.setGeometry(
                    QRect(self.roi_origin, event.pos()).normalized())
        elif event.buttons() == Qt.RightButton:
            pass

    def keyPressEvent(self, event):
        """
        keyboard pressed handler

        - Esc: aborts ROI rubberband and hides it
        - Enter: applys ROI from rubberband to camera
        """
        if event.key() == Qt.Key_Enter:
            # apply the ROI set by the rubberband
            self.parent().apply_roi()
        elif event.key() == Qt.Key_Escape:
            # hide rubberband
            self._abort_roi()
            self.update()

    def hide_mask(self):
        """hides and disables the needle mask
        """
        self._needle_mask.hide()
        self._mask = None

    def show_mask(self):
        """shows the needle mask
        """
        self._needle_mask.show()
        self.update_mask()

    def update_mask(self):
        """update mask from widget
        """
        mask_rect = self._needle_mask.get_mask_geometry()
        self._mask = self.mapToImage(*mask_rect[:])

    @Slot(np.ndarray, bool)
    def update_image(self, cv_img: np.ndarray, eval: bool = True):
        """ 
        Updates the image_label with a new opencv image
        
        :param cv_img: camera image array
        :param eval: if True: do image processing on given image

        .. seealso:: :py:meth:`camera_control.CameraControl.update_image`
        """
        self._raw_image = cv_img
        try:
            # evaluate droplet only if camera is running or if a oneshot eval is requested
            if eval:
                try:
                    self._droplet.is_valid = False
                    evaluate_droplet(cv_img, self.get_baseline_y(), self._mask)
                except (ContourError, cv2.error, TypeError):
                    pass
                except Exception as ex:
                    logging.exception("Exception thrown in %s",
                                      "fcn:evaluate_droplet",
                                      exc_info=ex)
            else:
                self._droplet.is_valid = False
            qt_img = self._convert_cv_qt(cv_img)
            self._pixmap = qt_img
            if self._image_size_invalid:
                self._image_size = np.shape(cv_img)
                self.set_new_baseline_constraints()
                self._image_size_invalid = False
            self.update()
            # del cv_img
        except Exception as ex:
            logging.exception("Exception thrown in %s",
                              "class:camera_preview fcn:update_image",
                              exc_info=ex)

    def grab_image(self, raw=False):
        if raw:
            return self._convert_cv_qt(self._raw_image, False)
        else:
            return self.doubleBufferPaint(self._double_buffer)

    def _convert_cv_qt(self, cv_img: np.ndarray, scaled=True):
        """
        Convert from an opencv image to QPixmap
        
        :param cv_img: opencv image as numpy array
        :param scaled: if true or omitted returns an image scaled to widget dimensions
        :returns: opencv image as full size QImage or QPixmap scaled to widget dimensions
        """
        #rgb_image = cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB)
        #h, w, ch = rgb_image.shape
        h, w, ch = cv_img.shape
        bytes_per_line = ch * w
        qimg = QtGui.QImage(cv_img, w, h, bytes_per_line,
                            QtGui.QImage.Format_Grayscale8)
        if scaled:
            qimg_scaled = qimg.scaled(self.size(), Qt.KeepAspectRatio)
            return QPixmap.fromImage(qimg_scaled)
        else:
            return qimg

    def map_droplet_drawing_vals(self, droplet: Droplet):
        """ 
        convert the droplet values from image coords into pixmap coords and values better for drawing 
        
        :param droplet: droplet object containing the data
        :returns: **tuple** (tangent_l, tangent_r, int_l, int_r, center, maj, min)

            - **tangent_l**: start and end coordinates left tangent as (x1,y1,x2,y2)
            - **tangent_r**: start and end coordinates right tangent as (x1,y1,x2,y2)
            - **int_l**: left intersection of ellipse and baseline as (x,y)
            - **int_l**: right intersection of ellipse and baseline as (x,y)
            - **center**: center of the ellipse as (x,y)
            - **maj**: major axis length of the ellipse
            - **min**: minor axis length of the ellipse
        """
        tangent_l = tuple(
            self.mapFromImage(droplet.line_l[0:1]) +
            self.mapFromImage(droplet.line_l[2:3]))
        #tuple(map(lambda x: self.mapFromImage(*x), droplet.line_l))
        tangent_r = tuple(
            self.mapFromImage(droplet.line_r[0:1]) +
            self.mapFromImage(droplet.line_r[2:3]))
        #tuple(map(lambda x: self.mapFromImage(*x), droplet.line_r))
        center = self.mapFromImage(*droplet.center)
        maj, min = self.mapFromImage(droplet.maj, droplet.min)
        int_l = self.mapFromImage(*droplet.int_l)
        int_r = self.mapFromImage(*droplet.int_r)
        return tangent_l, tangent_r, int_l, int_r, center, maj, min

    def mapToImage(self, x=None, y=None, w=None, h=None):
        """ 
        Convert QLabel coordinates to image pixel coordinates

        :param x: x coordinate to be transformed
        :param y: y coordinate to be transformed
        :returns: x or y or Tuple (x,y) of the transformed coordinates, depending on what parameters where given
        """
        scale_x, scale_y, offset_x, offset_y = self.get_from_image_transform()
        res: List[int] = []
        if x is not None:
            # subtract half the width delta, then scale
            tr_x = int(round((x - offset_x) / scale_x))
            res.append(tr_x)
        if y is not None:
            tr_y = int(round((y - offset_y) / scale_y))
            res.append(tr_y)
        if w is not None:
            tr_w = int(round(w / scale_x))
            res.append(tr_w)
        if h is not None:
            tr_h = int(round(h / scale_y))
            res.append(tr_h)
        return tuple(res) if len(res) > 1 else res[0]

    def mapFromImage(self, x=None, y=None, w=None, h=None):
        """ 
        Convert Image pixel coordinates to QLabel coordinates

        :param x: x coordinate to be transformed
        :param y: y coordinate to be transformed
        :returns: x or y or Tuple (x,y) of the transformed coordinates, depending on what parameters where given
        """
        scale_x, scale_y, offset_x, offset_y = self.get_from_image_transform()
        res: List[int] = []
        if x is not None:
            tr_x = int(round((x * scale_x) + offset_x))
            res.append(tr_x)
        if y is not None:
            tr_y = int(round((y * scale_y) + offset_y))
            res.append(tr_y)
        if w is not None:
            tr_w = int(round(w * scale_x))
            res.append(tr_w)
        if h is not None:
            tr_h = int(round(h * scale_y))
            res.append(tr_h)
        return tuple(res) if len(res) > 1 else res[0]

    def get_from_image_transform(self):
        """ 
        Gets the scale and offset for a Image to QLabel coordinate transform 

        :returns: 4-Tuple: Scale factors for x and y as tuple, Offset as tuple (x,y)
        """
        pw, ph = self._pixmap.size().toTuple()  # scaled image size
        ih, iw = self._image_size[0], self._image_size[
            1]  # original size of image
        cw, ch = self.size().toTuple()  # display container size
        scale_x = float(pw / iw)
        offset_x = abs(pw - cw) / 2
        scale_y = float(ph / ih)
        offset_y = abs(ph - ch) / 2
        return scale_x, scale_y, offset_x, offset_y

    def show_baseline(self):
        """ Show the baseline selector """
        self._baseline.show()

    def hide_baseline(self):
        """ Hide the baseline selector """
        self._baseline.hide()

    def hide_rubberband(self):
        """ Hide the rubberband """
        self._roi_rubber_band.hide()

    def get_baseline_y(self) -> int:
        """ 
        return the y value the baseline is on in image coordinates 
        
        :returns: y value of baseline in image coordinates
        """
        y_base = self._baseline.y_level
        y = self.mapToImage(y=y_base)
        return y

    def set_new_baseline_constraints(self):
        """ set the min and max y value for the baseline """
        pix_size = self._pixmap.size()
        offset_y = int(round(abs(pix_size.height() - self.height()) / 2))
        self._baseline.max_level = pix_size.height() + offset_y
        self._baseline.min_level = offset_y

    def get_roi(self):
        """ return the ROI selected by the rubberband """
        x, y = self._roi_rubber_band.mapToParent(QPoint(0, 0)).toTuple()
        w, h = self._roi_rubber_band.size().toTuple()
        self.hide_rubberband()
        x, y, w, h = self.mapToImage(x, y, w, h)
        #w,h = self.mapToImage(x=w, y=h)
        return x, y, w, h

    def _abort_roi(self):
        """
        abort ROI set by hiding the rubberband selector
        """
        self._roi_rubber_band.hide()
        logging.info("aborted ROI select")

    def invalidate_imagesize(self):
        """
        invalidate image size, causes image size to be reevaluated on next camera image
        """
        self._image_size_invalid = True
コード例 #5
0
ファイル: mandelbrot.py プロジェクト: amirkogit/QtTestGround
class MandelbrotWidget(QWidget):
    def __init__(self, parent=None):
        super(MandelbrotWidget, self).__init__(parent)

        self.thread = RenderThread()
        self.pixmap = QPixmap()
        self.pixmapOffset = QPoint()
        self.lastDragPos = QPoint()

        self.centerX = DefaultCenterX
        self.centerY = DefaultCenterY
        self.pixmapScale = DefaultScale
        self.curScale = DefaultScale

        self.thread.renderedImage.connect(self.updatePixmap)

        self.setWindowTitle("Mandelbrot")
        self.setCursor(Qt.CrossCursor)
        self.resize(550, 400)

    def paintEvent(self, event):
        painter = QPainter(self)
        painter.fillRect(self.rect(), Qt.black)

        if self.pixmap.isNull():
            painter.setPen(Qt.white)
            painter.drawText(self.rect(), Qt.AlignCenter,
                    "Rendering initial image, please wait...")
            return

        if self.curScale == self.pixmapScale:
            painter.drawPixmap(self.pixmapOffset, self.pixmap)
        else:
            scaleFactor = self.pixmapScale / self.curScale
            newWidth = int(self.pixmap.width() * scaleFactor)
            newHeight = int(self.pixmap.height() * scaleFactor)
            newX = self.pixmapOffset.x() + (self.pixmap.width() - newWidth) / 2
            newY = self.pixmapOffset.y() + (self.pixmap.height() - newHeight) / 2

            painter.save()
            painter.translate(newX, newY)
            painter.scale(scaleFactor, scaleFactor)
            exposed, _ = painter.matrix().inverted()
            exposed = exposed.mapRect(self.rect()).adjusted(-1, -1, 1, 1)
            painter.drawPixmap(exposed, self.pixmap, exposed)
            painter.restore()

        text = "Use mouse wheel or the '+' and '-' keys to zoom. Press and " \
                "hold left mouse button to scroll."
        metrics = painter.fontMetrics()
        textWidth = metrics.width(text)

        painter.setPen(Qt.NoPen)
        painter.setBrush(QColor(0, 0, 0, 127))
        painter.drawRect((self.width() - textWidth) / 2 - 5, 0, textWidth + 10,
                metrics.lineSpacing() + 5)
        painter.setPen(Qt.white)
        painter.drawText((self.width() - textWidth) / 2,
                metrics.leading() + metrics.ascent(), text)

    def resizeEvent(self, event):
        self.thread.render(self.centerX, self.centerY, self.curScale, self.size())

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Plus:
            self.zoom(ZoomInFactor)
        elif event.key() == Qt.Key_Minus:
            self.zoom(ZoomOutFactor)
        elif event.key() == Qt.Key_Left:
            self.scroll(-ScrollStep, 0)
        elif event.key() == Qt.Key_Right:
            self.scroll(+ScrollStep, 0)
        elif event.key() == Qt.Key_Down:
            self.scroll(0, -ScrollStep)
        elif event.key() == Qt.Key_Up:
            self.scroll(0, +ScrollStep)
        else:
            super(MandelbrotWidget, self).keyPressEvent(event)

    def wheelEvent(self, event):
        numDegrees = event.angleDelta().y() / 8
        numSteps = numDegrees / 15.0
        self.zoom(pow(ZoomInFactor, numSteps))

    def mousePressEvent(self, event):
        if event.buttons() == Qt.LeftButton:
            self.lastDragPos = QPoint(event.pos())

    def mouseMoveEvent(self, event):
        if event.buttons() & Qt.LeftButton:
            self.pixmapOffset += event.pos() - self.lastDragPos
            self.lastDragPos = QPoint(event.pos())
            self.update()

    def mouseReleaseEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.pixmapOffset += event.pos() - self.lastDragPos
            self.lastDragPos = QPoint()

            deltaX = (self.width() - self.pixmap.width()) / 2 - self.pixmapOffset.x()
            deltaY = (self.height() - self.pixmap.height()) / 2 - self.pixmapOffset.y()
            self.scroll(deltaX, deltaY)

    def updatePixmap(self, image, scaleFactor):
        if not self.lastDragPos.isNull():
            return

        self.pixmap = QPixmap.fromImage(image)
        self.pixmapOffset = QPoint()
        self.lastDragPosition = QPoint()
        self.pixmapScale = scaleFactor
        self.update()

    def zoom(self, zoomFactor):
        self.curScale *= zoomFactor
        self.update()
        self.thread.render(self.centerX, self.centerY, self.curScale,
                self.size())

    def scroll(self, deltaX, deltaY):
        self.centerX += deltaX * self.curScale
        self.centerY += deltaY * self.curScale
        self.update()
        self.thread.render(self.centerX, self.centerY, self.curScale,
                self.size())