Beispiel #1
0
def find_display_size(display: GameDisplay, view: QGraphicsView,
                      target_size: QSize) -> QSize:
    max_width = None
    max_height = None
    min_width = min_height = 1
    display_width = display.width()
    display_height = display.height()
    while True:
        scene_size = view.contentsRect().size()
        if scene_size.width() == target_size.width():
            min_width = max_width = display_width
        elif scene_size.width() < target_size.width():
            min_width = display_width + 1
        else:
            max_width = display_width - 1
        if scene_size.height() == target_size.height():
            min_height = max_height = display_height
        elif scene_size.height() < target_size.height():
            min_height = display_height + 1
        else:
            max_height = display_height - 1
        if max_width is None:
            display_width *= 2
        else:
            display_width = (min_width + max_width) // 2
        if max_height is None:
            display_height *= 2
        else:
            display_height = (min_height + max_height) // 2
        if min_width == max_width and min_height == max_height:
            return QSize(display_width, display_height)
        display.resize(display_width, display_height)
        view.grab()  # Force layout recalculation.
Beispiel #2
0
 def aspect_fit(self, size: QSize):
     win_gradient = size.height() / size.width()
     pix_gradient = self.pixmap().height() / self.pixmap().width()
     if win_gradient > pix_gradient:
         self.resize(size.width(), int(size.width() * pix_gradient))
         self.factor = round(size.width() / self.pixmap().width(), 2)
     else:
         self.resize(int(size.height() / pix_gradient), size.height())
         self.factor = round(size.height() / self.pixmap().height(), 2)
 def calculateSize(self, sizeType):
     totalSize = QSize()
     for wrapper in self.list:
         position = wrapper.position
         itemSize = QSize()
         if sizeType == BorderLayout.MinimumSize:
             itemSize = wrapper.item.minimumSize()
         else:  # sizeType == BorderLayout.SizeHint
             itemSize = wrapper.item.sizeHint()
         if position in (BorderLayout.North, BorderLayout.South,
                         BorderLayout.Center):
             totalSize.setHeight(totalSize.height() + itemSize.height())
         if position in (BorderLayout.West, BorderLayout.East,
                         BorderLayout.Center):
             totalSize.setWidth(totalSize.width() + itemSize.width())
     return totalSize
Beispiel #4
0
    def printImage(self):  #Ok
        """Imprimir Imagem
        """
        printer = QPrinter()
        printer.setOutputFormat(QPrinter.NativeFormat)

        prnt_dlg = QPrintDialog(printer)

        if (prnt_dlg.exec_() == QPrintDialog.Accepted):

            painter = QPainter()

            painter.begin(printer)

            rect = QRect(painter.viewport())

            size = QSize(self.img_lbl.pixmap().size())
            size.scale(rect.size(), Qt.KeepAspectRatio)

            painter.setViewport(rect.x(), rect.y(), size.width(),
                                size.height())
            painter.setWindow(self.img_lbl.pixmap().rect())

            painter.drawPixmap(0, 0, self.img_lbl.pixmap())

            painter.end()
Beispiel #5
0
    def save_image_to(self, path):

        TOP_MARGIN = 50
        LEFT_MARGIN = 50

        # Determine the size of the entire graph
        graph_size = self._graph_size()

        image_size = QSize(graph_size.width() + LEFT_MARGIN * 2,
                           graph_size.height() + TOP_MARGIN * 2
                           )

        image = QImage(image_size, QImage.Format_ARGB32)
        image.fill(Qt.white)  # white background

        painter = QPainter(image)
        painter.translate(TOP_MARGIN, LEFT_MARGIN)
        painter.setRenderHint(QPainter.TextAntialiasing)
        self._paint(painter,
                    QPoint(-TOP_MARGIN, -LEFT_MARGIN),
                    QPoint(image_size.width(), image_size.height())
                    )
        painter.end()

        image.save(path)
    def SetData(self,
                pSize=None,
                dataLen=0,
                state="",
                waifuSize=None,
                waifuDataLen=0,
                waifuState="",
                waifuTick=0,
                isInit=False):
        self.UpdateSlider()
        self.epsLabel.setText("位置:{}/{}".format(self.readImg.curIndex + 1,
                                                self.readImg.maxPic))
        if pSize or isInit:
            if not pSize:
                pSize = QSize(0, 0)
            self.resolutionLabel.setText("分辨率:{}x{}".format(
                str(pSize.width()), str(pSize.height())))

        if dataLen or isInit:
            self.sizeLabel.setText("大小: " + ToolUtil.GetDownloadSize(dataLen))

        if waifuSize or isInit:
            if not waifuSize:
                waifuSize = QSize(0, 0)
            self.resolutionWaifu.setText("分辨率:{}x{}".format(
                str(waifuSize.width()), str(waifuSize.height())))
        if waifuDataLen or isInit:
            self.sizeWaifu.setText("大小:" +
                                   ToolUtil.GetDownloadSize(waifuDataLen))

        if state or isInit:
            self.stateLable.setText("状态:" + state)

        if waifuState or isInit:
            if waifuState == QtFileData.WaifuStateStart:
                self.stateWaifu.setStyleSheet("color:red;")
            elif waifuState == QtFileData.WaifuStateEnd:
                self.stateWaifu.setStyleSheet("color:green;")
            elif waifuState == QtFileData.WaifuStateFail:
                self.stateWaifu.setStyleSheet("color:red;")
            else:
                self.stateWaifu.setStyleSheet("color:dark;")
            if config.CanWaifu2x:
                self.stateWaifu.setText("状态:" + waifuState)
        if waifuTick or isInit:
            self.tickLabel.setText("耗时:" + str(waifuTick) + "s")
Beispiel #7
0
 def _read_image(self, path: Path) -> Optional[QImage]:
     data = self._read_file(path)
     if data:
         image = QImage()
         image.loadFromData(data, path.suffix[1:])
         max_size = QSize(600, 400)
         image_size = image.size()
         if image_size.width() > max_size.width() or image_size.height(
         ) > max_size.height():
             image = image.scaled(max_size,
                                  Qt.AspectRatioMode.KeepAspectRatio)
         return image
Beispiel #8
0
 def motion(self, event, size: QSize):
     w0, h0 = size.width(), size.height()
     self.mouse_pre_x, self.mouse_pre_y = self.mouse_x, self.mouse_y
     self.mouse_x = (2.0 * event.x() - w0) / w0
     self.mouse_y = (h0 - 2.0 * event.y()) / h0
     if self.button & Qt.LeftButton:
         if self.modifiers & Qt.AltModifier:
             self.camera.rotation(self.mouse_x, self.mouse_y,
                                  self.mouse_pre_x, self.mouse_pre_y)
         elif self.modifiers & Qt.ShiftModifier:
             self.camera.translation(self.mouse_x, self.mouse_y,
                                     self.mouse_pre_x, self.mouse_pre_y)
     self.updated.emit()
class %CLASS%(QWidget):
    def __init__(self, parent_node_instance):
        super(%CLASS%, self).__init__()

        # leave these lines ------------------------------
        self.parent_node_instance = parent_node_instance
        # ------------------------------------------------

        self.video_size = QSize(400, 300)
        self.timer = QTimer()
        self.capture = None

        self.image_label = QLabel()
        self.image_label.setFixedSize(self.video_size)

        self.main_layout = QVBoxLayout()
        self.main_layout.addWidget(self.image_label)
        self.setLayout(self.main_layout)

        self.setup_camera()

        self.resize(self.video_size)

    def setup_camera(self):
        self.capture = cv2.VideoCapture(0 + cv2.CAP_DSHOW)
        self.capture.set(cv2.CAP_PROP_FRAME_WIDTH, self.video_size.width())
        self.capture.set(cv2.CAP_PROP_FRAME_HEIGHT, self.video_size.height())

        self.timer.timeout.connect(M(self.display_video_stream))
        self.timer.start(30)

    def display_video_stream(self):
        _, frame = self.capture.read()
        frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        # frame = cv2.flip(frame, 1)
        image = QImage(frame, frame.shape[1], frame.shape[0],
                       frame.strides[0], QImage.Format_RGB888)
        scaled_image = image.scaled(self.video_size)
        self.image_label.setPixmap(QPixmap.fromImage(scaled_image))

        self.parent_node_instance.video_picture_updated(frame)

    def get_data(self):
        return {}  # self.text()

    def set_data(self, data):
        pass #self.setText(data)


    def remove_event(self):
        self.timer.stop()
Beispiel #10
0
    def __init__(self, ctx: RenderContext, size: QSize):
        self.ctx = ctx
        self._size = size
        self._handle = self._texture = self._rbo = 0

        self.ctx.makeCurrent()
        self._handle = GL.glGenFramebuffers(1)
        GL.glBindFramebuffer(GL.GL_FRAMEBUFFER, self._handle)

        # Render to texture
        self._texture = GL.glGenTextures(1)
        GL.glBindTexture(GL.GL_TEXTURE_2D, self._texture)
        GL.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_RGBA, size.width(),
                        size.height(), 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE,
                        None)
        GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER,
                           GL.GL_LINEAR)
        GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER,
                           GL.GL_LINEAR)
        GL.glFramebufferTexture2D(GL.GL_FRAMEBUFFER, GL.GL_COLOR_ATTACHMENT0,
                                  GL.GL_TEXTURE_2D, self._texture, 0)

        # But use render buffer for depth and stencil buffers
        self._rbo = GL.glGenRenderbuffers(1)
        GL.glBindRenderbuffer(GL.GL_RENDERBUFFER, self._rbo)
        GL.glRenderbufferStorage(GL.GL_RENDERBUFFER, GL.GL_DEPTH24_STENCIL8,
                                 size.width(), size.height())
        GL.glBindRenderbuffer(GL.GL_RENDERBUFFER, 0)
        GL.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER,
                                     GL.GL_DEPTH_STENCIL_ATTACHMENT,
                                     GL.GL_RENDERBUFFER, self._rbo)

        result = GL.glCheckFramebufferStatus(GL.GL_FRAMEBUFFER)
        if result != GL.GL_FRAMEBUFFER_COMPLETE:
            raise ValueError(result)

        GL.glBindFramebuffer(GL.GL_FRAMEBUFFER, 0)
Beispiel #11
0
 def read_settings(self):
     settings = get_settings()
     # noinspection PyArgumentList
     screen = QApplication.desktop().screenGeometry()
     h = min(screen.height() * 5 / 6., 900)
     size = QSize(min(screen.width() * 5 / 6., 1200), h)
     pos = settings.value("pos", None)
     savesize = settings.value("size", size)
     if savesize.width() > screen.width():
         savesize.setWidth(size.width())
     if savesize.height() > screen.height():
         savesize.setHeight(size.height())
     self.resize(savesize)
     if ((pos is None or pos.x() + savesize.width() > screen.width()
          or pos.y() + savesize.height() > screen.height())):
         self.move(screen.center() - self.rect().center())
     else:
         self.move(pos)
    def drawForeground(self, painter, rect):
        gridSize = QSize(10, 10)
        if self._showGrid:
            rect = self.getImageDims()

            pen = QPen(
                QColor(styleColor[0], styleColor[1], styleColor[2], 150), 3)
            pen.setStyle(QtCore.Qt.CustomDashLine)
            pen.setDashPattern([1, 2])

            painter.setPen(pen)

            d = rect.width() / gridSize.width()
            for y in range(1, gridSize.height()):
                painter.drawLine(QPoint(0, int(y * d)),
                                 QPoint(int(rect.width()), int(y * d)))

            for x in range(1, gridSize.width()):
                painter.drawLine(QPoint(int(x * d), 0),
                                 QPoint(int(x * d), int(rect.height())))
    def createFramebufferObject(self, size: QSize) -> QOpenGLFramebufferObject:
        qDebug('RendererHelper::createFramebufferObject()')
        macSize = QSize(size.width() / 2, size.height() / 2)

        format = QOpenGLFramebufferObjectFormat()
        format.setAttachment(QOpenGLFramebufferObject.Depth)

        # ifdef Q_OS_MAC
        #     std::unique_ptr<QOpenGLFramebufferObject> framebufferObject(new QOpenGLFramebufferObject(macSize, format))
        # else
        framebufferObject = QOpenGLFramebufferObject(size, format)
        # endif
        self.__m_vtkRenderWindow.SetBackLeftBuffer(GL.GL_COLOR_ATTACHMENT0)
        self.__m_vtkRenderWindow.SetFrontLeftBuffer(GL.GL_COLOR_ATTACHMENT0)
        self.__m_vtkRenderWindow.SetBackBuffer(GL.GL_COLOR_ATTACHMENT0)
        self.__m_vtkRenderWindow.SetFrontBuffer(GL.GL_COLOR_ATTACHMENT0)
        self.__m_vtkRenderWindow.SetSize(framebufferObject.size().width(),
                                         framebufferObject.size().height())
        self.__m_vtkRenderWindow.SetOffScreenRendering(True)
        self.__m_vtkRenderWindow.Modified()
        framebufferObject.release()
        return framebufferObject
Beispiel #14
0
 def mouse(self, event, size: QSize):
     self.mouse_x = (2.0 * event.x() - size.width()) / size.width()
     self.mouse_y = (size.height() - 2.0 * event.y()) / size.height()
     self.button = event.button()
     self.modifiers = event.modifiers()
     self.updated.emit()
class RandomWalker(object):
    MOVE_UP = 0
    MOVE_DOWN = 1
    MOVE_RIGHT = 2
    MOVE_LEFT = 3

    def __init__(self):
        self.rect = QRect()
        self.path = []
        self.start_pos = QVector2D()
        self.pos = QVector2D()
        self.resolution = QSize()

        self.up_vector = QVector2D(0, -1)
        self.down_vector = QVector2D(0, 1)
        self.left_vector = QVector2D(-1, 0)
        self.right_vector = QVector2D(1, 0)

        self.head_color = QColor(250, 60, 50)
        self.tail_color = QColor(70, 70, 70)

    def reset(self):
        self.path = []
        self.pos = self.start_pos

    def setup(self):
        self.reset()

    def get_option(self):
        choice = random.randint(0, 3)
        pos_copy = QVector2D(self.pos)

        if choice == self.MOVE_UP:
            pos_copy += self.up_vector
        elif choice == self.MOVE_DOWN:
            pos_copy += self.down_vector
        elif choice == self.MOVE_LEFT:
            pos_copy += self.left_vector
        elif choice == self.MOVE_RIGHT:
            pos_copy += self.right_vector
        return pos_copy

    def tick(self):
        """
        Tick the random walker to do something
        """
        while True:
            pos = self.get_option()
            is_valid = True
            if pos.x() > self.resolution.width():
                is_valid = False
            elif pos.x() < 0:
                is_valid = False
            elif pos.y() > self.resolution.height():
                is_valid = False
            elif pos.y() < 0:
                is_valid = False

            if is_valid:
                self.path.append(pos)
                self.pos = pos
                break

    def paint_cell(self, painter, pos, color):
        chunk_x = float(self.rect.width()) / self.resolution.width()
        chunk_y = float(self.rect.height()) / self.resolution.height()
        x = pos.x() * chunk_x
        y = pos.y() * chunk_y
        painter.fillRect(x - chunk_x / 2, y - chunk_y - 2, math.ceil(chunk_x),
                         math.ceil(chunk_y), color)

    def paint(self, painter):
        for p in self.path:
            self.paint_cell(painter, p, self.tail_color)

        self.paint_cell(painter, self.pos, self.head_color)
Beispiel #16
0
 def calculate_proper_size(self, from_size: QSize):
     minimum_length = min(from_size.width(), from_size.height())
     return QSize(minimum_length - self.spacing(),
                  minimum_length - self.spacing())
class SearchBarEditor(QTableView):
    """A Google-like search bar, implemented as a QTableView with a _CustomLineEditDelegate in the first row.
    """

    data_committed = Signal()

    def __init__(self, parent, tutor=None):
        """Initializes instance.

        Args:
            parent (QWidget): parent widget
            tutor (QWidget, NoneType): another widget used for positioning.
        """
        super().__init__(parent)
        self._tutor = tutor
        self._base_size = QSize()
        self._base_offset = QPoint()
        self._original_text = None
        self._orig_pos = None
        self.first_index = QModelIndex()
        self.model = QStandardItemModel(self)
        self.proxy_model = QSortFilterProxyModel(self)
        self.proxy_model.setSourceModel(self.model)
        self.proxy_model.filterAcceptsRow = self._proxy_model_filter_accepts_row
        self.setModel(self.proxy_model)
        self.verticalHeader().hide()
        self.horizontalHeader().hide()
        self.setShowGrid(False)
        self.setMouseTracking(True)
        self.setTabKeyNavigation(False)
        delegate = _CustomLineEditDelegate(self)
        delegate.text_edited.connect(self._handle_delegate_text_edited)
        self.setItemDelegateForRow(0, delegate)

    def set_data(self, current, items):
        """Populates model.

        Args:
            current (str)
            items (Sequence(str))
        """
        item_list = [QStandardItem(current)]
        for item in items:
            qitem = QStandardItem(item)
            item_list.append(qitem)
            qitem.setFlags(~Qt.ItemIsEditable)
        self.model.invisibleRootItem().appendRows(item_list)
        self.first_index = self.proxy_model.mapFromSource(
            self.model.index(0, 0))

    def set_base_size(self, size):
        self._base_size = size

    def set_base_offset(self, offset):
        self._base_offset = offset

    def update_geometry(self):
        """Updates geometry.
        """
        self.horizontalHeader().setDefaultSectionSize(self._base_size.width())
        self.verticalHeader().setDefaultSectionSize(self._base_size.height())
        self._orig_pos = self.pos() + self._base_offset
        if self._tutor:
            self._orig_pos += self._tutor.mapTo(self.parent(),
                                                self._tutor.rect().topLeft())
        self.refit()

    def refit(self):
        self.move(self._orig_pos)
        table_height = self.verticalHeader().length()
        size = QSize(self._base_size.width(),
                     table_height + 2).boundedTo(self.parent().size())
        self.resize(size)
        # Adjust position if widget is outside parent's limits
        bottom_right = self.mapToGlobal(self.rect().bottomRight())
        parent_bottom_right = self.parent().mapToGlobal(
            self.parent().rect().bottomRight())
        x_offset = max(0, bottom_right.x() - parent_bottom_right.x())
        y_offset = max(0, bottom_right.y() - parent_bottom_right.y())
        self.move(self.pos() - QPoint(x_offset, y_offset))

    def data(self):
        return self.first_index.data(Qt.EditRole)

    @Slot("QString")
    def _handle_delegate_text_edited(self, text):
        """Filters model as the first row is being edited."""
        self._original_text = text
        self.proxy_model.setFilterRegExp("^" + text)
        self.proxy_model.setData(self.first_index, text)
        self.refit()

    def _proxy_model_filter_accepts_row(self, source_row, source_parent):
        """Always accept first row.
        """
        if source_row == 0:
            return True
        return QSortFilterProxyModel.filterAcceptsRow(self.proxy_model,
                                                      source_row,
                                                      source_parent)

    def keyPressEvent(self, event):
        """Sets data from current index into first index as the user navigates
        through the table using the up and down keys.
        """
        super().keyPressEvent(event)
        event.accept(
        )  # Important to avoid unhandled behavior when trying to navigate outside view limits
        # Initialize original text. TODO: Is there a better place for this?
        if self._original_text is None:
            self.proxy_model.setData(self.first_index, event.text())
            self._handle_delegate_text_edited(event.text())
        # Set data from current index in model
        if event.key() in (Qt.Key_Up, Qt.Key_Down):
            current = self.currentIndex()
            if current.row() == 0:
                self.proxy_model.setData(self.first_index, self._original_text)
            else:
                self.proxy_model.setData(self.first_index, current.data())

    def currentChanged(self, current, previous):
        super().currentChanged(current, previous)
        self.edit_first_index()

    def edit_first_index(self):
        """Edits first index if valid and not already being edited.
        """
        if not self.first_index.isValid():
            return
        if self.isPersistentEditorOpen(self.first_index):
            return
        self.edit(self.first_index)

    def mouseMoveEvent(self, event):
        """Sets the current index to the one hovered by the mouse."""
        if not self.currentIndex().isValid():
            return
        index = self.indexAt(event.pos())
        if index.row() == 0:
            return
        self.setCurrentIndex(index)

    def mousePressEvent(self, event):
        """Commits data."""
        index = self.indexAt(event.pos())
        if index.row() == 0:
            return
        self.proxy_model.setData(self.first_index, index.data(Qt.EditRole))
        self.data_committed.emit()
Beispiel #18
0
class LevyFlight(object):
    MOVE_UP = 0
    MOVE_DOWN = 1
    MOVE_RIGHT = 2
    MOVE_LEFT = 3

    def create_random_vector2d(self):
        a = random.random() * math.pi * 2
        return QVector2D(math.cos(a), math.sin(a))

    def __init__(self):
        self.rect = QRect()
        self.path = []
        self.start_pos = QVector2D()
        self.pos = QVector2D()
        self.resolution = QSize()

        self.up_vector = QVector2D(0, -1)
        self.down_vector = QVector2D(0, 1)
        self.left_vector = QVector2D(-1, 0)
        self.right_vector = QVector2D(1, 0)

        self.head_color = QColor(250, 60, 50)
        self.tail_color = QColor(70, 70, 70)

    def reset(self):
        self.path = []
        self.pos = self.start_pos

    def setup(self):
        self.reset()

    def get_next_pos(self):
        pos_copy = QVector2D(self.pos)
        v = self.create_random_vector2d()
        if random.random() < 0.01:
            pos_copy += v * random.randint(20, 40)
        else:
            pos_copy += v * random.randint(1, 5)
        return pos_copy

    def tick(self):
        """
        Tick the random walker to do something
        """

        while True:
            pos = self.get_next_pos()
            is_valid = True
            if pos.x() > self.resolution.width():
                is_valid = False
            elif pos.x() < 0:
                is_valid = False
            elif pos.y() > self.resolution.height():
                is_valid = False
            elif pos.y() < 0:
                is_valid = False

            if is_valid:
                self.path.append(pos)
                self.pos = pos
                break

    def paint(self, painter):
        chunk_x = float(self.rect.width()) / self.resolution.width()
        chunk_y = float(self.rect.height()) / self.resolution.height()

        painter_path = QPainterPath()
        for i, pos in enumerate(self.path):
            x = pos.x() * chunk_x
            y = pos.y() * chunk_y
            p = QPoint(x, y)
            if i == 0:
                painter_path.moveTo(p)
            else:
                painter_path.lineTo(p)

        painter.drawPath(painter_path)
Beispiel #19
0
    def resize_image_viewer(self, new_size: QSize):
        width = max(50, min(new_size.width(), self.MAX_SIZE.width()))
        height = max(50, min(new_size.height(), self.MAX_SIZE.height()))
        new_size = QSize(width, height)

        self.resize(new_size)
Beispiel #20
0
def size_to_point(size: QSize) -> QPoint:
    return QPoint(size.width(), size.height())
class TColorPickerHue(QWidget):
    changed = Signal(int)

    def __init__(self, parent):
        super(TColorPickerHue, self).__init__(parent)
        self.setStatusTip("Hue. To control use Mouse Wheel or Shift+Up/Down")
        self._size = QSize(1, 91)
        self._scale = 3
        self.setFixedSize(QSize(23, self._size.height() * self._scale + 4))
        self._pixmap = QPixmap()
        self._pos = 0
        self.draw()

    def draw(self):
        ih = self._size.height()
        bits = bytearray(ih * 4)
        color = QColor()
        for h in range(ih):
            color.setHsv(360 * h / (ih - 1), 255, 255)
            color = qcolor_linear_to_srgb(color)
            (r, g, b, a) = color.getRgb()
            # For 3ds Max use bits[index] = chr(value)
            bits[h * 4 + 0] = b
            bits[h * 4 + 1] = g
            bits[h * 4 + 2] = r
            # [h * 4 + 3] = 0xff

        img = QImage(bits, 1, ih, QImage.Format_RGB32)
        self._pixmap = QPixmap.fromImage(img).scaled(
            self.minimumSize() - QSize(4, 4), Qt.IgnoreAspectRatio,
            Qt.FastTransformation)
        self.update()

    def paintEvent(self, event):
        painter = QPainter(self)
        brush = self.palette().window().color()
        painter.fillRect(self.rect(), brush)
        painter.drawPixmap(2, 2, self._pixmap)
        painter.save()
        pen = QPen(QBrush(Qt.black), 2., Qt.SolidLine, Qt.SquareCap,
                   Qt.MiterJoin)
        painter.setPen(pen)
        painter.drawRect(QRect(1, self._pos * self._scale + 1, 21, 5))
        painter.restore()

    def set_pos(self, y):
        ih = self._size.height() - 1
        self._pos = clamp(y, 0, ih)
        hue = int(360 * self._pos / ih)
        self.update()
        self.changed.emit(hue)

    def set_remap_pos(self, y):
        # Remap mouse position
        y = int(y / self._scale)
        self.set_pos(y)

    def mouseMoveEvent(self, event):
        self.set_remap_pos(event.pos().y() - 2)
        event.accept()

    def mousePressEvent(self, event):
        self.set_remap_pos(event.pos().y() - 2)
        event.accept()

    def keyPressEvent(self, event):
        key = event.key()
        if key in [Qt.Key_Left, Qt.Key_Right, Qt.Key_Up, Qt.Key_Down]:
            y = self._pos
            if key == Qt.Key_Up: y -= 1
            if key == Qt.Key_Down: y += 1
            self.set_pos(y)
            event.accept()

    def scale(self, value):
        value = max(1, min(3, value))
        self._scale = value
        self._size = QSize(1, 91 * (4 - value))
        self.setFixedSize(QSize(23, self._size.height() * self._scale + 4))
        self._pos = 0
        self.draw()
class TColorPickerSV(QWidget):
    changed = Signal(QColor)
    doubleClicked = Signal()

    def __init__(self, parent):
        super(TColorPickerSV, self).__init__(parent)
        self.setStatusTip("Saturation, Value. To control use Arrows")
        self._size = QSize(25, 25)
        self._scale = 11
        self.setMinimumSize(self._size * self._scale)
        self._color = QColor(Qt.white)
        self._pixmap = QPixmap()
        self._pos = QPoint(24, 24)
        self._hue = 0
        self._saturation = 255
        self._value = 255

    def set_hue(self, hue):
        hue = clamp(hue, 0, 360)
        self._hue = hue if hue < 360 else 0
        iw = self._size.width()
        ih = self._size.height()
        bpl = iw * 4
        bits = bytearray(iw * ih * 4)
        color = QColor()
        for v in range(ih):
            for s in range(iw):
                color.setHsv(hue, 255.0 / (iw - 1) * s,
                             255.0 - 255.0 / (ih - 1) * v)
                color = qcolor_linear_to_srgb(color)
                (r, g, b, a) = color.getRgb()
                # For 3ds Max use bits[index] = chr(value)
                bits[bpl * v + 4 * s + 0] = b
                bits[bpl * v + 4 * s + 1] = g
                bits[bpl * v + 4 * s + 2] = r
                # bits[bpl * v + 4*s + 3] = 0xff

        img = QImage(bits, iw, ih, QImage.Format_RGB32)
        self._pixmap = QPixmap.fromImage(img).scaled(self.minimumSize(),
                                                     Qt.KeepAspectRatio,
                                                     Qt.FastTransformation)
        self.set_pos(self._pos)

    def set_pos(self, pos):
        iw = self._size.width() - 1
        ih = self._size.height() - 1
        self._pos.setX(clamp(pos.x(), 0, iw))
        self._pos.setY(clamp(pos.y(), 0, ih))
        self._saturation = clamp(255 * self._pos.x() / iw, 0, 255)
        self._value = clamp(255 * self._pos.y() / ih, 0, 255)
        self._color = QColor.fromHsv(self._hue, self._saturation, self._value)

        self.update()
        self.changed.emit(self._color)

    def set_remap_pos(self, mouse_pos):
        # Remap mouse position
        pos = QPoint(int(mouse_pos.x() / self._scale),
                     int(self._size.height() - mouse_pos.y() / self._scale))
        self.set_pos(pos)

    def paintEvent(self, event):
        painter = QPainter(self)
        painter.drawPixmap(0, 0, self._pixmap)
        painter.save()
        painter.setCompositionMode(QPainter.RasterOp_SourceXorDestination)
        pen = QPen(QBrush(Qt.white), 2., Qt.SolidLine, Qt.SquareCap,
                   Qt.MiterJoin)
        painter.setPen(pen)
        s = self._scale
        painter.drawRect(
            QRect(self._pos.x() * s - 1,
                  (self._size.height() - 1 - self._pos.y()) * s - 1, s + 2,
                  s + 2))
        painter.restore()

    def mouseMoveEvent(self, event):
        self.set_remap_pos(event.pos())
        event.accept()

    def mousePressEvent(self, event):
        self.set_remap_pos(event.pos())
        event.accept()

    def mouseDoubleClickEvent(self, event):
        self.doubleClicked.emit()
        event.accept()

    def keyPressEvent(self, event):
        key = event.key()
        if key in [Qt.Key_Left, Qt.Key_Right, Qt.Key_Up, Qt.Key_Down]:
            x, y = self._pos.x(), self._pos.y()
            if key == Qt.Key_Left: x -= 1
            if key == Qt.Key_Right: x += 1
            if key == Qt.Key_Up: y += 1
            if key == Qt.Key_Down: y -= 1
            self.set_pos(QPoint(x, y))
            event.accept()
Beispiel #23
0
class ViewpointsListModel(QAbstractListModel):
    """
    Model class to the viewpoins list.

    It returns the name of a viewpoint, associated with the current topic as
    well as an icon of the snapshot file that is referenced in the viewpoint. If
    no snapshot is referenced then no icon is returned.  An icon is 10x10
    millimeters in dimension. The actual sizes and offsets, in pixels, are
    stored in variables containing 'Q'. All other sizes and offset variables
    hold values in millimeters.These sizes and offsets are scaled to the
    currently active screen, retrieved by `util.getCurrentQScreen()`.
    """
    def __init__(self, snapshotModel, parent=None):

        QAbstractListModel.__init__(self, parent=None)
        self.viewpoints = []
        """ List of instances of `ViewpointReference` """
        # used to retrieve the snapshot icons.
        self.snapshotModel = snapshotModel
        """ Reference to SnapshotModel used to retrieve icons of the snapshots
        """

        # set up the sizes and offsets
        self._iconQSize = None
        """ Size in pixels """
        self._iconSize = QSize(10, 10)
        """ Size in millimeters """
        self.calcSizes()

    def data(self, index, role=Qt.DisplayRole):
        """ Returns the file name and an icon of the snapshot associated to a
        viewpoint. """

        if not index.isValid():
            return None

        viewpoint = self.viewpoints[index.row()]
        if role == Qt.DisplayRole:
            # return the name of the viewpoints file
            return str(viewpoint.file) + " (" + str(viewpoint.id) + ")"

        elif role == Qt.DecorationRole:
            # if a snapshot is linked, return an icon of it.
            filename = str(viewpoint.snapshot)
            icon = self.snapshotModel.imgFromFilename(filename)
            if icon is None:  # snapshot is not listed in markup and cannot be loaded
                return None

            scaledIcon = icon.scaled(self._iconQSize, Qt.KeepAspectRatio)
            return scaledIcon

    def rowCount(self, parent=QModelIndex()):
        """ Returns the amount of viewpoints that shall be listed in the view
        """

        return len(self.viewpoints)

    @Slot()
    def resetItems(self, topic=None):
        """ If `topic != None` load viewpoints associated with `topic`, else
        delete the internal state of the model """

        self.beginResetModel()

        if topic is None:
            self.viewpoints = []
        else:
            self.viewpoints = [vp[1] for vp in pI.getViewpoints(topic, False)]

        self.endResetModel()

    @Slot()
    def resetView(self):
        """ Reset the view of FreeCAD to the state before the first viewpoint
        was applied """

        pI.resetView()

    @Slot()
    def calcSizes(self):
        """ Convert the millimeter sizes/offsets into pixels depending on the
        current screen. """

        screen = util.getCurrentQScreen()
        # pixels per millimeter (not parts per million)
        ppm = screen.logicalDotsPerInch() / util.MMPI

        width = self._iconSize.width() * ppm
        height = self._iconSize.height() * ppm
        self._iconQSize = QSize(width, height)

    @Slot(QModelIndex)
    def activateViewpoint(self, index):
        """ Manipulates FreeCAD's object viewer according to the setting of the
        viewpoint. """

        if not index.isValid() or index.row() >= len(self.viewpoints):
            return False

        vpRef = self.viewpoints[index.row()]
        camType = None
        if vpRef.viewpoint.oCamera is not None:
            camType = CamType.ORTHOGONAL
        elif vpRef.viewpoint.pCamera is not None:
            camType = CamType.PERSPECTIVE

        result = pI.activateViewpoint(vpRef.viewpoint, camType)
        if result == pI.OperationResults.FAILURE:
            return False
        return True

    def setIconSize(self, size: QSize):
        """ Size is expected to be given in millimeters. """

        self._iconSize = size
        self.calcSizes()
Beispiel #24
0
class RetroDrawWidget(QWidget):
    """
    Defines widget for displaying and handling all retro drawing.
    """
    def __init__(self, fgIndex, bgIndex, palette, parent=None):
        super(RetroDrawWidget, self).__init__(parent)

        self.canvasSize = QSize(256, 192)
        self.canvasCenter = QPoint(self.canvasSize.width() / 2,
                                   self.canvasSize.height() / 2)
        self.fgIndex = fgIndex
        self.bgIndex = bgIndex
        self.palette = palette

        self.scale = 4
        self.screenSize = self.canvasSize * self.scale
        self.screenCenter = self.canvasCenter * self.scale

        self.grid = QImage(self.screenSize, QImage.Format_RGBA8888)
        self.grid.fill(QColor(0, 0, 0, 0))
        for y in range(0, self.screenSize.height()):
            for x in range(0, self.screenSize.width(), 8 * self.scale):
                self.grid.setPixelColor(x, y, QColor(0, 0, 0, 255))

        for x in range(0, self.screenSize.width()):
            for y in range(0, self.screenSize.height(), 8 * self.scale):
                self.grid.setPixelColor(x, y, QColor(0, 0, 0, 255))
        self._gridEnabled = True
        self._gridOpacity = 0.2

        self._guideFilename = None
        self._guide = None
        self._guideEnabled = True
        self._guideOpacity = 0.2
        self._guideCoords = QPoint(0, 0)
        self._guideZoom = 1.0

        self._scratch = QImage(self.screenSize, QImage.Format_RGBA8888)
        self._scratch.fill(QColor(0, 0, 0, 0))

        self.drawable = ZXSpectrumBuffer()

        self.setCursor(Qt.CrossCursor)

        self._mouseLastPos = self.getLocalMousePos()
        self._mouseDelta = QPoint(0, 0)
        self._mousePressed = MouseButton.NONE
        self._drawMode = DrawingMode.DOTTED

        self._lineState = None

    def encodeToJSON(self):
        rdict = dict()
        rdict["fg_index"] = self.fgIndex
        rdict["bg_index"] = self.bgIndex
        rdict["palette"] = self.palette
        rdict["grid_enabled"] = self._gridEnabled
        rdict["grid_opacity"] = self._gridOpacity
        rdict["guide_filename"] = self._guideFilename
        rdict["guide_enabled"] = self._guideEnabled
        rdict["guide_opacity"] = self._guideOpacity
        rdict["guide_coords_x"] = self._guideCoords.x()
        rdict["guide_coords_y"] = self._guideCoords.y()
        rdict["guide_zoom"] = self._guideZoom
        rdict["drawable"] = self.drawable.encodeToJSON()
        return rdict

    def decodeFromJSON(self, json):
        self.fgIndex = json["fg_index"]
        self.bgIndex = json["bg_index"]
        self.palette = json["palette"]
        self._gridEnabled = json["grid_enabled"]
        self._gridOpacity = json["grid_opacity"]
        self._guideFilename = json["guide_filename"]
        self._guide = QPixmap(self._guideFilename)
        self._guideEnabled = json["guide_enabled"]
        self._guideOpacity = json["guide_opacity"]
        self._guideCoords.setX(json["guide_coords_x"])
        self._guideCoords.setY(json["guide_coords_y"])
        self._guideZoom = json["guide_zoom"]
        self.drawable.decodeFromJSON(json["drawable"])

    def sizeHint(self):
        return self.screenSize

    def minimumSizeHint(self):
        return self.screenSize

    def getLocalMousePos(self):
        return self.mapFromGlobal(QCursor.pos())

    def paintEvent(self, event):
        super(RetroDrawWidget, self).paintEvent(event)

        painter = QPainter(self)

        rectTarget = self.rect()
        rectSource = QRect(QPoint(0, 0), self.canvasSize)
        painter.drawPixmap(rectTarget, self.drawable.qpixmap, rectSource)

        if self._guide and self._guideEnabled:
            painter.setOpacity(self._guideOpacity)
            self._paintZoomedGuide(painter)
        if self._gridEnabled:
            painter.setOpacity(self._gridOpacity)
            painter.drawImage(rectTarget, self.grid, rectTarget)

        painter.setOpacity(1.0)
        painter.drawImage(rectTarget, self._scratch, rectTarget)

        painter.end()

    def mousePressEvent(self, event):
        self._mouseLastPos = self.getLocalMousePos()

        if event.button() == Qt.LeftButton:
            self._mousePressed = MouseButton.LEFT
        elif event.button() == Qt.RightButton:
            self._mousePressed = MouseButton.RIGHT

        if self._drawMode == DrawingMode.PEN:
            if self._mousePressed == MouseButton.LEFT:
                self.doDraw(event.localPos(), True)

        elif self._drawMode == DrawingMode.DOTTED:
            if self._mousePressed == MouseButton.LEFT:
                self.doDraw(event.localPos(), True)
            elif self._mousePressed == MouseButton.RIGHT:
                self.doDraw(event.localPos(), False)

        elif self._drawMode == DrawingMode.ERASE:
            if self._mousePressed == MouseButton.LEFT:
                self.doDraw(event.localPos(), False)

        elif self._drawMode == DrawingMode.LINE:
            if self._mousePressed == MouseButton.LEFT:
                self._lineState = [event.localPos(), event.localPos()]
                painter = QPainter(self._scratch)
                painter.setPen(Qt.black)
                painter.drawLine(self._lineState[0], self._lineState[1])
                painter.end()
                self.update(self.rect())

        elif self._drawMode == DrawingMode.ATTR:
            if self._mousePressed == MouseButton.LEFT:
                self.doDrawAttr(event.localPos())

    def mouseReleaseEvent(self, event):
        if self._drawMode == DrawingMode.LINE:
            if self._mousePressed == MouseButton.LEFT and self._lineState:
                self._lineState[1] = event.localPos()
                painter = QPainter(self._scratch)
                painter.setPen(Qt.black)
                painter.drawLine(self._lineState[0], self._lineState[1])

                self.doDrawLine(self._lineState[0], self._lineState[1])

                painter.end()
                self._lineState = None
                self._scratch.fill(QColor(0, 0, 0, 0))
                self.update(self.rect())

        self._mousePressed = MouseButton.NONE

    def mouseMoveEvent(self, event):
        oldMousePos = self._mouseLastPos
        newMousePos = self.getLocalMousePos()
        self._mouseDelta = newMousePos - self._mouseLastPos
        self._mouseLastPos = newMousePos

        if self._drawMode == DrawingMode.PEN:
            if self._mousePressed == MouseButton.LEFT:
                self.doDrawLine(oldMousePos, newMousePos)

        if self._drawMode == DrawingMode.DOTTED:
            if self._mousePressed == MouseButton.LEFT:
                self.doDraw(newMousePos, True)
            elif self._mousePressed == MouseButton.RIGHT:
                self.doDraw(newMousePos, False)

        elif self._drawMode == DrawingMode.ERASE:
            if self._mousePressed == MouseButton.LEFT:
                self.doDraw(newMousePos, False)

        elif self._drawMode == DrawingMode.GUIDE:
            if self._mousePressed == MouseButton.LEFT:
                self._guideCoords += self._mouseDelta
                self.update(self.rect())

        elif self._drawMode == DrawingMode.LINE:
            if self._mousePressed == MouseButton.LEFT and self._lineState:
                painter = QPainter(self._scratch)
                self._scratch.fill(QColor(0, 0, 0, 0))
                painter.setPen(Qt.black)
                self._lineState[1] = event.localPos()
                painter.drawLine(self._lineState[0], self._lineState[1])
                painter.end()
                self.update(self.rect())

        elif self._drawMode == DrawingMode.ATTR:
            if self._mousePressed == MouseButton.LEFT:
                self.doDrawAttr(event.localPos())

    def wheelEvent(self, event):
        if self._mousePressed:
            if self._drawMode == DrawingMode.GUIDE:
                delta = event.pixelDelta().y() * 0.01
                if delta != 0.0:
                    self._guideZoom += delta
                    self._guideZoom = self.clamp(self._guideZoom, 0.1, 8.0)
                    self.update(self.rect())

    @staticmethod
    def clamp(value, min, max):
        if value < min:
            return min
        elif value > max:
            return max
        return value

    def doDraw(self, localPos, setPixel):
        x = localPos.x() // self.scale
        y = localPos.y() // self.scale

        if setPixel:
            self.drawable.setPixel(x, y, self.fgIndex, self.bgIndex,
                                   self.palette)
        else:
            self.drawable.erasePixel(x, y, self.fgIndex, self.bgIndex,
                                     self.palette)

        self.update(self.rect())

    def doDrawAttr(self, localPos):
        x = localPos.x() // self.scale
        y = localPos.y() // self.scale

        self.drawable.setAttr(x, y, self.fgIndex, self.bgIndex, self.palette)
        self.update(self.rect())

    def doDrawLine(self, localStartPos, localEndPos):
        x1 = localStartPos.x() // self.scale
        y1 = localStartPos.y() // self.scale
        x2 = localEndPos.x() // self.scale
        y2 = localEndPos.y() // self.scale
        self.drawable.drawLine(x1, y1, x2, y2, self.fgIndex, self.bgIndex,
                               self.palette)
        self.update(self.rect())

    def setColor(self, fgIndex, bgIndex, palette):
        self.fgIndex = fgIndex
        self.bgIndex = bgIndex
        self.palette = palette

    def saveImage(self, filename, format=None):
        self.drawable.saveBuffer(filename)

    def setGrid(self, checked):
        self._gridEnabled = checked
        self.repaint()

    def setGridOpacity(self, value):
        self._gridOpacity = value / 100.0
        self.repaint()

    def setGuideImage(self, filename):
        self._guideFilename = filename
        self._guide = QPixmap(self._guideFilename)
        self.repaint()

    def setGuide(self, checked):
        self._guideEnabled = checked
        self.repaint()

    def setGuideOpacity(self, value):
        self._guideOpacity = value / 100.0
        self.repaint()

    def setMode(self, mode):
        self._drawMode = mode

    def clear(self):
        self.drawable.clear(self.fgIndex, self.bgIndex, self.palette)
        self.repaint()

    def _paintZoomedGuide(self, painter):
        guideZoom = self._guide.scaled(self._guide.width() * self._guideZoom,
                                       self._guide.height() * self._guideZoom,
                                       Qt.KeepAspectRatio)
        pos = QPoint(
            self._guideCoords.x() +
            (self.screenCenter.x() - guideZoom.width() / 2),
            self._guideCoords.y() +
            (self.screenCenter.y() - guideZoom.height() / 2))
        painter.drawPixmap(pos, guideZoom)

    def copyGuide(self):
        # This isn't the most efficient way to do this but it just needs to be
        # reasonably fast
        self.drawable.clear(self.fgIndex, self.bgIndex, self.palette)

        guide_copy = QImage(self.screenSize, QImage.Format_RGBA8888)
        guide_copy.fill(QColor("white"))

        painter = QPainter(guide_copy)
        self._paintZoomedGuide(painter)

        shrunk_guide = guide_copy.smoothScaled(self.canvasSize.width(),
                                               self.canvasSize.height())
        mono_guide = shrunk_guide.convertToFormat(QImage.Format_Mono)

        for x in range(0, self.canvasSize.width()):
            for y in range(0, self.canvasSize.height()):
                if mono_guide.pixel(x, y) == QColor("black"):
                    self.drawable.setPixel(x, y, self.fgIndex, self.bgIndex,
                                           self.palette)

        painter.end()
        self.repaint()
class Check(QFrame):
    def __init__(self, name, default=False):
        super().__init__()
        # Variables
        self.name = name
        self.hovered = False
        self.enabled = default
        self.padding = 5

        # Objects
        self.image = QImageReader()
        self.imageSize = QSize(20, 20)

        # Styling
        self.font = QFont('Arial', 8)
        self.font.setWeight(QFont.Bold)
        self.nameHeight = QFontMetrics(self.font).height()
        self.nameWidth = QFontMetrics(self.font).width(self.name)

        self.image.setScaledSize(self.imageSize)
        self.setFixedSize(
            self.imageSize.width() + self.padding + self.nameWidth,
            self.imageSize.height())

    def getEnabled(self):
        return self.enabled

    def setPadding(self, param):
        self.padding = param
        self.update()

    def enterEvent(self, event):
        super().enterEvent(event)
        self.hovered = True
        self.update()

    def leaveEvent(self, event):
        super().leaveEvent(event)
        self.hovered = False
        self.update()

    def mouseReleaseEvent(self, event):
        super().mousePressEvent(event)
        if event.button() == Qt.LeftButton:
            self.enabled = not self.enabled
            self.stateChanged.emit(self.enabled)
            self.update()

    def paintEvent(self, event):
        # Set checked depending on state
        self.image.setFileName(ThemeManager.CHECKED_PATH if self.
                               enabled else ThemeManager.UNCHECKED_PATH)

        # Init painter and draw image at correct location
        painter = QPainter(self)
        pixmap = QPixmap.fromImageReader(self.image)
        painter.setRenderHint(QPainter.HighQualityAntialiasing, True)
        painter.drawPixmap(0, (self.height() - pixmap.height()) / 2, pixmap)

        # Style font and draw text at correct location
        painter.setFont(self.font)
        pen = QPen()
        pen.setColor(ThemeManager.LABEL_QC if any([self.hovered, self.enabled])
                     else ThemeManager.LABEL_LOW_OPACITY_QC)
        painter.setPen(pen)
        painter.drawText(pixmap.width() + self.padding,
                         self.height() / 2 + self.nameHeight / 4, self.name)

    stateChanged = Signal(object)
Beispiel #26
0
 def calculate_center_location(from_size: QSize, item_size: QSize):
     x = max(from_size.width() // 2 - item_size.width() // 2, 0)
     y = max(from_size.height() // 2 - item_size.height() // 2, 0)
     return QPoint(x, y)
Beispiel #27
0
class ImageView(QWidget):
    button_timeout = QTimer()
    button_timeout.setInterval(100)
    button_timeout.setSingleShot(True)

    shortcut_timeout = QTimer()
    shortcut_timeout.setInterval(50)
    shortcut_timeout.setSingleShot(True)

    slider_timeout = QTimer()
    slider_timeout.setInterval(20)
    slider_timeout.setSingleShot(True)

    DEFAULT_SIZE = (800, 450)
    MARGIN = 400

    MAX_SIZE = QSize(4096, 4096)

    def __init__(self, app, ui):
        """ Image Overlay frameless window that stays on top by default

        :param modules.main_app.ViewerApp app: Viewer QApplication
        :param modules.main_ui.ViewerWindow ui: Viewer QWidget main app window showing controls
        """
        super(ImageView, self).__init__(ui)
        self.app, self.ui = app, ui

        self.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint | Qt.CustomizeWindowHint | Qt.Tool)
        self.setWindowIcon(IconRsc.get_icon('img'))
        self.setAttribute(Qt.WA_TranslucentBackground)
        self.setAttribute(Qt.WA_AcceptDrops, True)
        self.setStyleSheet("QWidget{background-color: darkgray;}")
        self.setFocusPolicy(Qt.StrongFocus)

        self.current_img = None
        self.img_list = list()
        self.img_index = 0
        self.img_size_factor = 1.0
        self.img_size = QSize(*self.DEFAULT_SIZE)
        self.img_loader = None  # will be the loader thread

        # Save window position for drag
        self.oldPos = self.pos()

        self.current_opacity = 1.0
        self.setWindowOpacity(1.0)

        # --- Image Loader ---
        self.img_load_controller = KnechtLoadImageController(self)
        self.img_load_controller.camera_available.connect(self.camera_data_available)

        # --- DG Send thread controller ---
        self.dg_thread_controller = SyncController(self)

        # --- Image canvas ---
        self.setLayout(QHBoxLayout())
        self.layout().setContentsMargins(0, 0, 0, 0)
        self.layout().setSpacing(0)
        self.img_canvas = QLabel(self)
        self.layout().addWidget(self.img_canvas)
        self.img_canvas.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
        self.img_canvas.setScaledContents(True)
        self.img_canvas.setObjectName('img_canvas')
        self.set_default_image()
        self.ui.reset()

        LOGGER.debug('Image View: %s', self.geometry())

        self.slider_timeout.timeout.connect(self.set_opacity_from_slider)
        self.ui.opacity_slider.setToolTip(_('Transparenz der Bildfläche festlegen [W/S]'))
        self.ui.opacity_slider.sliderReleased.connect(self.slider_timeout.start)
        self.ui.opacity_slider.valueChanged.connect(self.slider_timeout.start)

        self.ui.zoom_box.currentIndexChanged.connect(self.combo_box_size)
        self.ui.zoom_box.setToolTip(_('Anzeigegröße der Bilddatei anpassen [Q/E]'))

        self.ui.back_btn.pressed.connect(self.iterate_bck)
        self.ui.back_btn.setToolTip(_('Datei zurück navigieren [A oder <= Pfeiltaste]'))
        self.ui.fwd_btn.pressed.connect(self.iterate_fwd)
        self.ui.fwd_btn.setToolTip(_('Datei vorwärts navigieren [D oder => Pfeiltaste]'))
        self.ui.top_btn.setToolTip(_('Bildfläche immer im Vordergrund'))
        self.ui.top_btn.released.connect(self.toggle_stay_on_top)
        self.ui.vis_btn.setToolTip(_('Sichtbarkeit der Bildfläche an/aus [Tab]'))
        self.ui.vis_btn.released.connect(self.toggle_img_canvas)
        self.ui.input_btn.setToolTip(_('Maus und Tastatureingabe für Bildfläche de-/aktivieren'))
        self.ui.input_btn.released.connect(self.toggle_input_transparency)

        # --- DeltaGen Sync ---
        self.ui.sync_btn.setText(_('Sync DeltaGen Viewer'))
        self.ui.sync_btn.setToolTip(_('Bildfläche periodisch zum DeltaGen Viewer verschieben [F]'))
        self.ui.sync_btn.toggled.connect(self.dg_toggle_sync)
        # Toggle sync off when idle
        self.ui.app.idle_event.connect(self._dg_toggle_sync_idle)

        # -- Change DeltaGen Port button
        self.ui.dg_btn.setToolTip('DeltaGen external command port')
        self.ui.dg_btn.pressed.connect(self.change_deltagen_port)

        # --- Help button ---
        self.ui.help_btn.setToolTip(_('Hilfe anzeigen'))
        self.ui.help_btn.pressed.connect(self.display_shortcuts)

        # --- Pull button (depreciated) ---
        self.ui.focus_btn.setText(_('Pull DeltaGen Focus'))
        self.ui.focus_btn.pressed.connect(self.dg_toggle_pull)
        # Pulling focus is no longer necessary
        self.ui.focus_btn.hide()

        # --- Shortcuts ---
        self.shortcuts = ViewerShortcuts(self, self.ui)
        self.shortcuts.set_shortcuts(self.ui)

        # --- Drag n Drop ---
        self.drag_drop = DragNDropHandler(self)
        self.drag_drop.file_dropped.connect(self.ui.file_changed)

        # --- Info Overlay ---
        self.info_overlay = InfoOverlay(self)

        self.place_in_screen_center()

    def changeEvent(self, event):
        """ Not necessary when UI is normal window and is our parent.
            Used to be necessary when control window was also frameless.

        :param QEvent event:
        :return:
        """
        if event.type() == QEvent.WindowStateChange:
            if event.oldState() == Qt.WindowMinimized:
                LOGGER.debug('Restoring Image Overlay Window')
            if event.oldState() and Qt.WindowMinimized:
                LOGGER.debug('Image Overlay Window was restored')
            elif event.oldState() == Qt.WindowNoState:
                LOGGER.debug('Image Overlay Window minimized.')

    def change_deltagen_port(self):
        box = QMessageBox(self)
        box.setText(_('Port zwischen 3000-3999 angeben. '
                      'Muss mit DeltaGen>Preferences>Tools>External Commands übereinstimmen.'))
        box.setWindowTitle(_('DeltaGen Kommando Port'))

        port = QSpinBox(box)
        port.setMinimum(3000)
        port.setMaximum(3999)
        port.setValue(KnechtSettings.app.get('port', DG_TCP_PORT))
        box.layout().addWidget(port,  box.layout().rowCount() - 1, 0, 1, box.layout().columnCount())
        box.layout().addWidget(box.layout().takeAt(box.layout().rowCount() - 1).widget(), box.layout().rowCount(),
                               0, 1, box.layout().columnCount())
        box.exec_()

        if 3000 <= port.value() <= 3999:
            KnechtSettings.app['port'] = port.value()
            box = QMessageBox(self)
            box.setText(_('Anwendung neu starten um geänderten Port zu übernehmen.'))
            box.setWindowTitle(_('Neustart erforderlich'))
            box.exec_()

    def display_shortcuts(self, keep_overlay: bool=True):
        msg = WELCOME_MSG.format(' '.join(self.img_load_controller.FILE_TYPES))

        # Ensure visibility of image canvas
        if self.current_opacity < 0.7 or self.ui.vis_btn.isChecked():
            self.set_window_opacity(1.0)
            if self.ui.vis_btn.isChecked():
                self.ui.vis_btn.toggle()

        if keep_overlay:
            self.info_overlay.display_confirm(msg, (('[X]', None), ))
        else:
            self.info_overlay.display(msg, 6000)

    def set_default_image(self):
        self.current_img = IconRsc.get_pixmap('img_viewer_bg')
        self.img_canvas.setStyleSheet('background: rgba(0, 0, 0, 0);')
        self.img_canvas.setPixmap(self.current_img)
        self.img_size = self.current_img.size()
        self.img_size_factor = 1.0

        self.change_viewer_size()

    # ------ DeltaGen Sync -------
    def dg_toggle_btn(self, enabled: bool):
        """ Called by thread signal """
        self.ui.sync_btn.setEnabled(enabled)

    def dg_check_btn(self, checked: bool):
        """ Called by thread signal """
        self.ui.sync_btn.setChecked(checked)

    def dg_toggle_pull(self):
        """ Toggles pulling of the viewer window in front on/off """
        self.dg_thread_controller.toggle_pull()

    def _dg_toggle_sync_idle(self):
        """ Switch off sync if app is idling """
        if self.ui.sync_btn.isChecked():
            LOGGER.debug('Toggling sync off while application is idling.')
            # Toggle sync off and do not interrupt user with viewer reset
            self.dg_thread_controller.toggle_sync(reset_viewer=False)

    def dg_toggle_sync(self):
        if not self.ui.sync_btn.isEnabled():
            return

        self.dg_thread_controller.toggle_sync()

    # ------ IMAGES -------
    def set_img_path(self, file_path: Path):
        self.img_load_controller.set_img_path(file_path)
        self.ui.path_util.set_path_text(self.img_load_controller.img_dir)

    def _can_iterate_images(self) -> bool:
        if self.button_timeout.isActive():
            return False

        self.button_timeout.start()
        return True

    def iterate_fwd(self):
        if not self._can_iterate_images():
            return

        self.img_load_controller.iterate_fwd()

    def iterate_bck(self):
        if not self._can_iterate_images():
            return

        self.img_load_controller.iterate_bck()

    def no_image_found(self):
        self.set_default_image()
        self.ui.reset()

        self.info_overlay.display(_('Keine Bilddaten im Verzeichnis gefunden.'))

    def image_load_failed(self, error_msg=''):
        img_path = self.img_load_controller.current_image()

        if not error_msg:
            error_msg = img_path.as_posix()

        LOGGER.error('Could not load image file:\n%s', error_msg)

        self.set_default_image()
        self.ui.reset()

        self.img_loader = None

        self.info_overlay.display(_('Konnte keine Bilddaten laden: {}<br>{}').format(error_msg, img_path.as_posix()),
                                  8000, immediate=True)

    def image_loaded(self, image):
        def valid_img_name(img_path: Path) -> str:
            name = img_path.name
            if len(name) >= 85:
                name = f'{name[:65]}~{name[-20:]}'
            return name

        if not image:
            self.image_load_failed()
            return

        self.current_img = image

        self.img_canvas.setPixmap(self.current_img)
        self.img_size = self.current_img.size()
        self.change_viewer_size()

        prev_img_name = valid_img_name(self.img_load_controller.prev_image())
        img_name = valid_img_name(self.img_load_controller.current_image())
        next_img_name = valid_img_name(self.img_load_controller.next_image())

        self.ui.setWindowTitle(img_name)

        i = f'<span style="color: rgb(180, 180, 180);">' \
            f'{self.img_load_controller.get_valid_img_list_index(self.img_load_controller.img_index-1)+1:02d}/' \
            f'{len(self.img_load_controller.img_list):02d} - {prev_img_name}</span><br />'\
            f'{1+self.img_load_controller.img_index:02d}/' \
            f'{len(self.img_load_controller.img_list):02d} - {img_name}<br />' \
            f'<span style="color: rgb(180, 180, 180);">' \
            f'{self.img_load_controller.get_valid_img_list_index(self.img_load_controller.img_index+1)+1:02d}' \
            f'/{len(self.img_load_controller.img_list):02d} - {next_img_name}</span>'

        self.info_overlay.display(i, 3000, immediate=True)

    # ------ RESIZE -------
    def combo_box_size(self, idx):
        self.set_img_size_factor_from_combo_box()
        self.change_viewer_size()

    def set_img_size_factor_from_combo_box(self):
        data = self.ui.zoom_box.currentData()

        if data:
            self.img_size_factor = data

    def set_size_box_index(self, add_idx: int=0):
        cb = self.ui.zoom_box
        new_idx = min(cb.count() - 1, max(cb.currentIndex() + add_idx, 0))
        cb.setCurrentIndex(new_idx)

    def increase_size(self):
        self.set_size_box_index(1)
        self.set_img_size_factor_from_combo_box()
        self.change_viewer_size()

    def decrease_size(self):
        self.set_size_box_index(-1)
        self.set_img_size_factor_from_combo_box()
        self.change_viewer_size()

    def change_viewer_size(self):
        self.img_size_factor = max(0.01, min(self.img_size_factor, MAX_SIZE_FACTOR))

        w = round(self.img_size.width() * self.img_size_factor)
        h = round(self.img_size.height() * self.img_size_factor)
        new_size = QSize(w, h)

        self.resize_image_viewer(new_size)

    def resize_image_viewer(self, new_size: QSize):
        width = max(50, min(new_size.width(), self.MAX_SIZE.width()))
        height = max(50, min(new_size.height(), self.MAX_SIZE.height()))
        new_size = QSize(width, height)

        self.resize(new_size)

    # ------ OPACITY -------
    def increase_window_opacity(self):
        opacity = self.windowOpacity() + 0.15
        self.update_opacity_slider()
        self.set_window_opacity(opacity)

    def decrease_window_opacity(self):
        opacity = self.windowOpacity() - 0.15
        self.update_opacity_slider()
        self.set_window_opacity(opacity)

    def update_opacity_slider(self):
        self.ui.opacity_slider.setValue(round(self.windowOpacity() * self.ui.opacity_slider.maximum()))

    def set_opacity_from_slider(self):
        opacity = self.ui.opacity_slider.value() * 0.1
        self.set_window_opacity(opacity)

    def set_window_opacity(self, opacity):
        if self.shortcut_timeout.isActive():
            return

        opacity = max(0.05, min(1.0, opacity))
        self.current_opacity = opacity
        self.setWindowOpacity(opacity)

        self.shortcut_timeout.start()

    # --- Camera ----
    def camera_data_available(self, available: int):
        pal = QPalette()
        pal.setColor(QPalette.Button, QColor(240, 240, 240))

        if available == 0:
            self.ui.cam_btn.setEnabled(False)
        elif available == 1:
            self.ui.cam_btn.setEnabled(True)
        elif available == 2:
            pal.setColor(QPalette.Button, QColor(240, 150, 150))
            self.ui.cam_btn.setEnabled(True)

        self.ui.cam_btn.setAutoFillBackground(True)
        self.ui.cam_btn.setPalette(pal)
        self.ui.cam_btn.update()

    # ------ VISIBILITY -------
    def toggle_img_canvas(self):
        if self.shortcut_timeout.isActive():
            return

        if not self.ui.vis_btn.isChecked():
            self.setWindowOpacity(self.current_opacity)
        else:
            self.setWindowOpacity(0.0)

        self.shortcut_timeout.start()

    def _toggle_window_flag(self, window_flag: Qt) -> bool:
        enabled = False if self.windowFlags() & window_flag else True

        self.hide()
        self.setWindowFlag(window_flag, enabled)
        self.show()

        return enabled

    def switch_stay_on_top(self) -> bool:
        enabled = self._toggle_window_flag(Qt.WindowStaysOnTopHint)
        KnechtSettings.app['img_stay_on_top'] = enabled
        return enabled

    def toggle_stay_on_top(self):
        if self.shortcut_timeout.isActive():
            return

        enabled = self.switch_stay_on_top()
        self.info_overlay.display(_('Bildfläche erscheint nun nicht mehr im Vordergrund')
                                  if not enabled else _('Bildfläche erscheint nun immer im Vordergrund'),
                                  immediate=True)

        self.shortcut_timeout.start()

    def switch_input_transparency(self):
        enabled = self._toggle_window_flag(Qt.WindowTransparentForInput)
        KnechtSettings.app['img_input_transparent'] = enabled
        return enabled

    def toggle_input_transparency(self):
        if self.shortcut_timeout.isActive():
            return

        enabled = self.switch_input_transparency()
        self.info_overlay.display(_('Maus und Tastatureingabe für Bildfläche aktiviert.')
                                  if not enabled else _('Maus und Tastatureingabe für Bildfläche deaktiviert.'),
                                  immediate=True)

        self.shortcut_timeout.start()

    def hide_all(self):
        # self.ui.hide()
        self.hide()

    def show_all(self):
        self.place_inside_screen()
        self.showNormal()

        self.current_opacity = 1.0
        self.ui.opacity_slider.setValue(self.ui.opacity_slider.maximum())

    # ------ OVERRIDES -------
    def moveEvent(self, event):
        if self.moved_out_of_limit():
            event.ignore()
            return

        event.accept()

    def resizeEvent(self, event):
        if self.moved_out_of_limit():
            event.ignore()
            return

        event.accept()

    def moved_out_of_limit(self):
        limit = self.calculate_screen_limits()
        pos = self.geometry().topLeft()

        if not self.is_inside_limit(limit, pos):
            x = min(limit.width(), max(limit.x(), pos.x()))
            y = min(limit.height(), max(limit.y(), pos.y()))
            self.move(x, y)
            return True

        return False

    def place_inside_screen(self):
        limit = self.calculate_screen_limits()
        pos = self.geometry().topLeft()

        if not self.is_inside_limit(limit, pos):
            self.place_in_screen_center()

    def place_in_screen_center(self):
        screen = self.app.desktop().availableGeometry(self)

        center_x = screen.center().x() - self.geometry().width() / 2
        center_y = screen.center().y() - self.geometry().height() / 2

        self.move(center_x, center_y)

    def calculate_screen_limits(self):
        screen = QRect(self.app.desktop().x(), self.app.desktop().y(),
                       self.app.desktop().width(), self.app.desktop().availableGeometry().height())

        width_margin = round(self.geometry().width() / 2)
        height_margin = round(self.geometry().height() / 2)

        # Case where secondary screen has negative values
        desktop_width = screen.x() + screen.width()

        min_x = screen.x() - width_margin
        min_y = screen.y() - height_margin
        max_x = desktop_width - width_margin
        max_y = screen.height() - height_margin

        return QRect(min_x, min_y, max_x, max_y)

    def closeEvent(self, QCloseEvent):
        self.dg_thread_controller.exit()
        self.ui.close()
        QCloseEvent.accept()

    def mousePressEvent(self, event):
        self.oldPos = event.globalPos()

    def mouseMoveEvent(self, event):
        delta = QPoint(event.globalPos() - self.oldPos)

        self.move(self.x() + delta.x(), self.y() + delta.y())
        self.oldPos = event.globalPos()

    @staticmethod
    def is_inside_limit(limit: QRect, pos: QPoint):
        if pos.x() < limit.x() or pos.x() > limit.width():
            return False
        elif pos.y() < limit.y() or pos.y() > limit.height():
            return False

        return True
Beispiel #28
0
    def histogram(self, size=QSize(200, 200), bgColor=Qt.white, range=(0, 255),
                  chans=channelValues.RGB, chanColors=Qt.gray, mode='RGB', addMode=''):
        """
        Plot the image histogram with the
        specified color mode and channels.
        Histograms are smoothed using a Savisky-Golay filter and curves are scaled individually
        to fit the height of the plot.
        @param size: size of the histogram plot
        @type size: int or QSize
        @param bgColor: background color
        @type bgColor: QColor
        @param range: plot data range
        @type range: 2-uple of int or float
        @param chans: channels to plot b=0, G=1, R=2
        @type chans: list of indices
        @param chanColors: color or 3-uple of colors
        @type chanColors: QColor or 3-uple of QColor
        @param mode: color mode ((one among 'RGB', 'HSpB', 'Lab', 'Luminosity')
        @type mode: str
        @param addMode:
        @type addMode:
        @return: histogram plot
        @rtype: QImage
        """
        # convert size to QSize
        if type(size) is int:
            size = QSize(size, size)
        # alert threshold for clipped areas
        clipping_threshold = 0.02
        # clipping threshold for black and white points
        # scaling factor for the bin edges
        spread = float(range[1] - range[0])
        scale = size.width() / spread

        # per channel histogram function
        def drawChannelHistogram(painter, hist, bin_edges, color):
            # Draw the (smoothed) histogram for a single channel.
            # param painter: QPainter
            # param hist: histogram to draw
            # smooth the histogram (first and last bins excepted) for a better visualization of clipping.
            hist = np.concatenate(([hist[0]], SavitzkyGolayFilter.filter(hist[1:-1]), [hist[-1]]))
            M = max(hist[1:-1])
            # draw histogram
            imgH = size.height()
            for i, y in enumerate(hist):
                try:
                    h = int(imgH * y / M)
                except (ValueError, ArithmeticError):
                    # don't draw the channel histogram if M is too small:
                    # It may happen when channel values are concentrated
                    # on the first and/or last bins.
                    return
                h = min(h, imgH - 1)  # height of rect must be < height of img, otherwise fillRect does nothing
                rect = QRect(int((bin_edges[i] - range[0]) * scale), max(img.height() - h, 0),
                             int((bin_edges[i + 1] - bin_edges[i]) * scale+1), h)
                painter.fillRect(rect, color)
                # clipping indicators
                if i == 0 or i == len(hist)-1:
                    left = bin_edges[0 if i == 0 else -1]
                    if range[0] < left < range[1]:
                        continue
                    left = left - (10 if i > 0 else 0)
                    percent = hist[i] * (bin_edges[i+1]-bin_edges[i])
                    if percent > clipping_threshold:
                        # calculate the color of the indicator according to percent value
                        nonlocal gPercent
                        gPercent = min(gPercent, np.clip((0.05 - percent) / 0.03, 0, 1))
                        painter.fillRect(left, 0, 10, 10, QColor(255, 255*gPercent, 0))
        # green percent for clipping indicators
        gPercent = 1.0
        bufL = cv2.cvtColor(QImageBuffer(self)[:, :, :3], cv2.COLOR_BGR2GRAY)[..., np.newaxis]  # returns Y (YCrCb) : Y = 0.299*R + 0.587*G + 0.114*B
        buf = None  # TODO added 5/11/18 validate
        if mode == 'RGB':
            buf = QImageBuffer(self)[:, :, :3][:, :, ::-1]  # RGB
        elif mode == 'HSV':
            buf = self.getHSVBuffer()
        elif mode == 'HSpB':
            buf = self.getHspbBuffer()
        elif mode == 'Lab':
            buf = self.getLabBuffer()
        elif mode == 'Luminosity':
            chans = []
        img = QImage(size.width(), size.height(), QImage.Format_ARGB32)
        img.fill(bgColor)
        qp = QPainter(img)
        try:
            if type(chanColors) is QColor or type(chanColors) is Qt.GlobalColor:
                chanColors = [chanColors]*3
            # compute histograms
            # bins='auto' sometimes causes a huge number of bins ( >= 10**9) and memory error
            # even for small data size (<=250000), so we don't use it.
            # This is a numpy bug : in the module function_base.py
            # a reasonable upper bound for bins should be chosen to prevent memory error.
            if mode == 'Luminosity' or addMode == 'Luminosity':
                hist, bin_edges = np.histogram(bufL, bins=100, density=True)
                drawChannelHistogram(qp, hist, bin_edges, Qt.gray)
            hist_L, bin_edges_L = [0]*len(chans), [0]*len(chans)
            for i, ch in enumerate(chans):
                buf0 = buf[:, :, ch]
                hist_L[i], bin_edges_L[i] = np.histogram(buf0, bins=100, density=True)
                # to prevent artifacts, the histogram bins must be drawn
                # using the composition mode source_over. So, we use
                # a fresh QImage for each channel.
                tmpimg = QImage(size, QImage.Format_ARGB32)
                tmpimg.fill(bgColor)
                tmpqp = QPainter(tmpimg)
                try:
                    drawChannelHistogram(tmpqp, hist_L[i], bin_edges_L[i], chanColors[ch])
                finally:
                    tmpqp.end()
                # add the channnel hist to img
                qp.drawImage(QPoint(0,0), tmpimg)
                # subsequent images are added using composition mode Plus
                qp.setCompositionMode(QPainter.CompositionMode_Plus)
        finally:
            qp.end()
        buf = QImageBuffer(img)
        # if len(chans) > 1, clip gray area to improve the aspect of the histogram
        if len(chans) > 1:
            buf[:, :, :3] = np.where(np.min(buf, axis=-1)[:, :, np.newaxis] >= 100,
                                     np.array((100, 100, 100))[np.newaxis, np.newaxis, :], buf[:, :, :3])
        return img
Beispiel #29
0
class MainWidget(QWidget):
    def __init__(self, parent=None):
        super(MainWidget, self).__init__(parent)
        self.set_init_settings()
        self.setup_ui()

    def set_init_settings(self):
        self.video_size = QSize(800, 600)
        self.record_mode = False
        self.record_timer = None
        self.frame_count = 0
        self.camera_id = 1
        self.capture = None

    def setup_ui(self):
        """Initialize widgets.
        """
        self.camera_label = QLabel("Camera ID: ")
        self.camera_line_edit = QLineEdit("{}".format(self.camera_id))
        self.camera_width_label = QLabel("Width: ")
        self.camera_width_line_edit = QLineEdit("{}".format(
            self.video_size.width()))
        self.camera_height_label = QLabel("Height: ")
        self.camera_height_line_edit = QLineEdit("{}".format(
            self.video_size.height()))
        self.camera_load_button = QPushButton("Load Camera")

        self.camera_load_button.clicked.connect(self.setup_camera)

        self.camera_id_layout = QHBoxLayout()
        self.camera_id_layout.addWidget(self.camera_label)
        self.camera_id_layout.addWidget(self.camera_line_edit)
        self.camera_id_layout.addWidget(self.camera_width_label)
        self.camera_id_layout.addWidget(self.camera_width_line_edit)
        self.camera_id_layout.addWidget(self.camera_height_label)
        self.camera_id_layout.addWidget(self.camera_height_line_edit)
        self.camera_id_layout.addWidget(self.camera_load_button)

        self.image_label = CameraFrame()
        self.image_label.setFixedSize(self.video_size)

        self.record_button = QPushButton("Record")
        self.stop_button = QPushButton("Stop")
        self.record_button.clicked.connect(self.start_record)
        self.stop_button.clicked.connect(self.stop_record)
        self.record_button.setEnabled(False)
        self.stop_button.setEnabled(False)

        self.capture_setting = CaptureSettingWidget()

        self.sub1_layout = QVBoxLayout()
        self.sub1_layout.addLayout(self.camera_id_layout)
        self.sub1_layout.addWidget(self.image_label)
        self.sub12_layout = QHBoxLayout()
        self.sub12_layout.addWidget(self.record_button)
        self.sub12_layout.addWidget(self.stop_button)
        self.sub1_layout.addLayout(self.sub12_layout)

        self.sub2_layout = QVBoxLayout()
        self.sub2_layout.addWidget(self.capture_setting)

        self.main_layout = QHBoxLayout()
        self.main_layout.addLayout(self.sub1_layout)
        self.main_layout.addLayout(self.sub2_layout)

        self.setLayout(self.main_layout)

        self.record_timer = QTimer()
        self.record_timer.timeout.connect(self.save_data)

    def setup_camera(self):
        """Initialize camera.
        """
        self.change_camera_setting()
        if self.record_mode is True:
            self.stop_record()
        self.change_record_mode(self.record_mode)
        if self.capture is not None:
            self.capture.release()
        self.capture = cv2.VideoCapture(self.camera_id)
        self.capture.set(cv2.CAP_PROP_FRAME_WIDTH, self.video_size.width())
        self.capture.set(cv2.CAP_PROP_FRAME_HEIGHT, self.video_size.height())

        self.timer = QTimer()
        self.timer.timeout.connect(self.display_video_stream)
        self.timer.start(30)

    def change_camera_setting(self):
        self.camera_id = int(self.camera_line_edit.text())
        self.video_size = QSize(int(self.camera_width_line_edit.text()),
                                int(self.camera_height_line_edit.text()))
        self.image_label.setFixedSize(self.video_size)

    def display_video_stream(self):
        """Read frame from camera and repaint QLabel widget.
        """
        _, frame = self.capture.read()
        frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
        frame = cv2.flip(frame, 1)
        frame = self.draw_captured(frame)
        image = QImage(
            frame,
            frame.shape[1],
            frame.shape[0],
            frame.strides[0],
            QImage.Format_RGB888,
        )
        self.image_label.setPixmap(QPixmap.fromImage(image))

    def draw_captured(self, frame):
        pos, r = self.get_pos_r()
        frame = cv2.circle(
            frame,
            pos,
            r,
            (255, 69, 0),
            thickness=1,
            lineType=cv2.LINE_AA,
            shift=0,
        )
        frame = cv2.circle(
            frame,
            pos,
            1,
            (255, 69, 0),
            thickness=-1,
            lineType=cv2.LINE_AA,
            shift=0,
        )
        return frame

    def get_pos_r(self):
        if self.capture_setting.use_cursor:
            if self.image_label.npos is not None:
                return (
                    (self.image_label.npos.x(), self.image_label.npos.y()),
                    int(self.capture_setting.RADIUS * self.image_label.r_amp),
                )
            else:
                return (0, 0), self.capture_setting.RADIUS
        else:
            return (
                (self.capture_setting.X, self.capture_setting.Y),
                self.capture_setting.RADIUS,
            )

    def start_record(self):
        self.frame_count = 0
        self.record_mode = True
        self.change_record_mode(self.record_mode)
        self.capture_setting.set_save_dirpath()
        self.start_record_timer()

    def stop_record(self):
        self.stop_record_timer()
        convert_valid_json_data(self.capture_setting.get_full_save_dirpath())
        self.record_mode = False
        self.change_record_mode(self.record_mode)

    def start_record_timer(self):
        self.record_timer.start(
            int(1000 / self.capture_setting.captur_frame_per_s))

    def stop_record_timer(self):
        self.record_timer.stop()

    def save_data(self):
        self.frame_count += 1
        _, frame = self.capture.read()
        # frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
        frame = cv2.flip(frame, 1)
        pos, r = self.get_pos_r()

        # base_name = detail_now_name()
        base_name = "{:04}_{:02}".format(
            int(self.frame_count / self.capture_setting.captur_frame_per_s),
            self.frame_count % self.capture_setting.captur_frame_per_s,
        )
        dirpath = self.capture_setting.get_full_save_dirpath()
        image_name = "{}.png".format(base_name)

        # frame = cv2.resize(frame , (int(self.video_size.width()), int(self.video_size.height())))

        self.write_image(frame, os.path.join(dirpath, image_name))

        json_data = create_circle_labelme_json(pos, r, image_name,
                                               frame.shape[0], frame.shape[1],
                                               frame)
        json_path = "{}.json".format(base_name)
        with open(os.path.join(dirpath, json_path), "w") as f:
            json.dump(json_data, f, indent=2, ensure_ascii=False)

    def write_image(self, frame, image_path):
        cv2.imwrite(image_path, frame)

    def change_record_mode(self, new_mode):
        if new_mode:
            self.record_button.setEnabled(False)
            self.stop_button.setEnabled(True)
        else:
            self.stop_button.setEnabled(False)
            self.record_button.setEnabled(True)

    def __del__(self):
        if self.capture is not None:
            self.capture.release()
Beispiel #30
0
 def fits_inside(size1: QSize, size2: QSize):
     return size1.width() <= size2.width() and size1.height(
     ) <= size2.height()