class MainScreen(QMainWindow): def __init__(self): super().__init__() #self.setupUi(self) # Graphic Screen set self.img = QGraphicsPixmapItem(QPixmap('KarlaOnMyShoulder.jpg')) self.scene = QGraphicsScene() self.graphicsView = QGraphicsView() self.scene.addItem(self.img) self.graphicsView.setScene(self.scene) # Full Screen set size _WIDTH_ADD = 25 _HEIGHT_ADD = 25 self.setGeometry(0, 0, 640 + _WIDTH_ADD, 500 + _HEIGHT_ADD) self.graphicsView.viewport().installEventFilter(self) self.current_item = None self.start_pos = QPointF() self.end_pos = QPointF() self.show() def eventFilter(self, o, e): if self.graphicsView.viewport() is o: if e.type() == QEvent.MouseButtonPress: if e.buttons() & Qt.LeftButton: print("press") self.start_pos = self.end_pos = self.graphicsView.mapToScene( e.pos()) pen = QPen(QColor(240, 240, 240)) pen.setWidth(3) brush = QBrush(QColor(100, 255, 100, 100)) self.current_item = self.scene.addRect( QRectF(), pen, brush) self._update_item() elif e.type() == QEvent.MouseMove: if e.buttons( ) & Qt.LeftButton and self.current_item is not None: print("move") self.end_pos = self.graphicsView.mapToScene(e.pos()) self._update_item() elif e.type() == QEvent.MouseButtonRelease: print("release") self.end_pos = self.graphicsView.mapToScene(e.pos()) self._update_item() self.current_item = None return super().eventFilter(o, e) def _update_item(self): if self.current_item is not None: self.current_item.setRect( QRectF(self.start_pos, self.end_pos).normalized())
class SlideViewer(QWidget): eventSignal = pyqtSignal(PyQt5.QtCore.QEvent) def __init__(self, parent: QWidget = None, viewer_top_else_left=True): super().__init__(parent) self.init_view() self.init_labels(word_wrap=viewer_top_else_left) self.init_layout(viewer_top_else_left) def init_view(self): self.scene = MyGraphicsScene() self.view = QGraphicsView() self.view.setScene(self.scene) self.view.setTransformationAnchor(QGraphicsView.NoAnchor) self.view.viewport().installEventFilter(self) self.rubber_band = QRubberBand(QRubberBand.Rectangle, self) self.mouse_press_view = QPoint() self.view.horizontalScrollBar().sliderMoved.connect( self.on_view_changed) self.view.verticalScrollBar().sliderMoved.connect(self.on_view_changed) self.scale_initializer_deffered_function = None self.slide_view_params = None self.slide_helper = None def init_labels(self, word_wrap): # word_wrap = True self.level_downsample_label = QLabel() self.level_downsample_label.setWordWrap(word_wrap) self.level_size_label = QLabel() self.level_size_label.setWordWrap(word_wrap) self.selected_rect_label = QLabel() self.selected_rect_label.setWordWrap(word_wrap) self.mouse_pos_scene_label = QLabel() self.mouse_pos_scene_label.setWordWrap(word_wrap) self.view_rect_scene_label = QLabel() self.view_rect_scene_label.setWordWrap(word_wrap) self.labels_layout = QVBoxLayout() self.labels_layout.setAlignment(Qt.AlignTop) self.labels_layout.addWidget(self.level_downsample_label) self.labels_layout.addWidget(self.level_size_label) self.labels_layout.addWidget(self.mouse_pos_scene_label) # self.labels_layout.addWidget(self.selected_rect_label) self.labels_layout.addWidget(self.view_rect_scene_label) def init_layout(self, viewer_top_else_left=True): main_layout = QVBoxLayout( self) if viewer_top_else_left else QHBoxLayout(self) main_layout.addWidget(self.view, ) main_layout.addLayout(self.labels_layout) # main_layout.setContentsMargins(0, 0, 0, 0) self.setLayout(main_layout) """ If you want to start view frome some point at some level, specify <level> and <level_rect> params. level_rect : rect in dimensions of slide at level=level. If None - fits the whole size of slide """ def load(self, slide_view_params: SlideViewParams, preffered_rects_count=2000, zoom_step=1.15): self.zoom_step = zoom_step self.slide_view_params = slide_view_params self.slide_helper = SlideHelper(slide_view_params.slide_path) self.slide_graphics = SlideGraphicsGroup(slide_view_params, preffered_rects_count) self.scene.clear() self.scene.addItem(self.slide_graphics) if self.slide_view_params.level == -1 or self.slide_view_params.level is None: self.slide_view_params.level = self.slide_helper.get_max_level() self.slide_graphics.update_visible_level(self.slide_view_params.level) self.scene.setSceneRect( self.slide_helper.get_rect_for_level(self.slide_view_params.level)) def scale_initializer_deffered_function(): self.view.resetTransform() # print("size when loading: ", self.view.viewport().size()) if self.slide_view_params.level_rect: # self.view.fitInView(QRectF(*self.slide_view_params.level_rect), Qt.KeepAspectRatioByExpanding) self.view.fitInView(QRectF(*self.slide_view_params.level_rect), Qt.KeepAspectRatio) # print("after fit: ", self.get_current_view_scene_rect()) else: start_margins = QMarginsF(200, 200, 200, 200) start_image_rect_ = self.slide_helper.get_rect_for_level( self.slide_view_params.level) self.view.fitInView(start_image_rect_ + start_margins, Qt.KeepAspectRatio) self.scale_initializer_deffered_function = scale_initializer_deffered_function def eventFilter(self, qobj: 'QObject', event: QEvent): self.eventSignal.emit(event) event_processed = False # print("size when event: ", event, event.type(), self.view.viewport().size()) if isinstance(event, QShowEvent): """ we need it deffered because fitInView logic depends on current viewport size. Expecting at this point widget is finally resized before being shown at first """ if self.scale_initializer_deffered_function: # TODO labels start to occupy some space after view was already fitted, and labels will reduce size of viewport # self.update_labels() self.scale_initializer_deffered_function() self.on_view_changed() self.scale_initializer_deffered_function = None elif isinstance(event, QWheelEvent): event_processed = self.process_viewport_wheel_event(event) # we handle wheel event to prevent GraphicsView interpret it as scrolling elif isinstance(event, QMouseEvent): event_processed = self.process_mouse_event(event) return event_processed def process_viewport_wheel_event(self, event: QWheelEvent): # print("size when wheeling: ", self.view.viewport().size()) zoom_in = self.zoom_step zoom_out = 1 / zoom_in zoom_ = zoom_in if event.angleDelta().y() > 0 else zoom_out self.update_scale(event.pos(), zoom_) event.accept() self.on_view_changed() return True def process_mouse_event(self, event: QMouseEvent): if self.slide_helper is None: return False if event.button() == Qt.MiddleButton: if event.type() == QEvent.MouseButtonPress: self.slide_graphics.update_grid_visibility( not self.slide_graphics.slide_view_params.grid_visible) # items=self.scene.items() # QMessageBox.information(None, "Items", str(items)) return True # self.update_scale(QPoint(), 1.15) elif event.button() == Qt.LeftButton: if event.type() == QEvent.MouseButtonPress: self.mouse_press_view = QPoint(event.pos()) self.rubber_band.setGeometry( QRect(self.mouse_press_view, QSize())) self.rubber_band.show() return True elif event.type() == QEvent.MouseButtonRelease: self.rubber_band.hide() self.remember_selected_rect_params() self.slide_graphics.update_selected_rect_0_level( self.slide_view_params.selected_rect_0_level) self.update_labels() self.scene.invalidate() return True elif event.type() == QEvent.MouseMove: self.mouse_pos_scene_label.setText( "mouse_scene: " + point_to_str(self.view.mapToScene(event.pos()))) if not self.mouse_press_view.isNull(): self.rubber_band.setGeometry( QRect(self.mouse_press_view, event.pos()).normalized()) return True return False def remember_selected_rect_params(self): pos_scene = self.view.mapToScene(self.rubber_band.pos()) rect_scene = self.view.mapToScene( self.rubber_band.rect()).boundingRect() downsample = self.slide_helper.get_downsample_for_level( self.slide_view_params.level) selected_qrectf_0_level = QRectF(pos_scene * downsample, rect_scene.size() * downsample) self.slide_view_params.selected_rect_0_level = selected_qrectf_0_level.getRect( ) def update_scale(self, mouse_pos: QPoint, zoom): old_mouse_pos_scene = self.view.mapToScene(mouse_pos) old_view_scene_rect = self.view.mapToScene( self.view.viewport().rect()).boundingRect() old_level = self.get_best_level_for_scale( self.get_current_view_scale()) old_level_downsample = self.slide_helper.get_downsample_for_level( old_level) new_level = self.get_best_level_for_scale( self.get_current_view_scale() * zoom) new_level_downsample = self.slide_helper.get_downsample_for_level( new_level) level_scale_delta = 1 / (new_level_downsample / old_level_downsample) r = old_view_scene_rect.topLeft() m = old_mouse_pos_scene new_view_scene_rect_top_left = (m - (m - r) / zoom) * level_scale_delta new_view_scene_rect = QRectF( new_view_scene_rect_top_left, old_view_scene_rect.size() * level_scale_delta / zoom) new_scale = self.get_current_view_scale( ) * zoom * new_level_downsample / old_level_downsample transform = QTransform().scale(new_scale, new_scale).translate( -new_view_scene_rect.x(), -new_view_scene_rect.y()) new_rect = self.slide_helper.get_rect_for_level(new_level) self.scene.setSceneRect(new_rect) self.slide_view_params.level = new_level self.reset_view_transform() self.view.setTransform(transform, False) self.slide_graphics.update_visible_level(new_level) self.update_labels() def get_best_level_for_scale(self, scale): scene_width = self.scene.sceneRect().size().width() candidates = [0] for level in self.slide_helper.get_levels(): w, h = self.slide_helper.get_level_size(level) if scene_width * scale <= w: candidates.append(level) best_level = max(candidates) return best_level def update_labels(self): level_downsample = self.slide_helper.get_downsample_for_level( self.slide_view_params.level) level_size = self.slide_helper.get_level_size( self.slide_view_params.level) self.level_downsample_label.setText( "level, downsample: {}, {:.0f}".format( self.slide_view_params.level, level_downsample)) self.level_size_label.setText( "level_size: ({}, {})".format(*level_size)) self.view_rect_scene_label.setText( "view_scene: ({:.0f},{:.0f},{:.0f},{:.0f})".format( *self.get_current_view_scene_rect().getRect())) if self.slide_view_params.selected_rect_0_level: self.selected_rect_label.setText( "selected rect (0-level): ({:.0f},{:.0f},{:.0f},{:.0f})". format(*self.slide_view_params.selected_rect_0_level)) def on_view_changed(self): if self.scale_initializer_deffered_function is None and self.slide_view_params: self.slide_view_params.level_rect = self.get_current_view_scene_rect( ).getRect() self.update_labels() def reset_view_transform(self): self.view.resetTransform() self.view.horizontalScrollBar().setValue(0) self.view.verticalScrollBar().setValue(0) def get_current_view_scene_rect(self): return self.view.mapToScene(self.view.viewport().rect()).boundingRect() def get_current_view_scale(self): scale = self.view.transform().m11() return scale
class GraphEditor(QWidget): def __init__(self, parent=None): QWidget.__init__(self, parent) self.setMinimumSize(600, 400) random.seed(version=2) self.nodes = [] self.connections = [] # Widget layout and child widgets: self.horizontalLayout = QHBoxLayout(self) self.diagramScene = DiagramScene(self) self.diagramView = QGraphicsView(self.diagramScene, self) self.horizontalLayout.addWidget(self.diagramView) self.diagramView.setRenderHint(QtGui.QPainter.Antialiasing, on=True) self.started_connection = None def remove_connection(self, connection): self.diagramScene.removeItem(connection.arrow) self.diagramScene.removeItem(connection.arrow.text_label) self.connections.remove(connection) def start_connection(self, port): self.started_connection = Connection(port, None, self) def get_node_by_name(self, name): for node in self.nodes: if node.name == name: return node return None def is_connection_exists(self, first_node, last_node): for connection in self.connections: if connection.get_first_node() == first_node and connection.get_last_node() == last_node: return True return False def load_diagram_from_graph(self, graph): # Read diagram from graph edges self.nodes = [] self.connections = [] self.diagramScene.clear() size = math.sqrt(len(graph.edges)) x_shift = 100 y_shift = 100 i = 0 for edge in graph.edges: first_node = edge.from_node.text last_node = edge.to_node.text link_type = edge.text # First Node i += 1 x = x_shift * i y = y_shift f = self.add_node(x, y, first_node) if i > size: y_shift += 100 i = 0 # Last Node i += 1 x = x_shift * i y = y_shift l = self.add_node(x, y, last_node) if i > size: y_shift += 100 i = 0 connection = Connection(f.get_random_port(), None, self) connection.set_link_type(link_type) connection.setToPort(l.get_random_port()) self.connections.append(connection) # self.dump() # svg = QSvgGenerator() # svg.setFileName('graph.svg') # svg.setSize(QSize(self.diagramScene.width(), self.diagramScene.height())) # svg.setViewBox(QRect(0, 0, self.diagramScene.width(), self.diagramScene.height())) # svg.setTitle('Semantic Graph') # svg.setDescription('File created by RTA') # painter = QPainter() # painter.begin(svg) # self.diagramScene.render(painter) # painter.end() print('Triples loading is finished') def scene_mouse_move_event(self, event): if self.started_connection: pos = event.scenePos() self.started_connection.setEndPos(pos) def scene_mouse_release_event(self, event): if self.started_connection: pos = event.scenePos() items = self.diagramScene.items(pos) for item in items: if type(item) is PortItem: if (not self.is_connection_exists(self.started_connection.fromPort.parent_block, item.parent_block)) and ( self.started_connection.fromPort.parent_block is not item.parent_block): self.started_connection.setToPort(item) self.connections.append(self.started_connection) if self.started_connection.toPort is None: self.started_connection.delete() self.started_connection = None def add_node(self, x, y, text): node = Block(text, parent=self) node.setPos(x, y) self.diagramScene.addItem(node) self.nodes.append(node) return node def remove_node(self, node): delete_later = [] for connection in self.connections: if connection.get_last_node() == node or connection.get_first_node() == node: delete_later.append(connection) for item in delete_later: self.remove_connection(item) self.diagramScene.removeItem(node) self.nodes.remove(node) def remove_selected_items(self): for item in self.diagramScene.selectedItems(): if type(item) == Block: self.remove_node(item) def mousePressEvent(self, event): if event.buttons() == QtCore.Qt.RightButton: self.add_block_item(self.diagramView.mapToScene(event.pos())) # self.dump() def add_block_item(self, pos): text, ok = QInputDialog.getText(None, 'Добавить элемент', 'Введите название:') if ok: self.add_node(pos.x(), pos.y(), str(text)) def dump(self): print('||------- DUMP -------||') print('Nodes: ') for node in self.nodes: print(node.get_text()) print('\nEdges: ') for connection in self.connections: first_node = connection.get_first_node().get_text() last_node = connection.get_last_node().get_text() link_type = connection.get_link_type() print('{0} {1} {2}'.format(first_node, link_type, last_node)) print('||------- END --------||') def get_all_connections(self): return self.connections def get_all_nodes(self): return self.nodes def get_diagram_scene(self): return self.diagramScene def context_menu_event(self, event): menu = QMenu() add = menu.addAction('Добавить узел') add.triggered.connect(self.add_block_item) dump = menu.addAction('Dump') dump.triggered.connect(self.dump) menu.exec_(event.screenPos())
class QCliWidget(QWidget): interpreter = Interpreter() display_widget = None vkbd = None beamer = None result_from_queue = False set_tab_text = pyqtSignal(str) def __init__(self): super().__init__() self.unicode_fonts = UnicodeFonts() self.grid = QGridLayout() self.grid.setContentsMargins(0, 0, 0, 6) self.setLayout(self.grid) self.display_widget = QTextEditEnhanced() self.display_widget.setText( "type in the command 'man' down in the command line for getting started ..." ) self.display_widget.setReadOnly(True) self.addDisplayWidget() line = QInputLine() line.setPlaceholderText( "This is the command line. See 'man commandline' for details.") line.return_pressed.connect(self.commandEntered) self.grid.addWidget(line, 1, 0) vkbdButton = QPushButton(self) vkbdButton.clicked.connect(partial(self.vkbdButtonClicked, line)) vkbdButton.setIcon(QIcon.fromTheme('input-keyboard')) self.grid.addWidget(vkbdButton, 1, 1) zoomOutButton = QPushButton(self) zoomOutButton.setIcon(QIcon.fromTheme('zoom-out')) zoomOutButton.clicked.connect(self.onZoomOutClicked) self.grid.addWidget(zoomOutButton, 1, 2) zoomResetButton = QPushButton(self) zoomResetButton.setIcon(QIcon.fromTheme('zoom-original')) zoomResetButton.clicked.connect(self.onZoomResetClicked) self.grid.addWidget(zoomResetButton, 1, 3) zoomInButton = QPushButton(self) zoomInButton.setIcon(QIcon.fromTheme('zoom-in')) zoomInButton.clicked.connect(self.onZoomInClicked) self.grid.addWidget(zoomInButton, 1, 4) self.applyStylesheet() def applyStylesheet(self): config = ConfigFile(None, None) path = config.readVar('global', 'stylesheet') stylesheet = '' try: with open(path) as css: for line in css: stylesheet += line self.display_widget.setStyleSheet(stylesheet) except FileNotFoundError: pass def addDisplayWidget(self): self.view = QGraphicsView() self.scene = QGraphicsScene() self.scene.addWidget(self.display_widget) self.view.setScene(self.scene) self.view.setStyleSheet("QGraphicsView { border-style: none; }") self.grid.addWidget(self.view, 0, 0, 1, 0) self.resizeDisplayWidget() self.applyStylesheet() def resizeDisplayWidget(self): # the magick numbers are for keeping the size of the view allways small enough not to spawn an outer set of scrollbars: x = self.view.width() - 2.1 y = self.view.height() - 2.1 self.x, self.y = x, y mapped_rect = self.view.mapToScene(QRect(0, 0, x, y)).boundingRect() self.display_widget.setFixedSize(mapped_rect.width(), mapped_rect.height()) self.scene.setSceneRect(0, 0, mapped_rect.width(), mapped_rect.height()) def resizeEvent(self, event): self.resizeDisplayWidget() def vkbdButtonClicked(self, lineEdit): self.vkbd = QVirtualKeyboardWindow() self.vkbd.setLineEdit(lineEdit) def commandEntered(self, command): # to keep the display_widget in the correct size self.resize(self.x, self.y) print("command:", command) if '|' in command: command, pipe = command.split('|') self.handleCommand(command) pipe = pipe.strip() if pipe == 'beamer': if self.beamer: self.beamer.destroy() print('destroyed!!!') self.beamer = QBeamerWindow() from PyQt5.QtWidgets import QLabel, QPushButton widget = QLabel('blaaaa') self.beamer.setWidget(self.display_widget) #self.beamer.setText('test') self.beamer.routeToScreen() self.beamer.showFullScreen() else: #self.handleCommand(command) self.set_tab_text.emit(command) #self.activityIndicator() q = queue.Queue() self.interpreter_thread = HandleCommandThread(command, q) self.interpreter_thread.processResult.connect(self.processResult) self.interpreter_thread.clearDisplayWidget.connect( self.clearDisplayWidget) self.interpreter_thread.makeSnapshot.connect(self.makeSnapshot) self.interpreter_thread.stopQueueListener.connect( self.stopQueueListener) self.interpreter_thread.start() self.queue_thread = GetQueueItemsThread(q) self.queue_thread.processQueueItem.connect(self.processQueueItem) self.queue_thread.start() def stopQueueListener(self): self.queue_thread.stop() def processQueueItem(self, item): self.result_from_queue = True item = item.getItem() json_dict = json.loads(item) if json_dict['category'] == 'progressbar': last_type = type(self.display_widget) if not last_type == QProgressBar: self.display_widget.deleteLater() self.display_widget = QProgressBar() self.display_widget.setMinimum(json_dict['minimum']) self.display_widget.setMaximum(json_dict['maximum']) self.display_widget.setValue(json_dict['value']) if not last_type == QProgressBar: self.addDisplayWidget() else: result_object = Result() result_object.payload = item.getItem() self.resultInTextEdit(result_object) def processResult(self, result): if self.result_from_queue: self.result_from_queue = False else: #if result is None: #self.showErrorMessage('no result found') if hasattr(result, 'payload') and result.payload: if hasattr(result, 'category') and result.category == "table": try: result.payload[0] except IndexError: pass # datastructure does not fit to display type 'table' else: self.resultInTable(result) elif hasattr( result, 'category') and result.category == "multimedia_table": self.resultInMultimediaTable(result) elif hasattr(result, 'category') and result.category == "list": self.resultInTextEdit(result) elif hasattr(result, 'category') and result.category == "text": self.resultInTextEdit(result) elif hasattr(result, 'category') and result.category == "string": self.resultInTextEdit(result) elif hasattr(result, 'category') and result.category == "itemized": self.resultInItemizedWidget(result) elif hasattr(result, 'category') and result.category == "image": self.resultInImageWidget(result) elif hasattr(result, 'category') and result.category == "html": #self.resultInHTMLWidget(result) self.resultInTextEdit(result) elif hasattr(result, 'category') and result.category == 'diagram': self.resultInDiagram(result) elif hasattr(result, 'category') and result.category == 'bloodline': self.resultInBloodlineDiagram(result) elif hasattr(result, 'category') and result.category == 'command': self.showMapWidget() elif hasattr(result, 'error') and result.error: self.showErrorMessage(result.error) else: result = Result() result.payload = 'empty result set' self.resultInTextEdit(result) def activityIndicator(self): self.display_widget.deleteLater() label = QLabel() movie = QMovie('./assets/images/activity_indicator.gif') movie.start() label.setMovie(movie) self.display_widget = QWidget() layout = QVBoxLayout() self.display_widget.setLayout(layout) layout.addWidget(label, Qt.AlignCenter) self.addDisplayWidget() def clearDisplayWidget(self): self.display_widget.deleteLater() self.display_widget = QTextEditEnhanced() self.display_widget.setReadOnly(True) self.addDisplayWidget() def makeSnapshot(self): image = QImage(self.display_widget.size(), QImage.Format_ARGB32) painter = QPainter(image) if painter.isActive(): self.render(painter) painter.end() default_dir = path.join(path.expanduser('~')) filename = QFileDialog.getSaveFileName(self, 'Save Snapshot', default_dir) image.save(filename[0]) def resultInTable(self, result): self.display_widget.deleteLater() self.display_widget = QTableWidget() self.display_widget.setRowCount(len(result.payload)) self.display_widget.setColumnCount(len(result.payload[0])) try: self.display_widget.setHorizontalHeaderLabels(result.header) except TypeError: pass try: self.display_widget.setVerticalHeaderLabels(result.header_left) except TypeError: pass for row, line in enumerate(result.payload): for column, item in enumerate(line): table_item = QTableWidgetItem(str(item)) table_item.setFlags(Qt.ItemIsEnabled) self.unicode_fonts.applyFontToQWidget(str(item), table_item) self.display_widget.setItem(row, column, table_item) self.display_widget.resizeColumnsToContents() self.addDisplayWidget() def resultInMultimediaTable(self, result): self.display_widget.deleteLater() max_length = 0 for line in result.payload: if len(line) > max_length: max_length = len(line) self.display_widget = QTableWidget() self.display_widget.setRowCount(len(result.payload)) self.display_widget.setColumnCount(max_length) audio_count = 0 config = ConfigFile(None, None) deckpath = config.readPath("vocable", "deckpath") for row, line in enumerate(result.payload): deckname = line[0] for column, item in enumerate(line): if self.isImage(str(item)): pixmap = QPixmap() pixmap.load(path.join(deckpath, deckname, str(item))) pixmap = pixmap.scaled(QSize(60, 30), Qt.KeepAspectRatio) image_widget = QLabel() image_widget.setPixmap(pixmap) self.display_widget.setCellWidget(row, column, image_widget) elif self.isAudio(str(item)): splitted = item.split(',') if audio_count < len(splitted): audio_count = len(splitted) audio_widget = QAudioItems(path.join(deckpath, deckname), self.display_widget, 7, max_length) audio_widget.appendPlayButtonsList(splitted, row) else: table_item = QTableWidgetItem(str(item)) table_item.setFlags(Qt.ItemIsEnabled) #self.unicode_fonts.applyFontToQWidget(str(item), table_item) self.display_widget.setItem(row, column, table_item) self.display_widget.setColumnCount(max_length + audio_count) self.display_widget.resizeColumnsToContents() self.addDisplayWidget() def resultInTextEdit(self, result): self.display_widget.deleteLater() self.display_widget = QTextEditEnhanced() self.unicode_fonts.applyFontAndSizeToQWidget(result.toString(), self.display_widget) self.display_widget.setAcceptRichText(True) self.display_widget.setText(result.toString()) self.display_widget.setReadOnly(True) self.display_widget.setTextInteractionFlags( self.display_widget.textInteractionFlags() | Qt.TextSelectableByKeyboard) self.addDisplayWidget() def resultInHTMLWidget(self, result): self.display_widget.deleteLater() self.display_widget = QWebView() self.display_widget.setHtml(result.payload) self.addDisplayWidget() def resultInItemizedWidget(self, result): self.display_widget.deleteLater() self.display_widget = QItemizedWidget(result.payload) self.addDisplayWidget() def resultInImageWidget(self, result): self.display_widget.deleteLater() self.display_widget = QCustomizedGraphicsView() import PIL if type(result.payload) == PIL.Image.Image: from PIL.ImageQt import ImageQt qimage = ImageQt(result.payload) pixmap = QPixmap.fromImage(qimage) #pixmap = QPixmap("/tmp/tmprp3q0gi9.PNG") #pixmap.fromImage(image) item = self.display_widget.scene().addPixmap(pixmap) item.setPos(0, 0) self.addDisplayWidget() def resultInDiagram(self, result): self.display_widget.deleteLater() curve = QLineSeries() pen = curve.pen() pen.setColor(Qt.red) pen.setWidthF(2) curve.setPen(pen) for data in result.payload: if type(data['y']) == str: data['y'] = 0 curve.append(data['x'], data['y'], 10) chart = QChart() chart.setTitle(result.name) chart.legend().hide() chart.addSeries(curve) chart.createDefaultAxes() view = QChartViewEnhanced(chart) view.setRenderHint(QPainter.Antialiasing) self.display_widget = view self.addDisplayWidget() def resultInBloodlineDiagram(self, result): self.display_widget.deleteLater() self.display_widget = QBloodlineWidget(result.payload) self.addDisplayWidget() def showMapWidget(self): self.display_widget.deleteLater() self.display_widget = QMapWidget() self.display_widget.showPosition() self.addDisplayWidget() def showErrorMessage(self, message): self.display_widget.deleteLater() self.display_widget = QTextEditEnhanced() self.display_widget.setText(message) self.display_widget.setReadOnly(True) self.addDisplayWidget() def onZoomInClicked(self): if type(self.display_widget) == QTextEditEnhanced: self.display_widget.zoomIn() elif type(self.display_widget) == QChartViewEnhanced: self.display_widget.chart().zoomIn() else: self.view.scale(SCALE_FACTOR, SCALE_FACTOR) self.resizeDisplayWidget() def onZoomOutClicked(self): if type(self.display_widget) == QTextEditEnhanced: self.display_widget.zoomOut() elif type(self.display_widget) == QChartViewEnhanced: self.display_widget.chart().zoomOut() else: self.view.scale(1 / SCALE_FACTOR, 1 / SCALE_FACTOR) self.resizeDisplayWidget() def onZoomResetClicked(self): if type(self.display_widget) == QTextEditEnhanced: self.display_widget.zoomReset() elif type(self.display_widget) == QChartViewEnhanced: self.display_widget.chart().zoomReset() else: self.view.resetTransform() self.resizeDisplayWidget() def keyPressEvent(self, event): if (event.modifiers() & Qt.ControlModifier): if event.key() == Qt.Key_Plus: self.onZoomInClicked() elif event.key() == Qt.Key_Minus: self.onZoomOutClicked() def isImage(self, data): suffixes = ['.png', '.jpg', '.jpe', '.jpeg', '.svg', '.bmp'] for suffix in suffixes: if data.lower().endswith(suffix): return True return False def isAudio(self, data): suffixes = ['.ogg', '.wav', '.mp3', '.aiff', '.wma'] for suffix in suffixes: if data.lower().endswith(suffix): return True return False
class ViewWindow(QMainWindow): # QWidget): def __init__(self): super().__init__() self.pilorig = None self.savedImg = None self.controlPanel = None self.dirName, self.fileName = '', '' self.dirFileNames = [] self.fileIdx = -1 self.ctrl = None # transformation control widget self.locator = None # locator rectangle widget self.locator1 = None # locator 1st point self.locator2 = None # locator 2nd point self.scale = (1, 1) self.show_histogram = False # usage of QGraphicsScene and QGraphicsView is taken from # https://stackoverflow.com/questions/50851587/undo-functionality-for-qpainter-drawellipse-function self.gv = None self.scene = None self.scaledPixmap = None self.histogram = [] self.initUI() def initUI(self): self.setWindowTitle("PyQT Tuts!") transforms.editWindow = self bar = self.menuBar() makeMenu(bar, FILE_MENU, self) menu = bar.actions()[2].menu() menu.addSeparator() addAction(menu, self, 'Show &Histogram', self.toggleHistogram, 'Ctrl+H', 'Show/hide histogram') menu = bar.actions()[1].menu() menu.addSeparator() for item in transforms.getTransforms(): addAction(menu, self, item[0], item[2], item[3], item[4]) # item[1] (Icon is ignored, todo: add support) menu = bar.actions()[0].menu() menu.addSeparator() addAction(menu, self, '&Next', self.onNextBtn, 'Ctrl+N', 'Next image') addAction(menu, self, '&Prev', self.onPrevBtn, 'Ctrl+P', 'Prev image') addAction(menu, self, '&Save', self.onSaveImg, 'Ctrl+S', 'Save image') layout1 = QVBoxLayout() self.gv = QGraphicsView() self.scene = QGraphicsScene() self.gv.setScene(self.scene) self.gv.installEventFilter(self) layout1.addWidget(self.gv) panel = QWidget(self) self.controlPanel = QHBoxLayout() panel.setLayout(self.controlPanel) layout1.addWidget(panel) center = QWidget() center.setLayout(layout1) self.setCentralWidget(center) def toggleHistogram(self): self.show_histogram = not self.show_histogram self.make_histogram() def eventFilter(self, obj, event): if obj == self.gv and event.type() == QEvent.MouseButtonPress: p = self.gv.mapToScene(event.pos()) self.setLocation(p) return QWidget.eventFilter(self, obj, event) def setLocation(self, pos): if self.locator2 is not None: self.locator1 = None self.locator2 = None if self.locator1: self.locator2 = (pos.x(), pos.y()) self.drawLocator() else: self.locator1 = (pos.x(), pos.y()) def drawLocator(self): if self.locator: self.scene.removeItem(self.locator) self.locator = None pen = QPen(Qt.red, 3) self.locator = self.scene.addRect(self.locator1[0], self.locator1[1], self.locator2[0] - self.locator1[0], self.locator2[1] - self.locator1[1], pen) print('locator', self.locator1, self.locator2) self.gv.update() # self.label.update() #QApplication.processEvents() def resizeEvent(self, event): QWidget.resizeEvent(self, event) self.sizeImage() def openImage(self, fileName): self.setWindowTitle(fileName) self.dirName = os.path.dirname(fileName) self.fileName = os.path.basename(fileName) self.dirFileNames = list(imgFiles(self.dirName)) self.fileIdx = self.dirFileNames.index(self.fileName) if fileName: self.pilorig = PIL.Image.open(fileName) self.sizeImage() def fullFileName(self): if self.fileName: return os.path.join(self.dirName, self.fileName) else: return None def showImage(self, fileName): self.openImage(fileName) self.show() def updateImage(self, pilImage): self.pilorig = pilImage self.sizeImage() def sizeImage(self): orig = pil2pixmap(self.pilorig) if self.pilorig: if self.scaledPixmap: self.scene.removeItem(self.scaledPixmap) self.scaledPixmap = None if self.locator: self.scene.removeItem(self.locator) self.locator = None pixmap = orig.scaled(self.gv.width(), self.gv.height(), Qt.KeepAspectRatio, transformMode=Qt.SmoothTransformation) self.scaledPixmap = self.scene.addPixmap(pixmap) self.scale = (orig.size().width() / pixmap.size().width(), orig.size().height() / pixmap.size().height()) self.make_histogram() def absLocatorRect(self): return [ self.locator1[0] * self.scale[0], self.locator1[1] * self.scale[1], self.locator2[0] * self.scale[0], self.locator2[1] * self.scale[1] ] def nextFileName(self): if self.fileIdx < len(self.dirFileNames) - 1: return os.path.join(self.dirName, self.dirFileNames[self.fileIdx + 1]) else: return None def prevFileName(self): if self.fileIdx > 0: return os.path.join(self.dirName, self.dirFileNames[self.fileIdx - 1]) else: return None def onNextBtn(self): fn = self.nextFileName() if fn: self.openImage(os.path.join(self.dirName, fn)) #@staticmethod def onPrevBtn(self): fn = self.prevFileName() if fn: self.openImage(os.path.join(self.dirName, fn)) def onSaveImg(self): options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog fileName, _ = QFileDialog.getSaveFileName( self, "Save as ...", os.path.join(self.dirName, self.fileName), "All Files (*);;Jpeg Files(*.jpg)", options=options) if fileName: print(fileName) self.pilorig.save(fileName) def make_histogram(self): # should it be a ShowWin class method? #self.canvas.delete('histogram') for it in self.histogram: self.scene.removeItem(it) del self.histogram[:] if not self.show_histogram: return img = self.pilorig if not img: return h = img.convert("L").histogram() maxVal = 1 for i in range(0, len(h)): if h[i] > maxVal: maxVal = h[i] _X = float(img.size[1]) x = 100 if _X > 100 else _X penGray = QPen(Qt.gray, 3) penRed = QPen(Qt.red, 3) for i in range(0, len(h)): if h[i] == maxVal: pen = penRed else: pen = penGray self.histogram.append( self.scene.addLine(i, x, i, x - x * h[i] / maxVal, pen)) self.histogram.append(self.scene.addRect(0, 0, len(h), x, penGray)) self.gv.update() # support transformation plugins def controlImg(self, checkCtrl=True): if checkCtrl and not self.ctrl: return None else: return self.savedImg def setCtrl(self, ctrl): if ctrl and self.ctrl: return # do not allow two control panels if self.ctrl and not ctrl: self.ctrl.setParent(None) self.ctrl = ctrl if ctrl: self.controlPanel.addWidget(ctrl) self.savedImg = self.pilorig def unsetCtrl(self): if self.ctrl: self.ctrl.setParent(None) self.ctrl = None
class Filter(QWidget): label_map = { 'K': 'Keep', 'T': 'Take', 'S': 'Stitch', 'M': 'Compare', 'C': 'Crop', 'D': 'Delete', None: '' } def __init__(self, parent, config, new_files): QWidget.__init__(self, parent) self.zoomLevel = 1.0 self.rotation = 0 self.all_images = ImageList() self.compare_set = ImageList() # start with all images self.images = self.all_images self.comparing = False self.src = config['Directories']['mid'] self.dst = os.getcwd() self.scan(self.src) self.new_files = new_files self.image = None self.image_actions = defaultdict(lambda: None) self.image_positions = {} self.original_position = None self.buildUI(parent) self.dir_dialog = QFileDialog(self) self.dir_dialog.setFileMode(QFileDialog.Directory) def buildUI(self, parent): # left labels self.splitter = QSplitter(self) self.splitter.setContentsMargins(QMargins(0, 0, 0, 0)) self.splitter.setOrientation(Qt.Horizontal) self.widget = QWidget(self.splitter) self.label_layout = QVBoxLayout(self.widget) for count, name in enumerate([ 'exposure_time', 'fnumber', 'iso_speed', 'focal_length', 'date', ]): key_label = QLabel(name.replace('_', ' ').title(), self.widget) # setattr(self, "key_label_%02d" % count, key_label) self.label_layout.addWidget(key_label) value_label = QLabel(self.widget) value_label.setAlignment(Qt.AlignRight) setattr(self, name, value_label) self.label_layout.addWidget(value_label) # TODO s = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.label_layout.addItem(s) # main view self.scene = QGraphicsScene() self.item = QGraphicsPixmapItem() self.scene.addItem(self.item) self.view = QGraphicsView(self.scene, parent) self.view.setFrameShadow(QFrame.Plain) self.view.setFrameStyle(QFrame.NoFrame) self.view.setDragMode(QGraphicsView.ScrollHandDrag) self.view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) brush = QBrush(QColor(20, 20, 20)) brush.setStyle(Qt.SolidPattern) self.view.setBackgroundBrush(brush) self.view.show() # "status bar" self.fname = QLabel(self) self.fname.setTextInteractionFlags(Qt.TextSelectableByMouse) spacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.tag_view = QLabel(self) status_bar = QHBoxLayout() status_bar.addWidget(self.fname) status_bar.addItem(spacer) status_bar.addWidget(self.tag_view) w = QWidget(self.splitter) v = QVBoxLayout(w) v.setContentsMargins(QMargins(0, 0, 0, 0)) v.addWidget(self.view) v.addLayout(status_bar) # TODO self.splitter.setSizes([10, 90]) h = QHBoxLayout(self) h.setContentsMargins(QMargins(0, 0, 0, 0)) h.addWidget(self.splitter) # now... ACTION!(s) for key, slot in ( (Qt.Key_Home, self.first_image), (Qt.Key_PageUp, self.prev_ten), (Qt.Key_Backspace, self.prev_image), (Qt.Key_Space, self.next_image), (Qt.Key_PageDown, self.next_ten), (Qt.Key_End, self.last_image), (Qt.Key_F, self.toggle_fullsize), (Qt.Key_K, self.keep), (Qt.Key_T, self.tag), (Qt.Key_S, self.stitch), (Qt.Key_M, self.select_for_compare), (Qt.Key_C, self.crop), (Qt.Key_D, self.delete), (Qt.Key_U, self.untag), (Qt.Key_X, self.expunge), (Qt.Key_Return, self.apply), (Qt.CTRL + Qt.Key_M, self.compare), (Qt.CTRL + Qt.Key_O, self.new_src), (Qt.CTRL + Qt.Key_S, self.save), ): action = QAction(parent) action.setShortcut(QKeySequence(key)) action.triggered.connect(slot) self.view.addAction(action) def scan(self, src): index = 0 logger.debug('scanning %r', src) for r, dirs, files in os.walk(os.path.abspath(src)): for name in sorted(files): if name[-4:].lower() in ('.jpg', '.png'): # logger.info ('found %s' % name) self.images.append(Image(index, os.path.join(r, name))) index += 1 def rotate_view(self): # we have to 'undo' the rotations, so the numbers are negative rotate = -self.image.rotation # undo the last rotation and apply the new one self.view.rotate(-self.rotation + rotate) self.rotation = rotate logger.debug(rotate, self.rotation) def zoom_to_fit(self): winSize = self.view.size() logger.debug(self.image.size, winSize) hZoom = winSize.width() / self.image.size.width() vZoom = winSize.height() / self.image.size.height() zoomLevel = min(hZoom, vZoom) self.zoom(zoomLevel) def zoom(self, zoomLevel): # logger.info (zoomLevel) scale = zoomLevel / self.zoomLevel # logger.info ("scaling", scale) self.view.scale(scale, scale) self.zoomLevel = zoomLevel def move_index(self, to=None, how_much=0): if self.image is not None: self.save_position() self.images.move_index(to, how_much) self.image = self.images.current_image self.show_image() def view_position(self): view_size = self.view.size() center = QPoint(view_size.width() / 2, view_size.height() / 2) position = self.view.mapToScene(center) return position def show_image(self): logger.info(self.image.path) self.image.read() self.rotate_view() self.item.setPixmap(self.image.pixmap) if self.zoomLevel != 1.0: self.zoom_to_fit() # we might have rotated the view, but the scene still has the image # in its original size, so we use that as bounding rect boundingRect = QRectF(self.item.pixmap().rect()) logger.debug(boundingRect) self.scene.setSceneRect(boundingRect) if self.image.index in self.image_positions: self.original_position = None position = self.image_positions[self.image.index] logger.debug("previously moved, back to that point: %f x %f", position.x(), position.y()) self.view.centerOn(position) else: # TODO: 'undo' the move position = self.view_position() logger.debug("original position: %f x %f", position.x(), position.y()) self.original_position = position self.view.centerOn(self.item) self.update_view() def update_view(self): self.fname.setText(self.image.path) label = self.label_map[self.image_actions[self.image]] self.tag_view.setText(label) meta = self.image.metadata date = read_image_date(self.image.path, meta) if date is None: self.date.setText('unknown') else: self.date.setText(date.isoformat()) self.fnumber.setText(str(meta.get_fnumber())) self.focal_length.setText(str(meta.get_focal_length())) self.iso_speed.setText(str(meta.get_iso_speed())) f = meta.get_exposure_time() if f is None: s = 'unknown' elif f.denominator == 1: s = '%ds' % f.numerator else: s = '%d/%ds' % (f.numerator, f.denominator) self.exposure_time.setText(s) def save_position(self): position = self.view_position() if (self.original_position is None or position.x() != self.original_position.x() or position.y() != self.original_position.y()): logger.debug("saving position: %f x %f", position.x(), position.y()) # this way (I hope) I only remember those positions which changed self.image_positions[self.image.index] = position # movements def first_image(self, *args): self.move_index(to=0) def prev_ten(self, *args): self.move_index(how_much=-10) def prev_image(self, *args): self.move_index(how_much=-1) def next_image(self, *args): self.move_index(how_much=+1) def next_ten(self, *args): self.move_index(how_much=+10) def last_image(self, *args): self.move_index(to=len(self.images) - 1) def toggle_fullsize(self, *args): # noooooooooooooooothing compares... if abs(self.zoomLevel - 1.0) < 0.000001: # logger.info ('fit') self.zoom_to_fit() else: # logger.info ('orig') self.zoom(1.0) # image actions # Keep -> /gallery/foo, resized def keep(self, *args): self.image_actions[self.image] = 'K' self.next_image() # Tag -> /gallery/foo, as-is def tag(self, *args): self.image_actions[self.image] = 'T' self.next_image() # Stitch -> 02-new/stitch def stitch(self, *args): self.image_actions[self.image] = 'S' self.next_image() # coMpare def select_for_compare(self, *args): if self.image_actions[self.image] == 'M': # TODO?: undo/toggle # NOTE: this can already be achieved by Untag pass else: self.image_actions[self.image] = 'M' # ugh insort(self.compare_set, self.image) logger.debug(self.compare_set.images) self.next_image() def compare(self): logger.info('comparing') self.comparing = True self.images = self.compare_set self.move_index() # Crop -> launch gwenview def crop(self, *args): self.image_actions[self.image] = 'C' self.next_image() # Delete -> /dev/null def delete(self, *args): self.image_actions[self.image] = 'D' if self.comparing: # remove the image from the list and refresh the view self.images.remove() self.image = self.images.current_image self.show_image() else: self.next_image() def untag(self, *args): try: del self.image_actions[self.image] # don't move, most probably I'm reconsidering what to do # but change the label self.tag_view.setText('') except KeyError: # tried to untag a non-tagged image pass def resize(self, src, dst): src_meta = GExiv2.Metadata(src) src_p = QPixmap(src) dst_p = src_p.scaled(4500, 3000, Qt.KeepAspectRatio, Qt.SmoothTransformation) dst_p.save(src) shutil.move(src, dst) # copy all the metadata dst_meta = GExiv2.Metadata(dst) for tag in src_meta.get_tags(): dst_meta[tag] = src_meta[tag] dst_meta.save_file() def apply(self, *args): if not self.comparing: hugin = False if len([ action for action in self.image_actions.values() if action in ('K', 'T') ]) > 0: self.new_dst() for img, action in sorted( self.image_actions.items(), key=lambda s: s[0].path): # sort by fname src = img.path dst = os.path.join(self.dst, os.path.basename(src)) try: if src in self.new_files and action not in ('C', 'D'): # rename src = rename_file(src) if action == 'K': # Keep -> /gallery/foo, as-is logger.info("%s -> %s" % (src, dst)) shutil.move(src, dst) elif action == 'T': # Tag -> /gallery/foo, resized self.resize(src, dst) elif action == 'S': # Stitch -> 02-new/stitch dst = os.path.join( '/home/mdione/Pictures/incoming/02-new/stitch', os.path.basename(src)) logger.info("%s -> %s" % (src, dst)) shutil.move(src, dst) hugin = True elif action == 'M': # coMpare -> 03-cur dst = os.path.join( '/home/mdione/Pictures/incoming/03-cur', os.path.basename(src)) logger.info("%s -> %s" % (src, dst)) shutil.move(src, dst) new_root = '/home/mdione/Pictures/incoming/03-cur' old_root = self.src elif action == 'C': # Crop -> launch gwenview os.system('gwenview %s' % src) # asume the file was saved under a new name # logger.info ("%s -> %s" % (src, dst)) # shutil.move (src, dst) elif action == 'D': # Delete -> /dev/null os.unlink(src) logger.info("%s deleted" % (src, )) except FileNotFoundError as e: logger.info(e) if hugin: os.system('hugin') self.reset() else: logger.info('back to all') self.comparing = False # untag all images marked for compare for image in self.compare_set: # but only those still marked 'M' if self.image_actions[image] == 'M': del self.image_actions[image] self.compare_set.clear() self.images = self.all_images self.move_index() def expunge(self, *args): for img, action in self.image_actions.items(): src = img.path try: if action == 'D': # Delete -> /dev/null os.unlink(src) logger.info("%s deleted" % (src, )) except FileNotFoundError as e: logger.info(e) self.reset() def reset(self, new_root=None): if new_root is not None: self.src = new_root self.image_actions.clear() self.all_images.clear() self.compare_set.clear() self.comparing = False self.scan(self.src) def new_dst(self, *args): self.dir_dialog.setDirectory(self.dst) if self.dir_dialog.exec(): self.dst = self.dir_dialog.selectedFiles()[0] def new_src(self, *args): self.dir_dialog.setDirectory(self.src) if self.dir_dialog.exec(): self.src = self.dir_dialog.selectedFiles()[0] self.reset() def save(self, *args): src = self.image.path self.dir_dialog.setDirectory(self.dst) if self.dir_dialog.exec(): dst_dir = self.dir_dialog.selectedFiles()[0] if src in self.new_files: src = rename_file(src) dst = os.path.join(dst_dir, os.path.basename(src)) logger.info("%s -> %s" % (src, dst)) self.resize(src, dst) self.next_image()
class WindowTime(QMainWindow): '''This class handles the main window.''' def __init__(self): super().__init__() self.menubar = self.menuBar() self.status = self.statusBar() exitAction = QAction('Exit', self) exitAction.setShortcut('Ctrl+Q') exitAction.setStatusTip('Exit application') exitAction.triggered.connect(self.close) fileMenu = self.menubar.addMenu('&File') fileMenu.addAction(exitAction) self.zoomlevel = 2 # self.pixmaps = {2: QPixmap('yeg-2.png'), 3: QPixmap('yeg-3.png'), # 4: QPixmap('yeg-4.png'), 5: QPixmap('yeg-5.png'), 6: QPixmap('yeg-6.png')} # # self.sizes = {1: 512, 2: 1024, 3: 2048, 4: 4096, 5: 8192, 6: 16384} self.pixmaps = { 2: QPixmap(os.path.join(__location__, 'yeg-2.png')), 3: QPixmap(os.path.join(__location__, 'yeg-3.png')), 4: QPixmap(os.path.join(__location__, 'yeg-4.png')), 5: QPixmap(os.path.join(__location__, 'yeg-5.png')) } self.sizes = {1: 512, 2: 1024, 3: 2048, 4: 4096, 5: 8192} self.pressed = False self.start = 0 self.dest = 0 self.center = (0, 0) self.howmanycars = 0 self.customer_id = 0 self.initUI() self.show() def initUI(self): #The QToolTip class provides tool tips (balloon help) for any widget. QToolTip.setFont(QFont('SansSerif', 10)) w = QWidget() #The QGridLayout class lays out widgets in a grid. grid = QGridLayout() #setLayout(self, Qlayout) Sets the layout manager for this widget to layout. #The QLayout argument has it's ownership transferred to Qt. w.setLayout(grid) #The QLabel widget provides a text or image display. labelZoom = QLabel('Zoom here pls') labelPassengers = QLabel('How many Passengers') #The QPushButton widget provides a command button. Takes in arguments #For text label btnZoomIn = QPushButton('+') btnZoomIn.setToolTip('Zoom In') btnZoomIn.clicked.connect(self.zoomIn) btnZoomOut = QPushButton('-') btnZoomOut.setToolTip('Zoom Out') btnZoomOut.clicked.connect(self.zoomOut) spinBox = QSpinBox() spinBox.setMaximum(5) spinBox.setMinimum(1) self.mapScene = GraphicsScene() self.mapScene.setSceneRect(0, 0, self.sizes[self.zoomlevel], self.sizes[self.zoomlevel]) self.mapScene.addPixmap(self.pixmaps[self.zoomlevel]) #ellipse = mapScene.addEllipse(50,100,50,50) self.mapView = QGraphicsView() self.mapView.setScene(self.mapScene) #Set it so user can drag map via cursor self.mapView.setDragMode(QGraphicsView.ScrollHandDrag) #Hide the scroll bar self.mapView.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.mapView.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) grid.addWidget(labelZoom, 0, 4) grid.addWidget(self.mapView, 0, 0, 4, 4) grid.addWidget(btnZoomIn, 1, 4) grid.addWidget(btnZoomOut, 2, 4) grid.addWidget(labelPassengers, 3, 4) grid.addWidget(spinBox, 4, 4) #How everything will look on the screen self.setGeometry(300, 300, 800, 600) self.setWindowTitle('Uber Pool') self.setCentralWidget(w) #CHANGED: This def setCenter(self): center = self.mapView.mapToScene(self.mapView.width() // 2, self.mapView.height() // 2) self.center = (ConversionFunctions.x_to_longitude( self.zoomlevel, center.x()), ConversionFunctions.y_to_latitude( self.zoomlevel, center.y())) def zoomIn(self): if self.zoomlevel < 5: self.pressed = False self.setCenter() self.zoomlevel += 1 self.updateSceneZoom() def zoomOut(self): if self.zoomlevel > 2: self.pressed = False self.setCenter() self.zoomlevel -= 1 self.updateSceneZoom() #updates the scence once the zoom button has been pressed. The scene is #updated on the center of the last scene def updateSceneZoom(self): self.mapScene.clear() self.mapScene.addPixmap(self.pixmaps[self.zoomlevel]) self.mapScene.setSceneRect(0, 0, self.sizes[self.zoomlevel], self.sizes[self.zoomlevel]) (x, y) = (ConversionFunctions.longitude_to_x(self.zoomlevel, self.center[0]), ConversionFunctions.latitude_to_y(self.zoomlevel, self.center[1])) self.mapView.centerOn(x, y) #returns the x,y position of the cursor on the map def handleClick(self, pos): return (pos.x(), pos.y()) # #Grab the x,y coords # (x, y) = (pos.x(), pos.y()) # #Convert them into lat,lon # lon = x_to_longitude(self.zoomlevel, x) # lat = y_to_latitude(self.zoomlevel, y) # # #Determine whether start location has already been decided # if self.pressed == False: # self.start = Vertex(lat, lon) # self.pressed = True # self.drawBubble(self.start) # else: # #Once dest has been decided, find shortest path and draw it # self.dest = Vertex(lat, lon) # self.pressed = False # self.drawBubble(self.dest) # self.controller.passRequest(self.start, self.dest, 1) def drawCar(self): #Draws the map in place self.setCenter() self.mapScene.clear() self.mapScene.addPixmap(self.pixmaps[self.zoomlevel]) self.mapScene.setSceneRect(0, 0, self.sizes[self.zoomlevel], self.sizes[self.zoomlevel]) (lat, lon) = self.center x = ConversionFunctions.longitude_to_x(self.zoomlevel, lon) y = ConversionFunctions.latitude_to_y(self.zoomlevel, lat) self.mapView.centerOn(x, y) #advance_taxi UberTaxi.advance_taxi(taxi_directory, cust_directory, g) for taxi in taxi_directory.values(): #gets coords from vertex lon = server.coordinates[taxi.loc][1] lat = server.coordinates[taxi.loc][0] #stored in lat, lon, and then converted newx = ConversionFunctions.longitude_to_x(self.zoomlevel, lon) newy = ConversionFunctions.latitude_to_y(self.zoomlevel, lat) #draws path and car self.mapScene.addRect(newx, newy, 15, 10) self.mapScene.addEllipse(newx - 2, newy + 10, 6, 6) self.mapScene.addEllipse(newx + 11, newy + 10, 6, 6) self.drawPath(taxi.path) def drawPath(self, path): linePen = QPen() linePen.setWidth(3) #Set the width of the line to be noticeable for i in range(len(path) - 1): lon1 = server.coordinates[path[i]][1] lat1 = server.coordinates[path[i]][0] lon2 = server.coordinates[path[i + 1]][1] lat2 = server.coordinates[path[i + 1]][0] self.mapScene.addLine( ConversionFunctions.longitude_to_x(self.zoomlevel, lon1), ConversionFunctions.latitude_to_y(self.zoomlevel, lat1), ConversionFunctions.longitude_to_x(self.zoomlevel, lon2), ConversionFunctions.latitude_to_y(self.zoomlevel, lat2), linePen)
class VideoPlayer(QWidget): def __init__(self, cfg, playlist, main, parent=None): super(VideoPlayer, self).__init__(parent) self.main = main self.playlist = playlist self.cfg = cfg self.mediaPlayer = QMediaPlayer(None, QMediaPlayer.VideoSurface) self.videoItem = VideoItem() self.videoItem.setAspectRatioMode(Qt.KeepAspectRatio) width = self.cfg["ui"]["viewCardWidth"] height = self.cfg["ui"]["viewCardHeight"] scene = QGraphicsScene(self) # scene.setSceneRect(0, 0, 838, 610) self.graphicsView = QGraphicsView(scene) # self.setFixedSize(960, 598) # self.graphicsView.mapToScene(28, 28) print(self.graphicsView.mapToScene(width, height)) self.videoItem.setSize(QSizeF(width, height)) scene.addItem(self.videoItem) openButton = QPushButton("Open...") openButton.clicked.connect(self.openFile) self.playButton = QPushButton() self.playButton.setEnabled(False) self.playButton.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay)) self.playButton.clicked.connect(self.play) self.positionSlider = QSlider(Qt.Horizontal) self.positionSlider.setRange(0, 0) self.positionSlider.sliderMoved.connect(self.setPosition) controlLayout = QHBoxLayout() controlLayout.setContentsMargins(0, 0, 0, 0) controlLayout.addWidget(openButton) controlLayout.addWidget(self.playButton) controlLayout.addWidget(self.positionSlider) layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self.graphicsView) layout.addLayout(controlLayout) self.setLayout(layout) self.mediaPlayer.setVideoOutput(self.videoItem) self.mediaPlayer.stateChanged.connect(self.mediaStateChanged) self.mediaPlayer.positionChanged.connect(self.positionChanged) self.mediaPlayer.durationChanged.connect(self.durationChanged) # def sizeHint(self): # return QSize(800, 600) def setFile(self, fileName): self.mediaPlayer.setMedia(QMediaContent(QUrl.fromLocalFile(fileName))) def openFile(self): fileName, _ = QFileDialog.getOpenFileName(self, "Open Movie", str(paths.DICTIONARIES)) if fileName != '': self.setFile(fileName) self.playButton.setEnabled(True) def play(self): if self.mediaPlayer.state() == QMediaPlayer.PlayingState: self.mediaPlayer.pause() else: self.mediaPlayer.play() def mediaStateChanged(self, state): if self.mediaPlayer.state() == QMediaPlayer.PlayingState: self.playButton.setIcon(self.style().standardIcon( QStyle.SP_MediaPause)) else: self.playButton.setIcon(self.style().standardIcon( QStyle.SP_MediaPlay)) def positionChanged(self, position): self.positionSlider.setValue(position) def durationChanged(self, duration): self.positionSlider.setRange(0, duration) def setPosition(self, position): self.mediaPlayer.setPosition(position)
class MyWinMap(QWidget): def __init__(self): super().__init__() self.initUI() def initUI(self): self.initWidget() # 初始化组件 self.initLayout() # 初始化布局 self.initWindow(800, 700) # 初始化窗口 self.initMap() # 初始化地图 self.initSerial(STR_COM) # 初始化串口 self.show() '''----------------------------------------------''' def initWidget(self): '''地图显示控件''' self.gscene_map = QGraphicsScene() self.gview_map = QGraphicsView(self.gscene_map) '''设定节点及阈值区''' self.chb_mark = QCheckBox('进入标记模式') self.chb_mark.toggle() self.sp_thre = QSpinBox() self.sp_thre.setRange(0, 10) # 设置上界和下界 '''数据显示区''' def initLayout(self): '''箱组声明''' self.gpbx_map = QGroupBox('地图', self) self.gpbx_mark_and_thre = QGroupBox('设定节点及阈值', self) self.gpbx_data = QGroupBox('数据', self) '''箱组布局类型''' self.lot_v_map = QVBoxLayout() self.lot_g_mark_and_thre = QGridLayout() self.lot_g_data = QGridLayout() self.lot_v_all = QVBoxLayout() '''箱组map布局设置''' self.lot_v_map.addWidget(self.gview_map, alignment=Qt.AlignHCenter) self.gpbx_map.setLayout(self.lot_v_map) '''箱组mark and thre布局设置''' # _ __ __ _ _ # |_|__|__|_|_| self.lot_g_mark_and_thre.addWidget(self.chb_mark, 0, 1, 1, 1, Qt.AlignCenter) self.lot_g_mark_and_thre.addWidget(self.sp_thre, 0, 3, 1, 1, Qt.AlignCenter) self.lot_g_mark_and_thre.setColumnStretch(0, 1) self.lot_g_mark_and_thre.setColumnStretch(1, 2) self.lot_g_mark_and_thre.setColumnStretch(2, 2) self.lot_g_mark_and_thre.setColumnStretch(3, 1) self.lot_g_mark_and_thre.setColumnStretch(4, 1) self.gpbx_mark_and_thre.setLayout(self.lot_g_mark_and_thre) '''箱组data布局设置''' for i in range(NODE_NUM): # 数据框 le_temp = QLineEdit('*') le_temp.setReadOnly(True) le_temp.setAlignment(Qt.AlignHCenter) self.lot_g_data.addWidget(le_temp, 0, i) for i in range(NODE_NUM): # 节点号框 lb_temp = QLabel('<div style="color:#d648ac;"><b>' + str(i + 1) + '</b></div>') lb_temp.setAlignment(Qt.AlignCenter) self.lot_g_data.addWidget(lb_temp, 1, i) self.gpbx_data.setLayout(self.lot_g_data) '''总布局设置''' self.lot_v_all.addWidget(self.gpbx_map) self.lot_v_all.addWidget(self.gpbx_mark_and_thre) self.lot_v_all.addWidget(self.gpbx_data) self.setLayout(self.lot_v_all) def initWindow(self, w, h): '''获取屏幕居中点信息''' center_point = QDesktopWidget().availableGeometry().center() self.center_point_x = center_point.x() self.center_point_y = center_point.y() '''窗口初始化''' self.setGeometry(0, 0, w, h) self.max_w = (self.center_point_x - 10) * 2 # 窗口允许的最大宽 self.max_h = (self.center_point_y - 20) * 2 # 窗口允许的最大高 self.setMaximumSize(self.max_w, self.max_h) # 防止窗口尺寸过大 self.moveToCenter(w, h) self.win_name = GUI_NAME # 窗口标题 self.setWindowTitle(self.win_name) def moveToCenter(self, w, h): '''窗口过大则先进行调整''' if (w > self.max_w) or (h > self.max_h): self.adjustSize() '''窗口居中''' topleft_point_x = (int)(self.center_point_x - w / 2) topleft_point_y = (int)(self.center_point_y - h / 2) self.move(topleft_point_x, topleft_point_y) def initMap(self): try: # 地图加载部分 self.pixmap_map = QPixmap('平面图_走廊_房间.png') if self.pixmap_map.isNull(): # 空图处理 self.initMapErrorHandle() return self.pixmap_map = self.pixmap_map.scaled(700, 600, Qt.KeepAspectRatio, Qt.SmoothTransformation) self.gscene_map.addPixmap(self.pixmap_map) # 固定边界以禁用滑动条 self.f_pixmap_map_x = float(self.pixmap_map.rect().x()) self.f_pixmap_map_y = float(self.pixmap_map.rect().y()) self.f_pixmap_map_w = float(self.pixmap_map.rect().width()) self.f_pixmap_map_h = float(self.pixmap_map.rect().height()) self.gview_map.setSceneRect(self.f_pixmap_map_x, self.f_pixmap_map_y, self.f_pixmap_map_w, self.f_pixmap_map_h) # 地图加载成功的标志位 self.b_map_loaded = True # 复选框信号连接 self.chb_mark.stateChanged.connect(self.updateMap) # view视图鼠标响应 self.gview_map.mousePressEvent = self.markMap w = self.width() h = self.height() self.moveToCenter(w, h) # 节点相关部分 self.node_list = [] # 存储节点的坐标及邻域信息 except Exception as e: print(e) self.initMapErrorHandle() def initMapErrorHandle(self): self.b_map_loaded = False self.text_warning = '<div style="font:20px;\ color:red;\ text-align:center;">\ <b>⚠ WARNING ⚠</b><br /><br />\ 地图加载出错啦<br /><br />\ ::>﹏<::\ </div>' self.gtext_warning = QGraphicsTextItem() self.gtext_warning.setHtml(self.text_warning) self.gscene_map.addItem(self.gtext_warning) def markMap(self, event): if self.b_map_loaded: if self.chb_mark.isChecked(): self.node_pos = self.gview_map.mapToScene(event.pos()) # 左键创建标记 if event.button() == Qt.LeftButton: if len(self.node_list) < NODE_NUM: _is_near_init = False # 标记模式下,初始化无近邻rssi _append_iter = [self.node_pos, _is_near_init] # 用list存储复合信息 self.node_list.append(_append_iter) # 绘图部分 self.drawNode(len(self.node_list), _append_iter[0], _append_iter[1]) # 右键回退标记 if event.button() == Qt.RightButton: if len(self.node_list) > 0: self.node_list.pop() self.gscene_map.clear() # 清空scene self.gscene_map.addPixmap(self.pixmap_map) # 重新加载地图 for i in range(len(self.node_list)): self.drawNode(i + 1, self.node_list[i][0], self.node_list[i][1]) def updateMap(self): if self.b_map_loaded: if not self.chb_mark.isChecked(): self.timer_map_refresh = QTimer(self) # 设定地图刷新定时器 self.timer_map_refresh.timeout.connect(self.redrawMap) self.timer_map_refresh.start(REFRESH_TIME_MS) # 设置刷新时间为100毫秒 def redrawMap(self): self.gscene_map.clear() # 清空scene self.gscene_map.addPixmap(self.pixmap_map) # 重新加载地图 for i in range(len(self.node_list)): self.drawNode(i + 1, self.node_list[i][0], self.node_list[i][1]) '''----------------------------------------------''' def drawNode(self, index, node_pos, is_near): # 样式设置 node_draw_r = 15 node_draw_x = node_pos.x() - node_draw_r node_draw_y = node_pos.y() - node_draw_r # node_draw_pos = QPointF(node_draw_x, node_draw_y) node_draw_pen = QPen(QColor(204, 47, 105), 3, Qt.SolidLine) node_draw_brush = QBrush(QColor(255, 110, 151), Qt.SolidPattern) # 正式画圆 self.gellipse_node = self.gscene_map.addEllipse( node_draw_x, node_draw_y, 2 * node_draw_r, 2 * node_draw_r, node_draw_pen, node_draw_brush) # 索引号 self.text_index = '<div style=\"font:26px;color:black;font-weight:900;\">' + \ str(index) + '</div>' self.gtext_index = QGraphicsTextItem() self.gtext_index.setHtml(self.text_index) self.gtext_index.setParentItem(self.gellipse_node) self.gtext_index.setPos(node_draw_x + 4, node_draw_y - 2) if is_near: # 若附近rssi判断有效 node_draw_r = 20 node_draw_x = node_pos.x() - node_draw_r node_draw_y = node_pos.y() - node_draw_r node_draw_pen = QPen(QColor(245, 229, 143), 10, Qt.DashLine) self.gscene_map.addEllipse(node_draw_x, node_draw_y, 2 * node_draw_r, 2 * node_draw_r, node_draw_pen) '''----------------------------------------------''' def initSerial(self, str_com): self.data_zigbee_list = [] # 初始化串口 self.ser_data = QSerialPort(STR_COM, baudRate=QSerialPort.Baud115200, readyRead=self.receive) self.ser_data.open(QIODevice.ReadWrite) if self.ser_data.isOpen(): print('串口开启成功!') else: print('串口打开失败……') @pyqtSlot() def receive(self): _data = self.ser_data.readLine().data() # 读取数据 if _data != b'': self.data_zigbee_list = [] # 清空数据 self.data = _data[0:2 * NODE_NUM].decode("gbk") # 存储数据 for i in range(len(self.data) // 2): # 每两位存储,用tuple存储,只读 data_iter = (self.data[2 * i:2 * i + 1], self.data[2 * i + 1:2 * i + 2]) self.data_zigbee_list.append(data_iter) # print('data_zigbee_list:', self.data_zigbee_list) self.updateSerial() else: print('串口接收内容为空!') def updateSerial(self): if not self.chb_mark.isChecked(): for i in range(NODE_NUM): if i < len(self.node_list): value_update = self.data_zigbee_list[i][1] # 更新节点列表 self.node_list[i][1] = (True if value_update == '1' else False) else: value_update = '*' self.lot_g_data.itemAt(i).widget().setText(value_update)