class BlockDiagramEditorView(QWidget): _size = 1_000 def __init__(self): super().__init__() self.labeledSlider = VLabeledSlider() self._qscene = QGraphicsScene() self._qscene.setSceneRect(-self._size / 2, -self._size / 2, self._size, self._size) self._qview = BlockDiagramView(scene=self._qscene, parent=self) layout = QHBoxLayout() layout.addWidget(self.labeledSlider, 0) layout.addWidget(self._qview, 1) layout.setAlignment(Qt.AlignCenter) self.widgetLayout = layout groupBox = QGroupBox() groupBox.setTitle("Block diagram") groupBox.setLayout(layout) parentLayout = QVBoxLayout() parentLayout.addWidget(groupBox) self.setLayout(parentLayout)
class GraphicsView(QGraphicsView): def __init__(self, **kwargs): super(GraphicsView, self).__init__(**kwargs) self._scene = QGraphicsScene(parent=self) self.current_image = QGraphicsPixmapItem() self._scene.addItem(self.current_image) self.setScene(self._scene) self.fitInView(self._scene.sceneRect(), Qt.KeepAspectRatio) self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn) self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) self.setMouseTracking(True) self.setRenderHint(QPainter.Antialiasing) def update_image(self, img: np.ndarray): rgb_image = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) h, w, ch = rgb_image.shape bytes_per_line = ch * w qimg = QImage(rgb_image.data, w, h, bytes_per_line, QImage.Format_RGB888) pixmap = QPixmap.fromImage(qimg) self.current_image.setPixmap(pixmap) self._scene.setSceneRect(0, 0, w, h) self.fitInView(self._scene.sceneRect(), Qt.KeepAspectRatio) def resizeEvent(self, e): self.fitInView(self._scene.sceneRect(), Qt.KeepAspectRatio) def wheelEvent(self, e): if e.angleDelta().y() > 0: self.scale(1.25, 1.25) elif e.angleDelta().y() < 0: self.scale(0.85, 0.85) def mousePressEvent(self, e): # シーン上のマウス位置を取得する pos = self.mapToScene(e.pos()) if e.button() == Qt.LeftButton: if e.modifiers() == Qt.NoModifier: pass elif e.modifiers() == Qt.AltModifier: self.setDragMode(QGraphicsView.ScrollHandDrag) QGraphicsView.mousePressEvent(self, e) def mouseReleaseEvent(self, e): QGraphicsView.mouseReleaseEvent(self, e) if e.button() == Qt.LeftButton: self.setDragMode(QGraphicsView.NoDrag) def calc_offset(self, p: QPoint): offset_x = p.x() - int(self.viewport().width() / 2) offset_y = p.y() - int(self.viewport().height() / 2) return QPoint(offset_x, offset_y)
class DataGraphWidget(QGraphicsView): selection_changed = Signal() def __init__(self, parent=None): super(DataGraphWidget, self).__init__(parent=parent) # Set up scene self.scene = QGraphicsScene(self) self.scene.setItemIndexMethod(QGraphicsScene.NoIndex) self.scene.setSceneRect(0, 0, 800, 300) self.setScene(self.scene) self.setWindowTitle("Glue data graph") self.setRenderHint(QPainter.Antialiasing) self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) self.setResizeAnchor(QGraphicsView.AnchorViewCenter) self.selection_level = 0 def resizeEvent(self, event): self.scene.setSceneRect(0, 0, self.width(), self.height()) self.relayout(reorder=False) def relayout(self, reorder=True): # Update radius for node in self.nodes: node.radius = self.height() / 30. layout_simple_circle(self.nodes, self.edges, center=(self.width() / 2, self.height() / 2), radius=self.height() / 3, reorder=reorder) # Update edge positions for edge in self.background_edges + self.edges: edge.update_position() # Set up labels self.left_nodes = [ node for node in self.nodes if node.node_position[0] < self.width() / 2 ] self.left_nodes = sorted(self.left_nodes, key=lambda x: x.node_position[1], reverse=True) self.right_nodes = [ node for node in self.nodes if node not in self.left_nodes ] self.right_nodes = sorted(self.right_nodes, key=lambda x: x.node_position[1], reverse=True) for i, node in enumerate(self.left_nodes): y = self.height() - (i + 1) / (len(self.left_nodes) + 1) * self.height() node.label_position = self.width() / 2 - self.height() / 2, y for i, node in enumerate(self.right_nodes): y = self.height() - (i + 1) / (len(self.right_nodes) + 1) * self.height() node.label_position = self.width() / 2 + self.height() / 2, y def set_data_collection(self, data_collection, old_links=None, new_links=None): # Get data and initialize nodes self.data_to_nodes = OrderedDict( (data, DataNode(data)) for data in data_collection) self.nodes = list(self.data_to_nodes.values()) # Get links and set up edges if old_links: self.background_edges = [ Edge(self.data_to_nodes[data1], self.data_to_nodes[data2], linewidth=1, zindex=1) for data1, data2 in get_connections( data_collection.external_links) ] else: self.background_edges = [] if new_links: self.edges = [ Edge(self.data_to_nodes[data1], self.data_to_nodes[data2]) for data1, data2 in get_connections(new_links) ] else: self.edges = [] # Figure out positions self.relayout() # Add nodes and edges to graph for node in self.nodes: node.add_to_scene(self.scene) for edge in self.background_edges + self.edges: edge.add_to_scene(self.scene) self.text_adjusted = False self.selected_edge = None self.selected_node1 = None self.selected_node2 = None def set_links(self, links): for edge in self.edges: edge.remove_from_scene(self.scene) self.edges = [ Edge(self.data_to_nodes[data1], self.data_to_nodes[data2]) for data1, data2 in get_connections(links) ] for edge in self.edges: edge.update_position() for edge in self.edges: edge.add_to_scene(self.scene) self._update_selected_edge() self._update_selected_colors() def paintEvent(self, event): super(DataGraphWidget, self).paintEvent(event) if not self.text_adjusted: for node in self.nodes: width = node.label.boundingRect().width() height = node.label.boundingRect().height() transform = QTransform() if node in self.left_nodes: transform.translate(-width, -height / 2) else: transform.translate(0, -height / 2) node.label.setTransform(transform) self.text_adjusted = True def manual_select(self, data1=None, data2=None): if data1 is None and data2 is not None: data1, data2 = data2, data1 if data2 is not None: self.selection_level = 2 elif data1 is not None: self.selection_level = 1 else: self.selection_level = 0 self.selected_node1 = self.data_to_nodes.get(data1, None) self.selected_node2 = self.data_to_nodes.get(data2, None) self._update_selected_edge() self._update_selected_colors() def find_object(self, event): for obj in list(self.nodes) + self.edges: if obj.contains(event.localPos()): return obj def mouseMoveEvent(self, event): # TODO: Don't update until the end # TODO: Only select object on top selected = self.find_object(event) if selected is None: if self.selection_level == 0: self.selected_node1 = None self.selected_node2 = None self._update_selected_edge() elif self.selection_level == 1: self.selected_node2 = None self._update_selected_edge() elif isinstance(selected, DataNode): if self.selection_level == 0: self.selected_node1 = selected self.selected_node2 = None elif self.selection_level == 1: if selected is not self.selected_node1: self.selected_node2 = selected self._update_selected_edge() elif isinstance(selected, Edge): if self.selection_level == 0: self.selected_edge = selected self.selected_node1 = selected.node_source self.selected_node2 = selected.node_dest self._update_selected_colors() self.selection_changed.emit() def mousePressEvent(self, event): # TODO: Don't update until the end # TODO: Only select object on top selected = self.find_object(event) if selected is None: self.selection_level = 0 self.selected_node1 = None self.selected_node2 = None self._update_selected_edge() elif isinstance(selected, DataNode): if self.selection_level == 0: self.selected_node1 = selected self.selection_level += 1 elif self.selection_level == 1: if selected is self.selected_node1: self.selected_node1 = None self.selection_level = 0 else: self.selected_node2 = selected self.selection_level = 2 elif self.selection_level == 2: if selected is self.selected_node2: self.selected_node2 = None self.selection_level = 1 else: self.selected_node1 = selected self.selected_node2 = None self.selection_level = 1 self._update_selected_edge() elif isinstance(selected, Edge): if self.selected_edge is selected and self.selection_level == 2: self.selected_edge = None self.selected_node1 = None self.selected_node2 = None self.selection_level = 0 else: self.selected_edge = selected self.selected_node1 = selected.node_source self.selected_node2 = selected.node_dest self.selection_level = 2 self.mouseMoveEvent(event) def _update_selected_edge(self): for edge in self.edges: if (edge.node_source is self.selected_node1 and edge.node_dest is self.selected_node2 or edge.node_source is self.selected_node2 and edge.node_dest is self.selected_node1): self.selected_edge = edge break else: self.selected_edge = None def _update_selected_colors(self): colors = {} if self.selected_node1 is not None and self.selection_level < 2: direct, indirect = find_connections(self.selected_node1, self.nodes, self.edges) for node in self.nodes: if node in direct or node in indirect: colors[node] = COLOR_CONNECTED else: colors[node] = COLOR_DISCONNECTED for edge in self.edges: if (edge.node_source is self.selected_node1 or edge.node_dest is self.selected_node1): colors[edge] = COLOR_CONNECTED if self.selected_edge is not None: colors[self.selected_edge] = COLOR_SELECTED if self.selected_node1 is not None: colors[self.selected_node1] = COLOR_SELECTED if self.selected_node2 is not None: colors[self.selected_node2] = COLOR_SELECTED self.set_colors(colors) def set_colors(self, colors): for obj in list(self.nodes) + self.edges: default_color = '0.8' if isinstance(obj, DataNode) else '0.5' obj.color = colors.get(obj, default_color) obj.update()
class CroppableCameraView(QGraphicsView): rectChanged = Signal(QRect) imageDisplayed = Signal(np.ndarray) videoStarted = Signal() mouseMoved = Signal(QMouseEvent) mousePressed = Signal(QMouseEvent) mouseReleased = Signal(QMouseEvent) def __init__(self, camera, **settings): super(CroppableCameraView, self).__init__() self.setRenderHint(QPainter.Antialiasing) self.cam = camera self.is_live = False self._cmin = 0 self._cmax = None self.settings = settings self._selecting = False self.needs_resize = False self.latest_array = None self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.scene = QGraphicsScene() #self.setFrameStyle(QFrame.NoFrame) self.setScene(self.scene) self.pixmapitem = self.scene.addPixmap(QPixmap()) self._uncropped_pixmap = None self.setMouseTracking(True) self.start = None c1 = QColor(0, 100, 220, 150) self.c2 = QColor(0, 100, 220, 50) self.c3 = QColor(0, 100, 220, 0) pen = QPen(c1, 2) self.selrect = self.scene.addRect(1, 1, 1, 1, pen, self.c3) self.selrect.setZValue(100) self.selrect.hide() def enable_selecting(self): print("Selection enabled") self._selecting = True def disable_selecting(self): self._selecting = False def mousePressEvent(self, event): if self._selecting: print("Mouse pressed") if not self.selrect.isVisible(): self.selrect.show() if event.button() == Qt.LeftButton: sp = self.mapToScene(event.pos()) self.start = (sp.x(), sp.y()) self.selrect.setRect(sp.x(), sp.y(), 0, 0) self.selrect.setBrush(self.c2) self.mousePressed.emit(event) def mouseMoveEvent(self, event): if self.start: x1, y1 = self.start sp = self.mapToScene(event.pos()) # Image width, height in scene coords ir = self.pixmapitem.boundingRect() sr = self.pixmapitem.sceneTransform().mapRect(ir) size = self.pixmapitem.pixmap().size() x2 = max(sr.left(), min(sr.right() - 1, sp.x())) y2 = max(sr.top(), min(sr.bottom() - 1, sp.y())) x1, x2 = sorted((x1, x2)) y1, y2 = sorted((y1, y2)) self.selrect.setRect(x1, y1, x2 - x1, y2 - y1) self.mouseMoved.emit(event) def mouseReleaseEvent(self, event): if self.start and event.button() == Qt.LeftButton: self.start = None self.selrect.setBrush(self.c3) self.rectChanged.emit(self.selrect.rect().toRect()) self.mouseReleased.emit(event) def update_rect(self, x, y, w, h): self.selrect.setRect(x, y, w, h) self.rectChanged.emit(self.selrect.rect().toRect()) def hideRect(self): self.selrect.hide() def showRect(self): self.selrect.show() def setRectVisible(self, visible): self.selrect.setVisible(visible) def isRectVisible(self): return self.selrect.isVisible() def rect(self): """QRect of the selection in camera coordinates""" # selrect in _image_ (pixmap) coordinates i_selrect = self.pixmapitem.sceneTransform().inverted()[0].mapRect( self.selrect.rect()) current_left = self.settings.get('left', 0) current_top = self.settings.get('top', 0) return i_selrect.toRect().translated(current_left, current_top) def crop_to_rect(self): was_live = self.is_live if was_live: self.stop_video() self.hideRect() rect = self.rect() self.settings['left'] = rect.left() self.settings['right'] = rect.right() self.settings['top'] = rect.top() self.settings['bot'] = rect.bottom() if was_live: self.start_video() else: if not self._uncropped_pixmap: self._uncropped_pixmap = self.pixmapitem.pixmap() self.pixmapitem.setPixmap(self._uncropped_pixmap.copy(rect)) self.setFixedSize(rect.width(), rect.height()) def uncrop(self): was_live = self.is_live if was_live: self.stop_video() for key in ['left', 'right', 'top', 'bot']: if key in self.settings: del self.settings[key] if was_live: self.start_video() else: pixmap = self._uncropped_pixmap self.pixmapitem.setPixmap(pixmap) ir = self.pixmapitem.boundingRect() sr = self.pixmapitem.sceneTransform().mapRect(ir) self.scene.setSceneRect(sr) self.setFixedSize(sr.width(), sr.height()) def mapSceneToPixmap(self, pt): transform = self.pixmapitem.sceneTransform().inverted()[0] return transform.mapRect(pt) if isinstance( pt, (QRect, QRectF)) else transform.map(pt) def mapViewToPixmap(self, view_pt): scene_pt = self.mapToScene(view_pt) return self.mapSceneToPixmap(scene_pt) def mapPixmapToScene(self, pixmap_pt): transform = self.pixmapitem.sceneTransform() map_func = transform.mapRect if isinstance(pixmap_pt, (QRect, QRectF)) else transform.map return map_func(pixmap_pt) def mapSceneToCamera(self, sc_pt): px_pt = self.mapSceneToPixmap(sc_pt) return QPoint(px_pt.x() + self.settings.get('left', 0), px_pt.y() + self.settings.get('top', 0)) def set_image(self, image_arr): pixmap = QPixmap(self._array_to_qimage(image_arr)) if not self.pixmapitem: self.pixmapitem = self.scene.addPixmap(pixmap) else: self.pixmapitem.setPixmap(pixmap) if self.needs_resize: self._autoresize_viewport() self.needs_resize = False def _autoresize_viewport(self): ir = self.pixmapitem.boundingRect() sr = self.pixmapitem.sceneTransform().mapRect(ir) self.scene.setSceneRect(sr) #self.setFixedSize(sr.width(), sr.height()) self.resize(sr.width(), sr.height()) #self.viewport().setMaximumSize(sr.width(), sr.height()) d = self.style().pixelMetric(QStyle.PM_DefaultFrameWidth) self.setMaximumSize(sr.width() + 2 * d, sr.height() + 2 * d) def grab_image(self): arr = self.cam.grab_image(**self.settings) self.set_image(arr) self.latest_array = arr self.imageDisplayed.emit(arr) def start_video(self): timer = QTimer() self.timer = timer timer.timeout.connect(self._wait_for_frame) self.cam.start_live_video(**self.settings) timer.start(0) # Run full throttle self.is_live = True self.needs_resize = True self.videoStarted.emit() def stop_video(self): self.timer.stop() self.cam.stop_live_video() self.is_live = False def _wait_for_frame(self): frame_ready = self.cam.wait_for_frame(timeout='0 ms') if frame_ready: arr = self.cam.latest_frame(copy=True) self.set_image(arr) self.latest_array = arr self.imageDisplayed.emit(arr) def _scale_image(self, arr): """Return a bytescaled copy of the image array""" if not self._cmax: self._cmax = arr.max() # Set cmax once from first image return scipy.misc.bytescale(arr, self._cmin, self._cmax) def _lut_scale_image(self, arr): if not self._cmax: self._cmax = arr.max() lut = scipy.misc.bytescale(np.arange(2**16), self._cmin, self._cmax) return lut[arr] def _create_lut(self, k): A = 2**15 B = 100 # k's scaling factor g = lambda i, k: A * ((k - B) * i) / ((2 * k) * x - (k + B) * A) def _array_to_qimage(self, arr): bpl = arr.strides[0] is_rgb = len(arr.shape) == 3 if is_rgb and arr.dtype == np.uint8: format = QImage.Format_RGB32 image = QImage(arr.data, self.cam.width, self.cam.height, bpl, format) elif not is_rgb and arr.dtype == np.uint8: # TODO: Somehow need to make sure data is ordered as I'm assuming format = QImage.Format_Indexed8 image = QImage(arr.data, self.cam.width, self.cam.height, bpl, format) self._saved_img = arr elif not is_rgb and arr.dtype == np.uint16: arr = self._scale_image(arr) format = QImage.Format_Indexed8 w, h = self.cam.width, self.cam.height image = QImage(arr.data, w, h, w, format) self._saved_img = arr # Save a reference to keep Qt from crashing else: raise Exception("Unsupported color mode") return image
class DataGraphWidget(QGraphicsView): selection_changed = Signal() def __init__(self, parent=None): super(DataGraphWidget, self).__init__(parent=parent) # Set up scene self.scene = QGraphicsScene(self) self.scene.setItemIndexMethod(QGraphicsScene.NoIndex) self.scene.setSceneRect(0, 0, 800, 300) self.setScene(self.scene) self.setWindowTitle("Glue data graph") self.setRenderHint(QPainter.Antialiasing) self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) self.setResizeAnchor(QGraphicsView.AnchorViewCenter) self.selection_level = 0 def resizeEvent(self, event): self.scene.setSceneRect(0, 0, self.width(), self.height()) self.relayout(reorder=False) def relayout(self, reorder=True): # Update radius for node in self.nodes: node.radius = self.height() / 30. layout_simple_circle(self.nodes, self.edges, center=(self.width() / 2, self.height() / 2), radius=self.height() / 3, reorder=reorder) # Update edge positions for edge in self.edges: edge.update_position() # Set up labels self.left_nodes = [node for node in self.nodes if node.node_position[0] < self.width() / 2] self.left_nodes = sorted(self.left_nodes, key=lambda x: x.node_position[1], reverse=True) self.right_nodes = [node for node in self.nodes if node not in self.left_nodes] self.right_nodes = sorted(self.right_nodes, key=lambda x: x.node_position[1], reverse=True) for i, node in enumerate(self.left_nodes): y = self.height() - (i + 1) / (len(self.left_nodes) + 1) * self.height() node.label_position = self.width() / 2 - self.height() / 2, y for i, node in enumerate(self.right_nodes): y = self.height() - (i + 1) / (len(self.right_nodes) + 1) * self.height() node.label_position = self.width() / 2 + self.height() / 2, y def set_data_collection(self, data_collection): # Get data and initialize nodes self.data_to_nodes = dict((data, DataNode(data)) for data in data_collection) self.nodes = list(self.data_to_nodes.values()) # Get links and set up edges self.edges = [Edge(self.data_to_nodes[data1], self.data_to_nodes[data2]) for data1, data2 in get_connections(data_collection.external_links)] # Figure out positions self.relayout() # Add nodes and edges to graph for node in self.nodes: node.add_to_scene(self.scene) for edge in self.edges: edge.add_to_scene(self.scene) self.text_adjusted = False self.selected_edge = None self.selected_node1 = None self.selected_node2 = None def set_links(self, links): for edge in self.edges: edge.remove_from_scene(self.scene) self.edges = [Edge(self.data_to_nodes[data1], self.data_to_nodes[data2]) for data1, data2 in get_connections(links)] for edge in self.edges: edge.update_position() for edge in self.edges: edge.add_to_scene(self.scene) self._update_selected_edge() self._update_selected_colors() def paintEvent(self, event): super(DataGraphWidget, self).paintEvent(event) if not self.text_adjusted: for node in self.nodes: width = node.label.boundingRect().width() height = node.label.boundingRect().height() transform = QTransform() if node in self.left_nodes: transform.translate(-width, -height / 2) else: transform.translate(0, -height / 2) node.label.setTransform(transform) self.text_adjusted = True def manual_select(self, data1=None, data2=None): if data1 is None and data2 is not None: data1, data2 = data2, data1 if data2 is not None: self.selection_level = 2 elif data1 is not None: self.selection_level = 1 else: self.selection_level = 0 self.selected_node1 = self.data_to_nodes.get(data1, None) self.selected_node2 = self.data_to_nodes.get(data2, None) self._update_selected_edge() self._update_selected_colors() def find_object(self, event): for obj in list(self.nodes) + self.edges: if obj.contains(event.localPos()): return obj def mouseMoveEvent(self, event): # TODO: Don't update until the end # TODO: Only select object on top selected = self.find_object(event) if selected is None: if self.selection_level == 0: self.selected_node1 = None self.selected_node2 = None self._update_selected_edge() elif self.selection_level == 1: self.selected_node2 = None self._update_selected_edge() elif isinstance(selected, DataNode): if self.selection_level == 0: self.selected_node1 = selected self.selected_node2 = None elif self.selection_level == 1: if selected is not self.selected_node1: self.selected_node2 = selected self._update_selected_edge() elif isinstance(selected, Edge): if self.selection_level == 0: self.selected_edge = selected self.selected_node1 = selected.node_source self.selected_node2 = selected.node_dest self._update_selected_colors() self.selection_changed.emit() def mousePressEvent(self, event): # TODO: Don't update until the end # TODO: Only select object on top selected = self.find_object(event) if selected is None: self.selection_level = 0 self.selected_node1 = None self.selected_node2 = None self._update_selected_edge() elif isinstance(selected, DataNode): if self.selection_level == 0: self.selected_node1 = selected self.selection_level += 1 elif self.selection_level == 1: if selected is self.selected_node1: self.selected_node1 = None self.selection_level = 0 else: self.selected_node2 = selected self.selection_level = 2 elif self.selection_level == 2: if selected is self.selected_node2: self.selected_node2 = None self.selection_level = 1 else: self.selected_node1 = selected self.selected_node2 = None self.selection_level = 1 self._update_selected_edge() elif isinstance(selected, Edge): if self.selected_edge is selected and self.selection_level == 2: self.selected_edge = None self.selected_node1 = None self.selected_node2 = None self.selection_level = 0 else: self.selected_edge = selected self.selected_node1 = selected.node_source self.selected_node2 = selected.node_dest self.selection_level = 2 self.mouseMoveEvent(event) def _update_selected_edge(self): for edge in self.edges: if (edge.node_source is self.selected_node1 and edge.node_dest is self.selected_node2 or edge.node_source is self.selected_node2 and edge.node_dest is self.selected_node1): self.selected_edge = edge break else: self.selected_edge = None def _update_selected_colors(self): colors = {} if self.selected_node1 is not None and self.selection_level < 2: direct, indirect = find_connections(self.selected_node1, self.nodes, self.edges) for node in self.nodes: if node in direct or node in indirect: colors[node] = COLOR_CONNECTED else: colors[node] = COLOR_DISCONNECTED for edge in self.edges: if (edge.node_source is self.selected_node1 or edge.node_dest is self.selected_node1): colors[edge] = COLOR_CONNECTED if self.selected_edge is not None: colors[self.selected_edge] = COLOR_SELECTED if self.selected_node1 is not None: colors[self.selected_node1] = COLOR_SELECTED if self.selected_node2 is not None: colors[self.selected_node2] = COLOR_SELECTED self.set_colors(colors) def set_colors(self, colors): for obj in list(self.nodes) + self.edges: default_color = '0.8' if isinstance(obj, DataNode) else '0.5' obj.color = colors.get(obj, default_color) obj.update()
class CroppableCameraView(QGraphicsView): rectChanged = Signal(QRect) imageDisplayed = Signal(np.ndarray) videoStarted = Signal() mouseMoved = Signal(QMouseEvent) mousePressed = Signal(QMouseEvent) mouseReleased = Signal(QMouseEvent) def __init__(self, camera, **settings): super(CroppableCameraView, self).__init__() self.setRenderHint(QPainter.Antialiasing) self.cam = camera self.is_live = False self._cmin = 0 self._cmax = None self.settings = settings self._selecting = False self.needs_resize = False self.latest_array = None self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.scene = QGraphicsScene() #self.setFrameStyle(QFrame.NoFrame) self.setScene(self.scene) self.pixmapitem = self.scene.addPixmap(QPixmap()) self._uncropped_pixmap = None self.setMouseTracking(True) self.start = None c1 = QColor(0, 100, 220, 150) self.c2 = QColor(0, 100, 220, 50) self.c3 = QColor(0, 100, 220, 0) pen = QPen(c1, 2) self.selrect = self.scene.addRect(1, 1, 1, 1, pen, self.c3) self.selrect.setZValue(100) self.selrect.hide() def enable_selecting(self): print("Selection enabled") self._selecting = True def disable_selecting(self): self._selecting = False def mousePressEvent(self, event): if self._selecting: print("Mouse pressed") if not self.selrect.isVisible(): self.selrect.show() if event.button() == Qt.LeftButton: sp = self.mapToScene(event.pos()) self.start = (sp.x(), sp.y()) self.selrect.setRect(sp.x(), sp.y(), 0, 0) self.selrect.setBrush(self.c2) self.mousePressed.emit(event) def mouseMoveEvent(self, event): if self.start: x1, y1 = self.start sp = self.mapToScene(event.pos()) # Image width, height in scene coords ir = self.pixmapitem.boundingRect() sr = self.pixmapitem.sceneTransform().mapRect(ir) size = self.pixmapitem.pixmap().size() x2 = max(sr.left(), min(sr.right()-1, sp.x())) y2 = max(sr.top(), min(sr.bottom()-1, sp.y())) x1, x2 = sorted((x1, x2)) y1, y2 = sorted((y1, y2)) self.selrect.setRect(x1, y1, x2-x1, y2-y1) self.mouseMoved.emit(event) def mouseReleaseEvent(self, event): if self.start and event.button() == Qt.LeftButton: self.start = None self.selrect.setBrush(self.c3) self.rectChanged.emit(self.selrect.rect().toRect()) self.mouseReleased.emit(event) def update_rect(self, x, y, w, h): self.selrect.setRect(x, y, w, h) self.rectChanged.emit(self.selrect.rect().toRect()) def hideRect(self): self.selrect.hide() def showRect(self): self.selrect.show() def setRectVisible(self, visible): self.selrect.setVisible(visible) def isRectVisible(self): return self.selrect.isVisible() def rect(self): """QRect of the selection in camera coordinates""" # selrect in _image_ (pixmap) coordinates i_selrect = self.pixmapitem.sceneTransform().inverted()[0].mapRect(self.selrect.rect()) current_left = self.settings.get('left', 0) current_top = self.settings.get('top', 0) return i_selrect.toRect().translated(current_left, current_top) def crop_to_rect(self): was_live = self.is_live if was_live: self.stop_video() self.hideRect() rect = self.rect() self.settings['left'] = rect.left() self.settings['right'] = rect.right() self.settings['top'] = rect.top() self.settings['bot'] = rect.bottom() if was_live: self.start_video() else: if not self._uncropped_pixmap: self._uncropped_pixmap = self.pixmapitem.pixmap() self.pixmapitem.setPixmap(self._uncropped_pixmap.copy(rect)) self.setFixedSize(rect.width(), rect.height()) def uncrop(self): was_live = self.is_live if was_live: self.stop_video() for key in ['left', 'right', 'top', 'bot']: if key in self.settings: del self.settings[key] if was_live: self.start_video() else: pixmap = self._uncropped_pixmap self.pixmapitem.setPixmap(pixmap) ir = self.pixmapitem.boundingRect() sr = self.pixmapitem.sceneTransform().mapRect(ir) self.scene.setSceneRect(sr) self.setFixedSize(sr.width(), sr.height()) def mapSceneToPixmap(self, pt): transform = self.pixmapitem.sceneTransform().inverted()[0] return transform.mapRect(pt) if isinstance(pt, (QRect, QRectF)) else transform.map(pt) def mapViewToPixmap(self, view_pt): scene_pt = self.mapToScene(view_pt) return self.mapSceneToPixmap(scene_pt) def mapPixmapToScene(self, pixmap_pt): transform = self.pixmapitem.sceneTransform() map_func = transform.mapRect if isinstance(pixmap_pt, (QRect, QRectF)) else transform.map return map_func(pixmap_pt) def mapSceneToCamera(self, sc_pt): px_pt = self.mapSceneToPixmap(sc_pt) return QPoint(px_pt.x() + self.settings.get('left', 0), px_pt.y() + self.settings.get('top', 0)) def set_image(self, image_arr): pixmap = QPixmap(self._array_to_qimage(image_arr)) if not self.pixmapitem: self.pixmapitem = self.scene.addPixmap(pixmap) else: self.pixmapitem.setPixmap(pixmap) if self.needs_resize: self._autoresize_viewport() self.needs_resize = False def _autoresize_viewport(self): ir = self.pixmapitem.boundingRect() sr = self.pixmapitem.sceneTransform().mapRect(ir) self.scene.setSceneRect(sr) #self.setFixedSize(sr.width(), sr.height()) self.resize(sr.width(), sr.height()) #self.viewport().setMaximumSize(sr.width(), sr.height()) d = self.style().pixelMetric(QStyle.PM_DefaultFrameWidth) self.setMaximumSize(sr.width() + 2*d, sr.height() + 2*d) def grab_image(self): arr = self.cam.grab_image(**self.settings) self.set_image(arr) self.latest_array = arr self.imageDisplayed.emit(arr) def start_video(self): timer = QTimer() self.timer = timer timer.timeout.connect(self._wait_for_frame) self.cam.start_live_video(**self.settings) timer.start(0) # Run full throttle self.is_live = True self.needs_resize = True self.videoStarted.emit() def stop_video(self): self.timer.stop() self.cam.stop_live_video() self.is_live = False def _wait_for_frame(self): frame_ready = self.cam.wait_for_frame(timeout='0 ms') if frame_ready: arr = self.cam.latest_frame(copy=True) self.set_image(arr) self.latest_array = arr self.imageDisplayed.emit(arr) def _scale_image(self, arr): """Return a bytescaled copy of the image array""" if not self._cmax: self._cmax = arr.max() # Set cmax once from first image return scipy.misc.bytescale(arr, self._cmin, self._cmax) def _lut_scale_image(self, arr): if not self._cmax: self._cmax = arr.max() lut = scipy.misc.bytescale(np.arange(2**16), self._cmin, self._cmax) return lut[arr] def _create_lut(self, k): A = 2**15 B = 100 # k's scaling factor g = lambda i, k: A*((k-B)*i) / ((2*k)*x - (k+B)*A) def _array_to_qimage(self, arr): bpl = arr.strides[0] is_rgb = len(arr.shape) == 3 if is_rgb and arr.dtype == np.uint8: format = QImage.Format_RGB32 image = QImage(arr.data, self.cam.width, self.cam.height, bpl, format) elif not is_rgb and arr.dtype == np.uint8: # TODO: Somehow need to make sure data is ordered as I'm assuming format = QImage.Format_Indexed8 image = QImage(arr.data, self.cam.width, self.cam.height, bpl, format) self._saved_img = arr elif not is_rgb and arr.dtype == np.uint16: arr = self._scale_image(arr) format = QImage.Format_Indexed8 w, h = self.cam.width, self.cam.height image = QImage(arr.data, w, h, w, format) self._saved_img = arr # Save a reference to keep Qt from crashing else: raise Exception("Unsupported color mode") return image
class GraphicsView(QGraphicsView): def __init__(self, parent=None): super(GraphicsView, self).__init__(parent) self.scene = QGraphicsScene() self.pixmapItem = QGraphicsPixmapItem() self.scene.setSceneRect(0, 0, 500, 500) self.setScene(self.scene) self.scene.addItem(self.pixmapItem) #would eventually make this fit to size of screen, waiting cause it's annoying. self.buildingParams = [] self.scalef = [1, 1] self.Buildings() def Buildings(self): filename = os.getcwd() + "\examplebuildings.png" img = cv2.imread(filename) imgray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret,thresh = cv2.threshold(imgray, 250, 255, 0) contours , h = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE) MinCntArea = 5000. MaxCntArea = 100000. for cnt in contours: if cv2.contourArea(cnt) > MinCntArea and cv2.contourArea(cnt) < MaxCntArea: cv2.drawContours(img, cnt, -1, (0, 255, 0), 3) # this is a green rectangle which shows the found contour #cv2.circle(img, (min(cnt.T[0][0]), min(cnt.T[1][0])), 4, 4) #makes a circle around the upper left corner #np.set_printoptions(threshold=np.inf) #this is to make it so that the whole numpy array prints to the terminal self.buildingParams.append([min(cnt.T[0][0]), min(cnt.T[1][0]), max(cnt.T[0][0])-min(cnt.T[0][0]), max(cnt.T[1][0])-min(cnt.T[1][0])]) # x, y, height, width # copy and paste these three lines if you would like to see what an opencv images looks like anywhere in the program #cv2.imshow('img', img) #cv2.waitKey(0) #cv2.destroyAllWindows() #cv2.rectangle(img, (self.buildingParams[0][0], self.buildingParams[0][1]), # (self.buildingParams[0][0] + self.buildingParams[0][2], self.buildingParams[0][1] + self.buildingParams[0][3]), # (255, 0, 0), -1) # draws a blue filled in rectangle - did this to make sure I had the indicies correct for x, y, width, and height height, width, channel = img.shape bytesPerLine = 3*width qimg = QImage(img.data, width, height, bytesPerLine, QImage.Format_RGB888).rgbSwapped() ##### need to add not null checks here self.ogimage = QPixmap.fromImage(qimg) self.image = self.ogimage.scaled(self.scene.width(), self.scene.height()) self.pixmapItem.setPixmap(self.image) self.updateScalef() self.buildingParams += self.scalef for i in range(len(self.buildingParams)-2): parameters = self.buildingParams rect = GraphicsRectItem(QRectF(QPointF(parameters[i][0] * parameters[4], parameters[i][1] * parameters[5]), QPointF((parameters[i][0] + parameters[i][2]) * parameters[4], (parameters[i][1] + parameters[i][3]) * parameters[5]))) rect.setAcceptHoverEvents(True) self.scene.addItem(rect) def updateScalef(self): self.scalef[0] = self.image.width()/self.ogimage.width() self.scalef[1] = self.image.height()/self.ogimage.height() def mousePressEvent(self, e): print(e.pos())