class UndoList(QDialog): __instance = None @classmethod def __getInstance(cls): return cls.__instance @classmethod def getInstance(cls): cls.__instance = cls() cls.getInstance = cls.__getInstance return cls.__instance def __init__(self): super().__init__() self.setWindowTitle("Command List") self.stack = UndoAction() self.view = QUndoView() self.view.setStack(self.stack) layout = QGridLayout() layout.addWidget(self.view) self.setLayout(layout)
class UndoDock(DockWidget): def __init__(self, parent=None): DockWidget.__init__(self, "历史", parent) self.setObjectName("undoViewDock") self._undo_view = QUndoView(self) self._clear_icon = QIcon(":/drive-harddisk.png") self._undo_view.setCleanIcon(self._clear_icon) self._undo_view.setUniformItemSizes(True) self._widget = QWidget(self) self._layout = QVBoxLayout(self._widget) self._layout.setStretch(0, 0) self._layout.setContentsMargins(0, 6, 6, 6) self._layout.addWidget(self._undo_view) self.setWidget(self._widget) self.retranslateUi() def set_stack(self, stack: QUndoStack): self._undo_view.setStack(stack) def set_group(self, group: QUndoGroup): self._undo_view.setGroup(group) def changeEvent(self, event: QEvent) -> None: pass # if e.type(): # self.LanguageChange # self.retranslateUi() def retranslateUi(self): self.setWindowTitle("历史") self._undo_view.setEmptyLabel("<空>")
def _createUndoView(self, history): undoGroup = QGroupBox(_("History of changes"), self) undoGroupLayout = QVBoxLayout() undoGroup.setLayout(undoGroupLayout) undoView = QUndoView(history, self) undoView.setEmptyLabel(_("<Original file>")) undoGroupLayout.addWidget(undoView) mainLayout = self.layout() mainLayout.addWidget(undoGroup)
def __init__(self): super().__init__() self.setWindowTitle("Command List") self.stack = UndoAction() self.view = QUndoView() self.view.setStack(self.stack) layout = QGridLayout() layout.addWidget(self.view) self.setLayout(layout)
def __init__(self, undoGroup, parent=None): super().__init__(parent) self.setObjectName("undoViewDock") self.mUndoView = QUndoView(undoGroup, self) cleanIcon = QIcon(":images/16x16/drive-harddisk.png") self.mUndoView.setCleanIcon(cleanIcon) self.mUndoView.setUniformItemSizes(True) widget = QWidget(self) layout = QVBoxLayout(widget) layout.setContentsMargins(5, 5, 5, 5) layout.addWidget(self.mUndoView) self.setWidget(widget) self.retranslateUi()
def __init__(self, parent=None): DockWidget.__init__(self, "历史", parent) self.setObjectName("undoViewDock") self._undo_view = QUndoView(self) self._clear_icon = QIcon(":/drive-harddisk.png") self._undo_view.setCleanIcon(self._clear_icon) self._undo_view.setUniformItemSizes(True) self._widget = QWidget(self) self._layout = QVBoxLayout(self._widget) self._layout.setStretch(0, 0) self._layout.setContentsMargins(0, 6, 6, 6) self._layout.addWidget(self._undo_view) self.setWidget(self._widget) self.retranslateUi()
def _create_toolbar(self, actions): """Fills the main toolbar with QActions""" self.addAction(actions.new) self.addAction(actions.open) self.addAction(actions.save) self.addAction(actions.export) self.addSeparator() self.addAction(actions.print) self.addSeparator() self.addAction(actions.undo) undo_button = self.widgetForAction(actions.undo) undo_view = QUndoView(self.main_window.undo_stack) add_toolbutton_widget(undo_button, undo_view) self.addAction(actions.redo) self.addSeparator() self.addAction(actions.cut) self.addAction(actions.copy) self.addAction(actions.copy_results) self.addAction(actions.paste) self.addAction(actions.paste) self.addSeparator() self.addAction(actions.toggle_spell_checker) self.addWidget(self.get_manager_button())
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._undo_stack = QUndoStack(self) self._undo_view = QUndoView(self._undo_stack) self._undo_view.setCleanIcon(QIcon(":/icons/images/document-save.svg")) self._undo_menu = QMenu(self) action = QWidgetAction(self._undo_menu) action.setDefaultWidget(self._undo_view) self._undo_menu.addAction(action) self._orig_point = None self._item_to_draw = None self._item_old_pos = None self._item_old_line_or_rect = None self._mode = None self._dialog = None self.setDragMode(QGraphicsView.RubberBandDrag)
class UndoDock(QDockWidget): def __init__(self, undoGroup, parent=None): super().__init__(parent) self.setObjectName("undoViewDock") self.mUndoView = QUndoView(undoGroup, self) cleanIcon = QIcon(":images/16x16/drive-harddisk.png") self.mUndoView.setCleanIcon(cleanIcon) self.mUndoView.setUniformItemSizes(True) widget = QWidget(self) layout = QVBoxLayout(widget) layout.setContentsMargins(5, 5, 5, 5) layout.addWidget(self.mUndoView) self.setWidget(widget) self.retranslateUi() def changeEvent(self, e): super().changeEvent(e) x = e.type() if x == QEvent.LanguageChange: self.retranslateUi() else: pass def retranslateUi(self): self.setWindowTitle(self.tr("History")) self.mUndoView.setEmptyLabel(self.tr("<empty>"))
class UndoDock(QDockWidget): def __init__(self, undoGroup, parent = None): super().__init__(parent) self.setObjectName("undoViewDock") self.mUndoView = QUndoView(undoGroup, self) cleanIcon = QIcon(":images/16x16/drive-harddisk.png") self.mUndoView.setCleanIcon(cleanIcon) self.mUndoView.setUniformItemSizes(True) widget = QWidget(self) layout = QVBoxLayout(widget) layout.setContentsMargins(5, 5, 5, 5) layout.addWidget(self.mUndoView) self.setWidget(widget) self.retranslateUi() def changeEvent(self, e): super().changeEvent(e) x = e.type() if x==QEvent.LanguageChange: self.retranslateUi() else: pass def retranslateUi(self): self.setWindowTitle(self.tr("History")) self.mUndoView.setEmptyLabel(self.tr("<empty>"))
def __init__(self, undoGroup, parent = None): super().__init__(parent) self.setObjectName("undoViewDock") self.mUndoView = QUndoView(undoGroup, self) cleanIcon = QIcon(":images/16x16/drive-harddisk.png") self.mUndoView.setCleanIcon(cleanIcon) self.mUndoView.setUniformItemSizes(True) widget = QWidget(self) layout = QVBoxLayout(widget) layout.setContentsMargins(5, 5, 5, 5) layout.addWidget(self.mUndoView) self.setWidget(widget) self.retranslateUi()
def nf2_mw(x2_mw): np2_acs(x2_mw) self.wu_mb = nf2_mb(x2_mw.menuBar()) self.wu_us = nf2_us(QUndoStack()) self.wu_uv = nf2_uv(x2_mw, QUndoView(self.wu_us)) self.wu_ds = nf2_ds(WDiagramScene()) x2_mw.setWindowTitle(GC_APP_NM) x2_mw.showEvent = lambda _: self.wn_on_shown() x2_mw.closeEvent = lambda _: self.wn_on_quit() x2_mw.setCentralWidget(QGraphicsView(self.wu_ds)) x2_mw.resize(710, 590) x2_mw.show() x2_mw.raise_() return x2_mw
class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("Pangolin") self.setUnifiedTitleAndToolBarOnMac(True) self.setGeometry(50, 50, 1200, 675) # Models and Views self.interface = PangoModelSceneInterface() self.tree_view = QTreeView() self.tree_view.setUniformRowHeights(True) self.tree_view.setModel(self.interface.model) self.interface.set_tree(self.tree_view) self.graphics_view = PangoGraphicsView() self.graphics_view.setScene(self.interface.scene) self.undo_view = QUndoView() # Dock widgets self.label_widget = PangoLabelWidget("Labels", self.tree_view) self.undo_widget = PangoUndoWidget("History", self.undo_view) self.file_widget = PangoFileWidget("Files") # Menu and toolbars self.menu_bar = PangoMenuBarWidget() self.tool_bar = PangoToolBarWidget() self.tool_bar.set_scene(self.interface.scene) self.tool_bar.label_select.setModel(self.interface.model) # Signals and Slots self.menu_bar.open_images_action.triggered.connect(self.load_images) self.menu_bar.export_action.triggered.connect(self.export_project) self.menu_bar.import_action.triggered.connect(self.import_project) self.menu_bar.save_project_action.triggered.connect(self.save_project) self.file_widget.file_view.selectionModel().currentChanged.connect( self.switch_image) self.file_widget.file_model.directoryLoaded.connect( self.after_loaded_images) self.tool_bar.label_select.currentIndexChanged.connect( self.interface.switch_label) self.tool_bar.del_labels_signal.connect(self.interface.del_labels) # Layouts self.bg = QWidget() self.setCentralWidget(self.bg) self.bg_layout = QVBoxLayout(self.bg) self.bg_layout.setContentsMargins(0, 0, 0, 0) self.bg_layout.setSpacing(0) self.bg_layout.addWidget(self.tool_bar) self.bg_layout.addWidget(self.graphics_view) self.addDockWidget(Qt.RightDockWidgetArea, self.label_widget) self.addDockWidget(Qt.RightDockWidgetArea, self.undo_widget) self.addDockWidget(Qt.LeftDockWidgetArea, self.file_widget) self.addToolBar(Qt.TopToolBarArea, self.menu_bar) # Shortcuts self.sh_reset_tool = QShortcut(QKeySequence('Esc'), self) self.sh_reset_tool.activated.connect(self.tool_bar.reset_tool) self.sh_inc_tool_size = QShortcut( QKeySequence(Qt.SHIFT + Qt.Key_Underscore), self) self.sh_inc_tool_size.activated.connect( lambda: self.tool_bar.set_tool_size(1, additive=True)) self.sh_dec_tool_size = QShortcut( QKeySequence(Qt.SHIFT + Qt.Key_Equal), self) self.sh_dec_tool_size.activated.connect( lambda: self.tool_bar.set_tool_size(-1, additive=True)) self.sh_select_next_image = QShortcut( QKeySequence(Qt.CTRL + Qt.Key_Down), self) self.sh_select_next_image.activated.connect( self.file_widget.select_next_image) self.sh_select_prev_image = QShortcut( QKeySequence(Qt.CTRL + Qt.Key_Up), self) self.sh_select_prev_image.activated.connect( self.file_widget.select_prev_image) self.sh_select_next_label = QShortcut( QKeySequence(Qt.CTRL + Qt.Key_Right), self) self.sh_select_next_label.activated.connect( self.tool_bar.label_select.select_next_label) self.sh_select_prev_label = QShortcut( QKeySequence(Qt.CTRL + Qt.Key_Left), self) self.sh_select_prev_label.activated.connect( self.tool_bar.label_select.select_prev_label) self.sh_undo = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Z), self) self.sh_undo.activated.connect(self.undo_widget.undo) self.sh_redo = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Y), self) self.sh_redo.activated.connect(self.undo_widget.redo) def switch_image(self, c_idx, p_idx): c_fpath = self.file_widget.file_model.filePath(c_idx) p_fpath = self.file_widget.file_model.filePath(p_idx) self.interface.scene.set_fpath(c_fpath) self.interface.scene.reset_com() self.graphics_view.fitInView(self.interface.scene.sceneRect(), Qt.KeepAspectRatio) # Handling unsaved changes if c_fpath not in self.interface.scene.change_stacks.keys(): self.interface.scene.change_stacks[c_fpath] = QUndoStack() self.undo_view.setStack(self.interface.scene.change_stacks[c_fpath]) self.interface.scene.stack = self.interface.scene.change_stacks[ c_fpath] self.interface.filter_tree(c_fpath, p_fpath) def load_images(self, action=None, fpath=None): if fpath is None: dialog = QFileDialog() dialog.setFileMode(QFileDialog.DirectoryOnly) fpath = QFileDialog.getExistingDirectory() if fpath != "": self.file_widget.file_model.setRootPath(fpath) root_idx = self.file_widget.file_model.index(fpath) self.file_widget.file_view.setRootIndex(root_idx) self.images_are_new = True def after_loaded_images(self): if self.images_are_new is True: folder_path = self.file_widget.file_model.rootPath() for f in sorted(os.listdir(folder_path)): if f.endswith(".jpg") or f.endswith(".png"): idx = self.file_widget.file_model.index( os.path.join(folder_path, f)) self.file_widget.file_view.setCurrentIndex(idx) break self.file_widget.file_view.scrollToTop() self.load_project() self.images_are_new = False def save_project(self, action=None, project_path=None): if project_path is None: project_path = os.path.join(self.file_widget.file_model.rootPath(), "pangolin_project.p") pickle_items = [] for k in self.interface.map.keys(): pickle_items.append( self.interface.model.itemFromIndex(QModelIndex(k))) pickle.dump(pickle_items, open(project_path, "wb")) def load_project(self, action=None, project_path=None): if project_path is None: project_path = os.path.join(self.file_widget.file_model.rootPath(), "pangolin_project.p") if os.path.exists(project_path): self.clear_project() unpickled_items = pickle.load(open(project_path, "rb")) for item in unpickled_items: if item.parent() is None: self.interface.model.appendRow(item) item.force_update() self.interface.filter_tree(self.interface.scene.fpath, None) def clear_project(self, action=None, show_dialog=True): self.interface.map.clear() self.interface.model.clear() self.interface.scene.full_clear() def export_project(self, action=None): folder_path = self.file_widget.file_model.rootPath() fpaths = self.interface.scene.change_stacks.keys() dialog = ExportSettingsDialog(self, fpaths) if dialog.exec(): s_fpaths = dialog.selected_fnames() file_format = dialog.file_format() if file_format == "PascalVOC (XML)": for fpath in s_fpaths: pascal_voc_write(self.interface, fpath) elif file_format == "COCO (JSON)": #export_fpath, _ = QFileDialog().getSaveFileName( # self, "Save Project", os.path.join(default, "annotations.xml"), # "XML files (*.xml)") pass elif file_format == "YOLOv3 (TXT)": for fpath in s_fpaths: yolo_write(self.interface, fpath) elif file_format == "Image Mask (PNG)": mask_folder = os.path.join(folder_path, "Masks") if not os.path.exists(mask_folder): os.mkdir(mask_folder) for fpath in s_fpaths: idx = self.file_widget.file_model.index(fpath) self.file_widget.file_view.setCurrentIndex(idx) image_mask_write(self.interface, fpath, mask_folder) self.interface.filter_tree(self.interface.scene.fpath, None) def import_project(self, action=None, folder=None): if folder is None: folder = self.file_widget.file_model.rootPath() fpaths = [] for fname in os.listdir(folder): pre, ext = os.path.splitext(fname) if ext in (".xml", ".txt"): fpaths.append(os.path.join(folder, fname)) if fpaths == []: return dialog = ImportSettingsDialog(self, fpaths) if dialog.exec(): s_fpaths = dialog.selected_fnames() for fpath in s_fpaths: pre, ext = os.path.splitext(fpath) if ext == ".xml": # PascalVOC (XML) pascal_voc_read(self.interface, fpath) elif ext == ".txt": yolo_read(self.interface, fpath) def unsaved_files_dialog(self): #if pfile != "": # # Save changes? # if self.interface.map: # result = self.unsaved_files_dialog() # if result == QMessageBox.Cancel: # return # elif result == QMessageBox.Save: # self.save_project() dialog = QMessageBox() dialog.setText("The project has been modified.") dialog.setInformativeText("Do you want to save your changes?") dialog.setStandardButtons(QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel) dialog.setDefaultButton(QMessageBox.Save) return dialog.exec() def unsaved_commands_dialog(self): res = QMessageBox().question( self.parent(), "Unsaved shape commands", "Command history will be cleaned, are you sure you want to continue?" ) if res == QMessageBox.Yes: for stack in self.interface.scene.change_stacks.values(): stack.clear() return res def export_warning_dialog(self): pass
def addUndoView(self, stack): view = QUndoView(stack) self.stackedWidget.addWidget(view) return view
def createUndoView(self, parent): # creates an undo stack view for current QGraphicsScene undoView = QUndoView(self.undoStack, parent) showUndoDialog(undoView, parent)
def setContent(self, widget): self.clear() undoview = QUndoView(widget.history, self) undoview.setEmptyLabel(_("<Original file>")) self.layout().addWidget(undoview)
class AnnotationsNetworkView(NetworkView): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._undo_stack = QUndoStack(self) self._undo_view = QUndoView(self._undo_stack) self._undo_view.setCleanIcon(QIcon(":/icons/images/document-save.svg")) self._undo_menu = QMenu(self) action = QWidgetAction(self._undo_menu) action.setDefaultWidget(self._undo_view) self._undo_menu.addAction(action) self._orig_point = None self._item_to_draw = None self._item_old_pos = None self._item_old_line_or_rect = None self._mode = None self._dialog = None self.setDragMode(QGraphicsView.RubberBandDrag) def setScene(self, scene: AnnotationsNetworkScene): scene.editAnnotationItemRequested.connect( self.on_edit_annotation_item_requested) self._undo_stack.clear() super().setScene(scene) def addAnnotationItem(self, item: QGraphicsItem, pos: QPointF): scene = self.scene() if scene is None: return if isinstance(item, (ArrowItem, RectItem, EllipseItem)): item.setPen( QPen(Qt.black, scene.getDefaultPenSizeFromRect(), Qt.SolidLine)) self._undo_stack.push(AddCommand(item, scene)) item.setPos(pos) scene.annotationAdded.emit(item) return item def addAnnotationLine(self, line: QLineF, pos: QPointF) -> Union[ArrowItem, None]: return self.addAnnotationArrow(line, pos, False, False) def addAnnotationArrow(self, line: QLineF, pos: QPointF, has_head=True, has_tail=False) -> Union[ArrowItem, None]: item = ArrowItem(line, has_head=has_head, has_tail=has_tail) return self.addAnnotationItem(item, pos) def addAnnotationRect(self, rect: QRectF, pos: QPointF) -> Union[RectItem, None]: return self.addAnnotationItem(RectItem(rect), pos) def addAnnotationEllipse( self, rect: QRectF, pos: QPointF) -> Union[QGraphicsEllipseItem, None]: return self.addAnnotationItem(EllipseItem(rect), pos) def addAnnotationText(self, text: str, font: QFont, pos: QPointF) -> Union[TextItem, None]: item = TextItem(text) item.setFont(font) return self.addAnnotationItem(item, pos) def setDrawMode(self, mode): self._mode = mode if mode is None: self.setDragMode(QGraphicsView.RubberBandDrag) self._orig_point = None else: self.setDragMode(QGraphicsView.NoDrag) def mode(self): return self._mode def undoView(self): return self._undo_view def undoStack(self): return self._undo_stack def undoMenu(self): return self._undo_menu def loadAnnotations(self, buffer: bytes) -> None: if not buffer: return scene = self.scene() if scene is None: return def read(len): nonlocal buffer, pos data = buffer[pos:pos + len] pos += len return data pos = 0 data = read(1) while data: if data in (b'L', b'A'): x1, y1, x2, y2 = struct.unpack("ffff", read(16)) line = QLineF(0., 0., x2, y2) if data == b'L': self.addAnnotationLine(line, QPointF(x1, y1)) elif data == b'A': data, = struct.unpack("B", read(1)) has_head = (data >> 0) & 1 has_tail = (data >> 1) & 1 self.addAnnotationArrow(line, QPointF(x1, y1), has_head, has_tail) elif data in (b'R', b'C'): x, y, w, h = struct.unpack("ffff", read(16)) rect = QRectF(0., 0., w, h) if data == b'R': self.addAnnotationRect(rect, QPointF(x, y)) elif data == b'C': self.addAnnotationEllipse(rect, QPointF(x, y)) elif data == b'T': x, y, len = struct.unpack("ffH", read(10)) text = read(len).decode() font_size, = struct.unpack("H", read(2)) font = QFont() font.setPointSize(font_size) self.addAnnotationText(text, font, QPointF(x, y)) data = read(1) self._undo_stack.setClean() def saveAnnotations(self) -> bytes: scene = self.scene() if scene is None: return b'' buffer = b'' for item in scene.items(): if isinstance(item, ArrowItem): char = b'A' if item.hasHead() or item.hasTail() else b'L' line = item.line() pos = item.pos() buffer += char buffer += struct.pack("ffff", pos.x(), pos.y(), line.x2(), line.y2()) if char == b'A': buffer += struct.pack("B", item.hasHead() + item.hasTail() * 2) elif isinstance(item, (RectItem, EllipseItem)): char = b'R' if isinstance(item, RectItem) else b'C' rect = item.rect() pos = item.pos() buffer += char buffer += struct.pack("ffff", pos.x(), pos.y(), rect.width(), rect.height()) elif isinstance(item, TextItem): pos = item.pos() text = item.text() font = item.font() buffer += b'T' buffer += struct.pack("ff", pos.x(), pos.y()) buffer += struct.pack("H", len(text)) buffer += str.encode(text) buffer += struct.pack("H", font.pointSize()) return buffer def deleteSelectedAnnotations(self): scene = self.scene() if scene is None: return for item in scene.annotationsLayer.childItems(): if item.isSelected(): self._undo_stack.push(DeleteCommand(item, self)) def on_edit_annotation_item_requested(self, item: QGraphicsItem): # Edit text item if isinstance(item, TextItem): def edit_text_item(result): if result == QDialog.Accepted: old_text = item.text() old_font = item.font() text, font_size = self._dialog.getValues() font = QFont() font.setPointSize(font_size) item.setText(text) item.setFont(font) self._undo_stack.push( EditTextCommand(item, old_text, old_font.pointSize(), self.scene())) self._dialog = TextItemInputDialog(self) self._dialog.setValues(item.text(), item.font().pointSize()) self._dialog.finished.connect(edit_text_item) self._dialog.open() def mouseDoubleClickEvent(self, event: QMouseEvent) -> None: scene = self.scene() if scene: if self._mode is None or self._mode == MODE_TEXT: item = self.itemAt(event.pos()) if self._mode is None: self.on_edit_annotation_item_requested(item) # Add a text item at mouse position elif self._mode == MODE_TEXT: def add_text_item(result): if result == QDialog.Accepted: text, font_size = self._dialog.getValues() font = QFont() font.setPointSize(font_size) self.addAnnotationText(text, font, pos) pos = self.mapToScene(event.pos()) self._dialog = TextItemInputDialog(self) self._dialog.setValues("", scene.getDefaultFontSizeFromRect()) self._dialog.finished.connect(add_text_item) self._dialog.open() else: super().mouseDoubleClickEvent(event) def mousePressEvent(self, event: QMouseEvent) -> None: scene = self.scene() if scene: # Edit item (line, rect, ellipse, etc) if self._mode is None: self._item_to_draw = item = self.itemAt(event.pos()) if item: self._item_old_pos = item.pos() event_pos = self.mapToScene(event.pos()) - item.pos() # Edit Arrows head and tails if isinstance(item, ArrowItem) and event.modifiers( ) & Qt.ShiftModifier == Qt.ShiftModifier: tol = item.pen().width() * 2. if (event_pos - item.line().p1()).manhattanLength() < tol: has_tail = item.hasTail() item.setTail(not has_tail) self._undo_stack.push( EditArrowCommand(item, has_tail, item.hasHead(), self.scene())) scene.arrowEdited.emit(item) elif (event_pos - item.line().p2()).manhattanLength() < tol: has_head = item.hasHead() item.setHead(not has_head) self._undo_stack.push( EditArrowCommand(item, item.hasTail(), has_head, self.scene())) scene.arrowEdited.emit(item) # Resize Line and Arrow if isinstance(item, ArrowItem): tol = item.pen().width() * 2. self._item_old_line_or_rect = item.line() if (event_pos - item.line().p1()).manhattanLength() < tol: self._orig_point = item.line().p2() + item.pos() elif (event_pos - item.line().p2()).manhattanLength() < tol: self._orig_point = item.line().p1() + item.pos() # Resize Rect and Ellipse elif isinstance(item, (EllipseItem, RectItem)): self._item_old_line_or_rect = item.rect() tol = item.pen().width() if (event_pos - item.rect().topLeft()).manhattanLength() < tol: self._orig_point = item.rect().bottomRight( ) + item.pos() elif (event_pos - item.rect().bottomRight() ).manhattanLength() < tol: self._orig_point = item.rect().topLeft( ) + item.pos() elif (event_pos - item.rect().topRight()).manhattanLength() < tol: self._orig_point = item.rect().bottomLeft( ) + item.pos() elif (event_pos - item.rect().bottomLeft() ).manhattanLength() < tol: self._orig_point = item.rect().topRight( ) + item.pos() # Define starting point of item (line, rect, ellipse, etc) elif self._mode != MODE_TEXT: self._orig_point = self.mapToScene(event.pos()) super().mousePressEvent(event) def mouseMoveEvent(self, event: QGraphicsSceneMouseEvent) -> None: scene = self.scene() if scene and self._orig_point is not None: # Edit line or arrow if self._mode in (MODE_LINE, MODE_ARROW) \ or (self._mode is None and isinstance(self._item_to_draw, QGraphicsLineItem)): pos = self.mapToScene(event.pos()) x = pos.x() - self._orig_point.x() y = pos.y() - self._orig_point.y() if self._item_to_draw and self._orig_point == self._item_to_draw.line( ).p2() + self._item_to_draw.pos(): # Moving line around head line = QLineF(0, 0, -x, -y) point = pos else: line = QLineF(0, 0, x, y) point = None if self._item_to_draw is None: if self._mode == MODE_LINE: self._item_to_draw = self.addAnnotationLine( line, self._orig_point) elif self._mode == MODE_ARROW: self._item_to_draw = self.addAnnotationArrow( line, self._orig_point) else: self._item_to_draw.setLine(line) if point is not None: self._item_to_draw.setPos(point) return # Edit rect or circle elif self._mode in (MODE_RECT, MODE_ELLIPSE) \ or (self._mode is None and isinstance(self._item_to_draw, (RectItem, EllipseItem))): pos = self.mapToScene(event.pos()) width = pos.x() - self._orig_point.x() height = pos.y() - self._orig_point.y() dx = 0 if width >= 0 else width dy = 0 if height >= 0 else height rect = QRectF(0, 0, abs(width), abs(height)) point = self._orig_point + QPointF(dx, dy) if self._item_to_draw is None: if self._mode == MODE_RECT: self._item_to_draw = self.addAnnotationRect( rect, point) elif self._mode == MODE_ELLIPSE: self._item_to_draw = self.addAnnotationEllipse( rect, point) else: self._item_to_draw.setRect(rect) self._item_to_draw.setPos(point) return super().mouseMoveEvent(event) def mouseReleaseEvent(self, event: QGraphicsSceneMouseEvent) -> None: if self._mode is None and self._item_to_draw is not None and event.button( ) == Qt.LeftButton: if isinstance(self._item_old_line_or_rect, QRectF): line_or_rect = self._item_to_draw.rect() elif isinstance(self._item_old_line_or_rect, QLineF): line_or_rect = self._item_to_draw.line() else: line_or_rect = None if line_or_rect is None or self._item_old_line_or_rect == line_or_rect: if self._item_old_pos != self._item_to_draw.pos(): self._undo_stack.push( MoveCommand(self._item_to_draw, self._item_old_pos, self.scene())) else: self._undo_stack.push( ResizeCommand(self._item_to_draw, self._item_old_pos, self._item_old_line_or_rect, self.scene())) self._item_old_pos = None self._item_old_line_or_rect = None self._item_to_draw = None self._orig_point = None super().mouseReleaseEvent(event)
def __init__(self, parent = None): super().__init__(parent) self.setWindowIcon(QtGui.QIcon(paths.ICON_PATH)) if settings.FRAMELESS.get_value(): self.setWindowFlags(QtCore.Qt.Window | QtCore.Qt.FramelessWindowHint) self._notification_frame = NotificationFrame(self) self.setWindowTitle(values.APPLICATION_NAME) self.layout().setContentsMargins(0, 0, 0, 0) self._printing_view = CubeableView(self) Context.focus_card_changed.connect(self._printing_view.new_cubeable) self._rating_view = RatingView() Context.focus_card_changed.connect(self._rating_view.on_focus_event) self._login_status_label = QtWidgets.QLabel('') LOGIN_CONTROLLER.login_success.connect(lambda u, h: self._login_status_label.setText(f'{u.username}@{h}')) LOGIN_CONTROLLER.login_failed.connect(lambda e: self._login_status_label.setText('')) LOGIN_CONTROLLER.login_terminated.connect(lambda: self._login_status_label.setText('')) LOGIN_CONTROLLER.login_pending.connect(lambda u, h: self._login_status_label.setText(f'logging in @ {h}')) self.statusBar().setContentsMargins(10, 0, 10, 0) self.statusBar().addPermanentWidget(self._login_status_label) Context.status_message.connect(lambda m, _t: self.statusBar().showMessage(m, _t)) self.statusBar().addPermanentWidget(QtWidgets.QLabel(version_formatted())) self._card_view_dock = Dock('Card View', 'card_view_dock', self, self._printing_view, wants_focus = False) Context.focus_freeze_changed.connect( lambda frozen: self._card_view_dock.setWindowTitle( 'Card View' + (' (frozen)' if frozen else '') ) ) self._rating_view_dock = Dock('Rating View', 'rating_view_dock', self, self._rating_view, wants_focus = False) Context.focus_freeze_changed.connect( lambda frozen: self._rating_view_dock.setWindowTitle( 'Rating View' + (' (frozen)' if frozen else '') ) ) self._card_adder = PrintingSelector(self) self._card_adder.add_printings.connect(self._on_add_printings) self._card_adder_dock = Dock('Card Adder', 'card adder dock', self, self._card_adder) self._undo_view = QUndoView(Context.undo_group) self._undo_view_dock = Dock('Undo View', 'undo view dock', self, self._undo_view) self._lobby_view = LobbiesView( LobbyModelClientConnection() ) self._lobby_view_dock = Dock( 'Lobby View', 'lobbies', self, self._lobby_view, allowed_areas = QtCore.Qt.RightDockWidgetArea | QtCore.Qt.LeftDockWidgetArea | QtCore.Qt.BottomDockWidgetArea, ) self._cube_view_minimap = GraphicsMiniView() Context.focus_scene_changed.connect(lambda scene: self._cube_view_minimap.set_scene(scene)) self._cube_view_minimap_dock = Dock('Minimap', 'minimap', self, self._cube_view_minimap, wants_focus = False) self._limited_sessions_view = LimitedSessionsView() self._limited_sessions_dock = Dock('Limited', 'Limited', self, self._limited_sessions_view) self._matches_view = ScheduledMatchesView() self._matches_view_dock = Dock('Matches', 'Matches', self, self._matches_view) self.addDockWidget(QtCore.Qt.LeftDockWidgetArea, self._card_adder_dock) self.addDockWidget(QtCore.Qt.RightDockWidgetArea, self._card_view_dock) self.addDockWidget(QtCore.Qt.RightDockWidgetArea, self._rating_view_dock) self.addDockWidget(QtCore.Qt.RightDockWidgetArea, self._undo_view_dock) self.addDockWidget(QtCore.Qt.RightDockWidgetArea, self._lobby_view_dock) self.addDockWidget(QtCore.Qt.RightDockWidgetArea, self._cube_view_minimap_dock) self.addDockWidget(QtCore.Qt.RightDockWidgetArea, self._limited_sessions_dock) self.addDockWidget(QtCore.Qt.RightDockWidgetArea, self._matches_view_dock) self._card_view_dock.hide() self._rating_view_dock.hide() self._card_adder_dock.hide() self._undo_view_dock.hide() self._lobby_view_dock.hide() self._cube_view_minimap_dock.hide() self._limited_sessions_dock.hide() self._matches_view_dock.hide() self._main_view = MainView(self) self.setCentralWidget(self._main_view) menu_bar = self.menuBar() all_menus = [ ( menu_bar.addMenu('File'), ( ('New Deck', 'Ctrl+N', self._new_deck), ('Open Deck', 'Ctrl+O', lambda: self.open(Deck)), ('Open Pool', 'Ctrl+P', lambda: self.open(Pool)), ('Save', 'Ctrl+S', self._save), ('Save As', 'Ctrl+Shift+S', self._save_as), ('Export Deck', 'Ctrl+Shift+E', self._export_deck), ('Close Tab', 'Ctrl+W', self._close_tab), 'line', ('Exit', 'Ctrl+Q', self.close), ), ), ( menu_bar.addMenu('Edit'), ( ('Undo', 'Ctrl+Z', Context.undo_group.undo), ('Redo', 'Ctrl+Shift+Z', Context.undo_group.redo), 'line', ('Add cards', 'Ctrl+F', self._add_cards), ('Sort Macroes', 'Ctrl+M', self._edit_sort_macroes), ), ), ( menu_bar.addMenu('View'), ( ('Card View', 'Meta+1', lambda: self._toggle_dock_view(self._card_view_dock)), ('Card Adder', 'Meta+2', lambda: self._toggle_dock_view(self._card_adder_dock)), ('Limited', 'Meta+3', lambda: self._toggle_dock_view(self._limited_sessions_dock)), ('Lobbies', 'Meta+4', lambda: self._toggle_dock_view(self._lobby_view_dock)), ('Matches', 'Meta+5', lambda: self._toggle_dock_view(self._matches_view_dock)), ('Rating', 'Meta+6', lambda: self._toggle_dock_view(self._rating_view_dock)), ('Undo', None, lambda: self._toggle_dock_view(self._undo_view_dock)), ('Minimap', None, lambda: self._toggle_dock_view(self._cube_view_minimap_dock)), ), ), ( menu_bar.addMenu('Connect'), ( ('Login', 'Ctrl+L', LoginDialog(self).exec_), ('Logout', None, LOGIN_CONTROLLER.log_out), ), ), ( menu_bar.addMenu('Draft'), ( ('Go To Latest', 'Alt+Up', self._draft_history_wrapper('go_to_latest')), ('Go Back', 'Alt+Left', self._draft_history_wrapper('go_backwards')), ('Go Forward', 'Alt+Right', self._draft_history_wrapper('go_forward')), ('Go To Start', 'Alt+Down', self._draft_history_wrapper('go_to_start')), ), ), ( menu_bar.addMenu('Simulate'), ( ('Sample Hand', 'Ctrl+H', self._sample_hand), ) ), ( menu_bar.addMenu('Preferences'), ( ('Settings', 'Ctrl+Alt+S', lambda: SettingsDialog.get().exec_()), ), ), ( menu_bar.addMenu('DB'), ( ('Info', None, lambda: DBInfoDialog().exec_()), ('Update', None, lambda: DBUpdateDialog().exec_()), ('Validate', None, lambda: LOGIN_CONTROLLER.validate(True)), ), ), ( menu_bar.addMenu('Help'), ( ('About', None, lambda: AboutDialog().exec_()), ), ), ] if Context.debug: all_menus.append( ( menu_bar.addMenu('Test'), ( ('Test', 'Ctrl+T', self._test), ), ) ) for menu, lines in all_menus: for line in lines: if line == 'line': menu.addSeparator() else: name, shortcut, action = line _action = QAction(name, self) if shortcut: _action.setShortcut(shortcut) _action.triggered.connect(action) menu.addAction(_action) self._create_action('toggle freeze focus', Context.toggle_frozen_focus, 'Alt+F') self._reset_dock_width = 500 self._reset_dock_height = 1200 self.resizeDocks([self._card_view_dock], [self._reset_dock_width], QtCore.Qt.Horizontal) Context.notification_message.connect(self._notification_frame.notify) Context.draft_started.connect(self._on_draft_started) self._load_state()
def __init__(self): super().__init__() self.setWindowTitle("Pangolin") self.setUnifiedTitleAndToolBarOnMac(True) self.setGeometry(50, 50, 1200, 675) # Models and Views self.interface = PangoModelSceneInterface() self.tree_view = QTreeView() self.tree_view.setUniformRowHeights(True) self.tree_view.setModel(self.interface.model) self.interface.set_tree(self.tree_view) self.graphics_view = PangoGraphicsView() self.graphics_view.setScene(self.interface.scene) self.undo_view = QUndoView() # Dock widgets self.label_widget = PangoLabelWidget("Labels", self.tree_view) self.undo_widget = PangoUndoWidget("History", self.undo_view) self.file_widget = PangoFileWidget("Files") # Menu and toolbars self.menu_bar = PangoMenuBarWidget() self.tool_bar = PangoToolBarWidget() self.tool_bar.set_scene(self.interface.scene) self.tool_bar.label_select.setModel(self.interface.model) # Signals and Slots self.menu_bar.open_images_action.triggered.connect(self.load_images) self.menu_bar.export_action.triggered.connect(self.export_project) self.menu_bar.import_action.triggered.connect(self.import_project) self.menu_bar.save_project_action.triggered.connect(self.save_project) self.file_widget.file_view.selectionModel().currentChanged.connect( self.switch_image) self.file_widget.file_model.directoryLoaded.connect( self.after_loaded_images) self.tool_bar.label_select.currentIndexChanged.connect( self.interface.switch_label) self.tool_bar.del_labels_signal.connect(self.interface.del_labels) # Layouts self.bg = QWidget() self.setCentralWidget(self.bg) self.bg_layout = QVBoxLayout(self.bg) self.bg_layout.setContentsMargins(0, 0, 0, 0) self.bg_layout.setSpacing(0) self.bg_layout.addWidget(self.tool_bar) self.bg_layout.addWidget(self.graphics_view) self.addDockWidget(Qt.RightDockWidgetArea, self.label_widget) self.addDockWidget(Qt.RightDockWidgetArea, self.undo_widget) self.addDockWidget(Qt.LeftDockWidgetArea, self.file_widget) self.addToolBar(Qt.TopToolBarArea, self.menu_bar) # Shortcuts self.sh_reset_tool = QShortcut(QKeySequence('Esc'), self) self.sh_reset_tool.activated.connect(self.tool_bar.reset_tool) self.sh_inc_tool_size = QShortcut( QKeySequence(Qt.SHIFT + Qt.Key_Underscore), self) self.sh_inc_tool_size.activated.connect( lambda: self.tool_bar.set_tool_size(1, additive=True)) self.sh_dec_tool_size = QShortcut( QKeySequence(Qt.SHIFT + Qt.Key_Equal), self) self.sh_dec_tool_size.activated.connect( lambda: self.tool_bar.set_tool_size(-1, additive=True)) self.sh_select_next_image = QShortcut( QKeySequence(Qt.CTRL + Qt.Key_Down), self) self.sh_select_next_image.activated.connect( self.file_widget.select_next_image) self.sh_select_prev_image = QShortcut( QKeySequence(Qt.CTRL + Qt.Key_Up), self) self.sh_select_prev_image.activated.connect( self.file_widget.select_prev_image) self.sh_select_next_label = QShortcut( QKeySequence(Qt.CTRL + Qt.Key_Right), self) self.sh_select_next_label.activated.connect( self.tool_bar.label_select.select_next_label) self.sh_select_prev_label = QShortcut( QKeySequence(Qt.CTRL + Qt.Key_Left), self) self.sh_select_prev_label.activated.connect( self.tool_bar.label_select.select_prev_label) self.sh_undo = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Z), self) self.sh_undo.activated.connect(self.undo_widget.undo) self.sh_redo = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Y), self) self.sh_redo.activated.connect(self.undo_widget.redo)
def __init__(self, debug=False): QMainWindow.__init__(self) # initialise filename self.filename = None # make the data store from .xmlstore import Store self.store = Store(debug = debug) # view the current table self.tableView = TableView(self) #self.tableView.setDragEnabled(True); #self.tableView.setDragDropMode(QAbstractItemView.InternalMove) #self.tableView.setAcceptDrops(True); #self.tableView.setDropIndicatorShown(True); self.tableView.verticalHeader().sectionMoved.connect(self.sectionMoved) self.tableView.verticalHeader().setSectionsMovable(True) self.setCentralWidget(self.tableView) # add a custom delegate to it self.delegate = ComboBoxDelegate() self.tableView.setItemDelegate(self.delegate) # dock the table selection on the left self.dock1 = QDockWidget(self) self.listView = ListView(self) self.listView.setDragEnabled(True); self.listView.setDragDropMode(QAbstractItemView.InternalMove) self.listView.setDropIndicatorShown(True); self.dock1.setWidget(self.listView) self.dock1.setFeatures( QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable) self.addDockWidget(Qt.LeftDockWidgetArea, self.dock1) # connect it to the populate method self.listView.activated.connect(self.populate) self.listView.clicked.connect(self.populate) # dock the undoView on the left self.dock2 = QDockWidget(self) self.undoView = QUndoView(self.store.stack) self.dock2.setWidget(self.undoView) self.dock2.setFeatures( QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable) self.dock2.setWindowTitle('Undo Stack') self.addDockWidget(Qt.LeftDockWidgetArea, self.dock2) # create a menubar self.menu = self.menuBar() # create file menu headings self.menuFile = self.menu.addMenu('File') self.menuFile.addAction('New', self.New).setShortcut('CTRL+N') self.menuFile.addAction('Open...', self.Open).setShortcut('CTRL+O') self.menuFile.addAction('Reload', self.Reload) self.menuFile.addAction('Save', self.Save).setShortcut('CTRL+S') self.menuFile.addAction('Save As...', self.SaveAs) self.menuFile.addSeparator() self.menuFile.addAction('Set Architecture...', self.setArch) self.menuFile.addSeparator() self.menuFile.addAction('Quit', self.closeEvent).setShortcuts(['CTRL+Q', 'ALT+F4']) # create edit menu headings self.menuEdit = self.menu.addMenu('Edit') self.menuEdit.addAction('Insert Row', self.tableView.insertRow).setShortcut('CTRL+I') self.menuEdit.addAction('Insert Row Under', self.tableView.insertRowUnder).setShortcut('CTRL+U') self.menuEdit.addAction('Remove Row', self.tableView.removeRow) self.menuEdit.addSeparator() self.menuEdit.addAction('Cut', self.tableView.cut).setShortcut('CTRL+X') self.menuEdit.addAction('Copy', self.tableView.copy).setShortcuts(['CTRL+C', 'CTRL+INS']) self.menuEdit.addAction('Paste', self.tableView.paste).setShortcuts(['CTRL+V', 'SHIFT+INS']) self.menuEdit.addAction('Clear', self.tableView.menuClear).setShortcut('CTRL+D') self.menuEdit.addSeparator() self.menuEdit.addAction('Fill Cells', self.tableView.fillCells).setShortcut('CTRL+L') self.menuEdit.addAction('Fill Cells and Increment', self.tableView.fillCellsInc).setShortcut('CTRL+R') self.menuEdit.addAction('Python Code...', self.tableView.pythonCode).setShortcut('CTRL+P') self.tableView.codeBox = pythonCode() self.menuEdit.addSeparator() self.menuEdit.addAction('Undo', self.store.stack.undo).setShortcut('CTRL+Z') self.menuEdit.addAction('Redo', self.store.stack.redo).setShortcut('CTRL+SHIFT+Z') # create component menu self.menuComponents = self.menu.addMenu('Components') self.resize(QSize(1000,500))