def __init__(self): super().__init__() self.setWindowTitle("My App") label = QLabel("Hello!") label.setAlignment(Qt.AlignCenter) self.setCentralWidget(label) toolbar = QToolBar("My main toolbar") toolbar.setIconSize(QSize(16, 16)) self.addToolBar(toolbar) button_action = QAction(QIcon("bug.png"), "Your button", self) button_action.setStatusTip("This is your button") button_action.triggered.connect(self.onMyToolBarButtonClick) button_action.setCheckable(True) toolbar.addAction(button_action) toolbar.addSeparator() button_action2 = QAction(QIcon("bug.png"), "Your button2", self) button_action2.setStatusTip("This is your button2") button_action2.triggered.connect(self.onMyToolBarButtonClick) button_action2.setCheckable(True) toolbar.addAction(button_action2) toolbar.addWidget(QLabel("Hello")) toolbar.addWidget(QCheckBox()) self.setStatusBar(QStatusBar(self))
def toolbar(self): toolbar = QToolBar("My main toolbar") toolbar.setIconSize(QSize(16, 16)) self.addToolBar(toolbar) mouse = QAction(QIcon('./icons/icons-01.png'), 'mouse', self, checkable=True) mouse.setStatusTip('mouse') mouse.triggered.connect(lambda: self.set_pointer('mouse')) square = QAction(QIcon('./icons/icons_Square.png'), 'square', self, checkable=True) square.setStatusTip('square') square.triggered.connect(lambda: self.set_pointer('square')) circle = QAction(QIcon('./icons_Circle.png'), 'circle', self, checkable=True) circle.setStatusTip('circle') circle.triggered.connect(lambda: self.set_pointer('circle')) crosshair = QAction(QIcon('./icons/icons_Crosshair.png'), 'crosshair', self, checkable=True) crosshair.setStatusTip('crosshair') crosshair.triggered.connect(lambda: self.set_pointer('cross')) brush = QAction(QIcon('./icons/icons_Brush.png'), 'brush', self, checkable=True) brush.setStatusTip('crosshair') brush.triggered.connect(lambda: self.set_pointer('brush')) group = QActionGroup(self, exclusive=True) for action in (mouse, square, circle, crosshair, brush): toolbar.addAction(action) group.addAction(action) annotations = QAction(QIcon('./icons/icons_Circle.png'), 'Annot', self, checkable=True) annotations.setStatusTip('Toggle annotations') annotations.triggered.connect(self.toggel_annot) toolbar.addAction(annotations) clear = QAction(QIcon('./icons/icons_Square.png'), 'Clear', self) clear.setStatusTip('Clear annotations') clear.triggered.connect(self.clear_annot) toolbar.addAction(clear) self.setStatusBar(QStatusBar(self))
def createToolBar(self): """ Criar barra de ferramentas """ tool_bar = QToolBar("Barra de Ferramentas") tool_bar.setIconSize(QSize(16, 16)) self.addToolBar(tool_bar) tool_bar.addAction(self.sair_act)
def __init__(self): super().__init__() self.setWindowTitle("My App") label = QLabel("Hello!") # The `Qt` namespace has a lot of attributes to customize # widgets. See: http://doc.qt.io/qt-5/qt.html label.setAlignment(Qt.AlignCenter) # Set the central widget of the Window. Widget will expand # to take up all the space in the window by default. self.setCentralWidget(label) toolbar = QToolBar("My main toolbar") toolbar.setIconSize(QSize(16, 16)) self.addToolBar(toolbar) button_action = QAction(QIcon("bug.png"), "&Your button", self) button_action.setStatusTip("This is your button") button_action.triggered.connect(self.onMyToolBarButtonClick) button_action.setCheckable(True) # You can enter keyboard shortcuts using key names (e.g. Ctrl+p) # Qt.namespace identifiers (e.g. Qt.CTRL + Qt.Key_P) # or system agnostic identifiers (e.g. QKeySequence.Print) button_action.setShortcut(QKeySequence("Ctrl+p")) toolbar.addAction(button_action) toolbar.addSeparator() button_action2 = QAction(QIcon("bug.png"), "Your &button2", self) button_action2.setStatusTip("This is your button2") button_action2.triggered.connect(self.onMyToolBarButtonClick) button_action2.setCheckable(True) toolbar.addAction(button_action) toolbar.addWidget(QLabel("Hello")) toolbar.addWidget(QCheckBox()) self.setStatusBar(QStatusBar(self)) menu = self.menuBar() file_menu = menu.addMenu("&File") file_menu.addAction(button_action) file_menu.addSeparator() file_submenu = file_menu.addMenu("Submenu") file_submenu.addAction(button_action2)
def _construct_layout(self): """ Construct the widget layout for the exchanges parameters tab """ layout = QVBoxLayout() row = QToolBar() row.addWidget(header("Complete parameters overview ")) row.setIconSize(QSize(24, 24)) row.addAction(qicons.question, "About parameters overview", self.explanation) layout.addWidget(row) layout.addWidget(horizontal_line()) layout.addWidget(self.table, 2) layout.addStretch(1) self.setLayout(layout)
def createToolBar(self): #Ok """ Criar barra de ferramentas para editor de fotos """ tool_bar = QToolBar("Barra de Ferramentas do Editor de Fotos") tool_bar.setIconSize(QSize(24, 24)) self.addToolBar(tool_bar) tool_bar.addAction(self.abre_act) tool_bar.addAction(self.salv_act) tool_bar.addAction(self.prnt_act) tool_bar.addAction(self.limp_act) tool_bar.addSeparator() tool_bar.addAction(self.sair_act)
def qtoolbar(self): if self._cached is not None: return self._cached toolbar = QToolBar(self.name, self.window) for action in self.actions: act = self._translate_element(toolbar, action) if act is not None: self._cached_actions.append(act) toolbar.setIconSize(QSize(16, 16)) self._cached = toolbar return toolbar
def __init__(self, *args, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) self.available_cameras = QCameraInfo.availableCameras() if not self.available_cameras: pass #quit self.status = QStatusBar() self.setStatusBar(self.status) self.save_path = "" self.viewfinder = QCameraViewfinder() self.viewfinder.show() self.setCentralWidget(self.viewfinder) # Set the default camera. self.select_camera(0) # Setup tools camera_toolbar = QToolBar("Camera") camera_toolbar.setIconSize(QSize(14, 14)) self.addToolBar(camera_toolbar) photo_action = QAction(QIcon(os.path.join('images', 'camera-black.png')), "Take photo...", self) photo_action.setStatusTip("Take photo of current view") photo_action.triggered.connect(self.take_photo) camera_toolbar.addAction(photo_action) change_folder_action = QAction(QIcon(os.path.join('images', 'blue-folder-horizontal-open.png')), "Change save location...", self) change_folder_action.setStatusTip("Change folder where photos are saved.") change_folder_action.triggered.connect(self.change_folder) camera_toolbar.addAction(change_folder_action) camera_selector = QComboBox() camera_selector.addItems([c.description() for c in self.available_cameras]) camera_selector.currentIndexChanged.connect( self.select_camera ) camera_toolbar.addWidget(camera_selector) self.setWindowTitle("NSAViewer") self.show()
def qtoolbar(self) -> QToolBar: tb = QToolBar(self.window) if self._small_icon: sm_ico_pm = tb.style().pixelMetric(QStyle.PM_SmallIconSize, None, tb) tb.setIconSize(QSize(sm_ico_pm, sm_ico_pm)) back_btn = NavToolButton(self._jump_history, False, tb) back_btn.triggered.connect(self._back_triggered) back_btn.triggeredFromMenu.connect(self._point_triggered) tb.addWidget(back_btn) fwd_btn = NavToolButton(self._jump_history, True, tb) fwd_btn.triggered.connect(self._forward_triggered) fwd_btn.triggeredFromMenu.connect(self._point_triggered) tb.addWidget(fwd_btn) return tb
class MainWindow(QWidget): def __init__(self, parent=None): super().__init__(parent) self.setWindowTitle("AxiGUI") self._config_dialog = ConfigDialog() self._plot_control = PlotControlWidget() # setup toolbar self._toolbar = QToolBar() self._toolbar.setIconSize(QSize(64, 64)) load_act = self._toolbar.addAction(QIcon("images/icons_open.png"), "Load") load_act.triggered.connect(lambda: self.load_svg()) config_act = self._toolbar.addAction( QIcon("images/icons_settings.png"), "Config") config_act.triggered.connect(lambda: self._config_dialog.exec_()) self._toolbar.addSeparator() self._plot_control.add_actions(self._toolbar) empty = QWidget() empty.setSizePolicy( QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)) self._toolbar.addWidget(empty) quit_act = self._toolbar.addAction(QIcon("images/icons_exit.png"), "Quit") quit_act.triggered.connect(lambda: QCoreApplication.quit()) # setup layout layout = QVBoxLayout() layout.setSpacing(0) layout.setMargin(0) layout.addWidget(self._toolbar) layout.addWidget(self._plot_control) self.setLayout(layout) def load_svg(self): # TODO: remember last folder opened in the session path = QFileDialog.getOpenFileName(self, "Choose SVG file:", "/Users/hhip/Drive/axidraw", "SVG (*.svg)") if path[0]: self._plot_control.load_svg(path[0])
def qtoolbar(self): toolbar = QToolBar(self.name, self.window) for action in self.actions: if isinstance(action, ToolbarSplitter): toolbar.addSeparator() else: if action.icon is not None: act = QAction(action.icon, action.name, toolbar) else: act = QAction(action.name, toolbar) if action.triggered is not None: act.triggered.connect(action.triggered) if action.tooltip: act.setToolTip(action.tooltip) toolbar.addAction(act) toolbar.setIconSize(QSize(16, 16)) return toolbar
def __init__(self): super().__init__() widget = QWidget() self.setCentralWidget(widget) toolbar = QToolBar("툴바") self.addToolBar(toolbar) action_text = QAction("텍스트", self) toolbar.addAction(action_text) action_icon = QAction(QIcon("icon.png"), "아이콘", self) toolbar.addAction(action_icon) toolbar.setIconSize(QSize(16, 16)) statusbar = QStatusBar(self) self.setStatusBar(statusbar) action_text.setStatusTip("액션_텍스트") action_icon.setStatusTip("액션_아이콘") action_icon.triggered.connect(self.on_click) action_icon.setCheckable(True)
def __init__(self, *args, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) self.browser = QWebEngineView() self.browser.setUrl(QUrl("http://google.com")) self.browser.urlChanged.connect(self.update_urlbar) self.browser.loadFinished.connect(self.update_title) self.setCentralWidget(self.browser) self.status = QStatusBar() self.setStatusBar(self.status) navtb = QToolBar("Navigation") navtb.setIconSize(QSize(16, 16)) self.addToolBar(navtb) back_btn = QAction(QIcon(os.path.join('images', 'arrow-180.png')), "Back", self) back_btn.setStatusTip("Back to previous page") back_btn.triggered.connect(self.browser.back) navtb.addAction(back_btn) next_btn = QAction(QIcon(os.path.join('images', 'arrow-000.png')), "Forward", self) next_btn.setStatusTip("Forward to next page") next_btn.triggered.connect(self.browser.forward) navtb.addAction(next_btn) reload_btn = QAction( QIcon(os.path.join('images', 'arrow-circle-315.png')), "Reload", self) reload_btn.setStatusTip("Reload page") reload_btn.triggered.connect(self.browser.reload) navtb.addAction(reload_btn) home_btn = QAction(QIcon(os.path.join('images', 'home.png')), "Home", self) home_btn.setStatusTip("Go home") home_btn.triggered.connect(self.navigate_home) navtb.addAction(home_btn) navtb.addSeparator() self.httpsicon = QLabel() # Yes, really! self.httpsicon.setPixmap( QPixmap(os.path.join('images', 'lock-nossl.png'))) navtb.addWidget(self.httpsicon) self.urlbar = QLineEdit() self.urlbar.returnPressed.connect(self.navigate_to_url) navtb.addWidget(self.urlbar) stop_btn = QAction(QIcon(os.path.join('images', 'cross-circle.png')), "Stop", self) stop_btn.setStatusTip("Stop loading current page") stop_btn.triggered.connect(self.browser.stop) navtb.addAction(stop_btn) # Uncomment to disable native menubar on Mac # self.menuBar().setNativeMenuBar(False) file_menu = self.menuBar().addMenu("&File") open_file_action = QAction( QIcon(os.path.join('images', 'disk--arrow.png')), "Open file...", self) open_file_action.setStatusTip("Open from file") open_file_action.triggered.connect(self.open_file) file_menu.addAction(open_file_action) save_file_action = QAction( QIcon(os.path.join('images', 'disk--pencil.png')), "Save Page As...", self) save_file_action.setStatusTip("Save current page to file") save_file_action.triggered.connect(self.save_file) file_menu.addAction(save_file_action) print_action = QAction(QIcon(os.path.join('images', 'printer.png')), "Print...", self) print_action.setStatusTip("Print current page") print_action.triggered.connect(self.print_page) file_menu.addAction(print_action) help_menu = self.menuBar().addMenu("&Help") about_action = QAction(QIcon(os.path.join('images', 'question.png')), "About MooseAche", self) about_action.setStatusTip("Find out more about MooseAche") # Hungry! about_action.triggered.connect(self.about) help_menu.addAction(about_action) navigate_mozarella_action = QAction( QIcon(os.path.join('images', 'lifebuoy.png')), "MooseAche Homepage", self) navigate_mozarella_action.setStatusTip("Go to MooseAche Homepage") navigate_mozarella_action.triggered.connect(self.navigate_mozarella) help_menu.addAction(navigate_mozarella_action) self.show() self.setWindowIcon(QIcon(os.path.join('images', 'ma-icon-64.png')))
def __init__(self): super().__init__() # tag::tabWidget[] self.tabs = QTabWidget() self.tabs.setDocumentMode(True) self.tabs.tabBarDoubleClicked.connect(self.tab_open_doubleclick) self.tabs.currentChanged.connect(self.current_tab_changed) self.tabs.setTabsClosable(True) self.tabs.tabCloseRequested.connect(self.close_current_tab) self.setCentralWidget(self.tabs) # end::tabWidget[] navtb = QToolBar("Navigation") navtb.setIconSize(QSize(16, 16)) self.addToolBar(navtb) back_btn = QAction(QIcon(os.path.join("icons", "arrow-180.png")), "Back", self) back_btn.setStatusTip("Back to previous page") back_btn.triggered.connect(lambda: self.tabs.currentWidget().back()) navtb.addAction(back_btn) next_btn = QAction(QIcon(os.path.join("icons", "arrow-000.png")), "Forward", self) next_btn.setStatusTip("Forward to next page") next_btn.triggered.connect(lambda: self.tabs.currentWidget().forward()) navtb.addAction(next_btn) reload_btn = QAction( QIcon(os.path.join("icons", "arrow-circle-315.png")), "Reload", self) reload_btn.setStatusTip("Reload page") reload_btn.triggered.connect( lambda: self.tabs.currentWidget().reload()) navtb.addAction(reload_btn) home_btn = QAction(QIcon(os.path.join("icons", "home.png")), "Home", self) home_btn.setStatusTip("Go home") home_btn.triggered.connect(self.navigate_home) navtb.addAction(home_btn) navtb.addSeparator() self.httpsicon = QLabel() # Yes, really! self.httpsicon.setPixmap( QPixmap(os.path.join("icons", "lock-nossl.png"))) navtb.addWidget(self.httpsicon) self.urlbar = QLineEdit() self.urlbar.returnPressed.connect(self.navigate_to_url) navtb.addWidget(self.urlbar) stop_btn = QAction(QIcon(os.path.join("icons", "cross-circle.png")), "Stop", self) stop_btn.setStatusTip("Stop loading current page") stop_btn.triggered.connect(lambda: self.tabs.currentWidget().stop()) navtb.addAction(stop_btn) self.menuBar().setNativeMenuBar(False) self.statusBar() file_menu = self.menuBar().addMenu("&File") new_tab_action = QAction( QIcon(os.path.join("icons", "ui-tab--plus.png")), "New Tab", self) new_tab_action.setStatusTip("Open a new tab") new_tab_action.triggered.connect(lambda _: self.add_new_tab()) file_menu.addAction(new_tab_action) open_file_action = QAction( QIcon(os.path.join("icons", "disk--arrow.png")), "Open file...", self) open_file_action.setStatusTip("Open from file") open_file_action.triggered.connect(self.open_file) file_menu.addAction(open_file_action) save_file_action = QAction( QIcon(os.path.join("icons", "disk--pencil.png")), "Save Page As...", self) save_file_action.setStatusTip("Save current page to file") save_file_action.triggered.connect(self.save_file) file_menu.addAction(save_file_action) print_action = QAction(QIcon(os.path.join("icons", "printer.png")), "Print...", self) print_action.setStatusTip("Print current page") print_action.triggered.connect(self.print_page) file_menu.addAction(print_action) # Create our system printer instance. self.printer = QPrinter() help_menu = self.menuBar().addMenu("&Help") about_action = QAction( QIcon(os.path.join("icons", "question.png")), "About Mozzarella Ashbadger", self, ) about_action.setStatusTip( "Find out more about Mozzarella Ashbadger") # Hungry! about_action.triggered.connect(self.about) help_menu.addAction(about_action) navigate_mozarella_action = QAction( QIcon(os.path.join("icons", "lifebuoy.png")), "Mozzarella Ashbadger Homepage", self, ) navigate_mozarella_action.setStatusTip( "Go to Mozzarella Ashbadger Homepage") navigate_mozarella_action.triggered.connect(self.navigate_mozarella) help_menu.addAction(navigate_mozarella_action) self.add_new_tab(QUrl("http://www.google.com"), "Homepage") self.show() self.setWindowTitle("Mozzarella Ashbadger") self.setWindowIcon(QIcon(os.path.join("icons", "ma-icon-64.png")))
class QtViewer(QWidget): """Full featured, stand-alone viewer suitable for displaying a :class:`vpype.Document` to a user.""" def __init__( self, document: Optional[vp.Document] = None, view_mode: ViewMode = ViewMode.PREVIEW, show_pen_up: bool = False, show_points: bool = False, parent=None, ): super().__init__(parent) self._settings = QSettings() self._settings.beginGroup("viewer") self.setWindowTitle("vpype viewer") self.setStyleSheet(""" QToolButton:pressed { background-color: rgba(0, 0, 0, 0.2); } """) self._viewer_widget = QtViewerWidget(parent=self) # setup toolbar self._toolbar = QToolBar() self._icon_size = QSize(32, 32) self._toolbar.setIconSize(self._icon_size) view_mode_grp = QActionGroup(self._toolbar) if _DEBUG_ENABLED: act = view_mode_grp.addAction("None") act.setCheckable(True) act.setChecked(view_mode == ViewMode.NONE) act.triggered.connect( functools.partial(self.set_view_mode, ViewMode.NONE)) act = view_mode_grp.addAction("Outline Mode") act.setCheckable(True) act.setChecked(view_mode == ViewMode.OUTLINE) act.triggered.connect( functools.partial(self.set_view_mode, ViewMode.OUTLINE)) act = view_mode_grp.addAction("Outline Mode (Colorful)") act.setCheckable(True) act.setChecked(view_mode == ViewMode.OUTLINE_COLORFUL) act.triggered.connect( functools.partial(self.set_view_mode, ViewMode.OUTLINE_COLORFUL)) act = view_mode_grp.addAction("Preview Mode") act.setCheckable(True) act.setChecked(view_mode == ViewMode.PREVIEW) act.triggered.connect( functools.partial(self.set_view_mode, ViewMode.PREVIEW)) self.set_view_mode(view_mode) # VIEW MODE # view modes view_mode_btn = QToolButton() view_mode_menu = QMenu(view_mode_btn) act = view_mode_menu.addAction("View Mode:") act.setEnabled(False) view_mode_menu.addActions(view_mode_grp.actions()) view_mode_menu.addSeparator() # show pen up act = view_mode_menu.addAction("Show Pen-Up Trajectories") act.setCheckable(True) act.setChecked(show_pen_up) act.toggled.connect(self.set_show_pen_up) self._viewer_widget.engine.show_pen_up = show_pen_up # show points act = view_mode_menu.addAction("Show Points") act.setCheckable(True) act.setChecked(show_points) act.toggled.connect(self.set_show_points) self._viewer_widget.engine.show_points = show_points # preview mode options view_mode_menu.addSeparator() act = view_mode_menu.addAction("Preview Mode Options:") act.setEnabled(False) # pen width pen_width_menu = view_mode_menu.addMenu("Pen Width") act_grp = PenWidthActionGroup(0.3, parent=pen_width_menu) act_grp.triggered.connect(self.set_pen_width_mm) pen_width_menu.addActions(act_grp.actions()) self.set_pen_width_mm(0.3) # pen opacity pen_opacity_menu = view_mode_menu.addMenu("Pen Opacity") act_grp = PenOpacityActionGroup(0.8, parent=pen_opacity_menu) act_grp.triggered.connect(self.set_pen_opacity) pen_opacity_menu.addActions(act_grp.actions()) self.set_pen_opacity(0.8) # debug view if _DEBUG_ENABLED: act = view_mode_menu.addAction("Debug View") act.setCheckable(True) act.toggled.connect(self.set_debug) # rulers view_mode_menu.addSeparator() act = view_mode_menu.addAction("Show Rulers") act.setCheckable(True) val = bool(self._settings.value("show_rulers", True)) act.setChecked(val) act.toggled.connect(self.set_show_rulers) self._viewer_widget.engine.show_rulers = val # units units_menu = view_mode_menu.addMenu("Units") unit_action_grp = QActionGroup(units_menu) unit_type = UnitType(self._settings.value("unit_type", UnitType.METRIC)) act = unit_action_grp.addAction("Metric") act.setCheckable(True) act.setChecked(unit_type == UnitType.METRIC) act.setData(UnitType.METRIC) act = unit_action_grp.addAction("Imperial") act.setCheckable(True) act.setChecked(unit_type == UnitType.IMPERIAL) act.setData(UnitType.IMPERIAL) act = unit_action_grp.addAction("Pixel") act.setCheckable(True) act.setChecked(unit_type == UnitType.PIXELS) act.setData(UnitType.PIXELS) unit_action_grp.triggered.connect(self.set_unit_type) units_menu.addActions(unit_action_grp.actions()) self._viewer_widget.engine.unit_type = unit_type view_mode_btn.setMenu(view_mode_menu) view_mode_btn.setIcon(load_icon("eye-outline.svg")) view_mode_btn.setText("View") view_mode_btn.setPopupMode(QToolButton.InstantPopup) view_mode_btn.setStyleSheet( "QToolButton::menu-indicator { image: none; }") self._toolbar.addWidget(view_mode_btn) # LAYER VISIBILITY self._layer_visibility_btn = QToolButton() self._layer_visibility_btn.setIcon( load_icon("layers-triple-outline.svg")) self._layer_visibility_btn.setText("Layer") self._layer_visibility_btn.setMenu(QMenu(self._layer_visibility_btn)) self._layer_visibility_btn.setPopupMode(QToolButton.InstantPopup) self._layer_visibility_btn.setStyleSheet( "QToolButton::menu-indicator { image: none; }") self._toolbar.addWidget(self._layer_visibility_btn) # FIT TO PAGE fit_act = self._toolbar.addAction(load_icon("fit-to-page-outline.svg"), "Fit") fit_act.triggered.connect(self._viewer_widget.engine.fit_to_viewport) # RULER # TODO: not implemented yet # self._toolbar.addAction(load_icon("ruler-square.svg"), "Units") # MOUSE COORDINATES> self._mouse_coord_lbl = QLabel("") self._mouse_coord_lbl.setMargin(6) self._mouse_coord_lbl.setAlignment(Qt.AlignVCenter | Qt.AlignRight) self._mouse_coord_lbl.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum) self._toolbar.addWidget(self._mouse_coord_lbl) # noinspection PyUnresolvedReferences self._viewer_widget.mouse_coords.connect( self.set_mouse_coords) # type: ignore # setup horizontal layout for optional side widgets self._hlayout = QHBoxLayout() self._hlayout.setSpacing(0) self._hlayout.setMargin(0) self._hlayout.addWidget(self._viewer_widget) widget = QWidget() widget.setLayout(self._hlayout) # setup global vertical layout layout = QVBoxLayout() layout.setSpacing(0) layout.setMargin(0) layout.addWidget(self._toolbar) layout.addWidget(widget) self.setLayout(layout) if document is not None: self.set_document(document) def add_side_widget(self, widget: QWidget) -> None: self._hlayout.addWidget(widget) def set_document(self, document: Optional[vp.Document]) -> None: self._viewer_widget.set_document(document) self._update_layer_menu() def _update_layer_menu(self): layer_menu = QMenu(self._layer_visibility_btn) doc = self._viewer_widget.document() if doc is not None: for layer_id in sorted(doc.layers): action = layer_menu.addAction(f"Layer {layer_id}") action.setCheckable(True) action.setChecked(True) # TODO: set color icon action.triggered.connect( functools.partial( self._viewer_widget.engine.toggle_layer_visibility, layer_id)) self._layer_visibility_btn.setMenu(layer_menu) def set_mouse_coords(self, txt: str) -> None: self._mouse_coord_lbl.setText(txt) def set_view_mode(self, mode: ViewMode) -> None: self._viewer_widget.engine.view_mode = mode def set_show_pen_up(self, show_pen_up: bool) -> None: self._viewer_widget.engine.show_pen_up = show_pen_up def set_show_points(self, show_points: bool) -> None: self._viewer_widget.engine.show_points = show_points def set_pen_width_mm(self, value: Union[float, QAction]) -> None: if isinstance(value, QAction): value = value.data() self._viewer_widget.engine.pen_width = value / 25.4 * 96.0 def set_pen_opacity(self, value: Union[float, QAction]) -> None: if isinstance(value, QAction): value = value.data() self._viewer_widget.engine.pen_opacity = value def set_debug(self, debug: bool) -> None: self._viewer_widget.engine.debug = debug def set_show_rulers(self, show_rulers: bool) -> None: self._viewer_widget.engine.show_rulers = show_rulers self._settings.setValue("show_rulers", show_rulers) def set_unit_type(self, sender: QAction) -> None: val = UnitType(sender.data()) self._viewer_widget.engine.unit_type = val self._settings.setValue("unit_type", val)
def __init__(self, *args, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) layout = QVBoxLayout() self.editor = TextEdit() # Setup the QTextEdit editor configuration self.editor.setAutoFormatting(QTextEdit.AutoAll) self.editor.selectionChanged.connect(self.update_format) # Initialize default font size. font = QFont('Times', 12) self.editor.setFont(font) # We need to repeat the size to init the current format. self.editor.setFontPointSize(12) # self.path holds the path of the currently open file. # If none, we haven't got a file open yet (or creating new). self.path = None layout.addWidget(self.editor) container = QWidget() container.setLayout(layout) self.setCentralWidget(container) self.status = QStatusBar() self.setStatusBar(self.status) # Uncomment to disable native menubar on Mac # self.menuBar().setNativeMenuBar(False) file_toolbar = QToolBar("File") file_toolbar.setIconSize(QSize(14, 14)) self.addToolBar(file_toolbar) file_menu = self.menuBar().addMenu("&File") open_file_action = QAction( QIcon(os.path.join('images', 'blue-folder-open-document.png')), "Open file...", self) open_file_action.setStatusTip("Open file") open_file_action.triggered.connect(self.file_open) file_menu.addAction(open_file_action) file_toolbar.addAction(open_file_action) save_file_action = QAction(QIcon(os.path.join('images', 'disk.png')), "Save", self) save_file_action.setStatusTip("Save current page") save_file_action.triggered.connect(self.file_save) file_menu.addAction(save_file_action) file_toolbar.addAction(save_file_action) saveas_file_action = QAction( QIcon(os.path.join('images', 'disk--pencil.png')), "Save As...", self) saveas_file_action.setStatusTip("Save current page to specified file") saveas_file_action.triggered.connect(self.file_saveas) file_menu.addAction(saveas_file_action) file_toolbar.addAction(saveas_file_action) print_action = QAction(QIcon(os.path.join('images', 'printer.png')), "Print...", self) print_action.setStatusTip("Print current page") print_action.triggered.connect(self.file_print) file_menu.addAction(print_action) file_toolbar.addAction(print_action) edit_toolbar = QToolBar("Edit") edit_toolbar.setIconSize(QSize(16, 16)) self.addToolBar(edit_toolbar) edit_menu = self.menuBar().addMenu("&Edit") undo_action = QAction( QIcon(os.path.join('images', 'arrow-curve-180-left.png')), "Undo", self) undo_action.setStatusTip("Undo last change") undo_action.triggered.connect(self.editor.undo) edit_menu.addAction(undo_action) redo_action = QAction(QIcon(os.path.join('images', 'arrow-curve.png')), "Redo", self) redo_action.setStatusTip("Redo last change") redo_action.triggered.connect(self.editor.redo) edit_toolbar.addAction(redo_action) edit_menu.addAction(redo_action) edit_menu.addSeparator() cut_action = QAction(QIcon(os.path.join('images', 'scissors.png')), "Cut", self) cut_action.setStatusTip("Cut selected text") cut_action.setShortcut(QKeySequence.Cut) cut_action.triggered.connect(self.editor.cut) edit_toolbar.addAction(cut_action) edit_menu.addAction(cut_action) copy_action = QAction( QIcon(os.path.join('images', 'document-copy.png')), "Copy", self) copy_action.setStatusTip("Copy selected text") cut_action.setShortcut(QKeySequence.Copy) copy_action.triggered.connect(self.editor.copy) edit_toolbar.addAction(copy_action) edit_menu.addAction(copy_action) paste_action = QAction( QIcon(os.path.join('images', 'clipboard-paste-document-text.png')), "Paste", self) paste_action.setStatusTip("Paste from clipboard") cut_action.setShortcut(QKeySequence.Paste) paste_action.triggered.connect(self.editor.paste) edit_toolbar.addAction(paste_action) edit_menu.addAction(paste_action) select_action = QAction( QIcon(os.path.join('images', 'selection-input.png')), "Select all", self) select_action.setStatusTip("Select all text") cut_action.setShortcut(QKeySequence.SelectAll) select_action.triggered.connect(self.editor.selectAll) edit_menu.addAction(select_action) edit_menu.addSeparator() wrap_action = QAction( QIcon(os.path.join('images', 'arrow-continue.png')), "Wrap text to window", self) wrap_action.setStatusTip("Toggle wrap text to window") wrap_action.setCheckable(True) wrap_action.setChecked(True) wrap_action.triggered.connect(self.edit_toggle_wrap) edit_menu.addAction(wrap_action) format_toolbar = QToolBar("Format") format_toolbar.setIconSize(QSize(16, 16)) self.addToolBar(format_toolbar) format_menu = self.menuBar().addMenu("&Format") # We need references to these actions/settings to update as selection changes, so attach to self. self.fonts = QFontComboBox() self.fonts.currentFontChanged.connect(self.editor.setCurrentFont) format_toolbar.addWidget(self.fonts) self.fontsize = QComboBox() self.fontsize.addItems([str(s) for s in FONT_SIZES]) # Connect to the signal producing the text of the current selection. Convert the string to float # and set as the pointsize. We could also use the index + retrieve from FONT_SIZES. self.fontsize.currentIndexChanged[str].connect( lambda s: self.editor.setFontPointSize(float(s))) format_toolbar.addWidget(self.fontsize) self.bold_action = QAction( QIcon(os.path.join('images', 'edit-bold.png')), "Bold", self) self.bold_action.setStatusTip("Bold") self.bold_action.setShortcut(QKeySequence.Bold) self.bold_action.setCheckable(True) self.bold_action.toggled.connect(lambda x: self.editor.setFontWeight( QFont.Bold if x else QFont.Normal)) format_toolbar.addAction(self.bold_action) format_menu.addAction(self.bold_action) self.italic_action = QAction( QIcon(os.path.join('images', 'edit-italic.png')), "Italic", self) self.italic_action.setStatusTip("Italic") self.italic_action.setShortcut(QKeySequence.Italic) self.italic_action.setCheckable(True) self.italic_action.toggled.connect(self.editor.setFontItalic) format_toolbar.addAction(self.italic_action) format_menu.addAction(self.italic_action) self.underline_action = QAction( QIcon(os.path.join('images', 'edit-underline.png')), "Underline", self) self.underline_action.setStatusTip("Underline") self.underline_action.setShortcut(QKeySequence.Underline) self.underline_action.setCheckable(True) self.underline_action.toggled.connect(self.editor.setFontUnderline) format_toolbar.addAction(self.underline_action) format_menu.addAction(self.underline_action) format_menu.addSeparator() self.alignl_action = QAction( QIcon(os.path.join('images', 'edit-alignment.png')), "Align left", self) self.alignl_action.setStatusTip("Align text left") self.alignl_action.setCheckable(True) self.alignl_action.triggered.connect( lambda: self.editor.setAlignment(Qt.AlignLeft)) format_toolbar.addAction(self.alignl_action) format_menu.addAction(self.alignl_action) self.alignc_action = QAction( QIcon(os.path.join('images', 'edit-alignment-center.png')), "Align center", self) self.alignc_action.setStatusTip("Align text center") self.alignc_action.setCheckable(True) self.alignc_action.triggered.connect( lambda: self.editor.setAlignment(Qt.AlignCenter)) format_toolbar.addAction(self.alignc_action) format_menu.addAction(self.alignc_action) self.alignr_action = QAction( QIcon(os.path.join('images', 'edit-alignment-right.png')), "Align right", self) self.alignr_action.setStatusTip("Align text right") self.alignr_action.setCheckable(True) self.alignr_action.triggered.connect( lambda: self.editor.setAlignment(Qt.AlignRight)) format_toolbar.addAction(self.alignr_action) format_menu.addAction(self.alignr_action) self.alignj_action = QAction( QIcon(os.path.join('images', 'edit-alignment-justify.png')), "Justify", self) self.alignj_action.setStatusTip("Justify text") self.alignj_action.setCheckable(True) self.alignj_action.triggered.connect( lambda: self.editor.setAlignment(Qt.AlignJustify)) format_toolbar.addAction(self.alignj_action) format_menu.addAction(self.alignj_action) format_group = QActionGroup(self) format_group.setExclusive(True) format_group.addAction(self.alignl_action) format_group.addAction(self.alignc_action) format_group.addAction(self.alignr_action) format_group.addAction(self.alignj_action) format_menu.addSeparator() # A list of all format-related widgets/actions, so we can disable/enable signals when updating. self._format_actions = [ self.fonts, self.fontsize, self.bold_action, self.italic_action, self.underline_action, # We don't need to disable signals for alignment, as they are paragraph-wide. ] # Initialize. self.update_format() self.update_title() self.show()
class WordSetWidget(PluginWidget): """Plugin to show handle word sets from user and gather matching variants as a new selection. """ ENABLE = True def __init__(self, parent=None): super().__init__(parent) self.conn = None self.set_names = list() self.setWindowTitle(self.tr("Word Sets")) self.toolbar = QToolBar() self.view = QListWidget() self.view.setIconSize(QSize(20, 20)) self.view.itemDoubleClicked.connect(self.open_wordset) self.view.setSelectionMode(QAbstractItemView.ExtendedSelection) # setup tool bar self.toolbar.setIconSize(QSize(16, 16)) self.toolbar.addAction(FIcon(0xF0415), self.tr("Add Word set"), self.add_wordset) self.edit_action = self.toolbar.addAction(FIcon(0xF0DC9), self.tr("Edit Word set"), self.open_wordset) self.remove_action = self.toolbar.addAction(FIcon(0xF0A7A), self.tr("Remove Word set"), self.remove_wordset) self.edit_action.setEnabled(False) self.remove_action.setEnabled(False) v_layout = QVBoxLayout() v_layout.setContentsMargins(0, 0, 0, 0) v_layout.setSpacing(0) v_layout.addWidget(self.view) v_layout.addWidget(self.toolbar) self.setLayout(v_layout) # Item selected in view self.view.selectionModel().selectionChanged.connect( self.on_item_selected) def on_item_selected(self, *args): """Enable the remove button when an item is selected""" # Get list of all selected model item indexes if self.view.selectionModel().selectedIndexes(): self.edit_action.setEnabled(True) self.remove_action.setEnabled(True) else: self.edit_action.setEnabled(False) self.remove_action.setEnabled(False) def import_wordset(self, words, wordset_name): """Import given words into a new wordset in database Warnings: There is NO CHECK on manual user's inputs! Except during DB insertion. Args: words(list): List of words to be inserted wordset_name(str): Name of the word set Returns: (boolean): Status of the wordset creation """ # Dump the list in a temporary file _, filename = tempfile.mkstemp() with open(filename, "w") as file: [file.write(word + "\n") for word in words] # Import the content of the temp file in DB result = import_cmd(self.conn, "wordsets", wordset_name, filename) if not result["success"]: LOGGER.error(result) QMessageBox.critical( self, self.tr("Error while importing set"), self.tr("Error while importing set '%s'") % wordset_name, ) os.remove(filename) return result["success"] def add_wordset(self): """Display a window to allow to add/edit/remove word sets The set is then imported in database. """ dialog = WordListDialog() if dialog.exec_() != QDialog.Accepted: return wordset_name = None while not wordset_name: wordset_name, _ = QInputDialog.getText( self, self.tr("Create a new set"), self.tr("Name of the new set:")) if not wordset_name: return if wordset_name in self.set_names: # Name already used QMessageBox.critical( self, self.tr("Error while creating set"), self. tr("Error while creating set '%s'; Name is already used") % wordset_name, ) wordset_name = None # Import & update view self.import_wordset(dialog.model.stringList(), wordset_name) self.populate() def remove_wordset(self): """Delete word set from database""" if len(self.view.selectedItems()) == 0: # if selection is empty return reply = QMessageBox.question( self, self.tr("Drop word set"), self.tr("Are you sure you want to remove the selected set(s)?"), QMessageBox.Yes | QMessageBox.No, ) if reply != QMessageBox.Yes: return # Delete all selected sets for i in self.view.selectedItems(): result = drop_cmd(self.conn, "wordsets", i.text()) if not result["success"]: LOGGER.error(result) QMessageBox.critical( self, self.tr("Error while deleting set"), self.tr("Error while deleting set '%s'") % i.text(), ) self.populate() def open_wordset(self): """Display a window to allow to edit the selected word set The previous set is dropped and the new is then imported in database. """ wordset_name = self.view.currentItem().text() dialog = WordListDialog() # populate dialog dialog.model.setStringList( list(get_words_in_set(self.conn, wordset_name))) if dialog.exec_() == QDialog.Accepted: # Drop previous drop_cmd(self.conn, "wordsets", wordset_name) # Import new self.import_wordset(dialog.model.stringList(), wordset_name) self.populate() def on_open_project(self, conn): """ override """ self.conn = conn self.on_refresh() def on_refresh(self): """ override """ if self.conn: self.populate() def populate(self): """Actualize the list of word sets""" self.view.clear() self.set_names = list() for data in get_wordsets(self.conn): set_name = data["name"] item = QListWidgetItem() item.setText(set_name) item.setIcon(FIcon(0xF0A38)) self.view.addItem(item) self.set_names.append(set_name)
def __init__(self): super().__init__() self.browser = QWebEngineView() self.browser.setUrl(QUrl("http://google.com")) self.setCentralWidget(self.browser) # tag::navigationSignals[] self.browser.urlChanged.connect(self.update_urlbar) self.browser.loadFinished.connect(self.update_title) # end::navigationSignals[] # tag::navigation1[] navtb = QToolBar("Navigation") navtb.setIconSize(QSize(16, 16)) self.addToolBar(navtb) back_btn = QAction(QIcon(os.path.join("icons", "arrow-180.png")), "Back", self) back_btn.setStatusTip("Back to previous page") back_btn.triggered.connect(self.browser.back) navtb.addAction(back_btn) # end::navigation1[] # tag::navigation2[] next_btn = QAction(QIcon(os.path.join("icons", "arrow-000.png")), "Forward", self) next_btn.setStatusTip("Forward to next page") next_btn.triggered.connect(self.browser.forward) navtb.addAction(next_btn) reload_btn = QAction( QIcon(os.path.join("icons", "arrow-circle-315.png")), "Reload", self) reload_btn.setStatusTip("Reload page") reload_btn.triggered.connect(self.browser.reload) navtb.addAction(reload_btn) home_btn = QAction(QIcon(os.path.join("icons", "home.png")), "Home", self) home_btn.setStatusTip("Go home") home_btn.triggered.connect(self.navigate_home) navtb.addAction(home_btn) # end::navigation2[] navtb.addSeparator() # tag::navigation3[] self.httpsicon = QLabel() # Yes, really! self.httpsicon.setPixmap( QPixmap(os.path.join("icons", "lock-nossl.png"))) navtb.addWidget(self.httpsicon) self.urlbar = QLineEdit() self.urlbar.returnPressed.connect(self.navigate_to_url) navtb.addWidget(self.urlbar) stop_btn = QAction(QIcon(os.path.join("icons", "cross-circle.png")), "Stop", self) stop_btn.setStatusTip("Stop loading current page") stop_btn.triggered.connect(self.browser.stop) navtb.addAction(stop_btn) # end::navigation3[] self.menuBar().setNativeMenuBar(False) self.statusBar() # tag::menuFile[] file_menu = self.menuBar().addMenu("&File") open_file_action = QAction( QIcon(os.path.join("icons", "disk--arrow.png")), "Open file...", self) open_file_action.setStatusTip("Open from file") open_file_action.triggered.connect(self.open_file) file_menu.addAction(open_file_action) save_file_action = QAction( QIcon(os.path.join("icons", "disk--pencil.png")), "Save Page As...", self) save_file_action.setStatusTip("Save current page to file") save_file_action.triggered.connect(self.save_file) file_menu.addAction(save_file_action) # end::menuFile[] # tag::menuPrint[] print_action = QAction(QIcon(os.path.join("icons", "printer.png")), "Print...", self) print_action.setStatusTip("Print current page") print_action.triggered.connect(self.print_page) file_menu.addAction(print_action) # Create our system printer instance. self.printer = QPrinter() # end::menuPrint[] # tag::menuHelp[] help_menu = self.menuBar().addMenu("&Help") about_action = QAction( QIcon(os.path.join("icons", "question.png")), "About Mozzarella Ashbadger", self, ) about_action.setStatusTip( "Find out more about Mozzarella Ashbadger") # Hungry! about_action.triggered.connect(self.about) help_menu.addAction(about_action) navigate_mozzarella_action = QAction( QIcon(os.path.join("icons", "lifebuoy.png")), "Mozzarella Ashbadger Homepage", self, ) navigate_mozzarella_action.setStatusTip( "Go to Mozzarella Ashbadger Homepage") navigate_mozzarella_action.triggered.connect(self.navigate_mozzarella) help_menu.addAction(navigate_mozzarella_action) # end::menuHelp[] self.show() self.setWindowIcon(QIcon(os.path.join("icons", "ma-icon-64.png")))
def __init__(self, *args, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) layout = QVBoxLayout() self.editor = QPlainTextEdit( ) # Could also use a QTextEdit and set self.editor.setAcceptRichText(False) # Setup the QTextEdit editor configuration fixedfont = QFontDatabase.systemFont(QFontDatabase.FixedFont) fixedfont.setPointSize(12) self.editor.setFont(fixedfont) # self.path holds the path of the currently open file. # If none, we haven't got a file open yet (or creating new). self.path = None layout.addWidget(self.editor) container = QWidget() container.setLayout(layout) self.setCentralWidget(container) self.status = QStatusBar() self.setStatusBar(self.status) file_toolbar = QToolBar("File") file_toolbar.setIconSize(QSize(14, 14)) self.addToolBar(file_toolbar) file_menu = self.menuBar().addMenu("&File") open_file_action = QAction( QIcon(os.path.join('images', 'blue-folder-open-document.png')), "Open file...", self) open_file_action.setStatusTip("Open file") open_file_action.triggered.connect(self.file_open) file_menu.addAction(open_file_action) file_toolbar.addAction(open_file_action) save_file_action = QAction(QIcon(os.path.join('images', 'disk.png')), "Save", self) save_file_action.setStatusTip("Save current page") save_file_action.triggered.connect(self.file_save) file_menu.addAction(save_file_action) file_toolbar.addAction(save_file_action) saveas_file_action = QAction( QIcon(os.path.join('images', 'disk--pencil.png')), "Save As...", self) saveas_file_action.setStatusTip("Save current page to specified file") saveas_file_action.triggered.connect(self.file_saveas) file_menu.addAction(saveas_file_action) file_toolbar.addAction(saveas_file_action) print_action = QAction(QIcon(os.path.join('images', 'printer.png')), "Print...", self) print_action.setStatusTip("Print current page") print_action.triggered.connect(self.file_print) file_menu.addAction(print_action) file_toolbar.addAction(print_action) edit_toolbar = QToolBar("Edit") edit_toolbar.setIconSize(QSize(16, 16)) self.addToolBar(edit_toolbar) edit_menu = self.menuBar().addMenu("&Edit") undo_action = QAction( QIcon(os.path.join('images', 'arrow-curve-180-left.png')), "Undo", self) undo_action.setStatusTip("Undo last change") undo_action.triggered.connect(self.editor.undo) edit_menu.addAction(undo_action) redo_action = QAction(QIcon(os.path.join('images', 'arrow-curve.png')), "Redo", self) redo_action.setStatusTip("Redo last change") redo_action.triggered.connect(self.editor.redo) edit_toolbar.addAction(redo_action) edit_menu.addAction(redo_action) edit_menu.addSeparator() cut_action = QAction(QIcon(os.path.join('images', 'scissors.png')), "Cut", self) cut_action.setStatusTip("Cut selected text") cut_action.triggered.connect(self.editor.cut) edit_toolbar.addAction(cut_action) edit_menu.addAction(cut_action) copy_action = QAction( QIcon(os.path.join('images', 'document-copy.png')), "Copy", self) copy_action.setStatusTip("Copy selected text") copy_action.triggered.connect(self.editor.copy) edit_toolbar.addAction(copy_action) edit_menu.addAction(copy_action) paste_action = QAction( QIcon(os.path.join('images', 'clipboard-paste-document-text.png')), "Paste", self) paste_action.setStatusTip("Paste from clipboard") paste_action.triggered.connect(self.editor.paste) edit_toolbar.addAction(paste_action) edit_menu.addAction(paste_action) select_action = QAction( QIcon(os.path.join('images', 'selection-input.png')), "Select all", self) select_action.setStatusTip("Select all text") select_action.triggered.connect(self.editor.selectAll) edit_menu.addAction(select_action) edit_menu.addSeparator() wrap_action = QAction( QIcon(os.path.join('images', 'arrow-continue.png')), "Wrap text to window", self) wrap_action.setStatusTip("Toggle wrap text to window") wrap_action.setCheckable(True) wrap_action.setChecked(True) wrap_action.triggered.connect(self.edit_toggle_wrap) edit_menu.addAction(wrap_action) self.update_title() self.show()
def __init__(self, *args, **kwargs): super(Notepad, self).__init__(*args, **kwargs) self.setWindowIcon(QIcon('arti.PNG')) self.setGeometry(200, 100, 700, 400) layout = QVBoxLayout() self.editor = QPlainTextEdit() self.editor.setFont(QFont('Roboto', 12)) # self.path holds the path of the currently open file. # If none, we haven't got a file open yet (or creating new). self.path = None layout.addWidget(self.editor) container = QWidget() container.setLayout(layout) self.setCentralWidget(container) self.status = QStatusBar() self.setStatusBar(self.status) file_toolbar = QToolBar("File") file_toolbar.setIconSize(QSize(14, 14)) self.addToolBar(file_toolbar) file_menu = self.menuBar().addMenu("&File") open_file_action = QAction(QIcon(), "Open file...", self) open_file_action.setStatusTip("Open file") open_file_action.triggered.connect(self.file_open) file_menu.addAction(open_file_action) file_toolbar.addAction(open_file_action) open_file_action.setShortcut(QKeySequence('Ctrl+O')) save_file_action = QAction(QIcon(), "Save", self) save_file_action.setStatusTip("Save current page") save_file_action.triggered.connect(self.file_save) file_menu.addAction(save_file_action) file_toolbar.addAction(save_file_action) save_file_action.setShortcut(QKeySequence('Ctrl+S')) saveas_file_action = QAction(QIcon(), "Save As...", self) saveas_file_action.setStatusTip("Save current page to specified file") saveas_file_action.triggered.connect(self.file_saveas) file_menu.addAction(saveas_file_action) file_toolbar.addAction(saveas_file_action) saveas_file_action.setShortcut(QKeySequence('Ctrl+Shift+S')) print_action = QAction(QIcon(), "Print...", self) print_action.setStatusTip("Print current page") print_action.triggered.connect(self.file_print) file_menu.addAction(print_action) file_toolbar.addAction(print_action) print_action.setShortcut(QKeySequence('Ctrl+P')) edit_toolbar = QToolBar("Edit") edit_toolbar.setIconSize(QSize(16, 16)) self.addToolBar(edit_toolbar) edit_menu = self.menuBar().addMenu("&Edit") undo_action = QAction(QIcon(), "Undo", self) undo_action.setStatusTip("Undo last change") undo_action.triggered.connect(self.editor.undo) edit_menu.addAction(undo_action) undo_action.setShortcut(QKeySequence('Ctrl+Z')) redo_action = QAction(QIcon(), "Redo", self) redo_action.setStatusTip("Redo last change") redo_action.triggered.connect(self.editor.redo) edit_toolbar.addAction(redo_action) edit_menu.addAction(redo_action) redo_action.setShortcut(QKeySequence('Ctrl+Y')) edit_menu.addSeparator() cut_action = QAction(QIcon(), "Cut", self) cut_action.setStatusTip("Cut selected text") cut_action.triggered.connect(self.editor.cut) edit_toolbar.addAction(cut_action) edit_menu.addAction(cut_action) cut_action.setShortcut(QKeySequence('Ctrl+X')) copy_action = QAction(QIcon(), "Copy", self) copy_action.setStatusTip("Copy selected text") copy_action.triggered.connect(self.editor.copy) edit_toolbar.addAction(copy_action) edit_menu.addAction(copy_action) copy_action.setShortcut(QKeySequence('Ctrl+C')) paste_action = QAction(QIcon(), "Paste", self) paste_action.setStatusTip("Paste from clipboard") paste_action.triggered.connect(self.editor.paste) edit_toolbar.addAction(paste_action) edit_menu.addAction(paste_action) paste_action.setShortcut(QKeySequence('Ctrl+V')) select_action = QAction(QIcon(), "Select all", self) select_action.setStatusTip("Select all text") select_action.triggered.connect(self.editor.selectAll) edit_menu.addAction(select_action) select_action.setShortcut(QKeySequence('Ctrl+A')) edit_menu.addSeparator() wrap_action = QAction(QIcon(), "Wrap text to window", self) wrap_action.setStatusTip("Toggle wrap text to window") wrap_action.setCheckable(True) wrap_action.setChecked(True) wrap_action.triggered.connect(self.edit_toggle_wrap) edit_menu.addAction(wrap_action) self.update_title() self.show()
class MainWindow(QMainWindow): def __init__(self, path_to_rom=""): super(MainWindow, self).__init__() self.setWindowIcon(icon("foundry.ico")) file_menu = QMenu("File") open_rom_action = file_menu.addAction("&Open ROM") open_rom_action.triggered.connect(self.on_open_rom) self.open_m3l_action = file_menu.addAction("&Open M3L") self.open_m3l_action.triggered.connect(self.on_open_m3l) file_menu.addSeparator() self.save_rom_action = file_menu.addAction("&Save ROM") self.save_rom_action.triggered.connect(self.on_save_rom) self.save_rom_as_action = file_menu.addAction("&Save ROM as ...") self.save_rom_as_action.triggered.connect(self.on_save_rom_as) """ file_menu.AppendSeparator() """ self.save_m3l_action = file_menu.addAction("&Save M3L") self.save_m3l_action.triggered.connect(self.on_save_m3l) """ file_menu.Append(ID_SAVE_LEVEL_TO, "&Save Level to", "") file_menu.AppendSeparator() file_menu.Append(ID_APPLY_IPS_PATCH, "&Apply IPS Patch", "") file_menu.AppendSeparator() file_menu.Append(ID_ROM_PRESET, "&ROM Preset", "") """ file_menu.addSeparator() settings_action = file_menu.addAction("&Settings") settings_action.triggered.connect(show_settings) file_menu.addSeparator() exit_action = file_menu.addAction("&Exit") exit_action.triggered.connect(lambda _: self.close()) self.menuBar().addMenu(file_menu) """ edit_menu = wx.Menu() edit_menu.Append(ID_EDIT_LEVEL, "&Edit Level", "") edit_menu.Append(ID_EDIT_OBJ_DEFS, "&Edit Object Definitions", "") edit_menu.Append(ID_EDIT_PALETTE, "&Edit Palette", "") edit_menu.Append(ID_EDIT_GRAPHICS, "&Edit Graphics", "") edit_menu.Append(ID_EDIT_MISC, "&Edit Miscellaneous", "") edit_menu.AppendSeparator() edit_menu.Append(ID_FREE_FORM_MODE, "&Free form Mode", "") edit_menu.Append(ID_LIMIT_SIZE, "&Limit Size", "") """ self.level_menu = QMenu("Level") self.select_level_action = self.level_menu.addAction("&Select Level") self.select_level_action.triggered.connect(self.open_level_selector) self.reload_action = self.level_menu.addAction("&Reload Level") self.reload_action.triggered.connect(self.reload_level) self.level_menu.addSeparator() self.edit_header_action = self.level_menu.addAction("&Edit Header") self.edit_header_action.triggered.connect(self.on_header_editor) self.edit_autoscroll = self.level_menu.addAction("Edit Autoscrolling") self.edit_autoscroll.triggered.connect(self.on_edit_autoscroll) self.menuBar().addMenu(self.level_menu) self.object_menu = QMenu("Objects") view_blocks_action = self.object_menu.addAction("&View Blocks") view_blocks_action.triggered.connect(self.on_block_viewer) view_objects_action = self.object_menu.addAction("&View Objects") view_objects_action.triggered.connect(self.on_object_viewer) self.object_menu.addSeparator() view_palettes_action = self.object_menu.addAction( "View Object Palettes") view_palettes_action.triggered.connect(self.on_palette_viewer) self.menuBar().addMenu(self.object_menu) self.view_menu = QMenu("View") self.view_menu.triggered.connect(self.on_menu) action = self.view_menu.addAction("Mario") action.setProperty(ID_PROP, ID_MARIO) action.setCheckable(True) action.setChecked(SETTINGS["draw_mario"]) action = self.view_menu.addAction("&Jumps on objects") action.setProperty(ID_PROP, ID_JUMP_OBJECTS) action.setCheckable(True) action.setChecked(SETTINGS["draw_jump_on_objects"]) action = self.view_menu.addAction("Items in blocks") action.setProperty(ID_PROP, ID_ITEM_BLOCKS) action.setCheckable(True) action.setChecked(SETTINGS["draw_items_in_blocks"]) action = self.view_menu.addAction("Invisible items") action.setProperty(ID_PROP, ID_INVISIBLE_ITEMS) action.setCheckable(True) action.setChecked(SETTINGS["draw_invisible_items"]) action = self.view_menu.addAction("Autoscroll Path") action.setProperty(ID_PROP, ID_AUTOSCROLL) action.setCheckable(True) action.setChecked(SETTINGS["draw_autoscroll"]) self.view_menu.addSeparator() action = self.view_menu.addAction("Jump Zones") action.setProperty(ID_PROP, ID_JUMPS) action.setCheckable(True) action.setChecked(SETTINGS["draw_jumps"]) action = self.view_menu.addAction("&Grid lines") action.setProperty(ID_PROP, ID_GRID_LINES) action.setCheckable(True) action.setChecked(SETTINGS["draw_grid"]) action = self.view_menu.addAction("Resize Type") action.setProperty(ID_PROP, ID_RESIZE_TYPE) action.setCheckable(True) action.setChecked(SETTINGS["draw_expansion"]) self.view_menu.addSeparator() action = self.view_menu.addAction("&Block Transparency") action.setProperty(ID_PROP, ID_TRANSPARENCY) action.setCheckable(True) action.setChecked(SETTINGS["block_transparency"]) self.view_menu.addSeparator() self.view_menu.addAction( "&Save Screenshot of Level").triggered.connect(self.on_screenshot) """ self.view_menu.Append(ID_BACKGROUND_FLOOR, "&Background & Floor", "") self.view_menu.Append(ID_TOOLBAR, "&Toolbar", "") self.view_menu.AppendSeparator() self.view_menu.Append(ID_ZOOM, "&Zoom", "") self.view_menu.AppendSeparator() self.view_menu.Append(ID_USE_ROM_GRAPHICS, "&Use ROM Graphics", "") self.view_menu.Append(ID_PALETTE, "&Palette", "") self.view_menu.AppendSeparator() self.view_menu.Append(ID_MORE, "&More", "") """ self.menuBar().addMenu(self.view_menu) help_menu = QMenu("Help") """ help_menu.Append(ID_ENEMY_COMPATIBILITY, "&Enemy Compatibility", "") help_menu.Append(ID_TROUBLESHOOTING, "&Troubleshooting", "") help_menu.AppendSeparator() help_menu.Append(ID_PROGRAM_WEBSITE, "&Program Website", "") help_menu.Append(ID_MAKE_A_DONATION, "&Make a Donation", "") help_menu.AppendSeparator() """ update_action = help_menu.addAction("Check for updates") update_action.triggered.connect(self.on_check_for_update) help_menu.addSeparator() video_action = help_menu.addAction("Feature Video on YouTube") video_action.triggered.connect(lambda: open_url(feature_video_link)) github_action = help_menu.addAction("Github Repository") github_action.triggered.connect(lambda: open_url(github_link)) discord_action = help_menu.addAction("SMB3 Rom Hacking Discord") discord_action.triggered.connect(lambda: open_url(discord_link)) help_menu.addSeparator() about_action = help_menu.addAction("&About") about_action.triggered.connect(self.on_about) self.menuBar().addMenu(help_menu) self.block_viewer = None self.object_viewer = None self.level_ref = LevelRef() self.level_ref.data_changed.connect(self._on_level_data_changed) self.context_menu = ContextMenu(self.level_ref) self.context_menu.triggered.connect(self.on_menu) self.level_view = LevelView(self, self.level_ref, self.context_menu) self.scroll_panel = QScrollArea() self.scroll_panel.setWidgetResizable(True) self.scroll_panel.setWidget(self.level_view) self.setCentralWidget(self.scroll_panel) self.spinner_panel = SpinnerPanel(self, self.level_ref) self.spinner_panel.zoom_in_triggered.connect(self.level_view.zoom_in) self.spinner_panel.zoom_out_triggered.connect(self.level_view.zoom_out) self.spinner_panel.object_change.connect(self.on_spin) self.object_list = ObjectList(self, self.level_ref, self.context_menu) self.object_dropdown = ObjectDropdown(self) self.object_dropdown.object_selected.connect( self._on_placeable_object_selected) self.level_size_bar = LevelSizeBar(self, self.level_ref) self.enemy_size_bar = EnemySizeBar(self, self.level_ref) self.jump_list = JumpList(self, self.level_ref) self.jump_list.add_jump.connect(self.on_jump_added) self.jump_list.edit_jump.connect(self.on_jump_edit) self.jump_list.remove_jump.connect(self.on_jump_removed) splitter = QSplitter(self) splitter.setOrientation(Qt.Vertical) splitter.addWidget(self.object_list) splitter.setStretchFactor(0, 1) splitter.addWidget(self.jump_list) splitter.setChildrenCollapsible(False) level_toolbar = QToolBar("Level Info Toolbar", self) level_toolbar.setContextMenuPolicy(Qt.PreventContextMenu) level_toolbar.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) level_toolbar.setOrientation(Qt.Horizontal) level_toolbar.setFloatable(False) level_toolbar.addWidget(self.spinner_panel) level_toolbar.addWidget(self.object_dropdown) level_toolbar.addWidget(self.level_size_bar) level_toolbar.addWidget(self.enemy_size_bar) level_toolbar.addWidget(splitter) level_toolbar.setAllowedAreas(Qt.LeftToolBarArea | Qt.RightToolBarArea) self.addToolBar(Qt.RightToolBarArea, level_toolbar) self.object_toolbar = ObjectToolBar(self) self.object_toolbar.object_selected.connect( self._on_placeable_object_selected) object_toolbar = QToolBar("Object Toolbar", self) object_toolbar.setContextMenuPolicy(Qt.PreventContextMenu) object_toolbar.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) object_toolbar.setFloatable(False) object_toolbar.addWidget(self.object_toolbar) object_toolbar.setAllowedAreas(Qt.LeftToolBarArea | Qt.RightToolBarArea) self.addToolBar(Qt.LeftToolBarArea, object_toolbar) self.menu_toolbar = QToolBar("Menu Toolbar", self) self.menu_toolbar.setOrientation(Qt.Horizontal) self.menu_toolbar.setIconSize(QSize(20, 20)) self.menu_toolbar.addAction( icon("settings.svg"), "Editor Settings").triggered.connect(show_settings) self.menu_toolbar.addSeparator() self.menu_toolbar.addAction( icon("folder.svg"), "Open ROM").triggered.connect(self.on_open_rom) self.menu_toolbar.addAction( icon("save.svg"), "Save Level").triggered.connect(self.on_save_rom) self.menu_toolbar.addSeparator() self.undo_action = self.menu_toolbar.addAction(icon("rotate-ccw.svg"), "Undo Action") self.undo_action.triggered.connect(self.level_ref.undo) self.undo_action.setEnabled(False) self.redo_action = self.menu_toolbar.addAction(icon("rotate-cw.svg"), "Redo Action") self.redo_action.triggered.connect(self.level_ref.redo) self.redo_action.setEnabled(False) self.menu_toolbar.addSeparator() play_action = self.menu_toolbar.addAction(icon("play-circle.svg"), "Play Level") play_action.triggered.connect(self.on_play) play_action.setWhatsThis( "Opens an emulator with the current Level set to 1-1.\nSee Settings." ) self.menu_toolbar.addSeparator() self.menu_toolbar.addAction(icon("zoom-out.svg"), "Zoom Out").triggered.connect( self.level_view.zoom_out) self.menu_toolbar.addAction(icon("zoom-in.svg"), "Zoom In").triggered.connect( self.level_view.zoom_in) self.menu_toolbar.addSeparator() header_action = self.menu_toolbar.addAction(icon("tool.svg"), "Edit Level Header") header_action.triggered.connect(self.on_header_editor) header_action.setWhatsThis( "<b>Header Editor</b><br/>" "Many configurations regarding the level are done in its header, like the length of " "the timer, or where and how Mario enters the level.<br/>") self.jump_destination_action = self.menu_toolbar.addAction( icon("arrow-right-circle.svg"), "Go to Jump Destination") self.jump_destination_action.triggered.connect( self._go_to_jump_destination) self.jump_destination_action.setWhatsThis( "Opens the level, that can be reached from this one, e.g. by entering a pipe." ) self.menu_toolbar.addSeparator() whats_this_action = QWhatsThis.createAction() whats_this_action.setWhatsThis( "Click on parts of the editor, to receive help information.") whats_this_action.setIcon(icon("help-circle.svg")) whats_this_action.setText("Starts 'What's this?' mode") self.menu_toolbar.addAction(whats_this_action) self.menu_toolbar.addSeparator() self.warning_list = WarningList(self, self.level_ref) warning_action = self.menu_toolbar.addAction( icon("alert-triangle.svg"), "Warning Panel") warning_action.setWhatsThis("Shows a list of warnings.") warning_action.triggered.connect(self.warning_list.show) warning_action.setDisabled(True) self.warning_list.warnings_updated.connect(warning_action.setEnabled) self.addToolBar(Qt.TopToolBarArea, self.menu_toolbar) self.status_bar = ObjectStatusBar(self, self.level_ref) self.setStatusBar(self.status_bar) self.delete_shortcut = QShortcut(QKeySequence(Qt.Key_Delete), self, self.remove_selected_objects) QShortcut(QKeySequence(Qt.CTRL + Qt.Key_X), self, self._cut_objects) QShortcut(QKeySequence(Qt.CTRL + Qt.Key_C), self, self._copy_objects) QShortcut(QKeySequence(Qt.CTRL + Qt.Key_V), self, self._paste_objects) QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Z), self, self.level_ref.undo) QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Y), self, self.level_ref.redo) QShortcut(QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_Z), self, self.level_ref.redo) QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Plus), self, self.level_view.zoom_in) QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Minus), self, self.level_view.zoom_out) QShortcut(QKeySequence(Qt.CTRL + Qt.Key_A), self, self.level_view.select_all) self.on_open_rom(path_to_rom) self.showMaximized() def _on_level_data_changed(self): self.undo_action.setEnabled(self.level_ref.undo_stack.undo_available) self.redo_action.setEnabled(self.level_ref.undo_stack.redo_available) self.jump_destination_action.setEnabled( self.level_ref.level.has_next_area) def _go_to_jump_destination(self): if not self.safe_to_change(): return level_address = self.level_ref.level.next_area_objects enemy_address = self.level_ref.level.next_area_enemies + 1 object_set = self.level_ref.level.next_area_object_set world, level = world_and_level_for_level_address(level_address) self.update_level(f"Level {world}-{level}", level_address, enemy_address, object_set) def on_play(self): """ Copies the ROM, including the current level, to a temporary directory, saves the current level as level 1-1 and opens the rom in an emulator. """ temp_dir = pathlib.Path(tempfile.gettempdir()) / "smb3foundry" temp_dir.mkdir(parents=True, exist_ok=True) path_to_temp_rom = temp_dir / "instaplay.rom" ROM().save_to(path_to_temp_rom) if not self._put_current_level_to_level_1_1(path_to_temp_rom): return if not self._set_default_powerup(path_to_temp_rom): return arguments = SETTINGS["instaplay_arguments"].replace( "%f", str(path_to_temp_rom)) arguments = shlex.split(arguments, posix=False) emu_path = pathlib.Path(SETTINGS["instaplay_emulator"]) if emu_path.is_absolute(): if emu_path.exists(): emulator = str(emu_path) else: QMessageBox.critical( self, "Emulator not found", f"Check it under File > Settings.\nFile {emu_path} not found." ) return else: emulator = SETTINGS["instaplay_emulator"] try: subprocess.run([emulator, *arguments]) except Exception as e: QMessageBox.critical(self, "Emulator command failed.", f"Check it under File > Settings.\n{str(e)}") def _open_rom(self, path_to_rom): with open(path_to_rom, "rb") as smb3_rom: data = smb3_rom.read() rom = SMB3Rom(bytearray(data)) return rom def _put_current_level_to_level_1_1(self, path_to_rom) -> bool: rom = self._open_rom(path_to_rom) # load world-1 data world_1 = SMB3World.from_world_number(rom, 1) # find position of "level 1" tile in world map for position in world_1.gen_positions(): if position.tile() == TILE_LEVEL_1: break else: QMessageBox.critical( self, "Couldn't place level", "Could not find a level 1 tile in World 1 to put your level at." ) return False if not self.level_ref.level.attached_to_rom: QMessageBox.critical( self, "Couldn't place level", "The Level is not part of the rom yet (M3L?). Try saving it into the ROM first.", ) return False # write level and enemy data of current level (layout_address, layout_bytes), (enemy_address, enemy_bytes) = self.level_ref.level.to_bytes() rom.write(layout_address, layout_bytes) rom.write(enemy_address, enemy_bytes) # replace level information with that of current level object_set_number = self.level_ref.object_set_number world_1.replace_level_at_position( (layout_address, enemy_address - 1, object_set_number), position) # save rom rom.save_to(path_to_rom) return True def _set_default_powerup(self, path_to_rom) -> bool: rom = self._open_rom(path_to_rom) *_, powerup, hasPWing = POWERUPS[SETTINGS["default_powerup"]] rom.write(Title_PrepForWorldMap + 0x1, bytes([powerup])) nop = 0xEA rts = 0x60 lda = 0xA9 staAbsolute = 0x8D # If a P-wing powerup is selected, another variable needs to be set with the P-wing value # This piece of code overwrites a part of Title_DebugMenu if hasPWing: Map_Power_DispHigh = 0x03 Map_Power_DispLow = 0xF3 # We need to start one byte before Title_DebugMenu to remove the RTS of Title_PrepForWorldMap # The assembly code below reads as follows: # LDA 0x08 # STA $03F3 # RTS rom.write( Title_DebugMenu - 0x1, bytes([ lda, 0x8, staAbsolute, Map_Power_DispLow, Map_Power_DispHigh, # The RTS to get out of the now extended Title_PrepForWorldMap rts, ]), ) # Remove code that resets the powerup value by replacing it with no-operations # Otherwise this code would copy the value of the normal powerup here # (So if the powerup would be Raccoon Mario, Map_Power_Disp would also be # set as Raccoon Mario instead of P-wing Map_Power_DispResetLocation = 0x3C5A2 rom.write(Map_Power_DispResetLocation, bytes([nop, nop, nop])) rom.save_to(path_to_rom) return True def on_screenshot(self, _) -> bool: if self.level_view is None: return False recommended_file = f"{os.path.expanduser('~')}/{ROM.name} - {self.level_view.level_ref.name}.png" pathname, _ = QFileDialog.getSaveFileName(self, caption="Save Screenshot", dir=recommended_file, filter=IMG_FILE_FILTER) if not pathname: return False # Proceed loading the file chosen by the user self.level_view.make_screenshot().save(pathname) return True def update_title(self): if self.level_view.level_ref is not None and ROM is not None: title = f"{self.level_view.level_ref.name} - {ROM.name}" else: title = "SMB3Foundry" self.setWindowTitle(title) def on_open_rom(self, path_to_rom="") -> bool: if not self.safe_to_change(): return False if not path_to_rom: # otherwise ask the user what new file to open path_to_rom, _ = QFileDialog.getOpenFileName( self, caption="Open ROM", filter=ROM_FILE_FILTER) if not path_to_rom: self._enable_disable_gui_elements() return False # Proceed loading the file chosen by the user try: ROM.load_from_file(path_to_rom) return self.open_level_selector(None) except IOError as exp: QMessageBox.warning(self, type(exp).__name__, f"Cannot open file '{path_to_rom}'.") return False finally: self._enable_disable_gui_elements() def on_open_m3l(self, _) -> bool: if not self.safe_to_change(): return False # otherwise ask the user what new file to open pathname, _ = QFileDialog.getOpenFileName(self, caption="Open M3L file", filter=M3L_FILE_FILTER) if not pathname: return False # Proceed loading the file chosen by the user try: with open(pathname, "rb") as m3l_file: self.level_view.from_m3l(bytearray(m3l_file.read())) except IOError as exp: QMessageBox.warning(self, type(exp).__name__, f"Cannot open file '{pathname}'.") return False self.level_view.level_ref.name = os.path.basename(pathname) self.update_gui_for_level() return True def safe_to_change(self) -> bool: if not self.level_ref: return True if self.level_ref.level.changed: answer = QMessageBox.question( self, "Please confirm", "Current content has not been saved! Proceed?", QMessageBox.No | QMessageBox.Yes, QMessageBox.No, ) return answer == QMessageBox.Yes else: return True def on_save_rom(self, _): self.save_rom(False) def on_save_rom_as(self, _): self.save_rom(True) def save_rom(self, is_save_as): safe_to_save, reason, additional_info = self.level_view.level_safe_to_save( ) if not safe_to_save: answer = QMessageBox.warning( self, reason, f"{additional_info}\n\nDo you want to proceed?", QMessageBox.No | QMessageBox.Yes, QMessageBox.No, ) if answer == QMessageBox.No: return if not self.level_ref.attached_to_rom: QMessageBox.information( self, "Importing M3L into ROM", "Please select the positions in the ROM you want the level objects and enemies/items to be stored.", QMessageBox.Ok, ) level_selector = LevelSelector(self) answer = level_selector.exec_() if answer == QMessageBox.Accepted: self.level_view.level_ref.attach_to_rom( level_selector.object_data_offset, level_selector.enemy_data_offset) if is_save_as: # if we save to another rom, don't consider the level # attached (to the current rom) self.level_view.level_ref.attached_to_rom = False else: return if is_save_as: pathname, _ = QFileDialog.getSaveFileName(self, caption="Save ROM as", filter=ROM_FILE_FILTER) if not pathname: return # the user changed their mind else: pathname = ROM.path level = self.level_ref.level for offset, data in level.to_bytes(): ROM().bulk_write(data, offset) try: ROM().save_to_file(pathname) except IOError as exp: QMessageBox.warning(self, f"{type(exp).__name__}", f"Cannot save ROM data to file '{pathname}'.") self.update_title() if not is_save_as: level.changed = False def on_save_m3l(self, _): suggested_file = self.level_view.level_ref.name if not suggested_file.endswith(".m3l"): suggested_file += ".m3l" pathname, _ = QFileDialog.getSaveFileName(self, caption="Save M3L as", filter=M3L_FILE_FILTER) if not pathname: return level = self.level_view.level_ref try: with open(pathname, "wb") as m3l_file: m3l_file.write(level.to_m3l()) except IOError as exp: QMessageBox.warning(self, type(exp).__name__, f"Couldn't save level to '{pathname}'.") def on_check_for_update(self): self.setCursor(Qt.WaitCursor) current_version = get_current_version_name() try: latest_version = get_latest_version_name() except ValueError as ve: QMessageBox.critical(self, "Error while checking for updates", f"Error: {ve}") return if current_version != latest_version: latest_release_url = f"{releases_link}/tag/{latest_version}" go_to_github_button = QPushButton(icon("external-link.svg"), "Go to latest release") go_to_github_button.clicked.connect( lambda: open_url(latest_release_url)) info_box = QMessageBox( QMessageBox.Information, "New release available", f"New Version {latest_version} is available.") info_box.addButton(QMessageBox.Cancel) info_box.addButton(go_to_github_button, QMessageBox.AcceptRole) info_box.exec_() else: QMessageBox.information( self, "No newer release", f"Version {current_version} is up to date.") self.setCursor(Qt.ArrowCursor) def on_menu(self, action: QAction): item_id = action.property(ID_PROP) if item_id in CHECKABLE_MENU_ITEMS: self.on_menu_item_checked(action) self.level_view.update() # if setting a checkbox, keep the menu open menu_of_action: QMenu = self.sender() menu_of_action.exec_() elif item_id in self.context_menu.get_all_menu_item_ids(): x, y = self.context_menu.get_position() if item_id == CMAction.REMOVE: self.remove_selected_objects() elif item_id == CMAction.ADD_OBJECT: selected_object = self.object_dropdown.currentIndex() if selected_object != -1: self.place_object_from_dropdown((x, y)) else: self.create_object_at(x, y) elif item_id == CMAction.CUT: self._cut_objects() elif item_id == CMAction.COPY: self._copy_objects() elif item_id == CMAction.PASTE: self._paste_objects(x, y) elif item_id == CMAction.FOREGROUND: self.bring_objects_to_foreground() elif item_id == CMAction.BACKGROUND: self.bring_objects_to_background() self.level_view.update() def reload_level(self): if not self.safe_to_change(): return level_name = self.level_view.level_ref.name object_data = self.level_view.level_ref.header_offset enemy_data = self.level_view.level_ref.enemy_offset object_set = self.level_view.level_ref.object_set_number self.update_level(level_name, object_data, enemy_data, object_set) def _on_placeable_object_selected(self, level_object: Union[LevelObject, EnemyObject]): if self.sender() is self.object_toolbar: self.object_dropdown.select_object(level_object) else: self.object_toolbar.select_object(level_object) @undoable def bring_objects_to_foreground(self): self.level_ref.level.bring_to_foreground( self.level_ref.selected_objects) @undoable def bring_objects_to_background(self): self.level_ref.level.bring_to_background( self.level_ref.selected_objects) @undoable def create_object_at(self, x, y): self.level_view.create_object_at(x, y) @undoable def create_enemy_at(self, x, y): self.level_view.create_enemy_at(x, y) def _cut_objects(self): self._copy_objects() self.remove_selected_objects() def _copy_objects(self): selected_objects = self.level_view.get_selected_objects().copy() if selected_objects: self.context_menu.set_copied_objects(selected_objects) @undoable def _paste_objects(self, x=None, y=None): self.level_view.paste_objects_at( self.context_menu.get_copied_objects(), x, y) @undoable def remove_selected_objects(self): self.level_view.remove_selected_objects() self.level_view.update() self.spinner_panel.disable_all() def on_menu_item_checked(self, action: QAction): item_id = action.property(ID_PROP) checked = action.isChecked() if item_id == ID_GRID_LINES: self.level_view.draw_grid = checked elif item_id == ID_TRANSPARENCY: self.level_view.transparency = checked elif item_id == ID_JUMPS: self.level_view.draw_jumps = checked elif item_id == ID_MARIO: self.level_view.draw_mario = checked elif item_id == ID_RESIZE_TYPE: self.level_view.draw_expansions = checked elif item_id == ID_JUMP_OBJECTS: self.level_view.draw_jumps_on_objects = checked elif item_id == ID_ITEM_BLOCKS: self.level_view.draw_items_in_blocks = checked elif item_id == ID_INVISIBLE_ITEMS: self.level_view.draw_invisible_items = checked elif item_id == ID_AUTOSCROLL: self.level_view.draw_autoscroll = checked SETTINGS["draw_mario"] = self.level_view.draw_mario SETTINGS["draw_jumps"] = self.level_view.draw_jumps SETTINGS["draw_grid"] = self.level_view.draw_grid SETTINGS["draw_expansion"] = self.level_view.draw_expansions SETTINGS[ "draw_jump_on_objects"] = self.level_view.draw_jumps_on_objects SETTINGS["draw_items_in_blocks"] = self.level_view.draw_items_in_blocks SETTINGS["draw_invisible_items"] = self.level_view.draw_invisible_items SETTINGS["draw_autoscroll"] = self.level_view.draw_autoscroll SETTINGS["block_transparency"] = self.level_view.transparency save_settings() @undoable def on_spin(self, _): selected_objects = self.level_ref.selected_objects if len(selected_objects) != 1: logging.error(selected_objects, RuntimeWarning) return selected_object = selected_objects[0] obj_type = self.spinner_panel.get_type() if isinstance(selected_object, LevelObject): domain = self.spinner_panel.get_domain() if selected_object.is_4byte: length = self.spinner_panel.get_length() else: length = None self.level_view.replace_object(selected_object, domain, obj_type, length) else: self.level_view.replace_enemy(selected_object, obj_type) self.level_ref.data_changed.emit() def fill_object_list(self): self.object_list.Clear() self.object_list.SetItems(self.level_view.get_object_names()) def open_level_selector(self, _): if not self.safe_to_change(): return level_selector = LevelSelector(self) level_was_selected = level_selector.exec_() == QDialog.Accepted if level_was_selected: self.update_level( level_selector.level_name, level_selector.object_data_offset, level_selector.enemy_data_offset, level_selector.object_set, ) return level_was_selected def on_block_viewer(self, _): if self.block_viewer is None: self.block_viewer = BlockViewer(parent=self) if self.level_ref.level is not None: self.block_viewer.object_set = self.level_ref.object_set.number self.block_viewer.palette_group = self.level_ref.object_palette_index self.block_viewer.show() def on_object_viewer(self, _): if self.object_viewer is None: self.object_viewer = ObjectViewer(parent=self) if self.level_ref.level is not None: object_set = self.level_ref.object_set.number graphics_set = self.level_ref.graphic_set self.object_viewer.set_object_and_graphic_set( object_set, graphics_set) if len(self.level_view.get_selected_objects()) == 1: selected_object = self.level_view.get_selected_objects()[0] if isinstance(selected_object, LevelObject): self.object_viewer.set_object(selected_object.domain, selected_object.obj_index, selected_object.length) self.object_viewer.show() def on_palette_viewer(self, _): PaletteViewer(self, self.level_ref).exec_() def on_edit_autoscroll(self, _): AutoScrollEditor(self, self.level_ref).exec_() def on_header_editor(self, _): HeaderEditor(self, self.level_ref).exec_() def update_level(self, level_name: str, object_data_offset: int, enemy_data_offset: int, object_set: int): try: self.level_ref.load_level(level_name, object_data_offset, enemy_data_offset, object_set) except IndexError: QMessageBox.critical( self, "Please confirm", "Failed loading level. The level offsets don't match.") return self.update_gui_for_level() def update_gui_for_level(self): self._enable_disable_gui_elements() self.update_title() self.jump_list.update() is_a_world_map = isinstance(self.level_ref.level, WorldMap) self.save_m3l_action.setEnabled(not is_a_world_map) self.edit_header_action.setEnabled(not is_a_world_map) if is_a_world_map: self.object_dropdown.Clear() self.object_dropdown.setEnabled(False) self.jump_list.setEnabled(False) self.jump_list.Clear() else: self.object_dropdown.setEnabled(True) self.object_dropdown.set_object_set( self.level_ref.object_set_number, self.level_ref.graphic_set) self.jump_list.setEnabled(True) self.object_toolbar.set_object_set(self.level_ref.object_set_number, self.level_ref.graphic_set) self.level_view.update() def _enable_disable_gui_elements(self): rom_elements = [ # entries in file menu self.open_m3l_action, self.save_rom_action, self.save_rom_as_action, # entry in level menu self.select_level_action, ] level_elements = [ # entry in file menu self.save_m3l_action, # top toolbar self.menu_toolbar, # other gui elements self.level_view, self.spinner_panel, self.object_toolbar, self.level_size_bar, self.enemy_size_bar, self.object_list, self.jump_list, self.object_toolbar, ] level_elements.extend(self.level_menu.actions()) level_elements.remove(self.select_level_action) level_elements.extend(self.object_menu.actions()) level_elements.extend(self.view_menu.actions()) for gui_element in rom_elements: gui_element.setEnabled(ROM.is_loaded()) for gui_element in level_elements: gui_element.setEnabled(ROM.is_loaded() and self.level_ref.level is not None) def on_jump_edit(self): index = self.jump_list.currentIndex().row() updated_jump = JumpEditor.edit_jump( self, self.level_view.level_ref.jumps[index]) self.on_jump_edited(updated_jump) @undoable def on_jump_added(self): self.level_view.add_jump() @undoable def on_jump_removed(self): self.level_view.remove_jump(self.jump_list.currentIndex().row()) @undoable def on_jump_edited(self, jump): index = self.jump_list.currentIndex().row() assert index >= 0 if isinstance(self.level_ref.level, Level): self.level_view.level_ref.jumps[index] = jump self.jump_list.item(index).setText(str(jump)) def on_jump_list_change(self, event): self.jump_list.set_jumps(event) def mouseReleaseEvent(self, event: QMouseEvent): if event.button() == Qt.MiddleButton: pos = self.level_view.mapFromGlobal(self.mapToGlobal( event.pos())).toTuple() self.place_object_from_dropdown(pos) @undoable def place_object_from_dropdown(self, pos: Tuple[int, int]) -> None: # the dropdown is synchronized with the toolbar, so it doesn't matter where to take it from level_object = self.object_dropdown.currentData(Qt.UserRole) self.object_toolbar.add_recent_object(level_object) if isinstance(level_object, LevelObject): self.level_view.create_object_at(*pos, level_object.domain, level_object.obj_index) elif isinstance(level_object, EnemyObject): self.level_view.add_enemy(level_object.obj_index, *pos, -1) def on_about(self, _): about = AboutDialog(self) about.show() def closeEvent(self, event: QCloseEvent): if not self.safe_to_change(): event.ignore() return super(MainWindow, self).closeEvent(event)
def __init__(self): super().__init__(); self.setWindowTitle("Roots") self.setFixedWidth(1200) self.resize(1200, 1200) self.threadpool = QThreadPool(); self.object_list = list() self.is_training_on = False self.interaction_under_training = None self.n_measurements_collected = 0 self.n_measurements_to_collect = 3 self.sensor_not_responding = True self.sensor_not_responding_timeout = 2000 # milliseconds self.database_connection = self.create_temporary_database() self.active_object = None self.number_of_objects_added = 0 self.sensor_start_freq = 250000 self.sensor_end_freq = 3000000 # creates the plot self.plotWidget = pyqtgraph.PlotWidget(title = "Sensor Response") self.plotWidget.setFixedHeight(300) self.plotWidget.getAxis("bottom").setLabel("Excitation frequency", "Hz") self.plotWidget.getAxis("left").setLabel("Volts", "V") self.dataPlot = self.plotWidget.plot() # timer used to see if the sensor is responding self.timer = QTimer() self.timer.setInterval(self.sensor_not_responding_timeout) self.timer.timeout.connect(self.timer_timeout) self.timer_timeout() # defines the actions in the file menu with button actions iconExit = QIcon("icons/icon_exit.png") btnActionExit = QAction(iconExit, "Exit", self) btnActionExit.setStatusTip("Click to terminate the program") btnActionExit.triggered.connect(self.exit) iconSave = QIcon("icons/icon_save.ico") buttonActionSave = QAction(iconSave, "Save current set of objects", self) # buttonActionSave.setStatusTip("Click to perform action 2") buttonActionSave.triggered.connect(self.save) iconOpen = QIcon("icons/icon_load.png") buttonActionOpen = QAction(iconOpen, "Load set of objects", self) buttonActionOpen.triggered.connect(self.open) # toolbar toolBar = QToolBar("Toolbar") toolBar.addAction(buttonActionSave) toolBar.addAction(buttonActionOpen) toolBar.setIconSize(QSize(64, 64)) toolBar.setStyleSheet(styles.toolbar) self.addToolBar(toolBar) # menu menuBar = self.menuBar() menuBar.setStyleSheet(styles.menuBar) menuFile = menuBar.addMenu("File") menuOptions = menuBar.addMenu("Options") menuView = menuBar.addMenu("View") menuConnect = menuBar.addMenu("Connect") menuFile.addAction(buttonActionSave) menuFile.addAction(buttonActionOpen) menuFile.addAction(btnActionExit) # status bar self.setStatusBar(QStatusBar(self)) # creates the "My Objects" label labelMyObjects = QLabel("My Objects") labelMyObjects.setFixedHeight(100) labelMyObjects.setAlignment(Qt.AlignCenter) labelMyObjects.setStyleSheet(styles.labelMyObjects) # button "add object" icon_plus = QIcon("icons/icon_add.png") self.btn_create_object = QPushButton("Add Object") self.btn_create_object.setCheckable(False) self.btn_create_object.setIcon(icon_plus) self.btn_create_object.setFixedHeight(80) self.btn_create_object.setStyleSheet(styles.addObjectButton) self.btn_create_object.clicked.connect(self.create_object) # defines the layout of the "My Objects" section self.verticalLayout = QVBoxLayout() self.verticalLayout.setContentsMargins(0,0,0,0) self.verticalLayout.addWidget(labelMyObjects) self.verticalLayout.addWidget(self.btn_create_object) self.spacer = QSpacerItem(0,2000, QSizePolicy.Expanding, QSizePolicy.Expanding) self.verticalLayout.setSpacing(0) self.verticalLayout.addSpacerItem(self.spacer) #adds spacer # defines the ComboBox which holds the names of the objects self.comboBox = QComboBox() self.comboBox.addItem("- no object selected") self.comboBox.currentIndexChanged.connect(self.comboBox_index_changed) self.comboBox.setFixedSize(300, 40) self.comboBox.setStyleSheet(styles.comboBox) self.update_comboBox() # defines the label "Selected Object" (above the comboBox) self.labelComboBox = QLabel() self.labelComboBox.setText("Selected Object:") self.labelComboBox.setStyleSheet(styles.labelComboBox) self.labelComboBox.adjustSize() # vertical layout for the combobox and its label self.VLayoutComboBox = QVBoxLayout() self.VLayoutComboBox.addWidget(self.labelComboBox) self.VLayoutComboBox.addWidget(self.comboBox) # label with the output text (the big one on the right) self.labelClassification = QLabel() self.labelClassification.setText("No interaction detected") self.labelClassification.setFixedHeight(80) self.labelClassification.setStyleSheet(styles.labelClassification) self.labelClassification.adjustSize() HLayoutComboBox = QHBoxLayout() HLayoutComboBox.addLayout(self.VLayoutComboBox) HLayoutComboBox.addSpacerItem(QSpacerItem(1000,0, QSizePolicy.Expanding, QSizePolicy.Expanding)); #adds spacer HLayoutComboBox.addWidget(self.labelClassification) # creates a frame that contains the combobox and the labels frame = QFrame() frame.setStyleSheet(styles.frame) frame.setLayout(HLayoutComboBox) # sets the window layout with the elements created before self.windowLayout = QVBoxLayout() self.windowLayout.addWidget(self.plotWidget) self.windowLayout.addWidget(frame) self.windowLayout.addLayout(self.verticalLayout) # puts everything into a frame and displays it on the window self.mainWindowFrame = QFrame() self.mainWindowFrame.setLayout(self.windowLayout) self.mainWindowFrame.setStyleSheet(styles.mainWindowFrame) self.setCentralWidget(self.mainWindowFrame) self.create_object() # creates one object at the beginning
def __init__(self, path_to_rom=""): super(MainWindow, self).__init__() self.setWindowIcon(icon("foundry.ico")) file_menu = QMenu("File") open_rom_action = file_menu.addAction("&Open ROM") open_rom_action.triggered.connect(self.on_open_rom) self.open_m3l_action = file_menu.addAction("&Open M3L") self.open_m3l_action.triggered.connect(self.on_open_m3l) file_menu.addSeparator() save_rom_action = file_menu.addAction("&Save ROM") save_rom_action.triggered.connect(self.on_save_rom) save_rom_as_action = file_menu.addAction("&Save ROM as ...") save_rom_as_action.triggered.connect(self.on_save_rom_as) """ file_menu.AppendSeparator() """ self.save_m3l_action = file_menu.addAction("&Save M3L") self.save_m3l_action.triggered.connect(self.on_save_m3l) """ file_menu.Append(ID_SAVE_LEVEL_TO, "&Save Level to", "") file_menu.AppendSeparator() file_menu.Append(ID_APPLY_IPS_PATCH, "&Apply IPS Patch", "") file_menu.AppendSeparator() file_menu.Append(ID_ROM_PRESET, "&ROM Preset", "") """ file_menu.addSeparator() settings_action = file_menu.addAction("&Settings") settings_action.triggered.connect(show_settings) file_menu.addSeparator() exit_action = file_menu.addAction("&Exit") exit_action.triggered.connect(lambda _: self.close()) self.menuBar().addMenu(file_menu) """ edit_menu = wx.Menu() edit_menu.Append(ID_EDIT_LEVEL, "&Edit Level", "") edit_menu.Append(ID_EDIT_OBJ_DEFS, "&Edit Object Definitions", "") edit_menu.Append(ID_EDIT_PALETTE, "&Edit Palette", "") edit_menu.Append(ID_EDIT_GRAPHICS, "&Edit Graphics", "") edit_menu.Append(ID_EDIT_MISC, "&Edit Miscellaneous", "") edit_menu.AppendSeparator() edit_menu.Append(ID_FREE_FORM_MODE, "&Free form Mode", "") edit_menu.Append(ID_LIMIT_SIZE, "&Limit Size", "") """ level_menu = QMenu("Level") select_level_action = level_menu.addAction("&Select Level") select_level_action.triggered.connect(self.open_level_selector) """ level_menu.Append(ID_GOTO_NEXT_AREA, "&Go to next Area", "") level_menu.AppendSeparator() """ self.reload_action = level_menu.addAction("&Reload Level") self.reload_action.triggered.connect(self.reload_level) level_menu.addSeparator() self.edit_header_action = level_menu.addAction("&Edit Header") self.edit_header_action.triggered.connect(self.on_header_editor) """ level_menu.Append(ID_EDIT_POINTERS, "&Edit Pointers", "") """ self.menuBar().addMenu(level_menu) object_menu = QMenu("Objects") view_blocks_action = object_menu.addAction("&View Blocks") view_blocks_action.triggered.connect(self.on_block_viewer) view_objects_action = object_menu.addAction("&View Objects") view_objects_action.triggered.connect(self.on_object_viewer) """ object_menu.AppendSeparator() object_menu.Append(ID_CLONE_OBJECT_ENEMY, "&Clone Object/Enemy", "") object_menu.AppendSeparator() object_menu.Append(ID_ADD_3_BYTE_OBJECT, "&Add 3 Byte Object", "") object_menu.Append(ID_ADD_4_BYTE_OBJECT, "&Add 4 Byte Object", "") object_menu.Append(ID_ADD_ENEMY, "&Add Enemy", "") object_menu.AppendSeparator() object_menu.Append(ID_DELETE_OBJECT_ENEMY, "&Delete Object/Enemy", "") object_menu.Append(ID_DELETE_ALL, "&Delete All", "") """ self.menuBar().addMenu(object_menu) view_menu = QMenu("View") view_menu.triggered.connect(self.on_menu) action = view_menu.addAction("Mario") action.setProperty(ID_PROP, ID_MARIO) action.setCheckable(True) action.setChecked(SETTINGS["draw_mario"]) action = view_menu.addAction("&Jumps on objects") action.setProperty(ID_PROP, ID_JUMP_OBJECTS) action.setCheckable(True) action.setChecked(SETTINGS["draw_jump_on_objects"]) action = view_menu.addAction("Items in blocks") action.setProperty(ID_PROP, ID_ITEM_BLOCKS) action.setCheckable(True) action.setChecked(SETTINGS["draw_items_in_blocks"]) action = view_menu.addAction("Invisible items") action.setProperty(ID_PROP, ID_INVISIBLE_ITEMS) action.setCheckable(True) action.setChecked(SETTINGS["draw_invisible_items"]) view_menu.addSeparator() action = view_menu.addAction("Jump Zones") action.setProperty(ID_PROP, ID_JUMPS) action.setCheckable(True) action.setChecked(SETTINGS["draw_jumps"]) action = view_menu.addAction("&Grid lines") action.setProperty(ID_PROP, ID_GRID_LINES) action.setCheckable(True) action.setChecked(SETTINGS["draw_grid"]) action = view_menu.addAction("Resize Type") action.setProperty(ID_PROP, ID_RESIZE_TYPE) action.setCheckable(True) action.setChecked(SETTINGS["draw_expansion"]) view_menu.addSeparator() action = view_menu.addAction("&Block Transparency") action.setProperty(ID_PROP, ID_TRANSPARENCY) action.setCheckable(True) action.setChecked(SETTINGS["block_transparency"]) view_menu.addSeparator() view_menu.addAction("&Save Screenshot of Level").triggered.connect( self.on_screenshot) """ view_menu.Append(ID_BACKGROUND_FLOOR, "&Background & Floor", "") view_menu.Append(ID_TOOLBAR, "&Toolbar", "") view_menu.AppendSeparator() view_menu.Append(ID_ZOOM, "&Zoom", "") view_menu.AppendSeparator() view_menu.Append(ID_USE_ROM_GRAPHICS, "&Use ROM Graphics", "") view_menu.Append(ID_PALETTE, "&Palette", "") view_menu.AppendSeparator() view_menu.Append(ID_MORE, "&More", "") """ self.menuBar().addMenu(view_menu) help_menu = QMenu("Help") """ help_menu.Append(ID_ENEMY_COMPATIBILITY, "&Enemy Compatibility", "") help_menu.Append(ID_TROUBLESHOOTING, "&Troubleshooting", "") help_menu.AppendSeparator() help_menu.Append(ID_PROGRAM_WEBSITE, "&Program Website", "") help_menu.Append(ID_MAKE_A_DONATION, "&Make a Donation", "") help_menu.AppendSeparator() """ update_action = help_menu.addAction("Check for updates") update_action.triggered.connect(self.on_check_for_update) help_menu.addSeparator() video_action = help_menu.addAction("Feature Video on YouTube") video_action.triggered.connect(lambda: open_url(feature_video_link)) discord_action = help_menu.addAction("SMB3 Rom Hacking Discord") discord_action.triggered.connect(lambda: open_url(discord_link)) help_menu.addSeparator() about_action = help_menu.addAction("&About") about_action.triggered.connect(self.on_about) self.menuBar().addMenu(help_menu) self.level_selector = LevelSelector(parent=self) self.block_viewer = None self.object_viewer = None self.level_ref = LevelRef() self.level_ref.data_changed.connect(self._on_level_data_changed) self.context_menu = ContextMenu(self.level_ref) self.context_menu.triggered.connect(self.on_menu) self.level_view = LevelView(self, self.level_ref, self.context_menu) self.scroll_panel = QScrollArea() self.scroll_panel.setWidgetResizable(True) self.scroll_panel.setWidget(self.level_view) self.setCentralWidget(self.scroll_panel) self.spinner_panel = SpinnerPanel(self, self.level_ref) self.spinner_panel.zoom_in_triggered.connect(self.level_view.zoom_in) self.spinner_panel.zoom_out_triggered.connect(self.level_view.zoom_out) self.spinner_panel.object_change.connect(self.on_spin) self.object_list = ObjectList(self, self.level_ref, self.context_menu) self.object_dropdown = ObjectDropdown(self) self.object_dropdown.object_selected.connect( self._on_placeable_object_selected) self.level_size_bar = LevelSizeBar(self, self.level_ref) self.enemy_size_bar = EnemySizeBar(self, self.level_ref) self.jump_list = JumpList(self, self.level_ref) self.jump_list.add_jump.connect(self.on_jump_added) self.jump_list.edit_jump.connect(self.on_jump_edit) self.jump_list.remove_jump.connect(self.on_jump_removed) splitter = QSplitter(self) splitter.setOrientation(Qt.Vertical) splitter.addWidget(self.object_list) splitter.setStretchFactor(0, 1) splitter.addWidget(self.jump_list) splitter.setChildrenCollapsible(False) level_toolbar = QToolBar("Level Info Toolbar", self) level_toolbar.setContextMenuPolicy(Qt.PreventContextMenu) level_toolbar.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) level_toolbar.setOrientation(Qt.Horizontal) level_toolbar.setFloatable(False) level_toolbar.addWidget(self.spinner_panel) level_toolbar.addWidget(self.object_dropdown) level_toolbar.addWidget(self.level_size_bar) level_toolbar.addWidget(self.enemy_size_bar) level_toolbar.addWidget(splitter) level_toolbar.setAllowedAreas(Qt.LeftToolBarArea | Qt.RightToolBarArea) self.addToolBar(Qt.RightToolBarArea, level_toolbar) self.object_toolbar = ObjectToolBar(self) self.object_toolbar.object_selected.connect( self._on_placeable_object_selected) object_toolbar = QToolBar("Object Toolbar", self) object_toolbar.setContextMenuPolicy(Qt.PreventContextMenu) object_toolbar.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) object_toolbar.setFloatable(False) object_toolbar.addWidget(self.object_toolbar) object_toolbar.setAllowedAreas(Qt.LeftToolBarArea | Qt.RightToolBarArea) self.addToolBar(Qt.LeftToolBarArea, object_toolbar) menu_toolbar = QToolBar("Menu Toolbar", self) menu_toolbar.setOrientation(Qt.Horizontal) menu_toolbar.setIconSize(QSize(20, 20)) menu_toolbar.addAction( icon("settings.svg"), "Editor Settings").triggered.connect(show_settings) menu_toolbar.addSeparator() menu_toolbar.addAction(icon("folder.svg"), "Open ROM").triggered.connect(self.on_open_rom) menu_toolbar.addAction( icon("save.svg"), "Save Level").triggered.connect(self.on_save_rom) menu_toolbar.addSeparator() self.undo_action = menu_toolbar.addAction(icon("rotate-ccw.svg"), "Undo Action") self.undo_action.triggered.connect(self.level_ref.undo) self.undo_action.setEnabled(False) self.redo_action = menu_toolbar.addAction(icon("rotate-cw.svg"), "Redo Action") self.redo_action.triggered.connect(self.level_ref.redo) self.redo_action.setEnabled(False) menu_toolbar.addSeparator() menu_toolbar.addAction(icon("play-circle.svg"), "Play Level").triggered.connect(self.on_play) menu_toolbar.addSeparator() menu_toolbar.addAction(icon("zoom-out.svg"), "Zoom Out").triggered.connect( self.level_view.zoom_out) menu_toolbar.addAction(icon("zoom-in.svg"), "Zoom In").triggered.connect( self.level_view.zoom_in) menu_toolbar.addSeparator() menu_toolbar.addAction(icon("tool.svg"), "Edit Level Header").triggered.connect( self.on_header_editor) self.jump_destination_action = menu_toolbar.addAction( icon("arrow-right-circle.svg"), "Go to Jump Destination") self.jump_destination_action.triggered.connect( self._go_to_jump_destination) menu_toolbar.addSeparator() # menu_toolbar.addAction(icon("help-circle.svg"), "What's this?") self.addToolBar(Qt.TopToolBarArea, menu_toolbar) self.status_bar = ObjectStatusBar(self, self.level_ref) self.setStatusBar(self.status_bar) self.delete_shortcut = QShortcut(QKeySequence(Qt.Key_Delete), self, self.remove_selected_objects) QShortcut(QKeySequence(Qt.CTRL + Qt.Key_X), self, self._cut_objects) QShortcut(QKeySequence(Qt.CTRL + Qt.Key_C), self, self._copy_objects) QShortcut(QKeySequence(Qt.CTRL + Qt.Key_V), self, self._paste_objects) QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Z), self, self.level_ref.undo) QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Y), self, self.level_ref.redo) QShortcut(QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_Z), self, self.level_ref.redo) QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Plus), self, self.level_view.zoom_in) QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Minus), self, self.level_view.zoom_out) QShortcut(QKeySequence(Qt.CTRL + Qt.Key_A), self, self.level_view.select_all) if not self.on_open_rom(path_to_rom): self.deleteLater() self.showMaximized()