def __init__(self, parent=None): super().__init__(parent) self.mTile = None self.mMapDocument = None self.mMapScene = MapScene(self) self.mMapView = MapView(self, MapViewMode.NoStaticContents) self.mToolManager = ToolManager(self) self.mApplyingChanges = False self.mSynchronizing = False self.setObjectName("TileCollisionEditor") widget = QWidget(self) layout = QVBoxLayout(widget) layout.setSpacing(0) layout.setContentsMargins(5, 5, 5, 5) self.mMapView.setScene(self.mMapScene) self.mMapView.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.mMapView.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) rectangleObjectsTool = CreateRectangleObjectTool(self) ellipseObjectsTool = CreateEllipseObjectTool(self) polygonObjectsTool = CreatePolygonObjectTool(self) polylineObjectsTool = CreatePolylineObjectTool(self) toolBar = QToolBar(self) toolBar.setMovable(False) toolBar.setFloatable(False) toolBar.setContextMenuPolicy(Qt.ActionsContextMenu) self.mToolManager = ToolManager(self) toolBar.addAction( self.mToolManager.registerTool(ObjectSelectionTool(self))) toolBar.addAction(self.mToolManager.registerTool( EditPolygonTool(self))) toolBar.addAction(self.mToolManager.registerTool(rectangleObjectsTool)) toolBar.addAction(self.mToolManager.registerTool(ellipseObjectsTool)) toolBar.addAction(self.mToolManager.registerTool(polygonObjectsTool)) toolBar.addAction(self.mToolManager.registerTool(polylineObjectsTool)) self.setCentralWidget(self.mMapView) self.addToolBar(toolBar) self.mMapScene.setSelectedTool(self.mToolManager.selectedTool()) self.mToolManager.selectedToolChanged.connect(self.setSelectedTool) zoomComboBox = QComboBox() self.statusBar().addPermanentWidget(zoomComboBox) zoomable = self.mMapView.zoomable() zoomable.connectToComboBox(zoomComboBox) undoShortcut = QShortcut(QKeySequence.Undo, self) redoShortcut = QShortcut(QKeySequence.Redo, self) cutShortcut = QShortcut(QKeySequence.Cut, self) copyShortcut = QShortcut(QKeySequence.Copy, self) pasteShortcut = QShortcut(QKeySequence.Paste, self) deleteShortcut = QShortcut(QKeySequence.Delete, self) deleteShortcut2 = QShortcut(QKeySequence.Back, self) undoShortcut.activated.connect(self.undo) redoShortcut.activated.connect(self.redo) cutShortcut.activated.connect(self.cut) copyShortcut.activated.connect(self.copy) pasteShortcut.activated.connect(self.paste) deleteShortcut.activated.connect(self.delete_) deleteShortcut2.activated.connect(self.delete_) self.retranslateUi() self.resize(300, 300) Utils.restoreGeometry(self)
def setup(self): # icons self.homeIcon = Icon('home.svg', '#0A0').getIcon() self.csvIcon = Icon('file-csv.svg', '#D50').getIcon() self.dbIcon = Icon('database.svg', '#666').getIcon() # Setup ToolBar toolBar = QToolBar('Left Menu', self) toolBar.setFloatable(False) toolBar.setMovable(False) toolBar.setIconSize(QSize(64, 64)) toolBar.setStyleSheet('background-color: #004; color: #FFF;') # Tabs Properties self.TabContainer.setTabsClosable(True) self.TabContainer.tabCloseRequested.connect(self.closeTab) # Home home = QAction(self.homeIcon, 'Home', self) home.triggered.connect(self.onHome) self.TabContainer.addTab(self.home, self.homeIcon, 'Home') # CSV loadCSV = QAction(self.csvIcon, 'Load CSV', self) loadCSV.triggered.connect(self.onLoadCSV) # DB loadDB = QAction(self.dbIcon, 'Load From DataBase', self) loadDB.triggered.connect(self.onLoadDB) # Add to ToolBar toolBar.addAction(home) toolBar.addAction(loadCSV) toolBar.addAction(loadDB) self.addToolBar(Qt.LeftToolBarArea, toolBar)
def __init__(self, parentWidget, settings): QWidget.__init__(self, parentWidget) self.settings = settings toolbar = QToolBar(self) toolbar.setFloatable(False) toolbar.setMovable(False) addAction = QAction("+", toolbar) addAction.triggered.connect(self.addNotepad) toolbar.addAction(addAction) removeAction = QAction("-", toolbar) removeAction.triggered.connect(self.removeNotepad) toolbar.addAction(removeAction) self.browserView = TreeWidget(self) hLayout = QVBoxLayout(self) hLayout.setContentsMargins(0, 0, 0, 1) hLayout.addWidget(toolbar) hLayout.addWidget(self.browserView) self.currentItem = None self.browserView.itemSelectionChanged.connect(self.handleItemSelected)
def init_menu(self): load_rom = self.create_action('載入Rom', self.load_rom) save_rom = self.create_action('保存Rom', self.save_rom) save_rom.setEnabled(False) save_as = self.create_action('另存為', self.save_as) save_as.setEnabled(False) close_child = self.create_action('關閉窗口', self.close_child) exit_editor = self.create_action('退出', self.close) file_menu = self.create_menu( '文件(&F)', None, [load_rom, save_rom, save_as, close_child, exit_editor]) value_editors = [ self.create_action(editor_name, self.open_editor_frame) for editor_name in CHILD_MAPPING ] edit_menu = self.create_menu('編輯(&E)', None, value_editors) edit_menu.setEnabled(False) self.menuBar().addMenu(file_menu) self.menuBar().addMenu(edit_menu) tool_bar = QToolBar('顯示標籤') tool_bar.setObjectName('工具') tool_bar.addActions(value_editors) tool_bar.setEnabled(False) tool_bar.setMovable(False) tool_bar.setVisible(False) self.addToolBar(tool_bar) action_group = QActionGroup(self) [(action_group.addAction(i), i.setCheckable(True)) for i in value_editors] action_group.setExclusive(True)
def _create_tool_bar(self): tools_toolbar = QToolBar('Tools') tools_toolbar.addAction(self.file_open) tools_toolbar.setFloatable(False) tools_toolbar.setMovable(False) self.addToolBar(tools_toolbar)
def invoke(self): """ the real setup function to show someting. """ self.setupUi(self) # make sure the first col show full self.yeahdoclisttree.header().setResizeMode(0, QHeaderView.ResizeToContents) self.__initAction() # toolbar . classToolbar = QToolBar() classToolbar.setIconSize(QSize(16, 16)) classToolbar.setMovable(False) self.rightsplitter.insertWidget(0, classToolbar) classToolbar.addAction(self.__actions["__yeahdoc_c_new__"]) classToolbar.addAction(self.__actions["__yeahdoc_c_edit__"]) classToolbar.addAction(self.__actions["__yeahdoc_c_rename__"]) classToolbar.addAction(self.__actions["__yeahdoc_c_delete__"]) classToolbar.addWidget(self.__evt_category_view()) # More useful gadgets self.togglebtn.setIcon(QIcon(getPath("iconDir", "yeahdoc/right.png"))) # read datas from db . self.__setupYeahdocCategoryDatas() self.__setupyeahdoclisttreeDatas()
def Menu_init(self): menubar = self.menuBar() # 菜单栏 mfile = menubar.addMenu("文件") themefile = menubar.addMenu("主题") # 重置 reset_canvas_act = QAction(QIcon("./icon/file.png"), "重置画布", self) reset_canvas_act.triggered.connect( self.reset_canvas_action) # To be done # 保存 save_canvas_act = QAction(QIcon("./icon/save.png"), "保存画布", self) save_canvas_act.triggered.connect(self.save_canvas_action) #默认主题: theme1_act = QAction("Blue theme", self) theme1_act.triggered.connect(self.theme1_action) theme2_act = QAction("Coffee theme", self) theme2_act.triggered.connect(self.theme2_action) # 将action添加到file上面 mfile.addAction(reset_canvas_act) mfile.addAction(save_canvas_act) #将action添加到theme上面 themefile.addAction(theme1_act) themefile.addAction(theme2_act) # 工具栏 toolbar = QToolBar() toolbar.setMovable(False) self.addToolBar(toolbar) toolbar.addAction(reset_canvas_act) toolbar.addAction(save_canvas_act)
def __init__(self, wallet_api: WalletAPI, parent: QWidget) -> None: super().__init__(parent) self._context = ListContext(wallet_api, self) cards = Cards(self._context, self) sort_action = QAction(self) sort_action.setIcon( read_QIcon("icons8-alphabetical-sorting-2-80-blueui.png")) sort_action.setToolTip(_("Sort")) sort_action.triggered.connect(self._on_sort_action) sort_action.setEnabled(self._context.sortable_list) toolbar = QToolBar(self) toolbar.setMovable(False) toolbar.setOrientation(Qt.Vertical) toolbar.setToolButtonStyle(Qt.ToolButtonIconOnly) toolbar.addAction(sort_action) self._layout = QHBoxLayout() self._layout.setContentsMargins(0, 0, 0, 0) self._layout.setSpacing(0) self._layout.addWidget(cards) self._layout.addWidget(toolbar) self.setLayout(self._layout) self._cards = cards self._sort_action = sort_action self._sort_type = 1
def __init__(self, wallet_api: WalletAPI, parent: Optional['ElectrumWindow']=None): super().__init__(parent) self._context = ListContext(wallet_api, self) cards = ContactCards(self._context, self) self.setStyleSheet(contactcard_stylesheet) add_contact_action = QAction(self) add_contact_action.setIcon(read_QIcon("icons8-plus-blueui.svg")) add_contact_action.setToolTip(_("Add new contact")) add_contact_action.triggered.connect(self._on_add_contact_action) sort_action = QAction(self) sort_action.setIcon(read_QIcon("icons8-alphabetical-sorting-2-80-blueui.png")) sort_action.setToolTip(_("Sort")) sort_action.triggered.connect(self._on_sort_action) toolbar = QToolBar(self) toolbar.setMovable(False) toolbar.setOrientation(Qt.Vertical) toolbar.setToolButtonStyle(Qt.ToolButtonIconOnly) toolbar.addAction(add_contact_action) toolbar.addAction(sort_action) self._layout = QHBoxLayout() self._layout.setContentsMargins(0, 0, 0, 0) self._layout.setSpacing(0) self._layout.addWidget(cards) self._layout.addWidget(toolbar) self.setLayout(self._layout) self._cards = cards self._sort_action = sort_action self._sort_type = 1
def __init__(self, parent): TitledToolbar.__init__(self, parent, 'Actions') toolbar = QToolBar(self) toolbar.setFloatable(False) toolbar.setMovable(False) self.saveAction = QAction(QIcon(":/icons/file-save.png"), "Save (Ctrl-S)", toolbar) self.saveAction.setShortcut(Qt.CTRL + Qt.Key_S) self.saveAction.triggered.connect(parent.save) toolbar.addAction(self.saveAction) self.nonprintableAction = QAction( QIcon(":/icons/view-nonprintable.png"), "View nonprintable chars", toolbar) self.nonprintableAction.setCheckable(True) self.nonprintableAction.triggered.connect(parent.toggleNonprintable) toolbar.addAction(self.nonprintableAction) self.undoAction = QAction(QIcon(":/icons/edit-undo.png"), "Undo (Ctrl-Z)", toolbar) # saveAction.setShortcut(Qt.CTRL + Qt.Key_Z); self.undoAction.triggered.connect(parent.undo) toolbar.addAction(self.undoAction) self.redoAction = QAction(QIcon(":/icons/edit-redo.png"), "Redo (Ctrl-Y)", toolbar) # saveAction.setShortcut(Qt.CTRL + Qt.Key_Y); self.redoAction.triggered.connect(parent.redo) toolbar.addAction(self.redoAction) self.backAction = QAction(QIcon(":/icons/view-back.png"), "Back", toolbar) self.backAction.setEnabled(False) self.backAction.triggered.connect(parent.navigateBack) toolbar.addAction(self.backAction) self.forwardAction = QAction(QIcon(":/icons/view-forward.png"), "Forward", toolbar) self.forwardAction.setEnabled(False) self.forwardAction.triggered.connect(parent.navigateForward) toolbar.addAction(self.forwardAction) insertImageAction = QAction(QIcon(":/icons/edit-insert-image.png"), "Insert Image", toolbar) insertImageAction.triggered.connect(parent.insertImage) toolbar.addAction(insertImageAction) insertFormulaAction = QAction("f(x)", toolbar) insertFormulaAction.triggered.connect(parent.insertFormula) toolbar.addAction(insertFormulaAction) findInPageAction = QAction(QIcon(":/icons/edit-find.png"), "Find in page (CTRL-F)", toolbar) findInPageAction.setShortcut(Qt.CTRL + Qt.Key_F) findInPageAction.triggered.connect(parent.findInPage) toolbar.addAction(findInPageAction) self.addWidget(toolbar)
def initUI(self): toolbar = QToolBar() toolbar.setMovable(False) self.addToolBar(Qt.LeftToolBarArea,toolbar) # Switched toolbar to left cause it looked cool # Saved Notes # ------------------------------------------------------------------------------------- # self.notes_heading = QtWidgets.QLabel('Saved Notes',self) toolbar.addWidget(self.notes_heading) toolbar.addSeparator() all_saved = ["title", "test", "example"] self.saved_note_buttons = [] for i in all_saved: self.saved_note_buttons.append(savedNote(i, self)) self.saved_note_buttons[::-1][0].display.triggered.connect(self.saved_note_buttons[::-1][0].clicked) toolbar.addAction(self.saved_note_buttons[::-1][0].display) # Pages heading # ------------------------------------------------------------------------------------- # self.pages_head = QtWidgets.QLabel('Pages',self) toolbar.addWidget(self.pages_head) toolbar.addSeparator() self.nextPageButton = QAction(QIcon('resources/NewPageIcon.PNG'),'Next/New page',self) self.nextPageButton.triggered.connect(self.NextPage) toolbar.addAction(self.nextPageButton) self.lastPageButton = QAction(QIcon('resources/InvalidLastPageIcon.PNG'),'Previous page',self) self.lastPageButton.triggered.connect(self.LastPage) toolbar.addAction(self.lastPageButton) self.pageDisplay = QtWidgets.QLabel('1 / 1', self)#.setAlignment(Qt.AlignCenter) Trying to center it (horizontally), not working rn. toolbar.addWidget(self.pageDisplay) # Special controls heading # ------------------------------------------------------------------------------------- # self.controls_head = QtWidgets.QLabel('Controls',self) toolbar.addWidget(self.controls_head) toolbar.addSeparator() self.interpretButton = QAction(QIcon('resources/InterpretIcon.PNG'),'Interpret',self) self.interpretButton.triggered.connect(self.ReadText) toolbar.addAction(self.interpretButton) self.boardSwitchButton = QAction('Swap Input', self) self.boardSwitchButton.triggered.connect(self.BoardSwitch) toolbar.addAction(self.boardSwitchButton) self.newLineButton = QAction('New line', self) self.newLineButton.triggered.connect(self.NewLine) toolbar.addAction(self.newLineButton) self.setGeometry(200,200,750,600) self.setWindowTitle('PiPad') self.show()
def initToolBar(self): enlargeAction = QAction(QIcon('icon/enlarge.png'), 'Enlarge', self) enlargeAction.setShortcut('Ctrl+E') enlargeAction.setStatusTip('Enlarge') enlargeAction.triggered.connect(self.enlarge) narrowAction = QAction(QIcon('icon/narrow.png'), 'Narrow', self) narrowAction.setShortcut('Ctrl+N') narrowAction.setStatusTip('Narrow') narrowAction.triggered.connect(self.narrow) restartAction = QAction(QIcon('icon/redo.png'), 'Restart', self) restartAction.setShortcut('Ctrl+R') restartAction.setStatusTip('Restart') restartAction.triggered.connect(self.restart) addLabelAction = QAction(QIcon('icon/add-label.png'), 'Label', self) addLabelAction.setShortcut('Ctrl+Q') addLabelAction.setStatusTip('Label') addLabelAction.triggered.connect(self.label) undoAction = QAction(QIcon('icon/undo.png'), 'Undo', self) undoAction.setShortcut('Ctrl+Z') undoAction.setStatusTip('Undo') undoAction.triggered.connect(self.undo) lastImageAction = QAction(QIcon('icon/top.png'), 'Last', self) lastImageAction.setStatusTip('Last') lastImageAction.triggered.connect(self.last) nextImageAction = QAction(QIcon('icon/down.png'), 'Next', self) nextImageAction.setStatusTip('Next') nextImageAction.triggered.connect(self.next) saveAction = QAction(QIcon('icon/save.png'), 'Save', self) saveAction.setShortcut('Ctrl+S') saveAction.setStatusTip('Save') saveAction.triggered.connect(self.save) toolbar = QToolBar('Label') toolbar.addAction(enlargeAction) toolbar.addSeparator() toolbar.addAction(narrowAction) toolbar.addSeparator() toolbar.addAction(restartAction) toolbar.addSeparator() toolbar.addAction(addLabelAction) toolbar.addSeparator() toolbar.addAction(undoAction) toolbar.addSeparator() toolbar.addAction(lastImageAction) toolbar.addSeparator() toolbar.addAction(nextImageAction) toolbar.addSeparator() toolbar.addAction(saveAction) toolbar.setMovable(False) toolbar.setStyleSheet("QWidget { background-color: %s }" % '#FEFEFE') self.addToolBar(Qt.RightToolBarArea, toolbar)
def __init__(self, parent = None): super().__init__(parent) self.mTile = None self.mMapDocument = None self.mMapScene = MapScene(self) self.mMapView = MapView(self, MapViewMode.NoStaticContents) self.mToolManager = ToolManager(self) self.mApplyingChanges = False self.mSynchronizing = False self.setObjectName("TileCollisionEditor") widget = QWidget(self) layout = QVBoxLayout(widget) layout.setSpacing(0) layout.setContentsMargins(5, 5, 5, 5) self.mMapView.setScene(self.mMapScene) self.mMapView.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.mMapView.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) rectangleObjectsTool = CreateRectangleObjectTool(self) ellipseObjectsTool = CreateEllipseObjectTool(self) polygonObjectsTool = CreatePolygonObjectTool(self) polylineObjectsTool = CreatePolylineObjectTool(self) toolBar = QToolBar(self) toolBar.setMovable(False) toolBar.setFloatable(False) toolBar.setContextMenuPolicy(Qt.ActionsContextMenu) self.mToolManager = ToolManager(self) toolBar.addAction(self.mToolManager.registerTool(ObjectSelectionTool(self))) toolBar.addAction(self.mToolManager.registerTool(EditPolygonTool(self))) toolBar.addAction(self.mToolManager.registerTool(rectangleObjectsTool)) toolBar.addAction(self.mToolManager.registerTool(ellipseObjectsTool)) toolBar.addAction(self.mToolManager.registerTool(polygonObjectsTool)) toolBar.addAction(self.mToolManager.registerTool(polylineObjectsTool)) self.setCentralWidget(self.mMapView) self.addToolBar(toolBar) self.mMapScene.setSelectedTool(self.mToolManager.selectedTool()) self.mToolManager.selectedToolChanged.connect(self.setSelectedTool) zoomComboBox = QComboBox() self.statusBar().addPermanentWidget(zoomComboBox) zoomable = self.mMapView.zoomable() zoomable.connectToComboBox(zoomComboBox) undoShortcut = QShortcut(QKeySequence.Undo, self) redoShortcut = QShortcut(QKeySequence.Redo, self) cutShortcut = QShortcut(QKeySequence.Cut, self) copyShortcut = QShortcut(QKeySequence.Copy, self) pasteShortcut = QShortcut(QKeySequence.Paste, self) deleteShortcut = QShortcut(QKeySequence.Delete, self) deleteShortcut2 = QShortcut(QKeySequence.Back, self) undoShortcut.activated.connect(self.undo) redoShortcut.activated.connect(self.redo) cutShortcut.activated.connect(self.cut) copyShortcut.activated.connect(self.copy) pasteShortcut.activated.connect(self.paste) deleteShortcut.activated.connect(self.delete_) deleteShortcut2.activated.connect(self.delete_) self.retranslateUi() self.resize(300, 300) Utils.restoreGeometry(self)
def initToolBar(self): toolbarBox = QToolBar(self) self.addToolBar(Qt.LeftToolBarArea, toolbarBox) toolbarBox.setMovable(False) toolbarBox.setStyleSheet("background-color:#444444;color:#000000;") resize = QAction(QIcon('Resize.png'), "Resize", self) toolbarBox.addAction(resize) toolbarBox.actionTriggered[QAction].connect(self.toolBtnPressed)
def __init__(self, toolbar): TitledToolbar.__init__(self, toolbar, 'Text style') self.currentStyle = None toolbar = QToolBar(self) toolbar.setFloatable(False) toolbar.setMovable(False) self.styleToAction = {} textKeywordAction = QAction(QIcon(':/icons/format-keyword.png'), 'Notepad link', toolbar) textKeywordAction.setCheckable(True) selector = ('olink', None, None) textKeywordAction.setProperty('style', selector) self.styleToAction[selector] = textKeywordAction textKeywordAction.triggered.connect(self.styleSelected) toolbar.addAction(textKeywordAction) textLinkAction = QAction(QIcon(':/icons/format-link.png'), 'Internet link', toolbar) textLinkAction.setCheckable(True) selector = ('link', None, None) textLinkAction.setProperty('style', selector) self.styleToAction[selector] = textLinkAction textLinkAction.triggered.connect(self.styleSelected) toolbar.addAction(textLinkAction) textBoldAction = QAction(QIcon(':/icons/format-text-emphasized.png'), 'Emphasize', toolbar) textBoldAction.setCheckable(True) selector = ('emphasis', None, None) textBoldAction.setProperty('style', selector) self.styleToAction[selector] = textBoldAction textBoldAction.triggered.connect(self.styleSelected) toolbar.addAction(textBoldAction) textHighlightAction = QAction( QIcon(':/icons/format-text-highlight.png'), 'Highlight', toolbar) textHighlightAction.setCheckable(True) selector = ('emphasis', 'role', 'highlight') textHighlightAction.setProperty('style', selector) self.styleToAction[selector] = textHighlightAction textHighlightAction.triggered.connect(self.styleSelected) toolbar.addAction(textHighlightAction) textCodeAction = QAction(QIcon(':/icons/format-text-code.png'), 'Code', toolbar) textCodeAction.setCheckable(True) selector = ('code', None, None) textCodeAction.setProperty('style', selector) self.styleToAction[selector] = textCodeAction textCodeAction.triggered.connect(self.styleSelected) toolbar.addAction(textCodeAction) self.addWidget(toolbar)
class SessionManager(QMainWindow): def __init__(self, parent=None): QListWidget.__init__(self, parent) self.setWindowTitle(tr("Saved Sessions")) self.setWindowFlags(Qt.Dialog) hideAction = QAction(self) hideAction.setShortcuts(["Esc", "Ctrl+W"]) hideAction.triggered.connect(self.hide) self.addAction(hideAction) self.sessionList = QListWidget(self) self.sessionList.itemActivated.connect(self.loadSession) self.setCentralWidget(self.sessionList) self.toolBar = QToolBar(self) self.toolBar.setMovable(False) self.toolBar.setContextMenuPolicy(Qt.CustomContextMenu) self.addToolBar(Qt.BottomToolBarArea, self.toolBar) self.loadButton = QPushButton(tr("&Load"), self) self.loadButton.clicked.connect( lambda: self.loadSession(self.sessionList.currentItem())) self.toolBar.addWidget(self.loadButton) self.saveButton = QPushButton(tr("&Save"), self) self.saveButton.clicked.connect(saveSessionManually) self.saveButton.clicked.connect(self.refresh) self.toolBar.addWidget(self.saveButton) deleteAction = QAction(self) deleteAction.setShortcut("Del") deleteAction.triggered.connect(self.delete) self.addAction(deleteAction) def refresh(self): self.sessionList.clear() if os.path.exists(settings.session_folder): sessions = os.listdir(settings.session_folder) for session in sessions: self.sessionList.addItem(session) def delete(self): if self.sessionList.hasFocus(): try: os.remove( os.path.join(settings.session_folder, self.sessionList.currentItem().text())) except: pass self.refresh() def show(self): self.refresh() QMainWindow.show(self) def loadSession(self, item): if os.path.exists(settings.session_folder): loadSession(os.path.join(settings.session_folder, item.text())) self.hide()
def _create_toolbar(self, color_maps): toolbar = QToolBar() toolbar.setFloatable(False) toolbar.setMovable(False) self._layout_combo = LayoutCombo() self._layout_combo_action = QWidgetAction(self._layout_combo) self._layout_combo_action.setDefaultWidget(self._layout_combo) toolbar.addAction(self._layout_combo_action) self._layout_combo.layout_changed.connect( self._slice_view_widget.set_plot_layout) # self._colormap_combo = ColormapCombo(['seismic', 'spectral', 'RdGy', 'hot', 'jet', 'gray']) self._colormap_combo = ColormapCombo(color_maps) self._colormap_combo.currentIndexChanged[int].connect( self._colormap_changed) toolbar.addWidget(self._colormap_combo) self._save_button = QToolButton() self._save_button.setToolTip("Save as image") self._save_button.setIcon(resource_icon("table_export.png")) self._save_button.clicked.connect(self._save_figure) toolbar.addWidget(self._save_button) self._settings_button = QToolButton() self._settings_button.setToolTip("Toggle settings visibility") self._settings_button.setIcon(resource_icon("cog.png")) self._settings_button.setCheckable(True) self._settings_button.toggled.connect(self._show_settings) toolbar.addWidget(self._settings_button) self._help_button = QToolButton() self._help_button.setToolTip("View help") self._help_button.setIcon(resource_icon("help.png")) self._help_button.setCheckable(True) self._help_button.toggled.connect(self._show_help) toolbar.addWidget(self._help_button) def toggle_on_close(event): self._settings_button.setChecked(False) event.accept() def toggle_on_close_help(event): self._help_button.setChecked(False) event.accept() self._settings_window.closeEvent = toggle_on_close self._help_window.closeEvent = toggle_on_close_help self._colormap_combo.setCurrentIndex(45) self.set_default_layout() return toolbar
def __init__(self, parent=None): super().__init__(parent) self.mOpacityLabel = QLabel() self.mOpacitySlider = QSlider(Qt.Horizontal) self.mLayerView = LayerView() self.mMapDocument = None self.mUpdatingSlider = False self.mChangingLayerOpacity = False self.mUpdatingSlider = False self.setObjectName("layerDock") widget = QWidget(self) layout = QVBoxLayout(widget) layout.setContentsMargins(5, 5, 5, 5) opacityLayout = QHBoxLayout() self.mOpacitySlider.setRange(0, 100) self.mOpacitySlider.setEnabled(False) opacityLayout.addWidget(self.mOpacityLabel) opacityLayout.addWidget(self.mOpacitySlider) self.mOpacityLabel.setBuddy(self.mOpacitySlider) handler = MapDocumentActionHandler.instance() newLayerMenu = QMenu(self) newLayerMenu.addAction(handler.actionAddTileLayer()) newLayerMenu.addAction(handler.actionAddObjectGroup()) newLayerMenu.addAction(handler.actionAddImageLayer()) newIcon = QIcon(":/images/16x16/document-new.png") newLayerButton = QToolButton() newLayerButton.setPopupMode(QToolButton.InstantPopup) newLayerButton.setMenu(newLayerMenu) newLayerButton.setIcon(newIcon) Utils.setThemeIcon(newLayerButton, "document-new") buttonContainer = QToolBar() buttonContainer.setFloatable(False) buttonContainer.setMovable(False) buttonContainer.setIconSize(QSize(16, 16)) buttonContainer.addWidget(newLayerButton) buttonContainer.addAction(handler.actionMoveLayerUp()) buttonContainer.addAction(handler.actionMoveLayerDown()) buttonContainer.addAction(handler.actionDuplicateLayer()) buttonContainer.addAction(handler.actionRemoveLayer()) buttonContainer.addSeparator() buttonContainer.addAction(handler.actionToggleOtherLayers()) listAndToolBar = QVBoxLayout() listAndToolBar.setSpacing(0) listAndToolBar.addWidget(self.mLayerView) listAndToolBar.addWidget(buttonContainer) layout.addLayout(opacityLayout) layout.addLayout(listAndToolBar) self.setWidget(widget) self.retranslateUi() self.mOpacitySlider.valueChanged.connect(self.sliderValueChanged) self.updateOpacitySlider()
def __init__(self, parent = None): super().__init__(parent) self.mOpacityLabel = QLabel() self.mOpacitySlider = QSlider(Qt.Horizontal) self.mLayerView = LayerView() self.mMapDocument = None self.mUpdatingSlider = False self.mChangingLayerOpacity = False self.mUpdatingSlider = False self.setObjectName("layerDock") widget = QWidget(self) layout = QVBoxLayout(widget) layout.setContentsMargins(5, 5, 5, 5) opacityLayout = QHBoxLayout() self.mOpacitySlider.setRange(0, 100) self.mOpacitySlider.setEnabled(False) opacityLayout.addWidget(self.mOpacityLabel) opacityLayout.addWidget(self.mOpacitySlider) self.mOpacityLabel.setBuddy(self.mOpacitySlider) handler = MapDocumentActionHandler.instance() newLayerMenu = QMenu(self) newLayerMenu.addAction(handler.actionAddTileLayer()) newLayerMenu.addAction(handler.actionAddObjectGroup()) newLayerMenu.addAction(handler.actionAddImageLayer()) newIcon = QIcon(":/images/16x16/document-new.png") newLayerButton = QToolButton() newLayerButton.setPopupMode(QToolButton.InstantPopup) newLayerButton.setMenu(newLayerMenu) newLayerButton.setIcon(newIcon) Utils.setThemeIcon(newLayerButton, "document-new") buttonContainer = QToolBar() buttonContainer.setFloatable(False) buttonContainer.setMovable(False) buttonContainer.setIconSize(QSize(16, 16)) buttonContainer.addWidget(newLayerButton) buttonContainer.addAction(handler.actionMoveLayerUp()) buttonContainer.addAction(handler.actionMoveLayerDown()) buttonContainer.addAction(handler.actionDuplicateLayer()) buttonContainer.addAction(handler.actionRemoveLayer()) buttonContainer.addSeparator() buttonContainer.addAction(handler.actionToggleOtherLayers()) listAndToolBar = QVBoxLayout() listAndToolBar.setSpacing(0) listAndToolBar.addWidget(self.mLayerView) listAndToolBar.addWidget(buttonContainer) layout.addLayout(opacityLayout) layout.addLayout(listAndToolBar) self.setWidget(widget) self.retranslateUi() self.mOpacitySlider.valueChanged.connect(self.sliderValueChanged) self.updateOpacitySlider()
def _defineToolBar(self) -> None: toolBar = QToolBar(self) toolBar.setMovable(False) # add actions toolBar.addAction(self.actionAdd) toolBar.addAction(self.actionRemove) toolBar.addSeparator() toolBar.addAction(self.actionRemoveAll) toolBar.addSeparator() toolBar.addAction(self.actionSave) self.addToolBar(toolBar)
def __init__(self, parent): TitledToolbar.__init__(self, parent, 'Actions') toolbar = QToolBar(self) toolbar.setFloatable(False) toolbar.setMovable(False) self.saveAction = QAction(QIcon(":/icons/file-save.png"), "Save (Ctrl-S)", toolbar) self.saveAction.setShortcut(Qt.CTRL + Qt.Key_S); self.saveAction.triggered.connect(parent.save) toolbar.addAction(self.saveAction) self.nonprintableAction = QAction(QIcon(":/icons/view-nonprintable.png"), "View nonprintable chars", toolbar) self.nonprintableAction.setCheckable(True); self.nonprintableAction.triggered.connect(parent.toggleNonprintable) toolbar.addAction(self.nonprintableAction) self.undoAction = QAction(QIcon(":/icons/edit-undo.png"), "Undo (Ctrl-Z)", toolbar) # saveAction.setShortcut(Qt.CTRL + Qt.Key_Z); self.undoAction.triggered.connect(parent.undo) toolbar.addAction(self.undoAction) self.redoAction = QAction(QIcon(":/icons/edit-redo.png"), "Redo (Ctrl-Y)", toolbar) # saveAction.setShortcut(Qt.CTRL + Qt.Key_Y); self.redoAction.triggered.connect(parent.redo) toolbar.addAction(self.redoAction) self.backAction = QAction(QIcon(":/icons/view-back.png"), "Back", toolbar) self.backAction.setEnabled(False) self.backAction.triggered.connect(parent.navigateBack) toolbar.addAction(self.backAction) self.forwardAction = QAction(QIcon(":/icons/view-forward.png"), "Forward", toolbar) self.forwardAction.setEnabled(False) self.forwardAction.triggered.connect(parent.navigateForward) toolbar.addAction(self.forwardAction) insertImageAction = QAction(QIcon(":/icons/edit-insert-image.png"), "Insert Image", toolbar) insertImageAction.triggered.connect(parent.insertImage) toolbar.addAction(insertImageAction) insertFormulaAction = QAction("f(x)", toolbar) insertFormulaAction.triggered.connect(parent.insertFormula) toolbar.addAction(insertFormulaAction) findInPageAction = QAction(QIcon(":/icons/edit-find.png"), "Find in page (CTRL-F)", toolbar) findInPageAction.setShortcut(Qt.CTRL + Qt.Key_F); findInPageAction.triggered.connect(parent.findInPage) toolbar.addAction(findInPageAction) self.addWidget(toolbar)
def __init__(self, toolbar): TitledToolbar.__init__(self, toolbar, 'Text style') self.currentStyle = None toolbar = QToolBar(self) toolbar.setFloatable(False) toolbar.setMovable(False) self.styleToAction = {} textKeywordAction = QAction(QIcon(':/icons/format-keyword.png'), 'Notepad link', toolbar) textKeywordAction.setCheckable(True); selector = ('olink', None, None) textKeywordAction.setProperty('style', selector) self.styleToAction[selector] = textKeywordAction textKeywordAction.triggered.connect(self.styleSelected) toolbar.addAction(textKeywordAction) textLinkAction = QAction(QIcon(':/icons/format-link.png'), 'Internet link', toolbar) textLinkAction.setCheckable(True); selector = ('link', None, None) textLinkAction.setProperty('style', selector) self.styleToAction[selector] = textLinkAction textLinkAction.triggered.connect(self.styleSelected) toolbar.addAction(textLinkAction) textBoldAction = QAction(QIcon(':/icons/format-text-emphasized.png'), 'Emphasize', toolbar) textBoldAction.setCheckable(True); selector = ('emphasis', None, None) textBoldAction.setProperty('style', selector) self.styleToAction[selector] = textBoldAction textBoldAction.triggered.connect(self.styleSelected) toolbar.addAction(textBoldAction) textHighlightAction = QAction(QIcon(':/icons/format-text-highlight.png'), 'Highlight', toolbar) textHighlightAction.setCheckable(True); selector = ('emphasis', 'role', 'highlight') textHighlightAction.setProperty('style', selector) self.styleToAction[selector] = textHighlightAction textHighlightAction.triggered.connect(self.styleSelected) toolbar.addAction(textHighlightAction) textCodeAction = QAction(QIcon(':/icons/format-text-code.png'), 'Code', toolbar) textCodeAction.setCheckable(True); selector = ('code', None, None) textCodeAction.setProperty('style', selector) self.styleToAction[selector] = textCodeAction textCodeAction.triggered.connect(self.styleSelected) toolbar.addAction(textCodeAction) self.addWidget(toolbar)
def init_topmenu(self): topmenu = QToolBar() Sync_Button = QPushButton("Sync") Sync_Button.clicked.connect(self.do_sync) Last_Updated = QLabel("Last Updated:") settings = QPushButton("Settings") settings.clicked.connect(self.popup_settings) topmenu.addWidget(Sync_Button) topmenu.addSeparator() topmenu.addWidget(Last_Updated) topmenu.addWidget(settings) # Do not let the user drag the toolbar around topmenu.setMovable(False) return topmenu
def _createContractionToolbar(self): toolbar = QToolBar(parent=self) toolbar.setFloatable(True) toolbar.setMovable(True) toolbar.hide() action = toolbar.addAction('next') action.triggered.connect(self.next_hg) action = toolbar.addAction('previous') action.triggered.connect(self.previous_hg) action = toolbar.addAction('stop') action.triggered.connect(self.stopContractionPlayerSlot) self.contraction_toolbar = toolbar
def setupToolbar(self): """Create the toolbar for navigating web pages.""" toolbar = QToolBar() toolbar.setIconSize(QSize(24, 24)) toolbar.setMovable(False) self.addToolBar(toolbar) # Create actions back_act = QAction(QIcon("icons/back.png"), "Back Button", toolbar) back_act.triggered.connect(self.web_view.back) forward_act = QAction(QIcon("icons/forward.png"), "Forward Button", toolbar) forward_act.triggered.connect(self.web_view.forward) # Add actions to the toolbar toolbar.addAction(back_act) toolbar.addAction(forward_act)
def __init__(self): super().__init__() Util.Style.applyStyle(self) Util.Style.applyWindowTitle(self) Util.Style.applyWindowIcon(self) self.stack = QStackedWidget() self.stackList = {} self.jsonView = None self.requestView = None mainToolbar = QToolBar() mainToolbar.setMovable(False) topWidget = QWidget() topWidget.setFixedHeight(8) mainToolbar.addWidget(topWidget) self.requestAction = mainToolbar.addAction( PResource.invertIcon(Parapluie.Icon_Link_Svg), "Request", self.showRequest) self.requestAction.setCheckable(True) self.jsonAction = mainToolbar.addAction( PResource.invertIcon(Parapluie.Icon_Web_Development_Svg), "Viewer", self.showJSONViewer) self.jsonAction.setCheckable(True) stretchWidget = QWidget() stretchWidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) mainToolbar.addWidget(stretchWidget) mainToolbar.addAction( PResource.invertIcon(Parapluie.Icon_Information_Svg), "Information", self.openAbout) self.addToolBar(Qt.LeftToolBarArea, mainToolbar) bottomWidget = QWidget() bottomWidget.setFixedHeight(8) mainToolbar.addWidget(bottomWidget) self.stack.currentChanged.connect(self.stackChange) self.setCentralWidget(self.stack) self.setContentsMargins(0, 0, 0, 0) self.showRequest()
def add_toolbar(self, title=None, toolbar=None): """Creates, adds and returns a QToolBar """ if 1 != bool(toolbar) + bool(title): raise ValueError('Just one of toolbar or title should be given') if not toolbar: toolbar = QToolBar(title) toolbar.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum) self.addTab(toolbar, toolbar.windowTitle()) # This style applies to QToolButtons that are immediate children # of the toolbar - it does not apply to QToolButtons that are # contained within QWidgets added to toolbar. toolbar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) toolbar.setMovable(False) toolbar.setFloatable(False) return toolbar
class SessionManager(QMainWindow): def __init__(self, parent=None): QListWidget.__init__(self, parent) self.setWindowTitle(tr("Saved Sessions")) self.setWindowFlags(Qt.Dialog) hideAction = QAction(self) hideAction.setShortcuts(["Esc", "Ctrl+W"]) hideAction.triggered.connect(self.hide) self.addAction(hideAction) self.sessionList = QListWidget(self) self.sessionList.itemActivated.connect(self.loadSession) self.setCentralWidget(self.sessionList) self.toolBar = QToolBar(self) self.toolBar.setMovable(False) self.toolBar.setContextMenuPolicy(Qt.CustomContextMenu) self.addToolBar(Qt.BottomToolBarArea, self.toolBar) self.loadButton = QPushButton(tr("&Load"), self) self.loadButton.clicked.connect(lambda: self.loadSession(self.sessionList.currentItem())) self.toolBar.addWidget(self.loadButton) self.saveButton = QPushButton(tr("&Save"), self) self.saveButton.clicked.connect(saveSessionManually) self.saveButton.clicked.connect(self.refresh) self.toolBar.addWidget(self.saveButton) deleteAction = QAction(self) deleteAction.setShortcut("Del") deleteAction.triggered.connect(self.delete) self.addAction(deleteAction) def refresh(self): self.sessionList.clear() if os.path.exists(settings.session_folder): sessions = os.listdir(settings.session_folder) for session in sessions: self.sessionList.addItem(session) def delete(self): if self.sessionList.hasFocus(): try: os.remove(os.path.join(settings.session_folder, self.sessionList.currentItem().text())) except: pass self.refresh() def show(self): self.refresh() QMainWindow.show(self) def loadSession(self, item): if os.path.exists(settings.session_folder): loadSession(os.path.join(settings.session_folder, item.text())) self.hide()
def add_toolbar(self, name: str, toolbar: QToolBar, text: str = 'untitled toolbar'): b = self.top_toolbar_tab.add_button(text) toolbar.tab_button = b b.clicked.connect(lambda: self.switch_toolbar(name)) self.addToolBarBreak(Qt.TopToolBarArea) self.addToolBar(toolbar) toolbar.setObjectName(name) self.toolbars[name] = toolbar toolbar.setMovable(False) toolbar.setFloatable(False) if self._current_toolbar_name != '': self.refresh_toolbar_appearance()
def __init__(self): super(MainWindow, self).__init__() self.ports = serial.listports() self.port = None # remove close & maximize window buttons #self.setWindowFlags(Qt.CustomizeWindowHint|Qt.WindowMinimizeButtonHint) self.setMinimumSize(850, 450) self.mdiArea = QMdiArea() self.mdiArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.mdiArea.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.setCentralWidget(self.mdiArea) self.mdiArea.subWindowActivated.connect(self.updateMenus) self.mdiArea.setViewMode(QMdiArea.TabbedView) self.windowMapper = QSignalMapper(self) self.windowMapper.mapped[QWidget].connect(self.setActiveSubWindow) self.child = None self.createActions() self.createMenus() self.createStatusBar() self.updateMenus() self.readSettings() self.setWindowTitle("VISCAM") mytoolbar = QToolBar() ports_menu = QComboBox() ports_menu.addItem('Output Port') ports_menu.insertSeparator(1) for port in self.ports: ports_menu.addItem(port) self.ports_menu = ports_menu ports_menu.currentTextChanged.connect(self.setActivePort) mytoolbar.addWidget(ports_menu) mytoolbar.addSeparator() mytoolbar.setMovable(False) mytoolbar.setFixedHeight(60) self.addToolBar(Qt.TopToolBarArea, mytoolbar)
def __init__(self, parent = None): super().__init__(parent) self.mExpandedGroups = QMapList() self.mObjectsView = ObjectsView() self.mMapDocument = None self.setObjectName("ObjectsDock") self.mActionObjectProperties = QAction(self) self.mActionObjectProperties.setIcon(QIcon(":/images/16x16/document-properties.png")) Utils.setThemeIcon(self.mActionObjectProperties, "document-properties") self.mActionObjectProperties.triggered.connect(self.objectProperties) handler = MapDocumentActionHandler.instance() widget = QWidget(self) layout = QVBoxLayout(widget) layout.setContentsMargins(5, 5, 5, 5) layout.setSpacing(0) layout.addWidget(self.mObjectsView) self.mActionNewLayer = QAction(self) self.mActionNewLayer.setIcon(QIcon(":/images/16x16/document-new.png")) self.mActionNewLayer.triggered.connect(handler.actionAddObjectGroup().triggered) self.mActionMoveToGroup = QAction(self) self.mActionMoveToGroup.setIcon(QIcon(":/images/16x16/layer-object.png")) toolBar = QToolBar() toolBar.setFloatable(False) toolBar.setMovable(False) toolBar.setIconSize(QSize(16, 16)) toolBar.addAction(self.mActionNewLayer) toolBar.addAction(handler.actionDuplicateObjects()) toolBar.addAction(handler.actionRemoveObjects()) toolBar.addAction(self.mActionMoveToGroup) button = toolBar.widgetForAction(self.mActionMoveToGroup) self.mMoveToMenu = QMenu(self) button.setPopupMode(QToolButton.InstantPopup) button.setMenu(self.mMoveToMenu) self.mMoveToMenu.aboutToShow.connect(self.aboutToShowMoveToMenu) self.mMoveToMenu.triggered.connect(self.triggeredMoveToMenu) toolBar.addAction(self.mActionObjectProperties) layout.addWidget(toolBar) self.setWidget(widget) self.retranslateUi() DocumentManager.instance().documentAboutToClose.connect(self.documentAboutToClose)
def __init__(self, parent=None): super().__init__(parent) self.mMapDocument = None self.mPropertyBrowser = PropertyBrowser() self.setObjectName("propertiesDock") self.mActionAddProperty = QAction(self) self.mActionAddProperty.setEnabled(False) self.mActionAddProperty.setIcon(QIcon(":/images/16x16/add.png")) self.mActionAddProperty.triggered.connect(self.addProperty) self.mActionRemoveProperty = QAction(self) self.mActionRemoveProperty.setEnabled(False) self.mActionRemoveProperty.setIcon(QIcon(":/images/16x16/remove.png")) self.mActionRemoveProperty.triggered.connect(self.removeProperty) self.mActionRenameProperty = QAction(self) self.mActionRenameProperty.setEnabled(False) self.mActionRenameProperty.setIcon(QIcon(":/images/16x16/rename.png")) self.mActionRenameProperty.triggered.connect(self.renameProperty) Utils.setThemeIcon(self.mActionAddProperty, "add") Utils.setThemeIcon(self.mActionRemoveProperty, "remove") Utils.setThemeIcon(self.mActionRenameProperty, "rename") toolBar = QToolBar() toolBar.setFloatable(False) toolBar.setMovable(False) toolBar.setIconSize(QSize(16, 16)) toolBar.addAction(self.mActionAddProperty) toolBar.addAction(self.mActionRemoveProperty) toolBar.addAction(self.mActionRenameProperty) widget = QWidget(self) layout = QVBoxLayout(widget) layout.setContentsMargins(5, 5, 5, 5) layout.setSpacing(0) layout.addWidget(self.mPropertyBrowser) layout.addWidget(toolBar) widget.setLayout(layout) self.setWidget(widget) manager = DocumentManager.instance() manager.currentDocumentChanged.connect(self.mapDocumentChanged) self.mPropertyBrowser.currentItemChangedSignal.connect( self.currentItemChanged) self.retranslateUi()
def __init__(self, parent = None): super().__init__(parent) self.mMapDocument = None self.mPropertyBrowser = PropertyBrowser() self.setObjectName("propertiesDock") self.mActionAddProperty = QAction(self) self.mActionAddProperty.setEnabled(False) self.mActionAddProperty.setIcon(QIcon(":/images/16x16/add.png")) self.mActionAddProperty.triggered.connect(self.addProperty) self.mActionRemoveProperty = QAction(self) self.mActionRemoveProperty.setEnabled(False) self.mActionRemoveProperty.setIcon(QIcon(":/images/16x16/remove.png")) self.mActionRemoveProperty.triggered.connect(self.removeProperty) self.mActionRenameProperty = QAction(self) self.mActionRenameProperty.setEnabled(False) self.mActionRenameProperty.setIcon(QIcon(":/images/16x16/rename.png")) self.mActionRenameProperty.triggered.connect(self.renameProperty) Utils.setThemeIcon(self.mActionAddProperty, "add") Utils.setThemeIcon(self.mActionRemoveProperty, "remove") Utils.setThemeIcon(self.mActionRenameProperty, "rename") toolBar = QToolBar() toolBar.setFloatable(False) toolBar.setMovable(False) toolBar.setIconSize(QSize(16, 16)) toolBar.addAction(self.mActionAddProperty) toolBar.addAction(self.mActionRemoveProperty) toolBar.addAction(self.mActionRenameProperty) widget = QWidget(self) layout = QVBoxLayout(widget) layout.setContentsMargins(5, 5, 5, 5) layout.setSpacing(0) layout.addWidget(self.mPropertyBrowser) layout.addWidget(toolBar) widget.setLayout(layout) self.setWidget(widget) manager = DocumentManager.instance() manager.currentDocumentChanged.connect(self.mapDocumentChanged) self.mPropertyBrowser.currentItemChangedSignal.connect(self.currentItemChanged) self.retranslateUi()
class Record(object): def __init__(self): r = Record() r.foo = 'oof' setattr(r, 'bar', 'rab') r.foo 'oof' r.bar 'rab' names = 'id description price'.split() values = [666, 'duct tape', 3.45] s = Record() for name, value in zip(names, values): setattr(s, name, value) s.__dict__ # If you are suffering from dict withdrawal symptoms {'price': 3.45, 'id': 666, 'description': 'duct tape'} return self def appToolBar(self, QToolBar, cfg): 'Create a Toolbar from configurations file' pxcfg = here+'z-data_/desktop.yaml' self.config = config.instruct(pxcfg).select( 'docAddToolBar').override(cfg)# || self.createToolBar() for button, cfg in self.config['buttons'].items(): self.createButton(button, cfg) def createToolBar(self): self.toolbar = QToolBar() self.toolbar.setMovable(True) self.toolbar.setFloatable(True) self.toolbar.setOrientation(Qt.Vertical) return self def createButton(self, button, cfg): '' toolbutton = QToolButton() toolbutton.setText(cfg['DisplayName']) toolbutton.setCheckable(cfg['checkable']) toolbutton.setAutoExclusive(cfg['setAutoExclusive']) self.toolbar.addWidget(toolbutton) return self
def __init__(self): super(MainWindow, self).__init__() # remove close & maximize window buttons #self.setWindowFlags(Qt.CustomizeWindowHint|Qt.WindowMinimizeButtonHint) self.setMinimumSize(500, 666) #self.setMaximumSize(1000,666) self.mdiArea = QMdiArea() self.mdiArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.mdiArea.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.setCentralWidget(self.mdiArea) self.mdiArea.subWindowActivated.connect(self.updateMenus) self.mdiArea.setViewMode(QMdiArea.TabbedView) self.windowMapper = QSignalMapper(self) self.windowMapper.mapped[QWidget].connect(self.setActiveSubWindow) self.child = None self.createActions() self.createMenus() self.createStatusBar() self.updateMenus() self.readSettings() self.setWindowTitle("LEKTURE") mytoolbar = QToolBar() #self.toolbar = self.addToolBar() mytoolbar.addAction(self.newAct) mytoolbar.addAction(self.openAct) mytoolbar.addAction(self.saveAct) mytoolbar.addAction(self.saveAsAct) mytoolbar.addSeparator() mytoolbar.addAction(self.outputsAct) mytoolbar.addAction(self.scenarioAct) self.scenarioAct.setVisible(False) mytoolbar.setMovable(False) mytoolbar.setFixedWidth(60) self.addToolBar(Qt.LeftToolBarArea, mytoolbar)
def __init__(self, style='default'): super(QRibbonWindow, self).__init__() self.style = style self.setObjectName('QRibbonWindow') self.tabs = [] self.groups = [] # 设置样式 self.default = default if self.style == 'default': _default = default.replace("{{margin}}", str(self.margin)) self.setStyleSheet(_default + '\n' + self.styleSheet()) # 去除右键菜单 self.setContextMenuPolicy(Qt.NoContextMenu) # 添加工具栏 toolBar = QToolBar(self) toolBar.setMouseTracking(True) self.ribbonWidget = QRibbonWidget(toolBar) toolBar.addWidget(self.ribbonWidget) toolBar.setAllowedAreas(Qt.NoToolBarArea) toolBar.setMovable(False) toolBar.setFloatable(False) self.addToolBar(toolBar) # 添加中心控件 self.centralWidget = QWidget(self) self.centralWidget.setMouseTracking(True) self.centralWidget.setObjectName("centralWidget") self.setCentralWidget(self.centralWidget) # 安装时间过滤器 self.installEventFilter(self) self.ribbonWidget.tabPanel.installEventFilter(self) self.minButton.installEventFilter(self) self.maxButton.installEventFilter(self) self.closeButton.installEventFilter(self) # 最小化、最大化、关闭按钮事件 self.minButton.clicked.connect(self.showMinimized) self.maxButton.clicked.connect(self.toggle_max) self.closeButton.clicked.connect(self.close)
def add_toolbar(self, name: str, toolbar: QToolBar, text: str = 'untitled toolbar'): """ 添加一个工具栏。 """ b = self.top_toolbar_tab.add_button(text) toolbar.tab_button = b b.clicked.connect(lambda: self.on_toolbar_switch_button_clicked(name)) if hasattr(self, 'toolbar_path'): self.insertToolBar(self.toolbar_path, toolbar) self.insertToolBarBreak(self.toolbar_path) else: self.addToolBarBreak(Qt.TopToolBarArea) self.addToolBar(toolbar) toolbar.setObjectName(name) self.toolbars[name] = toolbar toolbar.setMovable(False) toolbar.setFloatable(False) if self._current_toolbar_name != '': self.refresh_toolbar_appearance()
class MainWindow(QMainWindow): """ Main UI window Class creates window elements, fills main menu with items. If you need to access to some existing menu items - check action path in the class constructor, than use next code: :: core.actionManager().action("mFile/aOpen").setEnabled(True) core.actionManager().action("mFile/aOpen").triggered.connect(self.myCoolMethod) MainWindow instance is accessible as: :: from enki.core.core import core core.mainwindow() Created by the core """ hideAllWindows = pyqtSignal() """ hideAllWindows() **Signal** emitted, when user toggled "Hide all" . Dock widgets are closed automatically, but other widgets, i.e. search widget, must catch this signal and close themselves. """ # pylint: disable=W0105 directoryDropt = pyqtSignal(str) """ directoryDropt() **Signal** emitted, when user drag-n-dropt directory to main windowd. FileBrowser shows directory """ # pylint: disable=W0105 _STATE_FILE = os.path.join(enki.core.defines.CONFIG_DIR, "main_window_state.bin") _GEOMETRY_FILE = os.path.join(enki.core.defines.CONFIG_DIR, "main_window_geometry.bin") def __init__(self): QMainWindow.__init__(self) self._queuedMessageToolBar = None self._createdMenuPathes = [] self._createdActions = [] self._addedDockWidgets = [] if hasattr(self, 'setUnifiedTitleAndToolBarOnMac'): # missing on some PyQt5 versions self.setUnifiedTitleAndToolBarOnMac(True) self.setIconSize(QSize(16, 16)) self.setAcceptDrops(True) # Set corner settings for dock widgets self.setCorner(Qt.TopLeftCorner, Qt.LeftDockWidgetArea) self.setCorner(Qt.TopRightCorner, Qt.RightDockWidgetArea) self.setCorner(Qt.BottomLeftCorner, Qt.LeftDockWidgetArea) self.setCorner(Qt.BottomRightCorner, Qt.RightDockWidgetArea) self.setWindowTitle(self.defaultTitle()) # overwriten by workspace when file or it's modified state changes self.setWindowIcon(QIcon(':/enkiicons/logo/enki.svg')) # Create top tool bar self._topToolBar = QToolBar("topToolBar") self._topToolBar.setObjectName("topToolBar") self._topToolBar.setMovable(False) self._topToolBar.setIconSize(QSize(16, 16)) self._topToolBar.setContextMenuPolicy(Qt.PreventContextMenu) # to avoid possibility to hide the toolbar # Create menu bar self._menuBar = ActionMenuBar(self, core.actionManager()) self._initMenubarAndStatusBarLayout() # create central layout widget = QWidget(self) self._centralLayout = QVBoxLayout(widget) self._centralLayout.setContentsMargins(0, 0, 0, 0) self.setCentralWidget(widget) self.setStyleSheet('QMainWindow::separator{width: 4px}') def _initActions(self): """ Public method for actionManager. """ self._createMenuStructure() core.actionManager().action('mView/aOpenMainMenu').triggered.connect(self._openMainMenu) core.actionManager().action('mFile/aQuit').triggered.connect(self.onQuit) def terminate(self): """Explicitly called destructor """ if self._queuedMessageToolBar: self.removeToolBar(self._queuedMessageToolBar) del self._queuedMessageToolBar for act in self._createdActions: core.actionManager().removeAction(act) for menuPath in self._createdMenuPathes[::-1]: core.actionManager().removeMenu(menuPath) @staticmethod def _isMenuEmbeddedToTaskBar(): """On Unity (Ubuntu) and MacOS menu bar is embedded to task bar """ return 'UBUNTU_MENUPROXY' in os.environ or \ os.environ.get('XDG_CURRENT_DESKTOP') == 'Unity' or \ 'darwin' == sys.platform def _initMenubarAndStatusBarLayout(self): """Create top widget and put it on its place """ if not 'darwin' == sys.platform: # on Ubuntu toolbar, docs and editor area look as one widget. Ugly # Therefore it is separated with line. On Mac seems OK # I can't predict, how it will look on other platforms, therefore line is used for all, except Mac toolBarStyleSheet = "QToolBar {border: 0; border-bottom-width: 1; border-bottom-style: solid}""" self._topToolBar.setStyleSheet(toolBarStyleSheet) area = Qt.BottomToolBarArea if self._isMenuEmbeddedToTaskBar() else Qt.TopToolBarArea self.addToolBar(area, self._topToolBar) if self._isMenuEmbeddedToTaskBar(): # separate menu bar self.setMenuBar(self._menuBar) else: # menubar, statusbar and editor tool bar on one line self._menuBar.setAutoFillBackground(False) menuBarStyleSheet = """ QMenuBar {background-color: transparent; color: %s} QMenuBar::item:!selected {background: transparent;} """ % self.palette().color(QPalette.WindowText).name() self._menuBar.setStyleSheet(menuBarStyleSheet) self._menuBar.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Minimum) self._topToolBar.addWidget(self._menuBar) # Create status bar self._statusBar = _StatusBar(self) self._topToolBar.addWidget(self._statusBar) def _initQueuedMessageToolBar(self): from enki.core.queued_msg_tool_bar import QueuedMessageToolBar self._queuedMessageToolBar = QueuedMessageToolBar(self) area = Qt.TopToolBarArea if self._isMenuEmbeddedToTaskBar() else Qt.BottomToolBarArea self.addToolBar(area, self._queuedMessageToolBar) self._queuedMessageToolBar.setVisible(False) def _createMenuStructure(self): """Fill menu bar with items. The majority of items are not connected to the slots, Connections made by module, which implements menu item functionality, but, all items are in one place, because it's easier to create clear menu layout """ # create menubar menus and actions def menu(path, name, icon, visible=True): """Subfunction for create a menu in the main menu""" menuObject = core.actionManager().addMenu(path, name) if icon: menuObject.setIcon(QIcon(':/enkiicons/' + icon)) self._createdMenuPathes.append(path) if not visible: menuObject.setVisible(False) def action(path, name, icon, shortcut, tooltip, enabled, checkable=False): # pylint: disable=R0913 """Subfunction for create an action in the main menu""" if icon: # has icon actObject = core.actionManager().addAction(path, name, QIcon(':/enkiicons/' + icon), shortcut) else: actObject = core.actionManager().addAction(path, name, shortcut=shortcut) if tooltip: actObject.setStatusTip(tooltip) actObject.setEnabled(enabled) actObject.setCheckable(checkable) self._createdActions.append(actObject) def separator(menu): """Subfunction for insert separator to the menu""" core.actionManager().action(menu).menu().addSeparator() # pylint: disable=C0301 # enable long lines for menu items # Menu or action path Name Icon Shortcut Hint enabled checkable menu ("mFile", "File" , "" ) action("mFile/aOpenProject", "Open Pro&ject..." , "open.png", "Shift+Ctrl+O" ,"Open a project" , True) separator("mFile") menu ("mFile/mUndoClose", "Undo Close" , "recents.png") separator("mFile") action("mFile/aNew", "&New file..." , "new.png", 'Ctrl+N', "New file" , True) action("mFile/aOpen", "&Open..." , "open.png", "Ctrl+O" , "Open a file" , True) menu ("mFile/mSave", "&Save" , "save.png" ) action("mFile/mSave/aCurrent", "&Save" , "save.png" , "Ctrl+S" , "Save the current file" , False) action("mFile/mSave/aSaveAs", "Save As..." , "save.png" , "Ctrl+Alt+S" , "" , False) action("mFile/mSave/aAll", "Save &All" , "saveall.png", 'Shift+Ctrl+S', "Save all files" , False) menu ("mFile/mReload", "&Reload" , "reload.png" ) action("mFile/mReload/aCurrent", "Reload" , "reload.png" , 'F5', "Reload the current file", False) action("mFile/mReload/aAll", "Reload All" , "reload.png" , 'Shift+F5', "Reload all files" , True) menu ("mFile/mClose", "&Close" , "close.png" ) action("mFile/mClose/aCurrent", "&Close" , "close.png", "Ctrl+W", "Close the current file" , False) action("mFile/mClose/aAll", "Close &All" , "closeall.png", 'Shift+Ctrl+W', "Close all files" , False) menu ("mFile/mFileSystem", "File System" , "filesystem.png") action("mFile/mFileSystem/aRename", "Rename" , "edit.png", '', "Rename current file" , False) if platform.system() != 'Windows': action("mFile/mFileSystem/aToggleExecutable", "Make executable" , "", '', "Toggle executable mode" , False) separator("mFile") action("mFile/aQuit", "Quit" , "quit.png", 'Ctrl+Q', "Quit" , True) separator("mFile") menu ("mView", "View" , "" ) action("mView/aShowIncorrectIndentation", "Show incorrect indentation", "", "", "" , False, True) action("mView/aShowAnyWhitespaces", "Show any whitespace", "", "", "" , False, True) separator("mView") action("mView/aHideAll", "Hide all / Restore" , "", "Shift+Esc", "Hide all widgets" , True) action("mView/aOpenMainMenu", "Open main menu" , "", "F10", "" , True) separator("mView") menu ("mEdit", "Edit" , "" ) action("mEdit/aStripTrailingWhitespace", "Strip trailing whitespace when saving", "", "", "" , True, True) separator("mEdit") menu ("mEdit/mCopyPasteLines", "Copy-paste lines" , "" ) menu ("mEdit/mIndentation", "Indentation" , "" ) separator("mEdit") action("mEdit/aEnableVimMode", "Enable Vim mode" , "", "", "" , False, True) menu ("mNavigation", "Navigation" , "" ) action("mNavigation/aFocusCurrentDocument", "Focus to editor" , "text.png", "Ctrl+Return", "Focus current document" , False) menu ("mNavigation/mSearchReplace", "&Search && Replace" , "search-replace-directory.png") menu ("mNavigation/mBookmarks", "&Bookmarks" , "bookmark.png") separator("mNavigation"), action("mNavigation/aNext", "&Next file" , "next.png", "Ctrl+PgDown", "Next file" , False) action("mNavigation/aPrevious", "&Previous file" , "previous.png", "Ctrl+PgUp", "Previous file" , False) separator("mNavigation") action("mNavigation/aGoto", "Go go line..." , "goto.png", "Ctrl+G", "Go to line..." , False) menu ("mNavigation/mFileBrowser", "File browser" , ':/enkiicons/open.png', visible=False) menu ("mNavigation/mScroll", "Scroll file" , '') menu ("mSettings", "Settings" , "" ) menu ("mTools", "Tools" , "" ) menu ("mHelp", "Help" , "" ) @pyqtSlot() def _openMainMenu(self): fileMenu = core.actionManager().menu('mFile') self._menuBar.setActiveAction(fileMenu.menuAction()) def menuBar(self): """Reference to menuBar """ return self._menuBar def topToolBar(self): """Top tool bar. Contains main menu, position indicator, etc """ return self._topToolBar def statusBar(self): """Return main window status bar. It is located on the top tool bar """ return self._statusBar def setWorkspace(self, workspace): """Set central widget of the main window. Normally called only by core when initializing system """ self._centralLayout.addWidget(workspace) self.setFocusProxy(workspace) def defaultTitle(self): """Default title. Contains name and version """ return "%s v.%s" % (enki.core.defines.PACKAGE_NAME, enki.core.defines.PACKAGE_VERSION) def centralLayout(self): """Layout of the central widget. Contains Workspace and search widget """ return self._centralLayout def appendMessage(self, text, timeoutMs=10000): """Append message to the queue. It will be shown as non-modal at the bottom of the window. Use such notifications, which are too long or too important for status bar but, not so important, to interrupt an user with QMessageBox """ if self._queuedMessageToolBar is None: self._initQueuedMessageToolBar() self._queuedMessageToolBar.appendMessage(text, timeoutMs) def closeEvent(self, event): """NOT A PUBLIC API Close event handler. Shows save files dialog. Cancels close, if dialog was rejected """ # saving geometry BEFORE closing widgets, because state might be changed, when docks are closed self._saveState() self._saveGeometry() # request close all documents if not core.workspace().askToCloseAll(): event.ignore() return core.aboutToTerminate.emit() self.hide() core.workspace().forceCloseAllDocuments() return QMainWindow.closeEvent(self, event) def onQuit(self): # saving geometry BEFORE closing widgets, because state might be changed, when docks are closed self._saveState() self._saveGeometry() # request close all documents if not core.workspace().askToCloseAll(): return core.aboutToTerminate.emit() self.hide() core.workspace().forceCloseAllDocuments() return QApplication.quit() def _saveByteArray(self, path, title, data): """Load data, show error and return None if failed""" try: with open(path, 'wb') as f: f.write(data) except (OSError, IOError) as ex: error = str(ex) QMessageBox.critical(None, self.tr("Cannot save {}".format(title)), self.tr("Cannot create file '%s'\nError: %s" % (path, error))) return def _loadByteArray(self, path, title): """Load data, show error and return None if failed""" if os.path.exists(path): try: with open(path, 'rb') as f: return f.read() except (OSError, IOError) as ex: error = str(ex) QMessageBox.critical(None, self.tr("Cannot restore {}".format(title)), self.tr("Cannot read file '%s'\nError: %s" % (path, error))) return None def _saveState(self): """Save window state to main_window_state.bin file in the config directory """ self._saveByteArray(self._STATE_FILE, "main window state", self.saveState()) def loadState(self): """Restore window state from main_window_state.bin and config. Called by the core after all plugins had been initialized """ self._restoreGeometry() state = self._loadByteArray(self._STATE_FILE, "main window state") if state is not None: self.restoreState(state) else: # no state, first start self.showMaximized() for dock in self.findChildren(DockWidget): dock.show() def _saveGeometry(self): """Save window geometry to the config file """ self._saveByteArray(self._GEOMETRY_FILE, "main window geometry", self.saveGeometry()) def _restoreGeometry(self): """Restore window geometry to the config file """ geometry = self._loadByteArray(self._GEOMETRY_FILE, "main window geometry") if geometry is not None: self.restoreGeometry(geometry) def sizeHint(self): return QSize(900, 560) def dragEnterEvent(self, event): """QMainWindow method reimplementation. Say, that we are ready to accept dragged urls """ if event.mimeData().hasUrls(): # accept drag event.acceptProposedAction() # default handler QMainWindow.dragEnterEvent(self, event) def dropEvent(self, event): """QMainWindow method reimplementation. Open dropt files """ if event.mimeData().hasUrls(): for url in event.mimeData().urls(): localFile = url.toLocalFile() if os.path.isfile(localFile): core.workspace().openFile(localFile) elif os.path.isdir(localFile): self.directoryDropt.emit(localFile) # default handler QMainWindow.dropEvent(self, event) def addDockWidget(self, area, dock): pass # not a plugin API method """Add dock widget to previous position, if known. Otherwise add to specified area """ assert not dock in self._addedDockWidgets self._addedDockWidgets.append(dock) if not self.restoreDockWidget(dock): QMainWindow.addDockWidget(self, area, dock) """ Scroll view to make the cursor visible. Otherwise cursor can disappear from the viewport. QTimer is used because ensureCursorVisible works only after the dock has been drawn. A bad fix for #319 """ QTimer.singleShot(0, self._ensureCursorVisible) def _ensureCursorVisible(self): # When the timer fires, first check that there's still a workspace/document. if core.workspace() is not None: document = core.workspace().currentDocument() if document is not None: document.qutepart.ensureCursorVisible def removeDockWidget(self, dock): pass # not a plugin API method """Remove dock widget""" assert dock in self._addedDockWidgets self._addedDockWidgets.remove(dock) QMainWindow.removeDockWidget(self, dock) def restoreState(self, state): pass # not a plugin API method """Restore state shows widgets, which exist but shall not be installed on main window """ QMainWindow.restoreState(self, state) for dock in self.findChildren(DockWidget): if not dock in self._addedDockWidgets: dock.hide()
class MainWindow(QMainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__() self.setWindowTitle("RainbowBB") self.setParent(parent) self.initUI() def initUI(self): self.setStyleSheet("QCheckBox { background: palette(window); border-radius: 4px; padding: 2px; margin-right: 2px; }") self.toolBar = QToolBar(self) self.toolBar.setStyleSheet(stylesheet % (create_gradient("pastel"),)) self.toolBar.setMovable(False) self.toolBar.setContextMenuPolicy(Qt.CustomContextMenu) self.addToolBar(self.toolBar) self.reverseBox = QCheckBox("&Reverse", self) self.reverseBox.clicked.connect(lambda: self.updateGradient()) self.toolBar.addWidget(self.reverseBox) self.byWordBox = QCheckBox("By &word", self) self.toolBar.addWidget(self.byWordBox) self.bounceBox = QCheckBox("&Bounce", self) self.bounceBox.clicked.connect(lambda: self.updateGradient()) self.toolBar.addWidget(self.bounceBox) self.sizeList = QComboBox(self) self.sizeList.addItem("None") for num in range(1, 8): self.sizeList.addItem(str(num)) self.toolBar.addWidget(self.sizeList) self.cycleList = QComboBox(self) self.toolBar.addWidget(self.cycleList) self.cycleList.currentIndexChanged.connect(self.updateGradient) self.loadCycles() self.convertButton = QPushButton("&Convert", self) self.convertButton.clicked.connect(self.convert) self.toolBar.addWidget(self.convertButton) self.reloadButton = QPushButton("Reload", self) self.reloadButton.setShortcut("Alt+Shift+R") self.reloadButton.clicked.connect(self.loadCycles) self.toolBar.addWidget(self.reloadButton) self.inputDock = QDockWidget("Input", self) self.inputDock.setFeatures(QDockWidget.NoDockWidgetFeatures) self.inputDock.setContextMenuPolicy(Qt.CustomContextMenu) self.addDockWidget(Qt.LeftDockWidgetArea, self.inputDock) self.inputField = QTextEdit(self) self.inputField.setAcceptRichText(False) self.inputDock.setWidget(self.inputField) self.outputField = QTextEdit(self) self.outputField.setReadOnly(True) self.setCentralWidget(self.outputField) def updateGradient(self, index=None): if not index: index = self.cycleList.currentIndex() self.toolBar.setStyleSheet(stylesheet % (create_gradient(self.cycleList.itemText(index), self.reverseBox.isChecked(), self.bounceBox.isChecked()),)) def loadCycles(self): rainbowbb.load_cycles() self.cycleList.clear() self.cycleList.addItem("pastel") for cycle in sorted(list(rainbowbb.cycles.keys())): if cycle != "pastel": self.cycleList.addItem(cycle) def show(self): self.setVisible(True) self.inputField.setFocus() def convert(self): self.outputField.setPlainText(rainbowbb.size(rainbowbb.colorize(self.inputField.toPlainText(), self.cycleList.currentText(), self.reverseBox.isChecked(), not self.byWordBox.isChecked(), self.bounceBox.isChecked()), self.sizeList.currentText() if self.sizeList.currentText() != "None" else None))
class ClearHistoryDialog(QMainWindow): def __init__(self, parent=None): super(ClearHistoryDialog, self).__init__(parent) self.setWindowFlags(Qt.Dialog) self.setWindowTitle(tr("Clear Data")) closeWindowAction = QAction(self) closeWindowAction.setShortcuts(["Esc", "Ctrl+W", "Ctrl+Shift+Del"]) closeWindowAction.triggered.connect(self.close) self.addAction(closeWindowAction) self.contents = QWidget() self.layout = QVBoxLayout() self.contents.setLayout(self.layout) self.setCentralWidget(self.contents) label = QLabel(tr("What to clear:"), self) self.layout.addWidget(label) self.dataType = QComboBox(self) self.dataType.addItem(tr("History")) self.dataType.addItem(tr("Cookies")) self.dataType.addItem(tr("Cache")) self.dataType.addItem(tr("Persistent Storage")) self.dataType.addItem(tr("Everything")) self.layout.addWidget(self.dataType) self.toolBar = QToolBar(self) self.toolBar.setStyleSheet(common.blank_toolbar) self.toolBar.setMovable(False) self.toolBar.setContextMenuPolicy(Qt.CustomContextMenu) self.addToolBar(Qt.BottomToolBarArea, self.toolBar) self.clearHistoryButton = QPushButton(tr("Clear"), self) self.clearHistoryButton.clicked.connect(self.clearHistory) self.toolBar.addWidget(self.clearHistoryButton) self.closeButton = QPushButton(tr("Close"), self) self.closeButton.clicked.connect(self.close) self.toolBar.addWidget(self.closeButton) def show(self): self.setVisible(True) def display(self): self.show() self.activateWindow() def clearHistory(self): clear_everything = (self.dataType.currentIndex() == 5) if self.dataType.currentIndex() == 0 or clear_everything: data.clearHistory() if self.dataType.currentIndex() == 1 or clear_everything: data.clearCookies() if self.dataType.currentIndex() == 2 or clear_everything: network.clear_cache() path = settings.offline_cache_folder if os.path.isdir(path): if sys.platform.startswith("win"): try: subprocess.Popen(["rd", path]) except: pass else: try: subprocess.Popen(["rm", "-rf", path]) except: pass if self.dataType.currentIndex() == 3 or clear_everything: for subpath in ("WebpageIcons.db", "LocalStorage", "Databases",): path = os.path.abspath(os.path.join(settings.settings_folder, subpath)) if os.path.isfile(path): try: os.remove(path) except: pass elif os.path.isdir(path): if sys.platform.startswith("win"): try: subprocess.Popen(["rd", path]) except: pass else: try: subprocess.Popen(["rm", "-rf", path]) except: pass
class MainGlyphWindow(QMainWindow): def __init__(self, glyph, parent=None): super().__init__(parent) menuBar = self.menuBar() fileMenu = QMenu("&File", self) fileMenu.addAction("E&xit", self.close, QKeySequence.Quit) menuBar.addMenu(fileMenu) editMenu = QMenu("&Edit", self) self._undoAction = editMenu.addAction( "&Undo", self.undo, QKeySequence.Undo) self._redoAction = editMenu.addAction( "&Redo", self.redo, QKeySequence.Redo) editMenu.addSeparator() # XXX action = editMenu.addAction("C&ut", self.cutOutlines, QKeySequence.Cut) action.setEnabled(False) self._copyAction = editMenu.addAction( "&Copy", self.copyOutlines, QKeySequence.Copy) editMenu.addAction("&Paste", self.pasteOutlines, QKeySequence.Paste) editMenu.addAction( "Select &All", self.selectAll, QKeySequence.SelectAll) editMenu.addAction("&Deselect", self.deselect, "Ctrl+D") menuBar.addMenu(editMenu) glyphMenu = QMenu("&Glyph", self) glyphMenu.addAction("&Next Glyph", lambda: self.glyphOffset(1), "End") glyphMenu.addAction( "&Previous Glyph", lambda: self.glyphOffset(-1), "Home") glyphMenu.addAction("&Go To…", self.changeGlyph, "G") glyphMenu.addSeparator() self._layerAction = glyphMenu.addAction( "&Layer Actions…", self.layerActions, "L") menuBar.addMenu(glyphMenu) # create tools and buttons toolBars self._tools = [] self._toolsToolBar = QToolBar("Tools", self) self._toolsToolBar.setMovable(False) self._buttons = [] self._buttonsToolBar = QToolBar("Buttons", self) self._buttonsToolBar.setMovable(False) self.addToolBar(self._toolsToolBar) self.addToolBar(self._buttonsToolBar) # http://www.setnode.com/blog/right-aligning-a-button-in-a-qtoolbar/ self._layersToolBar = QToolBar("Layers", self) spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self._currentLayerBox = QComboBox(self) self._currentLayerBox.currentIndexChanged.connect( self._layerChanged) self._layersToolBar.addWidget(spacer) self._layersToolBar.addWidget(self._currentLayerBox) self._layersToolBar.setContentsMargins(0, 0, 2, 0) self._layersToolBar.setMovable(False) self.addToolBar(self._layersToolBar) viewMenu = self.createPopupMenu() viewMenu.setTitle("View") viewMenu.addSeparator() action = viewMenu.addAction("Lock Toolbars", self.lockToolBars) action.setCheckable(True) action.setChecked(True) menuBar.addMenu(viewMenu) self.view = GlyphView(self) self.setGlyph(glyph) selectionTool = self.installTool(SelectionTool) selectionTool.trigger() self.installTool(PenTool) self.installTool(RulerTool) self.installTool(KnifeTool) self.installButton(RemoveOverlapButton) self.setCentralWidget(self.view.scrollArea()) self.resize(900, 700) self.view.setFocus(True) # ---------- # Menu items # ---------- def glyphOffset(self, offset): currentGlyph = self.view.glyph() font = currentGlyph.font glyphOrder = font.glyphOrder # should be enforced in fontView already if not (glyphOrder and len(glyphOrder)): return index = glyphOrder.index(currentGlyph.name) newIndex = (index + offset) % len(glyphOrder) glyph = font[glyphOrder[newIndex]] self.setGlyph(glyph) def changeGlyph(self): glyph = self.view.glyph() newGlyph, ok = GotoDialog.getNewGlyph(self, glyph) if ok and newGlyph is not None: self.setGlyph(newGlyph) def layerActions(self): glyph = self.view.glyph() newLayer, action, ok = LayerActionsDialog.getLayerAndAction( self, glyph) if ok and newLayer is not None: # TODO: whole glyph for now, but consider selection too if not glyph.name in newLayer: newLayer.newGlyph(glyph.name) otherGlyph = newLayer[glyph.name] otherGlyph.disableNotifications() if action == "Swap": tempGlyph = TGlyph() otherGlyph.drawPoints(tempGlyph.getPointPen()) tempGlyph.width = otherGlyph.width otherGlyph.clearContours() glyph.drawPoints(otherGlyph.getPointPen()) otherGlyph.width = glyph.width if action != "Copy": glyph.disableNotifications() glyph.clearContours() if action == "Swap": tempGlyph.drawPoints(glyph.getPointPen()) glyph.width = tempGlyph.width glyph.enableNotifications() otherGlyph.enableNotifications() def undo(self): glyph = self.view.glyph() glyph.undo() def redo(self): glyph = self.view.glyph() glyph.redo() def cutOutlines(self): pass def copyOutlines(self): glyph = self.view.glyph() clipboard = QApplication.clipboard() mimeData = QMimeData() copyGlyph = glyph.getRepresentation("defconQt.FilterSelection") mimeData.setData("application/x-defconQt-glyph-data", pickle.dumps([copyGlyph.serialize( blacklist=("name", "unicode") )])) clipboard.setMimeData(mimeData) def pasteOutlines(self): glyph = self.view.glyph() clipboard = QApplication.clipboard() mimeData = clipboard.mimeData() if mimeData.hasFormat("application/x-defconQt-glyph-data"): data = pickle.loads(mimeData.data( "application/x-defconQt-glyph-data")) if len(data) == 1: pen = glyph.getPointPen() pasteGlyph = TGlyph() pasteGlyph.deserialize(data[0]) # TODO: if we serialize selected state, we don't need to do # this pasteGlyph.selected = True if len(pasteGlyph) or len(pasteGlyph.components) or \ len(pasteGlyph.anchors): glyph.prepareUndo() pasteGlyph.drawPoints(pen) def selectAll(self): glyph = self.view.glyph() glyph.selected = True if not len(glyph): for component in glyph.components: component.selected = True def deselect(self): glyph = self.view.glyph() for anchor in glyph.anchors: anchor.selected = False for component in glyph.components: component.selected = False glyph.selected = False def lockToolBars(self): action = self.sender() movable = not action.isChecked() for toolBar in ( self._toolsToolBar, self._buttonsToolBar, self._layersToolBar): toolBar.setMovable(movable) # -------------------------- # Tools & buttons management # -------------------------- def installTool(self, tool): action = self._toolsToolBar.addAction( QIcon(tool.iconPath), tool.name, self._setViewTool) action.setCheckable(True) num = len(self._tools) action.setData(num) action.setShortcut(QKeySequence(str(num + 1))) self._tools.append(tool(parent=self.view)) return action def uninstallTool(self, tool): pass # XXX def _setViewTool(self): action = self.sender() index = action.data() newTool = self._tools[index] if newTool == self.view.currentTool(): action.setChecked(True) return ok = self.view.setCurrentTool(newTool) # if view did change tool, disable them all and enable the one we want # otherwise, just disable the tool that was clicked. # previously we used QActionGroup to have exclusive buttons, but doing # it manually allows us to NAK a button change. if ok: for act in self._toolsToolBar.actions(): act.setChecked(False) action.setChecked(ok) def installButton(self, button): action = self._buttonsToolBar.addAction( QIcon(button.iconPath), button.name, self._buttonAction) action.setData(len(self._buttons)) self._buttons.append(button(parent=self.view)) return action def uninstallButton(self, button): pass # XXX def _buttonAction(self): action = self.sender() index = action.data() button = self._buttons[index] button.clicked() # -------------------- # Notification support # -------------------- # glyph def _subscribeToGlyph(self, glyph): if glyph is not None: glyph.addObserver(self, "_glyphChanged", "Glyph.Changed") glyph.addObserver(self, "_glyphNameChanged", "Glyph.NameChanged") glyph.addObserver( self, "_glyphSelectionChanged", "Glyph.SelectionChanged") undoManager = glyph.undoManager undoManager.canUndoChanged.connect(self._undoAction.setEnabled) undoManager.canRedoChanged.connect(self._redoAction.setEnabled) self._subscribeToFontAndLayerSet(glyph.font) def _unsubscribeFromGlyph(self, glyph): if glyph is not None: glyph.removeObserver(self, "Glyph.Changed") glyph.removeObserver(self, "Glyph.NameChanged") glyph.removeObserver(self, "Glyph.SelectionChanged") undoManager = glyph.undoManager undoManager.canUndoChanged.disconnect(self._undoAction.setEnabled) undoManager.canRedoChanged.disconnect(self._redoAction.setEnabled) self._unsubscribeFromFontAndLayerSet(glyph.font) def _glyphChanged(self, notification): self.view.glyphChanged() def _glyphNameChanged(self, notification): glyph = self.view.glyph() self.setWindowTitle(glyph.name, glyph.font) def _glyphSelectionChanged(self, notification): self._updateSelection() self.view.glyphChanged() def _fontInfoChanged(self, notification): self.view.fontInfoChanged() glyph = self.view.glyph() self.setWindowTitle(glyph.name, glyph.font) # layers & font def _subscribeToFontAndLayerSet(self, font): """Note: called by _subscribeToGlyph.""" if font is None: return font.info.addObserver(self, "_fontInfoChanged", "Info.Changed") layerSet = font.layers if layerSet is None: return layerSet.addObserver(self, '_layerSetLayerDeleted', 'LayerSet.LayerDeleted') for event in ('LayerSet.LayerAdded', 'LayerSet.LayerChanged', 'LayerSet.LayerOrderChanged'): layerSet.addObserver(self, '_layerSetEvents', event) def _unsubscribeFromFontAndLayerSet(self, font): """Note: called by _unsubscribeFromGlyph.""" if font is None: return font.info.removeObserver(self, "Info.Changed") layerSet = font.layers if layerSet is None: return for event in ('LayerSet.LayerAdded', 'LayerSet.LayerChanged', 'LayerSet.LayerOrderChanged', 'LayerSet.LayerDeleted'): layerSet.removeObserver(self, event) def _layerSetEvents(self, notification): self._updateLayerControls() def _layerSetLayerDeleted(self, notification): self._layerSetEvents(notification) self._currentLayerBox.setCurrentIndex(0) # other updaters def _updateUndoRedo(self): glyph = self.view.glyph() self._undoAction.setEnabled(glyph.canUndo()) self._redoAction.setEnabled(glyph.canRedo()) def _updateSelection(self): def hasSelection(): glyph = self.view.glyph() for contour in glyph: if len(contour.selection): return True for anchor in glyph.anchors: if anchor.selected: return True for component in glyph.components: if component.selected: return True return False self._copyAction.setEnabled(hasSelection()) # -------------- # Public Methods # -------------- def setGlyph(self, glyph): currentGlyph = self.view.glyph() self._unsubscribeFromGlyph(currentGlyph) self._subscribeToGlyph(glyph) self.view.setGlyph(glyph) self._updateLayerControls() self._updateUndoRedo() self._updateSelection() self.setWindowTitle(glyph.name, glyph.font) def setDrawingAttribute(self, attr, value, layerName=None): self.view.setDrawingAttribute(attr, value, layerName) def drawingAttribute(self, attr, layerName=None): return self.view.drawingAttribute(attr, layerName) # ----------------- # Layers management # ----------------- def _layerChanged(self, newLayerIndex): glyph = self.view.glyph() layer = self._currentLayerBox.itemData(newLayerIndex) if layer is None: layer = self._makeLayer() if layer is None: # restore comboBox to active index layerSet = glyph.layerSet index = layerSet.layerOrder.index(glyph.layer.name) self._setLayerBoxIndex(index) return if glyph.name in layer: newGlyph = layer[glyph.name] else: # TODO: make sure we mimic defcon ufo3 APIs for that newGlyph = self._makeLayerGlyph(layer, glyph) self.setGlyph(newGlyph) # setting the layer-glyph here app = QApplication.instance() app.setCurrentGlyph(newGlyph) def _makeLayer(self): # TODO: what with duplicate names? glyph = self.view.glyph() newLayerName, ok = AddLayerDialog.getNewLayerName(self) if ok: layerSet = glyph.layerSet # TODO: this should return the layer layerSet.newLayer(newLayerName) return layerSet[newLayerName] else: return None def _makeLayerGlyph(self, layer, currentGlyph): glyph = layer.newGlyph(currentGlyph.name) glyph.width = currentGlyph.width glyph.template = True return glyph def _updateLayerControls(self): comboBox = self._currentLayerBox glyph = self.view.glyph() comboBox.blockSignals(True) comboBox.clear() for layer in glyph.layerSet: comboBox.addItem(layer.name, layer) comboBox.setCurrentText(glyph.layer.name) comboBox.addItem("New layer…", None) comboBox.blockSignals(False) self._layerAction.setEnabled(len(glyph.layerSet) > 1) def _setLayerBoxIndex(self, index): comboBox = self._currentLayerBox comboBox.blockSignals(True) comboBox.setCurrentIndex(index) comboBox.blockSignals(False) # --------------------- # QMainWindow functions # --------------------- def event(self, event): if event.type() == QEvent.WindowActivate: app = QApplication.instance() app.setCurrentGlyph(self.view.glyph()) return super().event(event) def closeEvent(self, event): glyph = self.view.glyph() self._unsubscribeFromGlyph(glyph) event.accept() def setWindowTitle(self, title, font=None): if font is not None: title = "%s – %s %s" % ( title, font.info.familyName, font.info.styleName) super().setWindowTitle(title)
class GlyphWindow(BaseMainWindow): def __init__(self, glyph, parent=None): super().__init__(parent) self.setAttribute(Qt.WA_DeleteOnClose, False) self.setUnifiedTitleAndToolBarOnMac(True) self.view = GlyphCanvasView(self) # create tools and buttons toolBars self._tools = [] self._toolsToolBar = QToolBar(self.tr("Tools"), self) self._toolsToolBar.setMovable(False) self._buttons = [] self._buttonsToolBar = QToolBar(self.tr("Buttons"), self) self._buttonsToolBar.setMovable(False) self.addToolBar(self._toolsToolBar) self.addToolBar(self._buttonsToolBar) # http://www.setnode.com/blog/right-aligning-a-button-in-a-qtoolbar/ self._layersToolBar = QToolBar(self.tr("Layers"), self) spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self._currentLayerBox = QComboBox(self) self._currentLayerBox.currentIndexChanged.connect( self._layerChanged) self._layersToolBar.addWidget(spacer) self._layersToolBar.addWidget(self._currentLayerBox) self._layersToolBar.setContentsMargins(0, 0, 2, 0) self._layersToolBar.setMovable(False) self.addToolBar(self._layersToolBar) self.setGlyph(glyph) app = QApplication.instance() tools = app.drawingTools() for index, tool in enumerate(tools): action = self.installTool(tool) if not index: action.trigger() app.dispatcher.addObserver( self, "_drawingToolRegistered", "drawingToolRegistered") # TODO: drawingToolUnregistered self.installButton(RemoveOverlapButton) self.setCentralWidget(self.view) self.view.setFocus(True) self.readSettings() def readSettings(self): geometry = settings.glyphWindowGeometry() if geometry: self.restoreGeometry(geometry) def writeSettings(self): # TODO: save current tool? settings.setGlyphWindowGeometry(self.saveGeometry()) def setupMenu(self, menuBar): fileMenu = menuBar.fetchMenu(Entries.File) fontWindow = self.parent() if fontWindow is not None: fileMenu.fetchAction(Entries.File_Save, fontWindow.saveFile) fileMenu.fetchAction(Entries.File_Save_As, fontWindow.saveFileAs) fileMenu.fetchAction(Entries.File_Close, self.close) if fontWindow is not None: fileMenu.fetchAction(Entries.File_Reload, fontWindow.reloadFile) editMenu = menuBar.fetchMenu(Entries.Edit) self._undoAction = editMenu.fetchAction(Entries.Edit_Undo, self.undo) self._redoAction = editMenu.fetchAction(Entries.Edit_Redo, self.redo) editMenu.addSeparator() cutAction = editMenu.fetchAction(Entries.Edit_Cut, self.cutOutlines) copyAction = editMenu.fetchAction(Entries.Edit_Copy, self.copyOutlines) self._selectActions = (cutAction, copyAction) editMenu.fetchAction(Entries.Edit_Paste, self.pasteOutlines) editMenu.fetchAction(Entries.Edit_Select_All, self.selectAll) editMenu.fetchAction(Entries.Edit_Find, self.changeGlyph) # TODO: sort this out # editMenu.fetchAction(self.tr("&Deselect"), self.deselect, "Ctrl+D") # glyphMenu = menuBar.fetchMenu(self.tr("&Glyph")) # self._layerAction = glyphMenu.fetchAction( # self.tr("&Layer Actions…"), self.layerActions, "L") viewMenu = menuBar.fetchMenu(Entries.View) viewMenu.fetchAction(Entries.View_Zoom_In, lambda: self.view.zoom(1)) viewMenu.fetchAction(Entries.View_Zoom_Out, lambda: self.view.zoom(-1)) viewMenu.fetchAction(Entries.View_Reset_Zoom, self.view.fitScaleBBox) viewMenu.addSeparator() viewMenu.fetchAction( Entries.View_Next_Glyph, lambda: self.glyphOffset(1)) viewMenu.fetchAction( Entries.View_Previous_Glyph, lambda: self.glyphOffset(-1)) self._updateUndoRedo() # ---------- # Menu items # ---------- def saveFile(self): glyph = self.view.glyph() font = glyph.font if None not in (font, font.path): font.save() def glyphOffset(self, offset): currentGlyph = self.view.glyph() font = currentGlyph.font glyphOrder = font.glyphOrder # should be enforced in fontView already if not (glyphOrder and len(glyphOrder)): return index = glyphOrder.index(currentGlyph.name) newIndex = (index + offset) % len(glyphOrder) glyph = font[glyphOrder[newIndex]] self.setGlyph(glyph) def changeGlyph(self): glyph = self.view.glyph() newGlyph, ok = FindDialog.getNewGlyph(self, glyph) if ok and newGlyph is not None: self.setGlyph(newGlyph) def layerActions(self): glyph = self.view.glyph() newLayer, action, ok = LayerActionsDialog.getLayerAndAction( self, glyph) if ok and newLayer is not None: # TODO: whole glyph for now, but consider selection too if glyph.name not in newLayer: newLayer.newGlyph(glyph.name) otherGlyph = newLayer[glyph.name] otherGlyph.holdNotifications() if action == "Swap": tempGlyph = glyph.__class__() otherGlyph.drawPoints(tempGlyph.getPointPen()) tempGlyph.width = otherGlyph.width otherGlyph.clearContours() glyph.drawPoints(otherGlyph.getPointPen()) otherGlyph.width = glyph.width if action != "Copy": glyph.holdNotifications() glyph.clearContours() if action == "Swap": tempGlyph.drawPoints(glyph.getPointPen()) glyph.width = tempGlyph.width glyph.releaseHeldNotifications() otherGlyph.releaseHeldNotifications() def undo(self): glyph = self.view.glyph() glyph.undo() def redo(self): glyph = self.view.glyph() glyph.redo() def cutOutlines(self): glyph = self.view.glyph() self.copyOutlines() deleteUISelection(glyph) def copyOutlines(self): glyph = self.view.glyph() clipboard = QApplication.clipboard() mimeData = QMimeData() copyGlyph = glyph.getRepresentation("TruFont.FilterSelection") mimeData.setData("application/x-trufont-glyph-data", pickle.dumps([copyGlyph.serialize( blacklist=("name", "unicode") )])) clipboard.setMimeData(mimeData) def pasteOutlines(self): glyph = self.view.glyph() clipboard = QApplication.clipboard() mimeData = clipboard.mimeData() if mimeData.hasFormat("application/x-trufont-glyph-data"): data = pickle.loads(mimeData.data( "application/x-trufont-glyph-data")) if len(data) == 1: pen = glyph.getPointPen() pasteGlyph = glyph.__class__() pasteGlyph.deserialize(data[0]) # TODO: if we serialize selected state, we don't need to do # this pasteGlyph.selected = True if len(pasteGlyph) or len(pasteGlyph.components) or \ len(pasteGlyph.anchors): glyph.prepareUndo() pasteGlyph.drawPoints(pen) def selectAll(self): glyph = self.view.glyph() if glyph.selected: for anchor in glyph.anchors: anchor.selected = True for component in glyph.components: component.selected = True else: glyph.selected = True def deselect(self): glyph = self.view.glyph() for anchor in glyph.anchors: anchor.selected = False for component in glyph.components: component.selected = False glyph.selected = False def lockToolBars(self): action = self.sender() movable = not action.isChecked() for toolBar in ( self._toolsToolBar, self._buttonsToolBar, self._layersToolBar): toolBar.setMovable(movable) # -------------------------- # Tools & buttons management # -------------------------- def installTool(self, tool): action = self._toolsToolBar.addAction( QIcon(tool.iconPath), tool.name, self._setViewTool) action.setCheckable(True) num = len(self._tools) action.setData(num) action.setShortcut(QKeySequence(str(num + 1))) self._tools.append(tool(parent=self.view.widget())) return action def uninstallTool(self, tool): pass # XXX def _setViewTool(self): action = self.sender() index = action.data() newTool = self._tools[index] if newTool == self.view.currentTool(): action.setChecked(True) return ok = self.view.setCurrentTool(newTool) # if view did change tool, disable them all and enable the one we want # otherwise, just disable the tool that was clicked. # previously we used QActionGroup to have exclusive buttons, but doing # it manually allows us to NAK a button change. if ok: for act in self._toolsToolBar.actions(): act.setChecked(False) action.setChecked(ok) def installButton(self, button): action = self._buttonsToolBar.addAction( QIcon(button.iconPath), button.name, self._buttonAction) action.setData(len(self._buttons)) self._buttons.append(button(parent=self.view)) return action def uninstallButton(self, button): pass # XXX def _buttonAction(self): action = self.sender() index = action.data() button = self._buttons[index] button.clicked() # ------------- # Notifications # ------------- # app def _drawingToolRegistered(self, notification): tool = notification.data["tool"] self.installTool(tool) # glyph def _subscribeToGlyph(self, glyph): if glyph is not None: glyph.addObserver(self, "_glyphNameChanged", "Glyph.NameChanged") glyph.addObserver( self, "_glyphSelectionChanged", "Glyph.SelectionChanged") undoManager = glyph.undoManager undoManager.canUndoChanged.connect(self._setUndoEnabled) undoManager.canRedoChanged.connect(self._setRedoEnabled) self._subscribeToFontAndLayerSet(glyph.font) def _unsubscribeFromGlyph(self, glyph): if glyph is not None: glyph.removeObserver(self, "Glyph.NameChanged") glyph.removeObserver(self, "Glyph.SelectionChanged") undoManager = glyph.undoManager undoManager.canUndoChanged.disconnect(self._setUndoEnabled) undoManager.canRedoChanged.disconnect(self._setRedoEnabled) self._unsubscribeFromFontAndLayerSet(glyph.font) def _glyphNameChanged(self, notification): glyph = self.view.glyph() self.setWindowTitle(glyph.name, glyph.font) def _glyphSelectionChanged(self, notification): self._updateSelection() # layers & font def _subscribeToFontAndLayerSet(self, font): """Note: called by _subscribeToGlyph.""" if font is None: return font.info.addObserver(self, "_fontInfoChanged", "Info.Changed") layerSet = font.layers if layerSet is None: return layerSet.addObserver( self, '_layerSetLayerDeleted', 'LayerSet.LayerDeleted') for event in ('LayerSet.LayerAdded', 'LayerSet.LayerChanged', 'LayerSet.LayerOrderChanged'): layerSet.addObserver(self, '_layerSetEvents', event) def _unsubscribeFromFontAndLayerSet(self, font): """Note: called by _unsubscribeFromGlyph.""" if font is None: return font.info.removeObserver(self, "Info.Changed") layerSet = font.layers if layerSet is None: return for event in ('LayerSet.LayerAdded', 'LayerSet.LayerChanged', 'LayerSet.LayerOrderChanged', 'LayerSet.LayerDeleted'): layerSet.removeObserver(self, event) def _fontInfoChanged(self, notification): glyph = self.view.glyph() self.setWindowTitle(glyph.name, glyph.font) def _layerSetEvents(self, notification): self._updateLayerControls() def _layerSetLayerDeleted(self, notification): self._layerSetEvents(notification) self._currentLayerBox.setCurrentIndex(0) # other updaters def _updateSelection(self): def hasSelection(): glyph = self.view.glyph() for contour in glyph: if len(contour.selection): return True for anchor in glyph.anchors: if anchor.selected: return True for component in glyph.components: if component.selected: return True return False if not hasattr(self, "_selectActions"): return hasSelection = hasSelection() for action in self._selectActions: action.setEnabled(hasSelection) def _updateUndoRedo(self): glyph = self.view.glyph() self._setUndoEnabled(glyph.canUndo()) self._setRedoEnabled(glyph.canRedo()) def _setUndoEnabled(self, value): if not hasattr(self, "_undoAction"): return self._undoAction.setEnabled(value) def _setRedoEnabled(self, value): if not hasattr(self, "_redoAction"): return self._redoAction.setEnabled(value) # -------------- # Public Methods # -------------- def setGlyph(self, glyph): currentGlyph = self.view.glyph() self._unsubscribeFromGlyph(currentGlyph) self.view.setGlyph(glyph) self._subscribeToGlyph(glyph) self._updateLayerControls() self._updateUndoRedo() self._updateSelection() self.setWindowTitle(glyph.name, glyph.font) # setting the layer-glyph here app = QApplication.instance() app.setCurrentGlyph(glyph) # ----------------- # Layers management # ----------------- def _layerChanged(self, newLayerIndex): glyph = self.view.glyph() layer = self._currentLayerBox.itemData(newLayerIndex) if layer is None: layer = self._makeLayer() if layer is None: # restore comboBox to active index layerSet = glyph.layerSet index = layerSet.layerOrder.index(glyph.layer.name) self._setLayerBoxIndex(index) return if glyph.name in layer: newGlyph = layer[glyph.name] else: # TODO: make sure we mimic defcon ufo3 APIs for that newGlyph = self._makeLayerGlyph(layer, glyph) self.setGlyph(newGlyph) def _makeLayer(self): # TODO: what with duplicate names? glyph = self.view.glyph() newLayerName, color, ok = AddLayerDialog.getNewLayerNameAndColor(self) if ok: layerSet = glyph.layerSet layer = layerSet.newLayer(newLayerName) layer.color = color return layer return None def _makeLayerGlyph(self, layer, currentGlyph): glyph = layer.newGlyph(currentGlyph.name) glyph.width = currentGlyph.width glyph.template = True return glyph def _updateLayerControls(self): comboBox = self._currentLayerBox glyph = self.view.glyph() comboBox.blockSignals(True) comboBox.clear() for layer in glyph.layerSet: comboBox.addItem(layer.name, layer) comboBox.setCurrentText(glyph.layer.name) comboBox.addItem(self.tr("New layer…"), None) comboBox.blockSignals(False) if not hasattr(self, "_layerAction"): return self._layerAction.setEnabled(len(glyph.layerSet) > 1) def _setLayerBoxIndex(self, index): comboBox = self._currentLayerBox comboBox.blockSignals(True) comboBox.setCurrentIndex(index) comboBox.blockSignals(False) # --------------------- # QMainWindow functions # --------------------- def sizeHint(self): return QSize(1100, 750) def moveEvent(self, event): self.writeSettings() resizeEvent = moveEvent def event(self, event): if event.type() == QEvent.WindowActivate: app = QApplication.instance() app.setCurrentGlyph(self.view.glyph()) return super().event(event) def showEvent(self, event): app = QApplication.instance() data = dict(window=self) app.postNotification("glyphWindowWillOpen", data) super().showEvent(event) app.postNotification("glyphWindowOpened", data) def closeEvent(self, event): super().closeEvent(event) if event.isAccepted(): app = QApplication.instance() app.dispatcher.removeObserver(self, "drawingToolRegistered") data = dict(window=self) app.postNotification("glyphWindowWillClose", data) glyph = self.view.glyph() self._unsubscribeFromGlyph(glyph) self.view.closeEvent(event) def setWindowTitle(self, title, font=None): if font is not None: title = "%s – %s %s" % ( title, font.info.familyName, font.info.styleName) super().setWindowTitle(title)
class SimulationGui(QMainWindow): """ class for the graphical user interface """ # TODO enable closing plot docks by right-clicking their name runSimulation = pyqtSignal() stopSimulation = pyqtSignal() playbackTimeChanged = pyqtSignal() regimeFinished = pyqtSignal() finishedRegimeBatch = pyqtSignal(bool) def __init__(self): # constructor of the base class QMainWindow.__init__(self) QCoreApplication.setOrganizationName("RST") QCoreApplication.setOrganizationDomain("https://tu-dresden.de/rst") QCoreApplication.setApplicationVersion( pkg_resources.require("PyMoskito")[0].version) QCoreApplication.setApplicationName(globals()["__package__"]) # load settings self._settings = QSettings() self._read_settings() # initialize logger self._logger = logging.getLogger(self.__class__.__name__) # Create Simulation Backend self.guiProgress = None self.cmdProgress = None self.sim = SimulatorInteractor(self) self.runSimulation.connect(self.sim.run_simulation) self.stopSimulation.connect(self.sim.stop_simulation) self.sim.simulation_finalized.connect(self.new_simulation_data) self.currentDataset = None self.interpolator = None # sim setup viewer self.targetView = SimulatorView(self) self.targetView.setModel(self.sim.target_model) self.targetView.expanded.connect(self.target_view_changed) self.targetView.collapsed.connect(self.target_view_changed) # sim results viewer self.result_view = QTreeView() # the docking area allows to rearrange the user interface at runtime self.area = pg.dockarea.DockArea() # Window properties icon_size = QSize(25, 25) self.setCentralWidget(self.area) self.resize(1000, 700) self.setWindowTitle("PyMoskito") res_path = get_resource("mosquito.png") icon = QIcon(res_path) self.setWindowIcon(icon) # create docks self.propertyDock = pg.dockarea.Dock("Properties") self.animationDock = pg.dockarea.Dock("Animation") self.regimeDock = pg.dockarea.Dock("Regimes") self.dataDock = pg.dockarea.Dock("Data") self.logDock = pg.dockarea.Dock("Log") self.plotDockPlaceholder = pg.dockarea.Dock("Placeholder") # arrange docks self.area.addDock(self.animationDock, "right") self.area.addDock(self.regimeDock, "left", self.animationDock) self.area.addDock(self.propertyDock, "bottom", self.regimeDock) self.area.addDock(self.dataDock, "bottom", self.propertyDock) self.area.addDock(self.plotDockPlaceholder, "bottom", self.animationDock) self.area.addDock(self.logDock, "bottom", self.dataDock) self.non_plotting_docks = list(self.area.findAll()[1].keys()) # add widgets to the docks self.propertyDock.addWidget(self.targetView) if not vtk_available: self._logger.error("loading vtk failed with:{}".format(vtk_error_msg)) # check if there is a registered visualizer available_vis = get_registered_visualizers() self._logger.info("found visualizers: {}".format( [name for cls, name in available_vis])) if available_vis: # instantiate the first visualizer self._logger.info("loading visualizer '{}'".format(available_vis[0][1])) self.animationLayout = QVBoxLayout() if issubclass(available_vis[0][0], MplVisualizer): self.animationWidget = QWidget() self.visualizer = available_vis[0][0](self.animationWidget, self.animationLayout) self.animationDock.addWidget(self.animationWidget) elif issubclass(available_vis[0][0], VtkVisualizer): if vtk_available: # vtk window self.animationFrame = QFrame() self.vtkWidget = QVTKRenderWindowInteractor( self.animationFrame) self.animationLayout.addWidget(self.vtkWidget) self.animationFrame.setLayout(self.animationLayout) self.animationDock.addWidget(self.animationFrame) self.vtk_renderer = vtkRenderer() self.vtkWidget.GetRenderWindow().AddRenderer( self.vtk_renderer) self.visualizer = available_vis[0][0](self.vtk_renderer) self.vtkWidget.Initialize() else: self._logger.warning("visualizer depends on vtk which is " "not available on this system!") elif available_vis: raise NotImplementedError else: self.visualizer = None # regime window self.regime_list = QListWidget(self) self.regime_list.setSelectionMode(QAbstractItemView.ExtendedSelection) self.regimeDock.addWidget(self.regime_list) self.regime_list.itemDoubleClicked.connect(self.regime_dclicked) self._regimes = [] self.regime_file_name = "" self.actDeleteRegimes = QAction(self.regime_list) self.actDeleteRegimes.setText("&Delete Selected Regimes") # TODO shortcut works always, not only with focus on the regime list # self.actDeleteRegimes.setShortcutContext(Qt.WindowShortcut) self.actDeleteRegimes.setShortcut(QKeySequence(Qt.Key_Delete)) self.actDeleteRegimes.triggered.connect(self.remove_regime_items) self.actSave = QAction(self) self.actSave.setText('Save Results As') self.actSave.setIcon(QIcon(get_resource("save.png"))) self.actSave.setDisabled(True) self.actSave.setShortcut(QKeySequence.Save) self.actSave.triggered.connect(self.export_simulation_data) self.actLoadRegimes = QAction(self) self.actLoadRegimes.setText("Load Regimes from File") self.actLoadRegimes.setIcon(QIcon(get_resource("load.png"))) self.actLoadRegimes.setDisabled(False) self.actLoadRegimes.setShortcut(QKeySequence.Open) self.actLoadRegimes.triggered.connect(self.load_regime_dialog) self.actExitOnBatchCompletion = QAction(self) self.actExitOnBatchCompletion.setText("&Exit On Batch Completion") self.actExitOnBatchCompletion.setCheckable(True) self.actExitOnBatchCompletion.setChecked( self._settings.value("control/exit_on_batch_completion") == "True" ) self.actExitOnBatchCompletion.changed.connect( self.update_exit_on_batch_completion_setting) # regime management self.runningBatch = False self._current_regime_index = None self._current_regime_name = None self._regimes = [] self.regimeFinished.connect(self.run_next_regime) self.finishedRegimeBatch.connect(self.regime_batch_finished) # data window self.dataList = QListWidget(self) self.dataDock.addWidget(self.dataList) self.dataList.itemDoubleClicked.connect(self.create_plot) # actions for simulation control self.actSimulateCurrent = QAction(self) self.actSimulateCurrent.setText("&Simulate Current Regime") self.actSimulateCurrent.setIcon(QIcon(get_resource("simulate.png"))) self.actSimulateCurrent.setShortcut(QKeySequence("F5")) self.actSimulateCurrent.triggered.connect(self.start_simulation) self.actSimulateAll = QAction(self) self.actSimulateAll.setText("Simulate &All Regimes") self.actSimulateAll.setIcon(QIcon(get_resource("execute_regimes.png"))) self.actSimulateAll.setShortcut(QKeySequence("F6")) self.actSimulateAll.setDisabled(True) self.actSimulateAll.triggered.connect(self.start_regime_execution) # actions for animation control self.actAutoPlay = QAction(self) self.actAutoPlay.setText("&Autoplay Simulation") self.actAutoPlay.setCheckable(True) self.actAutoPlay.setChecked( self._settings.value("control/autoplay_animation") == "True" ) self.actAutoPlay.changed.connect(self.update_autoplay_setting) self.actPlayPause = QAction(self) self.actPlayPause.setText("Play Animation") self.actPlayPause.setIcon(QIcon(get_resource("play.png"))) self.actPlayPause.setDisabled(True) self.actPlayPause.setShortcut(QKeySequence(Qt.Key_Space)) self.actPlayPause.triggered.connect(self.play_animation) self.actStop = QAction(self) self.actStop.setText("Stop") self.actStop.setIcon(QIcon(get_resource("stop.png"))) self.actStop.setDisabled(True) self.actStop.triggered.connect(self.stop_animation) self.actSlow = QAction(self) self.actSlow.setText("Slowest") self.actSlow.setIcon(QIcon(get_resource("slow.png"))) self.actSlow.setDisabled(False) self.actSlow.triggered.connect(self.set_slowest_playback_speed) self.actFast = QAction(self) self.actFast.setText("Fastest") self.actFast.setIcon(QIcon(get_resource("fast.png"))) self.actFast.setDisabled(False) self.actFast.triggered.connect(self.set_fastest_playback_speed) self.speedControl = QSlider(Qt.Horizontal, self) self.speedControl.setMaximumSize(200, 25) self.speedControl.setTickPosition(QSlider.TicksBothSides) self.speedControl.setDisabled(False) self.speedControl.setMinimum(0) self.speedControl.setMaximum(12) self.speedControl.setValue(6) self.speedControl.setTickInterval(6) self.speedControl.setSingleStep(2) self.speedControl.setPageStep(3) self.speedControl.valueChanged.connect(self.update_playback_speed) self.timeSlider = QSlider(Qt.Horizontal, self) self.timeSlider.setMinimum(0) self.timeSliderRange = 1000 self.timeSlider.setMaximum(self.timeSliderRange) self.timeSlider.setTickInterval(1) self.timeSlider.setTracking(True) self.timeSlider.setDisabled(True) self.timeSlider.valueChanged.connect(self.update_playback_time) self.playbackTime = .0 self.playbackGain = 1 self.currentStepSize = .0 self.currentEndTime = .0 self.playbackTimer = QTimer() self.playbackTimer.timeout.connect(self.increment_playback_time) self.playbackTimeChanged.connect(self.update_gui) self.playbackTimeout = 33 # in [ms] -> 30 fps self.actResetCamera = QAction(self) self.actResetCamera.setText("Reset Camera") self.actResetCamera.setIcon(QIcon(get_resource("reset_camera.png"))) self.actResetCamera.setDisabled(True) if available_vis: self.actResetCamera.setEnabled(self.visualizer.can_reset_view) self.actResetCamera.triggered.connect(self.reset_camera_clicked) # postprocessing self.actPostprocessing = QAction(self) self.actPostprocessing.setText("Launch Postprocessor") self.actPostprocessing.setIcon(QIcon(get_resource("processing.png"))) self.actPostprocessing.setDisabled(False) self.actPostprocessing.triggered.connect(self.postprocessing_clicked) self.actPostprocessing.setShortcut(QKeySequence("F7")) self.postprocessor = None # toolbar self.toolbarSim = QToolBar("Simulation") self.toolbarSim.setContextMenuPolicy(Qt.PreventContextMenu) self.toolbarSim.setMovable(False) self.toolbarSim.setIconSize(icon_size) self.addToolBar(self.toolbarSim) self.toolbarSim.addAction(self.actLoadRegimes) self.toolbarSim.addAction(self.actSave) self.toolbarSim.addSeparator() self.toolbarSim.addAction(self.actSimulateCurrent) self.toolbarSim.addAction(self.actSimulateAll) self.toolbarSim.addSeparator() self.toolbarSim.addAction(self.actPlayPause) self.toolbarSim.addAction(self.actStop) self.toolbarSim.addWidget(self.timeSlider) self.toolbarSim.addSeparator() self.toolbarSim.addAction(self.actSlow) self.toolbarSim.addWidget(self.speedControl) self.toolbarSim.addAction(self.actFast) self.toolbarSim.addSeparator() self.toolbarSim.addAction(self.actPostprocessing) self.toolbarSim.addAction(self.actResetCamera) self.postprocessor = None # log dock self.logBox = QPlainTextEdit(self) self.logBox.setReadOnly(True) self.logDock.addWidget(self.logBox) # init logger for logging box self.textLogger = PlainTextLogger(logging.INFO) self.textLogger.set_target_cb(self.logBox.appendPlainText) logging.getLogger().addHandler(self.textLogger) # menu bar fileMenu = self.menuBar().addMenu("&File") fileMenu.addAction(self.actLoadRegimes) fileMenu.addAction(self.actSave) fileMenu.addAction("&Quit", self.close) editMenu = self.menuBar().addMenu("&Edit") editMenu.addAction(self.actDeleteRegimes) simMenu = self.menuBar().addMenu("&Simulation") simMenu.addAction(self.actSimulateCurrent) simMenu.addAction(self.actSimulateAll) simMenu.addAction(self.actExitOnBatchCompletion) simMenu.addAction(self.actPostprocessing) animMenu = self.menuBar().addMenu("&Animation") animMenu.addAction(self.actPlayPause) animMenu.addAction("&Increase Playback Speed", self.increment_playback_speed, QKeySequence(Qt.CTRL + Qt.Key_Plus)) animMenu.addAction("&Decrease Playback Speed", self.decrement_playback_speed, QKeySequence(Qt.CTRL + Qt.Key_Minus)) animMenu.addAction("&Reset Playback Speed", self.reset_playback_speed, QKeySequence(Qt.CTRL + Qt.Key_0)) animMenu.addAction(self.actAutoPlay) animMenu.addAction(self.actResetCamera) helpMenu = self.menuBar().addMenu("&Help") helpMenu.addAction("&Online Documentation", self.show_online_docs) helpMenu.addAction("&About", self.show_info) # status bar self.status = QStatusBar(self) self.setStatusBar(self.status) self.statusLabel = QLabel("Ready.") self.statusBar().addPermanentWidget(self.statusLabel) self.timeLabel = QLabel("current time: 0.0") self.statusBar().addPermanentWidget(self.timeLabel) self._logger.info("Simulation GUI is up and running.") def _read_settings(self): # add default settings if none are present if not self._settings.contains("path/simulation_results"): self._settings.setValue("path/simulation_results", os.path.join(os.path.curdir, "results", "simulation")) if not self._settings.contains("path/postprocessing_results"): self._settings.setValue("path/postprocessing_results", os.path.join(os.path.curdir, "results", "postprocessing")) if not self._settings.contains("path/metaprocessing_results"): self._settings.setValue("path/metaprocessing_results", os.path.join(os.path.curdir, "results", "metaprocessing")) if not self._settings.contains("control/autoplay_animation"): self._settings.setValue("control/autoplay_animation", "False") if not self._settings.contains("control/exit_on_batch_completion"): self._settings.setValue("control/exit_on_batch_completion", "False") def _write_settings(self): """ Store the application state. """ pass @pyqtSlot() def update_autoplay_setting(self): self._settings.setValue("control/autoplay_animation", str(self.actAutoPlay.isChecked())) @pyqtSlot() def update_exit_on_batch_completion_setting(self, state=None): if state is None: state = self.actExitOnBatchCompletion.isChecked() self._settings.setValue("control/exit_on_batch_completion", str(state)) def set_visualizer(self, vis): self.visualizer = vis self.vtkWidget.Initialize() @pyqtSlot() def play_animation(self): """ play the animation """ self._logger.debug("Starting Playback") # if we are at the end, start from the beginning if self.playbackTime == self.currentEndTime: self.timeSlider.setValue(0) self.actPlayPause.setText("Pause Animation") self.actPlayPause.setIcon(QIcon(get_resource("pause.png"))) self.actPlayPause.triggered.disconnect(self.play_animation) self.actPlayPause.triggered.connect(self.pause_animation) self.playbackTimer.start(self.playbackTimeout) @pyqtSlot() def pause_animation(self): """ pause the animation """ self._logger.debug("Pausing Playback") self.playbackTimer.stop() self.actPlayPause.setText("Play Animation") self.actPlayPause.setIcon(QIcon(get_resource("play.png"))) self.actPlayPause.triggered.disconnect(self.pause_animation) self.actPlayPause.triggered.connect(self.play_animation) def stop_animation(self): """ Stop the animation if it is running and reset the playback time. """ self._logger.debug("Stopping Playback") if self.actPlayPause.text() == "Pause Animation": # animation is playing -> stop it self.playbackTimer.stop() self.actPlayPause.setText("Play Animation") self.actPlayPause.setIcon(QIcon(get_resource("play.png"))) self.actPlayPause.triggered.disconnect(self.pause_animation) self.actPlayPause.triggered.connect(self.play_animation) self.timeSlider.setValue(0) @pyqtSlot() def start_simulation(self): """ start the simulation and disable start button """ if self._current_regime_index is None: regime_name = "" else: regime_name = str(self.regime_list.item( self._current_regime_index).text()) self.statusLabel.setText("simulating {}".format(regime_name)) self._logger.info("Simulating: {}".format(regime_name)) self.actSimulateCurrent.setIcon(QIcon( get_resource("stop_simulation.png"))) self.actSimulateCurrent.setText("Abort &Simulation") self.actSimulateCurrent.triggered.disconnect(self.start_simulation) self.actSimulateCurrent.triggered.connect(self.stop_simulation) if not self.runningBatch: self.actSimulateAll.setDisabled(True) self.guiProgress = QProgressBar(self) self.sim.simulationProgressChanged.connect(self.guiProgress.setValue) self.statusBar().addWidget(self.guiProgress) self.runSimulation.emit() @pyqtSlot() def stop_simulation(self): self.stopSimulation.emit() def export_simulation_data(self, ok): """ Query the user for a custom name and export the current simulation results. :param ok: unused parameter from QAction.triggered() Signal """ self._save_data() def _save_data(self, file_path=None): """ Save the current simulation results. If *fie_name* is given, the result will be saved to the specified location, making automated exporting easier. Args: file_path(str): Absolute path of the target file. If `None` the use will be asked for a storage location. """ regime_name = self._regimes[self._current_regime_index]["Name"] if file_path is None: # get default path path = self._settings.value("path/simulation_results") # create canonic file name suggestion = self._simfile_name(regime_name) else: path = os.path.dirname(file_path) suggestion = os.path.basename(file_path) # check if path exists otherwise create it if not os.path.isdir(path): box = QMessageBox() box.setText("Export Folder does not exist yet.") box.setInformativeText("Do you want to create it? \n" "{}".format(os.path.abspath(path))) box.setStandardButtons(QMessageBox.Ok | QMessageBox.No) box.setDefaultButton(QMessageBox.Ok) ret = box.exec_() if ret == QMessageBox.Ok: os.makedirs(path) else: path = os.path.abspath(os.path.curdir) file_path = None # If no path was given, present the default and let the user choose if file_path is None: dialog = QFileDialog(self) dialog.setAcceptMode(QFileDialog.AcceptSave) dialog.setFileMode(QFileDialog.AnyFile) dialog.setDirectory(path) dialog.setNameFilter("PyMoskito Results (*.pmr)") dialog.selectFile(suggestion) if dialog.exec_(): file_path = dialog.selectedFiles()[0] else: self._logger.warning("Export Aborted") return -1 # ask whether this should act as new default path = os.path.abspath(os.path.dirname(file_path)) if path != self._settings.value("path/simulation_results"): box = QMessageBox() box.setText("Use this path as new default?") box.setInformativeText("{}".format(path)) box.setStandardButtons(QMessageBox.Yes | QMessageBox.No) box.setDefaultButton(QMessageBox.Yes) ret = box.exec_() if ret == QMessageBox.Yes: self._settings.setValue("path/simulation_results", path) self.currentDataset.update({"regime name": regime_name}) with open(file_path, "wb") as f: pickle.dump(self.currentDataset, f, protocol=4) self.statusLabel.setText("results saved to {}".format(file_path)) self._logger.info("results saved to {}".format(file_path)) def _simfile_name(self, regime_name): """ Create a canonical name for a simulation result file """ suggestion = (time.strftime("%Y%m%d-%H%M%S") + "_" + regime_name + ".pmr") return suggestion def load_regime_dialog(self): regime_path = os.path.join(os.curdir) dialog = QFileDialog(self) dialog.setFileMode(QFileDialog.ExistingFile) dialog.setDirectory(regime_path) dialog.setNameFilter("Simulation Regime files (*.sreg)") if dialog.exec_(): file = dialog.selectedFiles()[0] self.load_regimes_from_file(file) def load_regimes_from_file(self, file_name): """ load simulation regime from file :param file_name: """ self.regime_file_name = os.path.split(file_name)[-1][:-5] self._logger.info("loading regime file: {0}".format(self.regime_file_name)) with open(file_name.encode(), "r") as f: self._regimes += yaml.load(f) self._update_regime_list() if self._regimes: self.actSimulateAll.setDisabled(False) self._logger.info("loaded {} regimes".format(len(self._regimes))) self.statusBar().showMessage("loaded {} regimes.".format(len(self._regimes)), 1000) return def _update_regime_list(self): self.regime_list.clear() for reg in self._regimes: self._logger.debug("adding '{}' to regime list".format(reg["Name"])) self.regime_list.addItem(reg["Name"]) def remove_regime_items(self): if self.regime_list.currentRow() >= 0: # flag all selected files as invalid items = self.regime_list.selectedItems() for item in items: del self._regimes[self.regime_list.row(item)] self.regime_list.takeItem(self.regime_list.row(item)) @pyqtSlot(QListWidgetItem) def regime_dclicked(self, item): """ Apply the selected regime to the current target. """ self.apply_regime_by_name(str(item.text())) def apply_regime_by_name(self, regime_name): """ Apply the regime given by `regime_name` und update the regime index. Returns: bool: `True` if successful, `False` if errors occurred. """ # get regime idx try: idx = list(map(itemgetter("Name"), self._regimes)).index(regime_name) except ValueError as e: self._logger.error("apply_regime_by_name(): Error no regime called " "'{0}'".format(regime_name)) return False # apply return self._apply_regime_by_idx(idx) def _apply_regime_by_idx(self, index=0): """ Apply the given regime. Args: index(int): Index of the regime in the `RegimeList` . Returns: bool: `True` if successful, `False` if errors occurred. """ if index >= len(self._regimes): self._logger.error("applyRegime: index error! ({})".format(index)) return False reg_name = self._regimes[index]["Name"] self.statusBar().showMessage("regime {} applied.".format(reg_name), 1000) self._logger.info("applying regime '{}'".format(reg_name)) self._current_regime_index = index self._current_regime_name = reg_name return self.sim.set_regime(self._regimes[index]) @pyqtSlot() def start_regime_execution(self): """ Simulate all regimes in the regime list. """ self.actSimulateAll.setText("Stop Simulating &All Regimes") self.actSimulateAll.setIcon(QIcon(get_resource("stop_batch.png"))) self.actSimulateAll.triggered.disconnect(self.start_regime_execution) self.actSimulateAll.triggered.connect(self.stop_regime_excecution) self.runningBatch = True self._current_regime_index = -1 self.regimeFinished.emit() def run_next_regime(self): """ Execute the next regime in the regime batch. """ # are we finished? if self._current_regime_index == len(self._regimes) - 1: self.finishedRegimeBatch.emit(True) return suc = self._apply_regime_by_idx(self._current_regime_index + 1) if not suc: self.finishedRegimeBatch.emit(False) return self.start_simulation() @pyqtSlot() def stop_regime_excecution(self): """ Stop the batch process. """ self.stopSimulation.emit() self.finishedRegimeBatch.emit(False) def regime_batch_finished(self, status): self.runningBatch = False self.actSimulateAll.setDisabled(False) self.actSave.setDisabled(True) self.actSimulateAll.setText("Simulate &All Regimes") self.actSimulateAll.setIcon(QIcon(get_resource("execute_regimes.png"))) self.actSimulateAll.triggered.disconnect(self.stop_regime_excecution) self.actSimulateAll.triggered.connect(self.start_regime_execution) if status: self.statusLabel.setText("All regimes have been simulated") self._logger.info("All Regimes have been simulated") else: self._logger.error("Batch simulation has been aborted") if self._settings.value("control/exit_on_batch_completion") == "True": self._logger.info("Shutting down SimulationGUI") self.close() @pyqtSlot(str, dict, name="new_simulation_data") def new_simulation_data(self, status, data): """ Slot to be called when the simulation interface has completed the current job and new data is available. Args: status (str): Status of the simulation, either - `finished` : Simulation has been finished successfully or - `failed` : Simulation has failed. data (dict): Dictionary, holding the simulation data. """ self._logger.info("Simulation {}".format(status)) self.statusLabel.setText("Simulation {}".format(status)) self.actSimulateCurrent.setText("&Simulate Current Regime") self.actSimulateCurrent.setIcon(QIcon(get_resource("simulate.png"))) self.actSimulateCurrent.triggered.disconnect(self.stop_simulation) self.actSimulateCurrent.triggered.connect(self.start_simulation) self.actPlayPause.setDisabled(False) self.actStop.setDisabled(False) self.actSave.setDisabled(False) self.speedControl.setDisabled(False) self.timeSlider.setDisabled(False) self.sim.simulationProgressChanged.disconnect(self.guiProgress.setValue) self.statusBar().removeWidget(self.guiProgress) self.stop_animation() self.currentDataset = data if data: self._read_results() self._update_data_list() self._update_plots() if self._settings.value("control/autoplay_animation") == "True": self.actPlayPause.trigger() if self.runningBatch: regime_name = self._regimes[self._current_regime_index]["Name"] file_name = self._simfile_name(regime_name) self._save_data(os.path.join( self._settings.value("path/simulation_results"), file_name)) self.regimeFinished.emit() else: self.actSimulateAll.setDisabled(False) def _read_results(self): state = self.currentDataset["results"]["Solver"] self.interpolator = interp1d(self.currentDataset["results"]["time"], state, axis=0, bounds_error=False, fill_value=(state[0], state[-1])) self.currentStepSize = 1.0/self.currentDataset["simulation"][ "measure rate"] self.currentEndTime = self.currentDataset["simulation"]["end time"] self.validData = True def increment_playback_speed(self): self.speedControl.setValue(self.speedControl.value() + self.speedControl.singleStep()) def decrement_playback_speed(self): self.speedControl.setValue(self.speedControl.value() - self.speedControl.singleStep()) def reset_playback_speed(self): self.speedControl.setValue((self.speedControl.maximum() - self.speedControl.minimum())/2) def set_slowest_playback_speed(self): self.speedControl.setValue(self.speedControl.minimum()) def set_fastest_playback_speed(self): self.speedControl.setValue(self.speedControl.maximum()) def update_playback_speed(self, val): """ adjust playback time to slider value :param val: """ maximum = self.speedControl.maximum() self.playbackGain = 10**(3.0 * (val - maximum / 2) / maximum) @pyqtSlot() def increment_playback_time(self): """ go one time step forward in playback """ if self.playbackTime == self.currentEndTime: self.pause_animation() return increment = self.playbackGain * self.playbackTimeout / 1000 self.playbackTime = min(self.currentEndTime, self.playbackTime + increment) pos = int(self.playbackTime / self.currentEndTime * self.timeSliderRange) self.timeSlider.blockSignals(True) self.timeSlider.setValue(pos) self.timeSlider.blockSignals(False) self.playbackTimeChanged.emit() def update_playback_time(self): """ adjust playback time to slider value """ self.playbackTime = self.timeSlider.value()/self.timeSliderRange*self.currentEndTime self.playbackTimeChanged.emit() return def update_gui(self): """ updates the graphical user interface, including: - timestamp - visualisation - time cursor in diagrams """ if not self.validData: return self.timeLabel.setText("current time: %4f" % self.playbackTime) # update time cursor in plots self._update_time_cursors() # update state of rendering if self.visualizer: state = self.interpolator(self.playbackTime) self.visualizer.update_scene(state) if isinstance(self.visualizer, MplVisualizer): pass elif isinstance(self.visualizer, VtkVisualizer): self.vtkWidget.GetRenderWindow().Render() def _update_data_list(self): self.dataList.clear() for module_name, results in self.currentDataset["results"].items(): if not isinstance(results, np.ndarray): continue if len(results.shape) == 1: self.dataList.insertItem(0, module_name) elif len(results.shape) == 2: for col in range(results.shape[1]): self.dataList.insertItem( 0, self._build_entry_name(module_name, (col, )) ) elif len(results.shape) == 3: for col in range(results.shape[1]): for der in range(results.shape[2]): self.dataList.insertItem( 0, self._build_entry_name(module_name, (col, der)) ) def _build_entry_name(self, module_name, idx): """ Construct an identifier for a given entry of a module. Args: module_name (str): name of the module the entry belongs to. idx (tuple): Index of the entry. Returns: str: Identifier to use for display. """ # save the user from defining 1d entries via tuples if len(idx) == 1: m_idx = idx[0] else: m_idx = idx mod_settings = self.currentDataset["modules"] info = mod_settings.get(module_name, {}).get("output_info", None) if info: if m_idx in info: return ".".join([module_name, info[m_idx]["Name"]]) return ".".join([module_name] + [str(i) for i in idx]) def _get_index_from_suffix(self, module_name, suffix): info = self.currentDataset["modules"].get(module_name, {}).get( "output_info", None) idx = next((i for i in info if info[i]["Name"] == suffix), None) return idx def _get_units(self, entry): """ Return the unit that corresponds to a given entry. If no information is available, None is returned. Args: entry (str): Name of the entry. This can either be "Model.a.b" where a and b are numbers or if information is available "Model.Signal" where signal is the name of that part. Returns: """ args = entry.split(".") module_name = args.pop(0) info = self.currentDataset["modules"].get(module_name, {}).get( "output_info", None) if info is None: return None if len(args) == 1: try: idx = int(args[0]) except ValueError: idx = next((i for i in info if info[i]["Name"] == args[0]), None) else: idx = (int(a) for a in args) return info[idx]["Unit"] def create_plot(self, item): """ Creates a plot widget based on the given item. If a plot for this item is already open no new plot is created but the existing one is raised up again. Args: item(Qt.ListItem): Item to plot. """ title = str(item.text()) if title in self.non_plotting_docks: self._logger.error("Title '{}' not allowed for a plot window since" "it would shadow on of the reserved " "names".format(title)) # check if plot has already been opened if title in self.area.findAll()[1]: self.area.docks[title].raiseDock() return # collect data data = self._get_data_by_name(title) t = self.currentDataset["results"]["time"] unit = self._get_units(title) if "." in title: name = title.split(".")[1] else: name = title # create plot widget widget = pg.PlotWidget(title=title) widget.showGrid(True, True) widget.plot(x=t, y=data) widget.getPlotItem().getAxis("bottom").setLabel(text="Time", units="s") widget.getPlotItem().getAxis("left").setLabel(text=name, units=unit) # add a time line time_line = pg.InfiniteLine(self.playbackTime, angle=90, movable=False, pen=pg.mkPen("#FF0000", width=2.0)) widget.getPlotItem().addItem(time_line) # create dock container and add it to dock area dock = pg.dockarea.Dock(title, closable=True) dock.addWidget(widget) self.area.addDock(dock, "above", self.plotDockPlaceholder) def _get_data_by_name(self, name): tmp = name.split(".") module_name = tmp[0] if len(tmp) == 1: data = np.array(self.currentDataset["results"][module_name]) elif len(tmp) == 2: try: idx = int(tmp[1]) except ValueError: idx = self._get_index_from_suffix(module_name, tmp[1]) finally: data = self.currentDataset["results"][module_name][..., idx] elif len(tmp) == 3: idx = int(tmp[1]) der = int(tmp[2]) data = self.currentDataset["results"][module_name][..., idx, der] else: raise ValueError("Format not supported") return data def _update_time_cursors(self): """ Update the time lines of all plot windows """ for title, dock in self.area.findAll()[1].items(): if title in self.non_plotting_docks: continue for widget in dock.widgets: for item in widget.getPlotItem().items: if isinstance(item, pg.InfiniteLine): item.setValue(self.playbackTime) def _update_plots(self): """ Update the data in all plot windows """ for title, dock in self.area.findAll()[1].items(): if title in self.non_plotting_docks: continue if not self.dataList.findItems(dock.name(), Qt.MatchExactly): # no data for this plot -> remove it dock.close() continue for widget in dock.widgets: for item in widget.getPlotItem().items: if isinstance(item, pg.PlotDataItem): x_data = self.currentDataset["results"]["time"] y_data = self._get_data_by_name(dock.name()) item.setData(x=x_data, y=y_data) @pyqtSlot(QModelIndex) def target_view_changed(self, index): self.targetView.resizeColumnToContents(0) def postprocessing_clicked(self): """ starts the post- and metaprocessing application """ self._logger.info("launching postprocessor") self.statusBar().showMessage("launching postprocessor", 1000) if self.postprocessor is None: self.postprocessor = PostProcessor() self.postprocessor.show() def reset_camera_clicked(self): """ reset camera in vtk window """ self.visualizer.reset_camera() self.vtkWidget.GetRenderWindow().Render() def show_info(self): icon_lic = open(get_resource("license.txt"), "r").read() text = "This application was build using PyMoskito ver. {} .<br />" \ "PyMoskito is free software distributed under GPLv3. <br />" \ "It is developed by members of the " \ "<a href=\'https://tu-dresden.de/ing/elektrotechnik/rst'>" \ "Institute of Control Theory</a>" \ " at the <a href=\'https://tu-dresden.de'>" \ "Dresden University of Technology</a>. <br />" \ "".format(pkg_resources.require("PyMoskito")[0].version) \ + "<br />" + icon_lic box = QMessageBox.about(self, "PyMoskito", text) def show_online_docs(self): webbrowser.open("https://pymoskito.readthedocs.org") def closeEvent(self, QCloseEvent): self._logger.info("Close Event received, shutting down.") logging.getLogger().removeHandler(self.textLogger) super().closeEvent(QCloseEvent)
class MainWindow(QMainWindow): """This is the main application window class it defines the GUI window for the browser """ def parse_config(self, file_config, options): self.config = {} options = vars(options) for key, metadata in CONFIG_OPTIONS.items(): options_val = options.get(key) file_val = file_config.get(key) env_val = os.environ.get(metadata.get("env", '')) default_val = metadata.get("default") vals = metadata.get("values") debug("key: {}, default: {}, file: {}, options: {}".format( key, default_val, file_val, options_val )) if vals: options_val = (options_val in vals and options_val) or None file_val = (file_val in vals and file_val) or None env_val = (env_val in vals and env_val) or None if metadata.get("is_file"): filename = options_val or env_val if not filename: self.config[key] = default_val else: try: with open(filename, 'r') as fh: self.config[key] = fh.read() except IOError: debug("Could not open file {} for reading.".format( filename) ) self.config[key] = default_val else: set_values = [ val for val in (options_val, env_val, file_val) if val is not None ] if len(set_values) > 0: self.config[key] = set_values[0] else: self.config[key] = default_val if metadata.get("type") and self.config[key]: debug("{} cast to {}".format(key, metadata.get("type"))) self.config[key] = metadata.get("type")(self.config[key]) debug(repr(self.config)) def createAction(self, text, slot=None, shortcut=None, icon=None, tip=None, checkable=False, signal="triggered"): """Return a QAction given a number of common QAction attributes Just a shortcut function Originally borrowed from 'Rapid GUI Development with PyQT' by Mark Summerset """ action = QAction(text, self) if icon is not None: action.setIcon(QIcon.fromTheme( icon, QIcon(":/{}.png".format(icon)) )) if shortcut is not None and not shortcut.isEmpty(): action.setShortcut(shortcut) tip += " ({})".format(shortcut.toString()) if tip is not None: action.setToolTip(tip) action.setStatusTip(tip) if slot is not None: action.__getattr__(signal).connect(slot) if checkable: action.setCheckable() return action def __init__(self, options, parent=None): """Construct a MainWindow Object.""" super(MainWindow, self).__init__(parent) # Load config file self.setWindowTitle("Browser") debug("loading configuration from '{}'".format(options.config_file)) configfile = {} if options.config_file: configfile = yaml.safe_load(open(options.config_file, 'r')) self.parse_config(configfile, options) # self.popup will hold a reference to the popup window # if it gets opened self.popup = None # Stylesheet support if self.config.get("stylesheet"): try: with open(self.config.get("stylesheet")) as ss: self.setStyleSheet(ss.read()) except: debug( """Problem loading stylesheet file "{}", """ """using default style.""" .format(self.config.get("stylesheet")) ) self.setObjectName("global") # If the whitelist is activated, add the bookmarks and start_url if self.config.get("whitelist"): # we can just specify whitelist = True, # which should whitelist just the start_url and bookmark urls. whitelist = self.config.get("whitelist") if type(whitelist) is not list: whitelist = [] whitelist.append(str(QUrl( self.config.get("start_url") ).host())) bookmarks = self.config.get("bookmarks") if bookmarks: whitelist += [ str(QUrl(b.get("url")).host()) for k, b in bookmarks.items() ] self.config["whitelist"] = set(whitelist) # uniquify and optimize debug("Generated whitelist: " + str(whitelist)) # If diagnostic is enabled, connect CTRL+ALT+? to show some diagnistic info if (self.config.get("enable_diagnostic")): self.diagnostic_action = self.createAction( "Show Diagnostic", self.show_diagnostic, QKeySequence("Ctrl+Alt+/"), tip='' ) self.addAction(self.diagnostic_action) self.build_ui() # ## END OF CONSTRUCTOR ## # def build_ui(self): """Set up the user interface for the main window. Unlike the constructor, this method is re-run whenever the browser is "reset" by the user. """ debug("build_ui") inactivity_timeout = self.config.get("timeout") quit_button_tooltip = ( self.config.get("quit_button_mode") == 'close' and "Click here to quit the browser." or """Click here when you are done. It will clear your browsing history""" """ and return you to the start page.""") qb_mode_callbacks = {'close': self.close, 'reset': self.reset_browser} to_mode_callbacks = {'close': self.close, 'reset': self.reset_browser, 'screensaver': self.screensaver} self.screensaver_active = False # ##Start GUI configuration## # self.browser_window = WcgWebView(self.config) self.browser_window.setObjectName("web_content") if ( self.config.get("icon_theme") is not None and QT_VERSION_STR > '4.6' ): QIcon.setThemeName(self.config.get("icon_theme")) self.setCentralWidget(self.browser_window) debug("loading {}".format(self.config.get("start_url"))) self.browser_window.setUrl(QUrl(self.config.get("start_url"))) if self.config.get("fullscreen"): self.showFullScreen() elif ( self.config.get("window_size") and self.config.get("window_size").lower() == 'max' ): self.showMaximized() elif self.config.get("window_size"): size = re.match(r"(\d+)x(\d+)", self.config.get("window_size")) if size: width, height = size.groups() self.setFixedSize(int(width), int(height)) else: debug('Ignoring invalid window size "{}"'.format( self.config.get("window_size") )) # Set up the top navigation bar if it's configured to exist if self.config.get("navigation"): self.navigation_bar = QToolBar("Navigation") self.navigation_bar.setObjectName("navigation") self.addToolBar(Qt.TopToolBarArea, self.navigation_bar) self.navigation_bar.setMovable(False) self.navigation_bar.setFloatable(False) # Standard navigation tools self.nav_items = {} self.nav_items["back"] = self.browser_window.pageAction(QWebPage.Back) self.nav_items["forward"] = self.browser_window.pageAction(QWebPage.Forward) self.nav_items["refresh"] = self.browser_window.pageAction(QWebPage.Reload) self.nav_items["stop"] = self.browser_window.pageAction(QWebPage.Stop) # The "I'm finished" button. self.nav_items["quit"] = self.createAction( self.config.get("quit_button_text"), qb_mode_callbacks.get(self.config.get("quit_button_mode"), self.reset_browser), QKeySequence("Alt+F"), None, quit_button_tooltip) # Zoom buttons self.nav_items["zoom_in"] = self.createAction( "Zoom In", self.zoom_in, QKeySequence("Alt++"), "zoom-in", "Increase the size of the text and images on the page") self.nav_items["zoom_out"] = self.createAction( "Zoom Out", self.zoom_out, QKeySequence("Alt+-"), "zoom-out", "Decrease the size of text and images on the page") if self.config.get("allow_printing"): self.nav_items["print"] = self.createAction( "Print", self.browser_window.print_webpage, QKeySequence("Ctrl+p"), "document-print", "Print this page") # Add all the actions to the navigation bar. for item in self.config.get("navigation_layout"): if item == "separator": self.navigation_bar.addSeparator() elif item == "spacer": # an expanding spacer. spacer = QWidget() spacer.setSizePolicy( QSizePolicy.Expanding, QSizePolicy.Preferred) self.navigation_bar.addWidget(spacer) elif item == "bookmarks": # Insert bookmarks buttons here. self.bookmark_buttons = [] for bookmark in self.config.get("bookmarks", {}).items(): debug("Bookmark:\n" + bookmark.__str__()) # bookmark name will use the "name" attribute, if present # or else just the key: bookmark_name = bookmark[1].get("name") or bookmark[0] # Create a button for the bookmark as a QAction, # which we'll add to the toolbar button = self.createAction( bookmark_name, lambda url=bookmark[1].get("url"): self.browser_window.load(QUrl(url)), QKeySequence.mnemonic(bookmark_name), None, bookmark[1].get("description") ) self.navigation_bar.addAction(button) self.navigation_bar.widgetForAction(button).setObjectName("navigation_button") else: action = self.nav_items.get(item, None) if action: self.navigation_bar.addAction(action) self.navigation_bar.widgetForAction(action).setObjectName("navigation_button") # This removes the ability to toggle off the navigation bar: self.nav_toggle = self.navigation_bar.toggleViewAction() self.nav_toggle.setVisible(False) # End "if show_navigation is True" block # set hidden quit action # For reasons I haven't adequately ascertained, # this shortcut fails now and then claiming # "Ambiguous shortcut overload". # No idea why, as it isn't consistent. self.really_quit = self.createAction( "", self.close, QKeySequence("Ctrl+Alt+Q"), None, "" ) self.addAction(self.really_quit) # Call a reset function after timeout if inactivity_timeout != 0: self.event_filter = InactivityFilter(inactivity_timeout) QCoreApplication.instance().installEventFilter(self.event_filter) self.browser_window.page().installEventFilter(self.event_filter) self.event_filter.timeout.connect( to_mode_callbacks.get(self.config.get("timeout_mode"), self.reset_browser)) else: self.event_filter = None # ##END OF UI SETUP## # def screensaver(self): """Enter "screensaver" mode This method puts the browser in screensaver mode, where a URL is displayed while the browser is idle. Activity causes the browser to return to the home screen. """ debug("screensaver started") self.screensaver_active = True if self.popup: self.popup.close() if self.config.get("navigation"): self.navigation_bar.hide() self.browser_window.setZoomFactor(self.config.get("zoom_factor")) self.browser_window.load(QUrl(self.config.get("screensaver_url"))) self.event_filter.timeout.disconnect() self.event_filter.activity.connect(self.reset_browser) def reset_browser(self): """Clear the history and reset the UI. Called whenever the inactivity filter times out, or when the user clicks the "finished" button in 'reset' mode. """ # Clear out the memory cache QWebSettings.clearMemoryCaches() self.browser_window.history().clear() # self.navigation_bar.clear() doesn't do its job, # so remove the toolbar first, then rebuild the UI. debug("RESET BROWSER") if self.event_filter: self.event_filter.blockSignals(True) if self.screensaver_active is True: self.screensaver_active = False self.event_filter.activity.disconnect() if self.event_filter: self.event_filter.blockSignals(False) if hasattr(self, "navigation_bar"): self.removeToolBar(self.navigation_bar) self.build_ui() def zoom_in(self): """Zoom in action callback. Note that we cap zooming in at a factor of 3x. """ if self.browser_window.zoomFactor() < 3.0: self.browser_window.setZoomFactor( self.browser_window.zoomFactor() + 0.1 ) self.nav_items["zoom_out"].setEnabled(True) else: self.nav_items["zoom_in"].setEnabled(False) def zoom_out(self): """Zoom out action callback. Note that we cap zooming out at 0.1x. """ if self.browser_window.zoomFactor() > 0.1: self.browser_window.setZoomFactor( self.browser_window.zoomFactor() - 0.1 ) self.nav_items["zoom_in"].setEnabled(True) else: self.nav_items["zoom_out"].setEnabled(False) def show_diagnostic(self): "Display a dialog box with some diagnostic info" data = { "OS": os.uname(), "USER": (os.environ.get("USER") or os.environ.get("USERNAME")), "Python": sys.version, "Qt": QT_VERSION_STR, "Script Date": ( datetime.datetime.fromtimestamp( os.stat(__file__).st_mtime).isoformat() ) } html = "\n".join([ "<h1>System Information</h1>", "<h2>Please click "", self.config.get("quit_button_text").replace("&", ''), "" when you are finished.</h2>", "<ul>", "\n".join([ "<li><b>{}</b>: {}</li>".format(k, v) for k, v in data.items() ]), "</ul>" ]) self.browser_window.setHtml(html)
class SettingsDialog(QDialog): def __init__(self, parent=None): super(SettingsDialog, self).__init__(parent) # Set layout. _layout = QVBoxLayout(self) _layout.setContentsMargins(0,0,0,0) self.setLayout(_layout) # Set window title. self.setWindowTitle(tr("Settings")) # Tab widget self.tabs = QTabWidget(self) self.layout().addWidget(self.tabs) self.tabs.addTab(GeneralSettingsPanel(self), tr("&General")) self.tabs.addTab(ContentSettingsPanel(self), tr("Con&tent")) self.tabs.addTab(AdremoverSettingsPanel(self), tr("Ad &Remover")) self.tabs.addTab(DataSettingsPanel(self), tr("&Data && Privacy")) self.tabs.addTab(NetworkSettingsPanel(self), tr("N&etwork")) self.tabs.addTab(ExtensionsSettingsPanel(self), tr("E&xtensions")) # Toolbar self.toolBar = QToolBar(self) self.toolBar.setMovable(False) self.toolBar.setContextMenuPolicy(Qt.CustomContextMenu) self.toolBar.setStyleSheet(common.blank_toolbar) self.layout().addWidget(self.toolBar) # Apply button applyButton = QPushButton(tr("&Apply"), self) applyButton.clicked.connect(self.saveSettings) self.toolBar.addWidget(applyButton) # Reload settings button closeButton = QPushButton(tr("&Close"), self) closeButton.clicked.connect(self.hide) self.toolBar.addWidget(closeButton) # Load settings self.loadSettings() def show(self): super(SettingsDialog, self).show() self.loadSettings() def url(self): return QUrl("") def icon(self): return common.complete_icon("preferences-system") # Method to load all settings. def loadSettings(self): for index in range(0, self.tabs.count()): self.tabs.widget(index).loadSettings() # Method to save all settings. def saveSettings(self): for index in range(0, self.tabs.count()): self.tabs.widget(index).saveSettings() settings.reset_extensions() settings.reload_userscripts() for window in browser.windows: try: window.reloadExtensions() except: pass try: window.applySettings() except: pass
class LicenseDialog(QMainWindow): def __init__(self, parent=None): super(LicenseDialog, self).__init__(parent) self.resize(420, 320) self.setWindowTitle(tr("Credits & Licensing")) self.setWindowFlags(Qt.Dialog) self.readme = "" self.license = "" self.thanks = "" self.authors = "" self.tabWidget = QTabWidget(self) self.setCentralWidget(self.tabWidget) for folder in (app_folder, os.path.dirname(app_folder)): for fname in os.listdir(folder): if fname.startswith("LICENSE"): try: f = open(os.path.join(folder, fname), "r") except: pass else: self.license = f.read() f.close() elif fname.startswith("THANKS"): try: f = open(os.path.join(folder, fname), "r") except: pass else: self.thanks = f.read() f.close() elif fname.startswith("AUTHORS"): try: f = open(os.path.join(folder, fname), "r") except: pass else: self.authors = f.read() f.close() elif fname.startswith("README"): try: f = open(os.path.join(folder, fname), "r") except: pass else: self.readme = f.read() f.close() self.readmeView = ReadOnlyTextEdit(self) self.readmeView.setText(self.readme) self.tabWidget.addTab(self.readmeView, tr("&README")) self.authorsView = ReadOnlyTextEdit(self) self.authorsView.setText(self.authors) self.tabWidget.addTab(self.authorsView, tr("&Authors")) self.thanksView = ReadOnlyTextEdit(self) self.thanksView.setText(self.thanks) self.tabWidget.addTab(self.thanksView, tr("&Thanks")) self.licenseView = ReadOnlyTextEdit(self) self.licenseView.setText(self.license) self.tabWidget.addTab(self.licenseView, tr("&License")) closeAction = QAction(self) closeAction.setShortcuts(["Esc", "Ctrl+W"]) closeAction.triggered.connect(self.hide) self.addAction(closeAction) self.toolBar = QToolBar(self) self.toolBar.setStyleSheet(blank_toolbar) self.toolBar.setMovable(False) self.toolBar.setContextMenuPolicy(Qt.CustomContextMenu) self.addToolBar(Qt.BottomToolBarArea, self.toolBar) self.toolBar.addWidget(HorizontalExpander(self)) self.closeButton = QPushButton(tr("&OK"), self) self.closeButton.clicked.connect(self.close) self.toolBar.addWidget(self.closeButton) self.closeButton.setFocus()
class MainWindow(QMainWindow, ui_window.Ui_Window): emulator_found = QtCore.pyqtSignal(dict) emulators_loaded = QtCore.pyqtSignal() def __init__(self): super(MainWindow, self).__init__() self.setupUi(self) self.emulators = { } self.settings = QSettings('SanderTheDragon', 'Qtendo') self.ui_create() self.ui_connect() self.settings_load() def showEvent(self, ev): QMainWindow.showEvent(self, ev) self.statusBar.showMsg('Searching for emulators', 1000) Thread(target=self.find_emulators, daemon=True).start() def closeEvent(self, ev): QMainWindow.closeEvent(self, ev) self.settings_save() def ui_create(self): #Add toolbar self.toolBar = QToolBar() self.toolBar.addAction(self.actionPageEmulation) self.toolBar.setFloatable(False) self.toolBar.setMovable(False) self.toolBar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) self.gridLayout.addWidget(self.toolBar, 1, 0) #Add a second toolbar on emulation page self.toolBarEmulation = QToolBar() self.toolBarEmulation.setFloatable(False) self.toolBarEmulation.setMovable(False) self.toolBarEmulation.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) self.pageEmulationLayout.addWidget(self.toolBarEmulation, 0, 0) #Add progress bar to status bar self.taskProgress = QProgressBar() self.taskProgress.setVal = lambda x: ( self.taskProgress.setVisible(True), self.taskProgress.setValue(x) ) self.taskProgress.setVal(0) self.taskProgress.setTextVisible(False) self.statusBar.addPermanentWidget(self.taskProgress) #Also print messages to terminal self.statusBar.showMsg = lambda msg, timeout: ( logging.info(msg), self.statusBar.showMessage(msg, timeout) ) #Styling self.setStyleSheet('QToolButton { padding-right: -3px; }') def ui_connect(self): #Menu actions self.actionQuit.triggered.connect(QCoreApplication.quit) self.actionSettings.triggered.connect(lambda: settings.SettingsDialog(parent=self).exec_()) self.actionAbout.triggered.connect(lambda: about.AboutDialog(parent=self).exec_()) #Toolbar actions self.actionPageEmulation.triggered.connect(lambda: self.stackedWidget.setCurrentIndex(0)) #Other signals self.emulator_found.connect(self.add_emulator) self.emulators_loaded.connect(self.reset_status) def settings_load(self): if self.settings.value('qtendo/window/restore', True, type=bool): self.restoreGeometry(self.settings.value('qtendo/window/geometry', type=QByteArray)) def settings_save(self): if self.settings.value('qtendo/window/restore', True, type=bool): self.settings.setValue('qtendo/window/geometry', self.saveGeometry()) def change_emulator(self, index): current = self.stackedWidgetEmulation.currentIndex() if current != index: emulator = self.emulators[list(self.emulators.keys())[current]] emulator['action'].setIcon(QIcon(emulator['action'].icon().pixmap(QSize(24, 24), QIcon.Disabled))) self.stackedWidgetEmulation.setCurrentIndex(index) emulator = self.emulators[list(self.emulators.keys())[index]] emulator['action'].setIcon(QIcon(':' + emulator['icon'])) def add_emulator(self, emulator): if len(emulator['path']) > 0: self.statusBar.showMsg('Found ' + emulator['name'], 1000) else: self.statusBar.showMsg('Failed to find ' + emulator['name'], 1000) self.emulators[emulator['name']] = emulator i = self.stackedWidgetEmulation.count() emulator['action'] = QAction() emulator['action'].setIcon(QIcon(':' + emulator['icon'])) if i > 0: self.toolBarEmulation.addSeparator() emulator['action'].setIcon(QIcon(emulator['action'].icon().pixmap(QSize(24, 24), QIcon.Disabled))) emulator['action'].setIconText(emulator['name']) emulator['action'].triggered.connect(lambda checked, index=i: self.change_emulator(index)) self.toolBarEmulation.addAction(emulator['action']) self.stackedWidgetEmulation.insertWidget(i, emulator['widget'](emulator)) if len(self.settings.value('emulation/emulator/' + emulator['name'].lower().replace(' ', '_') + '/path', emulator['path'], type=str)) == 0: self.toolBarEmulation.widgetForAction(emulator['action']).setStyleSheet('color: ' + QApplication.palette().color(QPalette.Disabled, QPalette.WindowText).name() + ';') emulator['reload_settings'] = self.reload_settings self.taskProgress.setVal(int((100.0 / float(emulator_count)) * float(i + 1))) def reset_status(self): self.statusBar.clearMessage() self.taskProgress.setValue(0) self.taskProgress.setVisible(False) def reload_settings(self): emulator = self.emulators[list(self.emulators.keys())[self.stackedWidgetEmulation.currentIndex()]] if len(self.settings.value('emulation/emulator/' + emulator['name'].lower().replace(' ', '_') + '/path', emulator['path'], type=str)) == 0: self.toolBarEmulation.widgetForAction(emulator['action']).setStyleSheet('color: ' + QApplication.palette().color(QPalette.Disabled, QPalette.WindowText).name() + ';') else: self.toolBarEmulation.widgetForAction(emulator['action']).setStyleSheet('') def find_emulators(self): #Search for FCEUX self.emulator_found.emit(fceux.find(None if not self.settings.contains('emulation/emulator/fceux/path') else self.settings.value('emulation/emulator/fceux/path', type=str))) #Search for ZSNES self.emulator_found.emit(zsnes.find(None if not self.settings.contains('emulation/emulator/zsnes/path') else self.settings.value('emulation/emulator/zsnes/path', type=str))) #Search for Mupen64Plus self.emulator_found.emit(mupen64plus.find(None if not self.settings.contains('emulation/emulator/mupen64plus/path') else self.settings.value('emulation/emulator/mupen64plus/path', type=str))) #Search for Dolphin Emulator self.emulator_found.emit(dolphin.find(None if not self.settings.contains('emulation/emulator/dolphin/path') else self.settings.value('emulation/emulator/dolphin/path', type=str))) #Search for Citra self.emulator_found.emit(citra.find(None if not self.settings.contains('emulation/emulator/citra/path') else self.settings.value('emulation/emulator/citra/path', type=str))) self.emulators_loaded.emit()
class AppWindow(QMainWindow): "The application's main window" def __init__(self): super().__init__() self.initUI() self.start_up() QTimer.singleShot(3000, self._check_update) def init_watchers(self): def remove_gallery(g): index = self.manga_list_view.find_index(g.id) if index: self.manga_list_view.remove_gallery([index]) def create_gallery(path): g_dia = gallerydialog.GalleryDialog(self, path) g_dia.SERIES.connect(self.manga_list_view.gallery_model.addRows) g_dia.show() def update_gallery(g): index = self.manga_list_view.find_index(g.id) if index: self.manga_list_view.replace_edit_gallery([g], index.row()) else: log_e("Could not find gallery to update from Watcher") def created(path): c_popup = file_misc.CreatedPopup(path, self) c_popup.ADD_SIGNAL.connect(create_gallery) def modified(path, gallery): mod_popup = file_misc.ModifiedPopup(path, gallery, self) def deleted(path, gallery): d_popup = file_misc.DeletedPopup(path, gallery, self) d_popup.UPDATE_SIGNAL.connect(update_gallery) d_popup.REMOVE_SIGNAL.connect(remove_gallery) def moved(new_path, gallery): mov_popup = file_misc.MovedPopup(new_path, gallery, self) mov_popup.UPDATE_SIGNAL.connect(update_gallery) self.watchers = file_misc.Watchers() self.watchers.gallery_handler.CREATE_SIGNAL.connect(created) self.watchers.gallery_handler.MODIFIED_SIGNAL.connect(modified) self.watchers.gallery_handler.MOVED_SIGNAL.connect(moved) self.watchers.gallery_handler.DELETED_SIGNAL.connect(deleted) if gui_constants.LOOK_NEW_GALLERY_STARTUP: self.notification_bar.add_text("Looking for new galleries...") try: class ScanDir(QObject): final_paths_and_galleries = pyqtSignal(list, list) def __init__(self, model_data, parent=None): super().__init__(parent) self.model_data = model_data def scan_dirs(self): db_data = self.model_data paths = [] for g in range(len(db_data)): paths.append(os.path.normcase(db_data[g].path)) contents = [] case_path = [] # needed for tile and artist parsing... e.g to avoid lowercase for m_path in gui_constants.MONITOR_PATHS: for p in os.listdir(m_path): abs_p = os.path.join(m_path, p) if os.path.isdir(abs_p) or p.endswith(utils.ARCHIVE_FILES): case_path.append(abs_p) contents.append(os.path.normcase(abs_p)) paths = sorted(paths) new_galleries = [] for c, x in enumerate(contents): y = utils.b_search(paths, x) if not y: # (path, number for case_path) new_galleries.append((x, c)) galleries = [] final_paths = [] if new_galleries: for g in new_galleries: gallery = gallerydb.Gallery() try: gallery.profile = utils.get_gallery_img(g[0]) except: gallery.profile = gui_constants.NO_IMAGE_PATH parser_dict = utils.title_parser(os.path.split(case_path[g[1]])[1]) gallery.title = parser_dict["title"] gallery.artist = parser_dict["artist"] galleries.append(gallery) final_paths.append(case_path[g[1]]) self.final_paths_and_galleries.emit(final_paths, galleries) # if gui_constants.LOOK_NEW_GALLERY_AUTOADD: # QTimer.singleShot(10000, self.gallery_populate(final_paths)) # return def show_new_galleries(final_paths, galleries): if final_paths and galleries: if gui_constants.LOOK_NEW_GALLERY_AUTOADD: self.gallery_populate(final_paths) else: if len(galleries) == 1: self.notification_bar.add_text( "{} new gallery was discovered in one of your monitored directories".format( len(galleries) ) ) else: self.notification_bar.add_text( "{} new galleries were discovered in one of your monitored directories".format( len(galleries) ) ) text = ( "These new galleries were discovered! Do you want to add them?" if len(galleries) > 1 else "This new gallery was discovered! Do you want to add it?" ) g_popup = file_misc.GalleryPopup((text, galleries), self) buttons = g_popup.add_buttons("Add", "Close") def populate_n_close(): self.gallery_populate(final_paths) g_popup.close() buttons[0].clicked.connect(populate_n_close) buttons[1].clicked.connect(g_popup.close) thread = QThread(self) self.scan_inst = ScanDir(self.manga_list_view.gallery_model._data) self.scan_inst.moveToThread(thread) self.scan_inst.final_paths_and_galleries.connect(show_new_galleries) self.scan_inst.final_paths_and_galleries.connect(lambda a: self.scan_inst.deleteLater()) thread.started.connect(self.scan_inst.scan_dirs) thread.finished.connect(thread.deleteLater) thread.start() except: self.notification_bar.add_text( "An error occured while attempting to scan for new galleries. Check happypanda.log." ) log.exception("An error occured while attempting to scan for new galleries.") def start_up(self): def normalize_first_time(): settings.set(2, "Application", "first time level") def done(): self.manga_list_view.gallery_model.init_data() if gui_constants.ENABLE_MONITOR and gui_constants.MONITOR_PATHS and all(gui_constants.MONITOR_PATHS): self.init_watchers() if gui_constants.FIRST_TIME_LEVEL != 2: normalize_first_time() if gui_constants.FIRST_TIME_LEVEL < 2: class FirstTime(file_misc.BasePopup): def __init__(self, parent=None): super().__init__(parent) main_layout = QVBoxLayout() info_lbl = QLabel( "Hi there! Some big changes are about to occur!\n" + "Please wait.. This will take at most a few minutes.\n" + "If not then try restarting the application." ) info_lbl.setAlignment(Qt.AlignCenter) main_layout.addWidget(info_lbl) prog = QProgressBar(self) prog.setMinimum(0) prog.setMaximum(0) prog.setTextVisible(False) main_layout.addWidget(prog) main_layout.addWidget(QLabel("Note: This popup will close itself when everything is ready")) self.main_widget.setLayout(main_layout) ft_widget = FirstTime(self) log_i("Invoking first time level 2") bridge = gallerydb.Bridge() thread = QThread(self) thread.setObjectName("Startup") bridge.moveToThread(thread) thread.started.connect(bridge.rebuild_galleries) bridge.DONE.connect(ft_widget.close) bridge.DONE.connect(self.setEnabled) bridge.DONE.connect(done) bridge.DONE.connect(bridge.deleteLater) thread.finished.connect(thread.deleteLater) thread.start() ft_widget.adjustSize() ft_widget.show() self.setEnabled(False) else: done() def initUI(self): self.center = QWidget() self.display = QStackedLayout() self.center.setLayout(self.display) # init the manga view variables self.manga_display() log_d("Create manga display: OK") # init the chapter view variables # self.chapter_display() self.m_l_view_index = self.display.addWidget(self.manga_list_main) self.m_t_view_index = self.display.addWidget(self.manga_table_view) # init toolbar self.init_toolbar() log_d("Create toolbar: OK") # init status bar self.init_stat_bar() log_d("Create statusbar: OK") self.system_tray = misc.SystemTray(QIcon(gui_constants.APP_ICO_PATH), self) gui_constants.SYSTEM_TRAY = self.system_tray tray_menu = QMenu(self) self.system_tray.setContextMenu(tray_menu) self.system_tray.setToolTip("Happypanda {}".format(gui_constants.vs)) tray_quit = QAction("Quit", tray_menu) tray_menu.addAction(tray_quit) tray_quit.triggered.connect(self.close) self.system_tray.show() log_d("Create system tray: OK") # self.display.addWidget(self.chapter_main) self.setCentralWidget(self.center) self.setWindowTitle("Happypanda") self.setWindowIcon(QIcon(gui_constants.APP_ICO_PATH)) props = settings.win_read(self, "AppWindow") if props.resize: x, y = props.resize self.resize(x, y) else: self.resize(gui_constants.MAIN_W, gui_constants.MAIN_H) posx, posy = props.pos self.move(posx, posy) self.show() log_d("Show window: OK") self.notification_bar = misc.NotificationOverlay(self) p = self.toolbar.pos() self.notification_bar.move(p.x(), p.y() + self.toolbar.height()) self.notification_bar.resize(self.width()) gui_constants.NOTIF_BAR = self.notification_bar log_d("Create notificationbar: OK") log_d("Window Create: OK") def _check_update(self): class upd_chk(QObject): UPDATE_CHECK = pyqtSignal(str) def __init__(self, **kwargs): super().__init__(**kwargs) def fetch_vs(self): import requests import time try: log_d("Checking Update") time.sleep(1.5) r = requests.get( "https://raw.githubusercontent.com/Pewpews/happypanda/master/VS.txt", verify="cacert.pem" ) a = r.text vs = a.strip() self.UPDATE_CHECK.emit(vs) except: log.exception("Checking Update: FAIL") self.UPDATE_CHECK.emit("this is a very long text which is is sure to be over limit") def check_update(vs): log_i("Received version: {}\nCurrent version: {}".format(vs, gui_constants.vs)) if vs != gui_constants.vs: if len(vs) < 10: self.notification_bar.add_text( "Version {} of Happypanda is".format(vs) + " available. Click here to update!", False ) self.notification_bar.clicked.connect( lambda: utils.open_web_link("https://github.com/Pewpews/happypanda/releases") ) self.notification_bar.set_clickable(True) else: self.notification_bar.add_text("An error occurred while checking for new version") self.update_instance = upd_chk() thread = QThread(self) self.update_instance.moveToThread(thread) thread.started.connect(self.update_instance.fetch_vs) self.update_instance.UPDATE_CHECK.connect(check_update) self.update_instance.UPDATE_CHECK.connect(self.update_instance.deleteLater) thread.finished.connect(thread.deleteLater) thread.start() def _web_metadata_picker(self, gallery, title_url_list, queue, parent=None): if not parent: parent = self text = "Which gallery do you want to extract metadata from?" s_gallery_popup = misc.SingleGalleryChoices(gallery, title_url_list, text, parent) s_gallery_popup.USER_CHOICE.connect(queue.put) def get_metadata(self, gal=None): thread = QThread(self) thread.setObjectName("App.get_metadata") fetch_instance = fetch.Fetch() if gal: galleries = [gal] else: if gui_constants.CONTINUE_AUTO_METADATA_FETCHER: galleries = [g for g in self.manga_list_view.gallery_model._data if not g.exed] else: galleries = self.manga_list_view.gallery_model._data if not galleries: self.notification_bar.add_text("Looks like we've already gone through all galleries!") return None fetch_instance.galleries = galleries self.notification_bar.begin_show() fetch_instance.moveToThread(thread) def done(status): self.notification_bar.end_show() fetch_instance.deleteLater() fetch_instance.GALLERY_PICKER.connect(self._web_metadata_picker) fetch_instance.GALLERY_EMITTER.connect(self.manga_list_view.replace_edit_gallery) fetch_instance.AUTO_METADATA_PROGRESS.connect(self.notification_bar.add_text) thread.started.connect(fetch_instance.auto_web_metadata) fetch_instance.FINISHED.connect(done) thread.finished.connect(thread.deleteLater) thread.start() # def style_tooltip(self): # palette = QToolTip.palette() # palette.setColor() def init_stat_bar(self): self.status_bar = self.statusBar() self.status_bar.setMaximumHeight(20) self.status_bar.setSizeGripEnabled(False) self.stat_info = QLabel() self.stat_info.setIndent(5) self.sort_main = QAction("Asc", self) sort_menu = QMenu() self.sort_main.setMenu(sort_menu) s_by_title = QAction("Title", sort_menu) s_by_artist = QAction("Artist", sort_menu) sort_menu.addAction(s_by_title) sort_menu.addAction(s_by_artist) self.status_bar.addPermanentWidget(self.stat_info) # self.status_bar.addAction(self.sort_main) self.temp_msg = QLabel() self.temp_timer = QTimer() self.manga_list_view.gallery_model.ROWCOUNT_CHANGE.connect(self.stat_row_info) self.manga_list_view.gallery_model.STATUSBAR_MSG.connect(self.stat_temp_msg) self.manga_list_view.STATUS_BAR_MSG.connect(self.stat_temp_msg) self.manga_table_view.STATUS_BAR_MSG.connect(self.stat_temp_msg) self.stat_row_info() def stat_temp_msg(self, msg): self.temp_timer.stop() self.temp_msg.setText(msg) self.status_bar.addWidget(self.temp_msg) self.temp_timer.timeout.connect(self.temp_msg.clear) self.temp_timer.setSingleShot(True) self.temp_timer.start(5000) def stat_row_info(self): r = self.manga_list_view.model().rowCount() t = self.manga_list_view.gallery_model._data_count self.stat_info.setText("Loaded {} of {} ".format(r, t)) def manga_display(self): "initiates the manga view" # list view self.manga_list_main = QWidget() # self.manga_list_main.setContentsMargins(-10, -12, -10, -10) self.manga_list_main.setContentsMargins(10, -9, -10, -10) # x, y, inverted_width, inverted_height self.manga_list_layout = QHBoxLayout() self.manga_list_main.setLayout(self.manga_list_layout) self.manga_list_view = gallery.MangaView(self) self.manga_list_view.clicked.connect(self.popup) self.manga_list_view.manga_delegate.POPUP.connect(self.popup) self.popup_window = self.manga_list_view.manga_delegate.popup_window self.manga_list_layout.addWidget(self.manga_list_view) # table view self.manga_table_main = QWidget() self.manga_table_layout = QVBoxLayout() self.manga_table_main.setLayout(self.manga_table_layout) self.manga_table_view = gallery.MangaTableView(self) self.manga_table_view.gallery_model = self.manga_list_view.gallery_model self.manga_table_view.sort_model = self.manga_list_view.sort_model self.manga_table_view.setModel(self.manga_table_view.sort_model) self.manga_table_view.sort_model.change_model(self.manga_table_view.gallery_model) self.manga_table_view.setColumnWidth(gui_constants.FAV, 20) self.manga_table_view.setColumnWidth(gui_constants.ARTIST, 200) self.manga_table_view.setColumnWidth(gui_constants.TITLE, 400) self.manga_table_view.setColumnWidth(gui_constants.TAGS, 300) self.manga_table_view.setColumnWidth(gui_constants.TYPE, 60) self.manga_table_view.setColumnWidth(gui_constants.CHAPTERS, 60) self.manga_table_view.setColumnWidth(gui_constants.LANGUAGE, 100) self.manga_table_view.setColumnWidth(gui_constants.LINK, 400) self.manga_table_layout.addWidget(self.manga_table_view) def search(self, srch_string): case_ins = srch_string.lower() if not gui_constants.ALLOW_SEARCH_REGEX: remove = "^$*+?{}\\|()[]" for x in remove: if x == "[" or x == "]": continue else: case_ins = case_ins.replace(x, ".") else: try: re.compile(case_ins) except re.error: return self.manga_list_view.sort_model.search(case_ins) def popup(self, index): if not self.popup_window.isVisible(): m_x = QCursor.pos().x() m_y = QCursor.pos().y() d_w = QDesktopWidget().width() d_h = QDesktopWidget().height() p_w = gui_constants.POPUP_WIDTH p_h = gui_constants.POPUP_HEIGHT index_rect = self.manga_list_view.visualRect(index) index_point = self.manga_list_view.mapToGlobal(index_rect.topRight()) # adjust so it doesn't go offscreen if d_w - m_x < p_w and d_h - m_y < p_h: # bottom self.popup_window.move(m_x - p_w + 5, m_y - p_h) elif d_w - m_x > p_w and d_h - m_y < p_h: self.popup_window.move(m_x + 5, m_y - p_h) elif d_w - m_x < p_w: self.popup_window.move(m_x - p_w + 5, m_y + 5) else: self.popup_window.move(index_point) self.popup_window.set_gallery(index.data(Qt.UserRole + 1)) self.popup_window.show() def favourite_display(self): "Switches to favourite display" if self.display.currentIndex() == self.m_l_view_index: self.manga_list_view.sort_model.fav_view() else: self.manga_table_view.sort_model.fav_view() def catalog_display(self): "Switches to catalog display" if self.display.currentIndex() == self.m_l_view_index: self.manga_list_view.sort_model.catalog_view() else: self.manga_table_view.sort_model.catalog_view() def settings(self): sett = settingsdialog.SettingsDialog(self) sett.scroll_speed_changed.connect(self.manga_list_view.updateGeometries) # sett.show() def init_toolbar(self): self.toolbar = QToolBar() self.toolbar.setFixedHeight(25) self.toolbar.setWindowTitle("Show") # text for the contextmenu # self.toolbar.setStyleSheet("QToolBar {border:0px}") # make it user defined? self.toolbar.setMovable(False) self.toolbar.setFloatable(False) # self.toolbar.setIconSize(QSize(20,20)) self.toolbar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) spacer_start = QWidget() # aligns the first actions properly spacer_start.setFixedSize(QSize(10, 1)) self.toolbar.addWidget(spacer_start) favourite_view_icon = QIcon(gui_constants.STAR_BTN_PATH) favourite_view_action = QAction(favourite_view_icon, "Favorites", self) favourite_view_action.setToolTip("Show only favourite galleries") favourite_view_action.triggered.connect(self.favourite_display) # need lambda to pass extra args self.toolbar.addAction(favourite_view_action) catalog_view_icon = QIcon(gui_constants.HOME_BTN_PATH) catalog_view_action = QAction(catalog_view_icon, "Library", self) catalog_view_action.setToolTip("Show all your galleries") # catalog_view_action.setText("Catalog") catalog_view_action.triggered.connect(self.catalog_display) # need lambda to pass extra args self.toolbar.addAction(catalog_view_action) self.toolbar.addSeparator() gallery_menu = QMenu() gallery_action = QToolButton() gallery_action.setText("Gallery ") gallery_action.setPopupMode(QToolButton.InstantPopup) gallery_action.setToolTip("Contains various gallery related features") gallery_action.setMenu(gallery_menu) add_gallery_icon = QIcon(gui_constants.PLUS_PATH) gallery_action_add = QAction(add_gallery_icon, "Add gallery", self) gallery_action_add.triggered.connect(self.manga_list_view.SERIES_DIALOG.emit) gallery_action_add.setToolTip("Add a single gallery thoroughly") gallery_menu.addAction(gallery_action_add) add_more_action = QAction(add_gallery_icon, "Add galleries...", self) add_more_action.setStatusTip("Add galleries from different folders") add_more_action.triggered.connect(lambda: self.populate(True)) gallery_menu.addAction(add_more_action) populate_action = QAction(add_gallery_icon, "Populate from folder...", self) populate_action.setStatusTip("Populates the DB with galleries from a single folder") populate_action.triggered.connect(self.populate) gallery_menu.addAction(populate_action) gallery_menu.addSeparator() metadata_action = QAction("Get metadata for all galleries", self) metadata_action.triggered.connect(self.get_metadata) gallery_menu.addAction(metadata_action) self.toolbar.addWidget(gallery_action) self.toolbar.addSeparator() misc_action = QToolButton() misc_action.setText("Misc ") misc_action_menu = QMenu() misc_action.setMenu(misc_action_menu) misc_action.setPopupMode(QToolButton.InstantPopup) misc_action.setToolTip("Contains misc. features") misc_action_random = QAction("Open random gallery", misc_action_menu) misc_action_random.triggered.connect(self.manga_list_view.open_random_gallery) misc_action_menu.addAction(misc_action_random) duplicate_check_simple = QAction("Simple duplicate finder", misc_action_menu) duplicate_check_simple.triggered.connect(lambda: self.manga_list_view.duplicate_check()) misc_action_menu.addAction(duplicate_check_simple) self.toolbar.addWidget(misc_action) spacer_middle = QWidget() # aligns buttons to the right spacer_middle.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.toolbar.addWidget(spacer_middle) self.grid_toggle_g_icon = QIcon(gui_constants.GRID_PATH) self.grid_toggle_l_icon = QIcon(gui_constants.LIST_PATH) self.grid_toggle = QToolButton() if self.display.currentIndex() == self.m_l_view_index: self.grid_toggle.setIcon(self.grid_toggle_l_icon) else: self.grid_toggle.setIcon(self.grid_toggle_g_icon) self.grid_toggle.setObjectName("gridtoggle") self.grid_toggle.clicked.connect(self.toggle_view) self.toolbar.addWidget(self.grid_toggle) self.search_bar = misc.LineEdit() if gui_constants.SEARCH_AUTOCOMPLETE: completer = QCompleter(self) completer.setModel(self.manga_list_view.gallery_model) completer.setCaseSensitivity(Qt.CaseInsensitive) completer.setCompletionMode(QCompleter.PopupCompletion) completer.setCompletionRole(Qt.DisplayRole) completer.setCompletionColumn(gui_constants.TITLE) completer.setFilterMode(Qt.MatchContains) self.search_bar.setCompleter(completer) if gui_constants.SEARCH_ON_ENTER: self.search_bar.returnPressed.connect(lambda: self.search(self.search_bar.text())) else: self.search_bar.textChanged[str].connect(self.search) self.search_bar.setPlaceholderText("Search title, artist, namespace & tags") self.search_bar.setMinimumWidth(150) self.search_bar.setMaximumWidth(500) self.toolbar.addWidget(self.search_bar) self.toolbar.addSeparator() settings_icon = QIcon(gui_constants.SETTINGS_PATH) settings_action = QAction("Set&tings", self) settings_action.triggered.connect(self.settings) self.toolbar.addAction(settings_action) spacer_end = QWidget() # aligns About action properly spacer_end.setFixedSize(QSize(10, 1)) self.toolbar.addWidget(spacer_end) self.addToolBar(self.toolbar) def toggle_view(self): """ Toggles the current display view """ if self.display.currentIndex() == self.m_l_view_index: self.display.setCurrentIndex(self.m_t_view_index) self.grid_toggle.setIcon(self.grid_toggle_g_icon) else: self.display.setCurrentIndex(self.m_l_view_index) self.grid_toggle.setIcon(self.grid_toggle_l_icon) # TODO: Improve this so that it adds to the gallery dialog, # so user can edit data before inserting (make it a choice) def populate(self, mixed=None): "Populates the database with gallery from local drive'" if mixed: gallery_view = misc.GalleryListView(self, True) gallery_view.SERIES.connect(self.gallery_populate) gallery_view.show() else: path = QFileDialog.getExistingDirectory(None, "Choose a folder containing your galleries") self.gallery_populate(path, True) def gallery_populate(self, path, validate=False): "Scans the given path for gallery to add into the DB" if len(path) is not 0: data_thread = QThread(self) data_thread.setObjectName("General gallery populate") loading = misc.Loading(self) if not loading.ON: misc.Loading.ON = True fetch_instance = fetch.Fetch() fetch_instance.series_path = path loading.show() def finished(status): def hide_loading(): loading.hide() if status: if len(status) != 0: def add_gallery(gallery_list): def append_to_model(x): self.manga_list_view.gallery_model.insertRows(x, None, len(x)) class A(QObject): done = pyqtSignal() prog = pyqtSignal(int) def __init__(self, obj, parent=None): super().__init__(parent) self.obj = obj self.galleries = [] def add_to_db(self): gui_constants.NOTIF_BAR.begin_show() gui_constants.NOTIF_BAR.add_text("Populating database...") for y, x in enumerate(self.obj): gui_constants.NOTIF_BAR.add_text( "Populating database {}/{}".format(y + 1, len(self.obj)) ) gallerydb.add_method_queue(gallerydb.GalleryDB.add_gallery_return, False, x) self.galleries.append(x) y += 1 self.prog.emit(y) append_to_model(self.galleries) gui_constants.NOTIF_BAR.end_show() self.done.emit() loading.progress.setMaximum(len(gallery_list)) a_instance = A(gallery_list) thread = QThread(self) thread.setObjectName("Database populate") def loading_show(): loading.setText("Populating database.\nPlease wait...") loading.show() def loading_hide(): loading.close() self.manga_list_view.gallery_model.ROWCOUNT_CHANGE.emit() def del_later(): try: a_instance.deleteLater() except NameError: pass a_instance.moveToThread(thread) a_instance.prog.connect(loading.progress.setValue) thread.started.connect(loading_show) thread.started.connect(a_instance.add_to_db) a_instance.done.connect(loading_hide) a_instance.done.connect(del_later) thread.finished.connect(thread.deleteLater) thread.start() data_thread.quit hide_loading() log_i("Populating DB from gallery folder: OK") if validate: gallery_list = misc.GalleryListView(self) gallery_list.SERIES.connect(add_gallery) for ser in status: gallery_list.add_gallery(ser, os.path.split(ser.path)[1]) # self.manga_list_view.gallery_model.populate_data() gallery_list.show() else: add_gallery(status) misc.Loading.ON = False else: log_d("No new gallery was found") loading.setText("No new gallery found") data_thread.quit misc.Loading.ON = False else: log_e("Populating DB from gallery folder: Nothing was added!") loading.setText("<font color=red>Nothing was added. Check happypanda_log for details..</font>") loading.progress.setStyleSheet("background-color:red;") data_thread.quit QTimer.singleShot(10000, loading.close) def fetch_deleteLater(): try: fetch_instance.deleteLater except NameError: pass def a_progress(prog): loading.progress.setValue(prog) loading.setText("Searching for galleries...") fetch_instance.moveToThread(data_thread) fetch_instance.DATA_COUNT.connect(loading.progress.setMaximum) fetch_instance.PROGRESS.connect(a_progress) data_thread.started.connect(fetch_instance.local) fetch_instance.FINISHED.connect(finished) fetch_instance.FINISHED.connect(fetch_deleteLater) data_thread.finished.connect(data_thread.deleteLater) data_thread.start() log_i("Populating DB from gallery folder") def resizeEvent(self, event): try: self.notification_bar.resize(event.size().width()) except AttributeError: pass return super().resizeEvent(event) def closeEvent(self, event): # watchers try: self.watchers.stop_all() except AttributeError: pass # settings settings.set(self.manga_list_view.current_sort, "General", "current sort") settings.win_save(self, "AppWindow") # temp dir try: for root, dirs, files in os.walk("temp", topdown=False): for name in files: os.remove(os.path.join(root, name)) for name in dirs: os.rmdir(os.path.join(root, name)) log_d("Empty temp on exit: OK") except: log_d("Empty temp on exit: FAIL") # error err = sys.exc_info() if all(err): log_c("Last error before exit:\n{}\n{}\n{}".format(err[0], err[1], err[2])) else: log_d("Normal Exit App: OK") super().closeEvent(event) app = QApplication.instance() app.quit() sys.exit()
class AppWindow(QMainWindow): "The application's main window" def __init__(self): super().__init__() self.center = QWidget() self.display = QStackedLayout() self.center.setLayout(self.display) # init the manga view variables self.manga_display() # init the chapter view variables self.chapter_display() # init toolbar self.init_toolbar() # init status bar self.init_stat_bar() self.display.addWidget(self.manga_main) self.display.addWidget(self.chapter_main) self.setCentralWidget(self.center) self.setWindowTitle("Happypanda") self.resize(1029, 650) self.show() def init_stat_bar(self): self.status_bar = self.statusBar() self.status_bar.setMaximumHeight(20) self.status_bar.setSizeGripEnabled(False) self.stat_info = QLabel() self.stat_info.setIndent(5) self.sort_main = QAction("Asc", self) sort_menu = QMenu() self.sort_main.setMenu(sort_menu) s_by_title = QAction("Title", sort_menu) s_by_artist = QAction("Artist", sort_menu) sort_menu.addAction(s_by_title) sort_menu.addAction(s_by_artist) self.status_bar.addPermanentWidget(self.stat_info) #self.status_bar.addAction(self.sort_main) self.temp_msg = QLabel() self.temp_timer = QTimer() self.manga_list_view.series_model.ROWCOUNT_CHANGE.connect(self.stat_row_info) self.manga_list_view.series_model.STATUSBAR_MSG.connect(self.stat_temp_msg) def stat_temp_msg(self, msg): self.temp_timer.stop() self.temp_msg.setText(msg) self.status_bar.addWidget(self.temp_msg) self.temp_timer.timeout.connect(self.temp_msg.clear) self.temp_timer.setSingleShot(True) self.temp_timer.start(5000) def stat_row_info(self): r = self.manga_list_view.series_model.rowCount() t = len(self.manga_list_view.series_model._data) self.stat_info.setText("<b>Showing {} of {} </b>".format(r, t)) def manga_display(self): "initiates the manga view" self.manga_main = QWidget() self.manga_main.setContentsMargins(-10, -12, -10, -10) self.manga_view = QHBoxLayout() self.manga_main.setLayout(self.manga_view) manga_delegate = series.CustomDelegate() manga_delegate.BUTTON_CLICKED.connect(self.setCurrentIndex) self.manga_list_view = series.MangaView() self.manga_list_view.setItemDelegate(manga_delegate) self.manga_view.addWidget(self.manga_list_view) def favourite_display(self): "initiates favourite display" pass def chapter_display(self): "Initiates chapter view" self.chapter_main = QWidget() self.chapter_main.setObjectName("chapter_main") # to allow styling this object self.chapter_layout = QHBoxLayout() self.chapter_main.setLayout(self.chapter_layout) #self.chapter_info.setContentsMargins(-8,-7,-7,-7) #self.chapter_info.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) self.chapter_info_view = series.ChapterInfo() self.chapter_layout.addWidget(self.chapter_info_view) chapter_list_view = series.ChapterView() self.chapter_layout.addWidget(chapter_list_view) #self.chapter.setCollapsible(0, True) #self.chapter.setCollapsible(1, False) def init_toolbar(self): self.toolbar = QToolBar() self.toolbar.setFixedHeight(30) self.toolbar.setWindowTitle("Show") # text for the contextmenu #self.toolbar.setStyleSheet("QToolBar {border:0px}") # make it user defined? self.toolbar.setMovable(False) self.toolbar.setFloatable(False) #self.toolbar.setIconSize(QSize(20,20)) self.toolbar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) spacer_start = QWidget() # aligns the first actions properly spacer_start.setFixedSize(QSize(10, 1)) self.toolbar.addWidget(spacer_start) favourite_view_icon = QIcon(gui_constants.STAR_BTN_PATH) favourite_view_action = QAction(favourite_view_icon, "Favourite", self) #favourite_view_action.setText("Manga View") favourite_view_action.triggered.connect(lambda: self.setCurrentIndex(1)) #need lambda to pass extra args self.toolbar.addAction(favourite_view_action) catalog_view_icon = QIcon(gui_constants.HOME_BTN_PATH) catalog_view_action = QAction(catalog_view_icon, "Library", self) #catalog_view_action.setText("Catalog") catalog_view_action.triggered.connect(lambda: self.setCurrentIndex(0)) #need lambda to pass extra args self.toolbar.addAction(catalog_view_action) self.toolbar.addSeparator() series_icon = QIcon(gui_constants.PLUS_PATH) series_action = QAction(series_icon, "Add series...", self) series_action.triggered.connect(self.manga_list_view.SERIES_DIALOG.emit) series_menu = QMenu() series_menu.addSeparator() populate_action = QAction("Populate from folder...", self) populate_action.triggered.connect(self.populate) series_menu.addAction(populate_action) series_action.setMenu(series_menu) self.toolbar.addAction(series_action) spacer_middle = QWidget() # aligns buttons to the right spacer_middle.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.toolbar.addWidget(spacer_middle) self.search_bar = QLineEdit() self.search_bar.setPlaceholderText("Search title, artist, genres") self.search_bar.setMaximumWidth(200) self.toolbar.addWidget(self.search_bar) self.toolbar.addSeparator() settings_icon = QIcon(gui_constants.SETTINGS_PATH) settings_action = QAction(settings_icon, "Set&tings", self) self.toolbar.addAction(settings_action) self.addToolBar(self.toolbar) spacer_end = QWidget() # aligns About action properly spacer_end.setFixedSize(QSize(10, 1)) self.toolbar.addWidget(spacer_end) def setCurrentIndex(self, number, index=None): """Changes the current display view. Params: number <- int (0 for manga view, 1 for chapter view Optional: index <- QModelIndex for chapter view Note: 0-based indexing """ if index is not None: self.chapter_info_view.display_manga(index) self.display.setCurrentIndex(number) else: self.display.setCurrentIndex(number) # TODO: Improve this so that it adds to the series dialog, # so user can edit data before inserting (make it a choice) def populate(self): "Populates the database with series from local drive'" msgbox = QMessageBox() msgbox.setText("<font color='red'><b>Use with care.</b></font> Choose a folder containing all your series'.") msgbox.setInformativeText("Oniichan, are you sure you want to do this?") msgbox.setStandardButtons(QMessageBox.Yes | QMessageBox.No) msgbox.setDefaultButton(QMessageBox.No) if msgbox.exec() == QMessageBox.Yes: path = QFileDialog.getExistingDirectory(None, "Choose a folder containing your series'") if len(path) is not 0: data_thread = QThread() loading_thread = QThread() loading = misc.Loading() if not loading.ON: misc.Loading.ON = True fetch_instance = fetch.Fetch() fetch_instance.series_path = path loading.show() def finished(status): if status: self.manga_list_view.series_model.populate_data() # TODO: make it spawn a dialog instead (from utils.py or misc.py) if loading.progress.maximum() == loading.progress.value(): misc.Loading.ON = False loading.hide() data_thread.quit else: loading.setText("<font color=red>An error occured. Try restarting..</font>") loading.progress.setStyleSheet("background-color:red") data_thread.quit def fetch_deleteLater(): try: fetch_instance.deleteLater except NameError: pass def thread_deleteLater(): #NOTE: Isn't this bad? data_thread.deleteLater def a_progress(prog): loading.progress.setValue(prog) loading.setText("Searching on local disk...\n(Will take a while on first time)") fetch_instance.moveToThread(data_thread) fetch_instance.DATA_COUNT.connect(loading.progress.setMaximum) fetch_instance.PROGRESS.connect(a_progress) data_thread.started.connect(fetch_instance.local) fetch_instance.FINISHED.connect(finished) fetch_instance.FINISHED.connect(fetch_deleteLater) fetch_instance.FINISHED.connect(thread_deleteLater) data_thread.start()
class ClearHistoryDialog(QDialog): def __init__(self, parent=None): super(ClearHistoryDialog, self).__init__(parent) self.setWindowFlags(Qt.Dialog) self.setWindowTitle(tr("Clear Data")) closeWindowAction = QAction(self) closeWindowAction.setShortcuts(["Esc", "Ctrl+W", "Ctrl+Shift+Del"]) closeWindowAction.triggered.connect(self.close) self.addAction(closeWindowAction) self.layout = QVBoxLayout() self.setLayout(self.layout) label = QLabel(tr("What to clear:"), self) self.layout.addWidget(label) self.dataType = QComboBox(self) self.dataType.addItem(tr("History")) self.dataType.addItem(tr("Cookies")) self.dataType.addItem(tr("Memory Caches")) self.dataType.addItem(tr("Persistent Storage")) self.dataType.addItem(tr("Everything")) self.layout.addWidget(self.dataType) self.toolBar = QToolBar(self) self.toolBar.setStyleSheet(common.blank_toolbar) self.toolBar.setMovable(False) self.toolBar.setContextMenuPolicy(Qt.CustomContextMenu) self.layout.addWidget(self.toolBar) self.clearHistoryButton = QPushButton(tr("Clear"), self) self.clearHistoryButton.clicked.connect(self.clearHistory) self.toolBar.addWidget(self.clearHistoryButton) self.closeButton = QPushButton(tr("Close"), self) self.closeButton.clicked.connect(self.close) self.toolBar.addWidget(self.closeButton) def display(self): self.show() self.activateWindow() def clearHistory(self): clear_everything = (self.dataType.currentIndex() == self.dataType.count()-1) if self.dataType.currentIndex() == 0 or clear_everything: data.clearHistory() if self.dataType.currentIndex() == 1 or clear_everything: data.clearCookies() if self.dataType.currentIndex() == 2 or clear_everything: QWebSettings.globalSettings().clearMemoryCaches() if self.dataType.currentIndex() == 3 or clear_everything: QWebSettings.globalSettings().setIconDatabasePath("") QWebSettings.globalSettings().setLocalStoragePath("") QWebSettings.globalSettings().setOfflineStoragePath("") QWebSettings.globalSettings().setOfflineWebApplicationCachePath("") for subpath in ("WebpageIcons.db", "LocalStorage", "Databases",): path = os.path.abspath(os.path.join(settings.settings_folder, subpath)) if os.path.isfile(path): try: os.remove(path) except: pass elif os.path.isdir(path): if sys.platform.startswith("win"): args = ["rmdir", "/s", "/q", "\"" + path + "\""] try: os.system(" ".join(args)) except: pass else: try: subprocess.Popen(["rm", "-rf", path]) except: pass QWebSettings.globalSettings().enablePersistentStorage(settings.settings_folder)
class MusicPlayer(QMainWindow): """MusicPlayer houses all of elements that directly interact with the main window.""" def __init__(self, parent=None): """Initialize the QMainWindow widget. The window title, window icon, and window size are initialized here as well as the following widgets: QMediaPlayer, QMediaPlaylist, QMediaContent, QMenuBar, QToolBar, QLabel, QPixmap, QSlider, QDockWidget, QListWidget, QWidget, and QVBoxLayout. The connect signals for relavant widgets are also initialized. """ super(MusicPlayer, self).__init__(parent) self.setWindowTitle('Mosaic') window_icon = utilities.resource_filename('mosaic.images', 'icon.png') self.setWindowIcon(QIcon(window_icon)) self.resize(defaults.Settings().window_size, defaults.Settings().window_size + 63) # Initiates Qt objects to be used by MusicPlayer self.player = QMediaPlayer() self.playlist = QMediaPlaylist() self.playlist_location = defaults.Settings().playlist_path self.content = QMediaContent() self.menu = self.menuBar() self.toolbar = QToolBar() self.art = QLabel() self.pixmap = QPixmap() self.slider = QSlider(Qt.Horizontal) self.duration_label = QLabel() self.playlist_dock = QDockWidget('Playlist', self) self.library_dock = QDockWidget('Media Library', self) self.playlist_view = QListWidget() self.library_view = library.MediaLibraryView() self.library_model = library.MediaLibraryModel() self.preferences = configuration.PreferencesDialog() self.widget = QWidget() self.layout = QVBoxLayout(self.widget) self.duration = 0 self.playlist_dock_state = None self.library_dock_state = None # Sets QWidget() as the central widget of the main window self.setCentralWidget(self.widget) self.layout.setContentsMargins(0, 0, 0, 0) self.art.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) # Initiates the playlist dock widget and the library dock widget self.addDockWidget(defaults.Settings().dock_position, self.playlist_dock) self.playlist_dock.setWidget(self.playlist_view) self.playlist_dock.setVisible(defaults.Settings().playlist_on_start) self.playlist_dock.setFeatures(QDockWidget.DockWidgetClosable) self.addDockWidget(defaults.Settings().dock_position, self.library_dock) self.library_dock.setWidget(self.library_view) self.library_dock.setVisible(defaults.Settings().media_library_on_start) self.library_dock.setFeatures(QDockWidget.DockWidgetClosable) self.tabifyDockWidget(self.playlist_dock, self.library_dock) # Sets the range of the playback slider and sets the playback mode as looping self.slider.setRange(0, self.player.duration() / 1000) self.playlist.setPlaybackMode(QMediaPlaylist.Sequential) # OSX system menu bar causes conflicts with PyQt5 menu bar if sys.platform == 'darwin': self.menu.setNativeMenuBar(False) # Initiates Settings in the defaults module to give access to settings.toml defaults.Settings() # Signals that connect to other methods when they're called self.player.metaDataChanged.connect(self.display_meta_data) self.slider.sliderMoved.connect(self.seek) self.player.durationChanged.connect(self.song_duration) self.player.positionChanged.connect(self.song_position) self.player.stateChanged.connect(self.set_state) self.playlist_view.itemActivated.connect(self.activate_playlist_item) self.library_view.activated.connect(self.open_media_library) self.playlist.currentIndexChanged.connect(self.change_index) self.playlist.mediaInserted.connect(self.initialize_playlist) self.playlist_dock.visibilityChanged.connect(self.dock_visiblity_change) self.library_dock.visibilityChanged.connect(self.dock_visiblity_change) self.preferences.dialog_media_library.media_library_line.textChanged.connect(self.change_media_library_path) self.preferences.dialog_view_options.dropdown_box.currentIndexChanged.connect(self.change_window_size) self.art.mousePressEvent = self.press_playback # Creating the menu controls, media controls, and window size of the music player self.menu_controls() self.media_controls() self.load_saved_playlist() def menu_controls(self): """Initiate the menu bar and add it to the QMainWindow widget.""" self.file = self.menu.addMenu('File') self.edit = self.menu.addMenu('Edit') self.playback = self.menu.addMenu('Playback') self.view = self.menu.addMenu('View') self.help_ = self.menu.addMenu('Help') self.file_menu() self.edit_menu() self.playback_menu() self.view_menu() self.help_menu() def media_controls(self): """Create the bottom toolbar and controls used for media playback.""" self.addToolBar(Qt.BottomToolBarArea, self.toolbar) self.toolbar.setMovable(False) play_icon = utilities.resource_filename('mosaic.images', 'md_play.png') self.play_action = QAction(QIcon(play_icon), 'Play', self) self.play_action.triggered.connect(self.player.play) stop_icon = utilities.resource_filename('mosaic.images', 'md_stop.png') self.stop_action = QAction(QIcon(stop_icon), 'Stop', self) self.stop_action.triggered.connect(self.player.stop) previous_icon = utilities.resource_filename('mosaic.images', 'md_previous.png') self.previous_action = QAction(QIcon(previous_icon), 'Previous', self) self.previous_action.triggered.connect(self.previous) next_icon = utilities.resource_filename('mosaic.images', 'md_next.png') self.next_action = QAction(QIcon(next_icon), 'Next', self) self.next_action.triggered.connect(self.playlist.next) repeat_icon = utilities.resource_filename('mosaic.images', 'md_repeat_none.png') self.repeat_action = QAction(QIcon(repeat_icon), 'Repeat', self) self.repeat_action.setShortcut('R') self.repeat_action.triggered.connect(self.repeat_song) self.toolbar.addAction(self.play_action) self.toolbar.addAction(self.stop_action) self.toolbar.addAction(self.previous_action) self.toolbar.addAction(self.next_action) self.toolbar.addAction(self.repeat_action) self.toolbar.addWidget(self.slider) self.toolbar.addWidget(self.duration_label) def file_menu(self): """Add a file menu to the menu bar. The file menu houses the Open File, Open Multiple Files, Open Playlist, Open Directory, and Exit Application menu items. """ self.open_action = QAction('Open File', self) self.open_action.setShortcut('O') self.open_action.triggered.connect(self.open_file) self.open_multiple_files_action = QAction('Open Multiple Files', self) self.open_multiple_files_action.setShortcut('M') self.open_multiple_files_action.triggered.connect(self.open_multiple_files) self.open_playlist_action = QAction('Open Playlist', self) self.open_playlist_action.setShortcut('CTRL+P') self.open_playlist_action.triggered.connect(self.open_playlist) self.open_directory_action = QAction('Open Directory', self) self.open_directory_action.setShortcut('D') self.open_directory_action.triggered.connect(self.open_directory) self.save_playlist_action = QAction('Save Playlist', self) self.save_playlist_action.setShortcut('CTRL+S') self.save_playlist_action.triggered.connect(self.save_playlist) self.exit_action = QAction('Quit', self) self.exit_action.setShortcut('CTRL+Q') self.exit_action.triggered.connect(self.closeEvent) self.file.addAction(self.open_action) self.file.addAction(self.open_multiple_files_action) self.file.addAction(self.open_playlist_action) self.file.addAction(self.open_directory_action) self.file.addSeparator() self.file.addAction(self.save_playlist_action) self.file.addSeparator() self.file.addAction(self.exit_action) def edit_menu(self): """Add an edit menu to the menu bar. The edit menu houses the preferences item that opens a preferences dialog that allows the user to customize features of the music player. """ self.preferences_action = QAction('Preferences', self) self.preferences_action.setShortcut('CTRL+SHIFT+P') self.preferences_action.triggered.connect(lambda: self.preferences.exec_()) self.edit.addAction(self.preferences_action) def playback_menu(self): """Add a playback menu to the menu bar. The playback menu houses """ self.play_playback_action = QAction('Play', self) self.play_playback_action.setShortcut('P') self.play_playback_action.triggered.connect(self.player.play) self.stop_playback_action = QAction('Stop', self) self.stop_playback_action.setShortcut('S') self.stop_playback_action.triggered.connect(self.player.stop) self.previous_playback_action = QAction('Previous', self) self.previous_playback_action.setShortcut('B') self.previous_playback_action.triggered.connect(self.previous) self.next_playback_action = QAction('Next', self) self.next_playback_action.setShortcut('N') self.next_playback_action.triggered.connect(self.playlist.next) self.playback.addAction(self.play_playback_action) self.playback.addAction(self.stop_playback_action) self.playback.addAction(self.previous_playback_action) self.playback.addAction(self.next_playback_action) def view_menu(self): """Add a view menu to the menu bar. The view menu houses the Playlist, Media Library, Minimalist View, and Media Information menu items. The Playlist item toggles the playlist dock into and out of view. The Media Library items toggles the media library dock into and out of view. The Minimalist View item resizes the window and shows only the menu bar and player controls. The Media Information item opens a dialog that shows information relevant to the currently playing song. """ self.dock_action = self.playlist_dock.toggleViewAction() self.dock_action.setShortcut('CTRL+ALT+P') self.library_dock_action = self.library_dock.toggleViewAction() self.library_dock_action.setShortcut('CTRL+ALT+L') self.minimalist_view_action = QAction('Minimalist View', self) self.minimalist_view_action.setShortcut('CTRL+ALT+M') self.minimalist_view_action.setCheckable(True) self.minimalist_view_action.triggered.connect(self.minimalist_view) self.view_media_info_action = QAction('Media Information', self) self.view_media_info_action.setShortcut('CTRL+SHIFT+M') self.view_media_info_action.triggered.connect(self.media_information_dialog) self.view.addAction(self.dock_action) self.view.addAction(self.library_dock_action) self.view.addSeparator() self.view.addAction(self.minimalist_view_action) self.view.addSeparator() self.view.addAction(self.view_media_info_action) def help_menu(self): """Add a help menu to the menu bar. The help menu houses the about dialog that shows the user information related to the application. """ self.about_action = QAction('About', self) self.about_action.setShortcut('H') self.about_action.triggered.connect(lambda: about.AboutDialog().exec_()) self.help_.addAction(self.about_action) def open_file(self): """Open the selected file and add it to a new playlist.""" filename, success = QFileDialog.getOpenFileName(self, 'Open File', '', 'Audio (*.mp3 *.flac)', '', QFileDialog.ReadOnly) if success: file_info = QFileInfo(filename).fileName() playlist_item = QListWidgetItem(file_info) self.playlist.clear() self.playlist_view.clear() self.playlist.addMedia(QMediaContent(QUrl().fromLocalFile(filename))) self.player.setPlaylist(self.playlist) playlist_item.setToolTip(file_info) self.playlist_view.addItem(playlist_item) self.playlist_view.setCurrentRow(0) self.player.play() def open_multiple_files(self): """Open the selected files and add them to a new playlist.""" filenames, success = QFileDialog.getOpenFileNames(self, 'Open Multiple Files', '', 'Audio (*.mp3 *.flac)', '', QFileDialog.ReadOnly) if success: self.playlist.clear() self.playlist_view.clear() for file in natsort.natsorted(filenames, alg=natsort.ns.PATH): file_info = QFileInfo(file).fileName() playlist_item = QListWidgetItem(file_info) self.playlist.addMedia(QMediaContent(QUrl().fromLocalFile(file))) self.player.setPlaylist(self.playlist) playlist_item.setToolTip(file_info) self.playlist_view.addItem(playlist_item) self.playlist_view.setCurrentRow(0) self.player.play() def open_playlist(self): """Load an M3U or PLS file into a new playlist.""" playlist, success = QFileDialog.getOpenFileName(self, 'Open Playlist', '', 'Playlist (*.m3u *.pls)', '', QFileDialog.ReadOnly) if success: playlist = QUrl.fromLocalFile(playlist) self.playlist.clear() self.playlist_view.clear() self.playlist.load(playlist) self.player.setPlaylist(self.playlist) for song_index in range(self.playlist.mediaCount()): file_info = self.playlist.media(song_index).canonicalUrl().fileName() playlist_item = QListWidgetItem(file_info) playlist_item.setToolTip(file_info) self.playlist_view.addItem(playlist_item) self.playlist_view.setCurrentRow(0) self.player.play() def save_playlist(self): """Save the media in the playlist dock as a new M3U playlist.""" playlist, success = QFileDialog.getSaveFileName(self, 'Save Playlist', '', 'Playlist (*.m3u)', '') if success: saved_playlist = "{}.m3u" .format(playlist) self.playlist.save(QUrl().fromLocalFile(saved_playlist), "m3u") def load_saved_playlist(self): """Load the saved playlist if user setting permits.""" saved_playlist = "{}/.m3u" .format(self.playlist_location) if os.path.exists(saved_playlist): playlist = QUrl().fromLocalFile(saved_playlist) self.playlist.load(playlist) self.player.setPlaylist(self.playlist) for song_index in range(self.playlist.mediaCount()): file_info = self.playlist.media(song_index).canonicalUrl().fileName() playlist_item = QListWidgetItem(file_info) playlist_item.setToolTip(file_info) self.playlist_view.addItem(playlist_item) self.playlist_view.setCurrentRow(0) def open_directory(self): """Open the selected directory and add the files within to an empty playlist.""" directory = QFileDialog.getExistingDirectory(self, 'Open Directory', '', QFileDialog.ReadOnly) if directory: self.playlist.clear() self.playlist_view.clear() for dirpath, __, files in os.walk(directory): for filename in natsort.natsorted(files, alg=natsort.ns.PATH): file = os.path.join(dirpath, filename) if filename.endswith(('mp3', 'flac')): self.playlist.addMedia(QMediaContent(QUrl().fromLocalFile(file))) playlist_item = QListWidgetItem(filename) playlist_item.setToolTip(filename) self.playlist_view.addItem(playlist_item) self.player.setPlaylist(self.playlist) self.playlist_view.setCurrentRow(0) self.player.play() def open_media_library(self, index): """Open a directory or file from the media library into an empty playlist.""" self.playlist.clear() self.playlist_view.clear() if self.library_model.fileName(index).endswith(('mp3', 'flac')): self.playlist.addMedia(QMediaContent(QUrl().fromLocalFile(self.library_model.filePath(index)))) self.playlist_view.addItem(self.library_model.fileName(index)) elif self.library_model.isDir(index): directory = self.library_model.filePath(index) for dirpath, __, files in os.walk(directory): for filename in natsort.natsorted(files, alg=natsort.ns.PATH): file = os.path.join(dirpath, filename) if filename.endswith(('mp3', 'flac')): self.playlist.addMedia(QMediaContent(QUrl().fromLocalFile(file))) playlist_item = QListWidgetItem(filename) playlist_item.setToolTip(filename) self.playlist_view.addItem(playlist_item) self.player.setPlaylist(self.playlist) self.player.play() def display_meta_data(self): """Display the current song's metadata in the main window. If the current song contains metadata, its cover art is extracted and shown in the main window while the track number, artist, album, and track title are shown in the window title. """ if self.player.isMetaDataAvailable(): file_path = self.player.currentMedia().canonicalUrl().toLocalFile() (album, artist, title, track_number, *__, artwork) = metadata.metadata(file_path) try: self.pixmap.loadFromData(artwork) except TypeError: self.pixmap = QPixmap(artwork) meta_data = '{} - {} - {} - {}' .format(track_number, artist, album, title) self.setWindowTitle(meta_data) self.art.setScaledContents(True) self.art.setPixmap(self.pixmap) self.layout.addWidget(self.art) def initialize_playlist(self, start): """Display playlist and reset playback mode when media inserted into playlist.""" if start == 0: if self.library_dock.isVisible(): self.playlist_dock.setVisible(True) self.playlist_dock.show() self.playlist_dock.raise_() if self.playlist.playbackMode() != QMediaPlaylist.Sequential: self.playlist.setPlaybackMode(QMediaPlaylist.Sequential) repeat_icon = utilities.resource_filename('mosaic.images', 'md_repeat_none.png') self.repeat_action.setIcon(QIcon(repeat_icon)) def press_playback(self, event): """Change the playback of the player on cover art mouse event. When the cover art is clicked, the player will play the media if the player is either paused or stopped. If the media is playing, the media is set to pause. """ if event.button() == 1 and configuration.Playback().cover_art_playback.isChecked(): if (self.player.state() == QMediaPlayer.StoppedState or self.player.state() == QMediaPlayer.PausedState): self.player.play() elif self.player.state() == QMediaPlayer.PlayingState: self.player.pause() def seek(self, seconds): """Set the position of the song to the position dragged to by the user.""" self.player.setPosition(seconds * 1000) def song_duration(self, duration): """Set the slider to the duration of the currently played media.""" duration /= 1000 self.duration = duration self.slider.setMaximum(duration) def song_position(self, progress): """Move the horizontal slider in sync with the duration of the song. The progress is relayed to update_duration() in order to display the time label next to the slider. """ progress /= 1000 if not self.slider.isSliderDown(): self.slider.setValue(progress) self.update_duration(progress) def update_duration(self, current_duration): """Calculate the time played and the length of the song. Both of these times are sent to duration_label() in order to display the times on the toolbar. """ duration = self.duration if current_duration or duration: time_played = QTime((current_duration / 3600) % 60, (current_duration / 60) % 60, (current_duration % 60), (current_duration * 1000) % 1000) song_length = QTime((duration / 3600) % 60, (duration / 60) % 60, (duration % 60), (duration * 1000) % 1000) if duration > 3600: time_format = "hh:mm:ss" else: time_format = "mm:ss" time_display = "{} / {}" .format(time_played.toString(time_format), song_length.toString(time_format)) else: time_display = "" self.duration_label.setText(time_display) def set_state(self, state): """Change the icon in the toolbar in relation to the state of the player. The play icon changes to the pause icon when a song is playing and the pause icon changes back to the play icon when either paused or stopped. """ if self.player.state() == QMediaPlayer.PlayingState: pause_icon = utilities.resource_filename('mosaic.images', 'md_pause.png') self.play_action.setIcon(QIcon(pause_icon)) self.play_action.triggered.connect(self.player.pause) elif (self.player.state() == QMediaPlayer.PausedState or self.player.state() == QMediaPlayer.StoppedState): self.play_action.triggered.connect(self.player.play) play_icon = utilities.resource_filename('mosaic.images', 'md_play.png') self.play_action.setIcon(QIcon(play_icon)) def previous(self): """Move to the previous song in the playlist. Moves to the previous song in the playlist if the current song is less than five seconds in. Otherwise, restarts the current song. """ if self.player.position() <= 5000: self.playlist.previous() else: self.player.setPosition(0) def repeat_song(self): """Set the current media to repeat and change the repeat icon accordingly. There are four playback modes: repeat none, repeat all, repeat once, and shuffle. Clicking the repeat button cycles through each playback mode. """ if self.playlist.playbackMode() == QMediaPlaylist.Sequential: self.playlist.setPlaybackMode(QMediaPlaylist.Loop) repeat_on_icon = utilities.resource_filename('mosaic.images', 'md_repeat_all.png') self.repeat_action.setIcon(QIcon(repeat_on_icon)) elif self.playlist.playbackMode() == QMediaPlaylist.Loop: self.playlist.setPlaybackMode(QMediaPlaylist.CurrentItemInLoop) repeat_on_icon = utilities.resource_filename('mosaic.images', 'md_repeat_once.png') self.repeat_action.setIcon(QIcon(repeat_on_icon)) elif self.playlist.playbackMode() == QMediaPlaylist.CurrentItemInLoop: self.playlist.setPlaybackMode(QMediaPlaylist.Random) repeat_icon = utilities.resource_filename('mosaic.images', 'md_shuffle.png') self.repeat_action.setIcon(QIcon(repeat_icon)) elif self.playlist.playbackMode() == QMediaPlaylist.Random: self.playlist.setPlaybackMode(QMediaPlaylist.Sequential) repeat_icon = utilities.resource_filename('mosaic.images', 'md_repeat_none.png') self.repeat_action.setIcon(QIcon(repeat_icon)) def activate_playlist_item(self, item): """Set the active media to the playlist item dobule-clicked on by the user.""" current_index = self.playlist_view.row(item) if self.playlist.currentIndex() != current_index: self.playlist.setCurrentIndex(current_index) if self.player.state() != QMediaPlayer.PlayingState: self.player.play() def change_index(self, row): """Highlight the row in the playlist of the active media.""" self.playlist_view.setCurrentRow(row) def minimalist_view(self): """Resize the window to only show the menu bar and audio controls.""" if self.minimalist_view_action.isChecked(): if self.playlist_dock.isVisible(): self.playlist_dock_state = True if self.library_dock.isVisible(): self.library_dock_state = True self.library_dock.close() self.playlist_dock.close() QTimer.singleShot(10, lambda: self.resize(500, 0)) else: self.resize(defaults.Settings().window_size, defaults.Settings().window_size + 63) if self.library_dock_state: self.library_dock.setVisible(True) if self.playlist_dock_state: self.playlist_dock.setVisible(True) def dock_visiblity_change(self, visible): """Change the size of the main window when the docks are toggled.""" if visible and self.playlist_dock.isVisible() and not self.library_dock.isVisible(): self.resize(defaults.Settings().window_size + self.playlist_dock.width() + 6, self.height()) elif visible and not self.playlist_dock.isVisible() and self.library_dock.isVisible(): self.resize(defaults.Settings().window_size + self.library_dock.width() + 6, self.height()) elif visible and self.playlist_dock.isVisible() and self.library_dock.isVisible(): self.resize(defaults.Settings().window_size + self.library_dock.width() + 6, self.height()) elif (not visible and not self.playlist_dock.isVisible() and not self.library_dock.isVisible()): self.resize(defaults.Settings().window_size, defaults.Settings().window_size + 63) def media_information_dialog(self): """Show a dialog of the current song's metadata.""" if self.player.isMetaDataAvailable(): file_path = self.player.currentMedia().canonicalUrl().toLocalFile() else: file_path = None dialog = information.InformationDialog(file_path) dialog.exec_() def change_window_size(self): """Change the window size of the music player.""" self.playlist_dock.close() self.library_dock.close() self.resize(defaults.Settings().window_size, defaults.Settings().window_size + 63) def change_media_library_path(self, path): """Change the media library path to the new path selected in the preferences dialog.""" self.library_model.setRootPath(path) self.library_view.setModel(self.library_model) self.library_view.setRootIndex(self.library_model.index(path)) def closeEvent(self, event): """Override the PyQt close event in order to handle save playlist on close.""" playlist = "{}/.m3u" .format(self.playlist_location) if defaults.Settings().save_playlist_on_close: self.playlist.save(QUrl().fromLocalFile(playlist), "m3u") else: if os.path.exists(playlist): os.remove(playlist) QApplication.quit()
class MainWindow(QMainWindow): def __init__(self): super(MainWindow, self).__init__() self.initUI() def initUI(self): self.setWindowTitle('Fanns - Finance Analysis Neural-Nets System') self.setMinimumSize(800, 600) self.initToolBar() self.initMenuBar() self.initMainBoard() self.show() def initMainBoard(self): self.mainBoard = QWidget() self.setCentralWidget(self.mainBoard) self.pagesStatus = [0]*5 self.pages = [QWidget(self.mainBoard) for i in self.pagesStatus] self.mainBoard.setStyleSheet("padding:10px") def initToolBar(self): self.toolBar = QToolBar("Tools") self.toolBar.setMovable(False) self.addToolBar(Qt.LeftToolBarArea, self.toolBar) self.toolBar.setIconSize(QSize(20, 20)) self.setStyleSheet( "QToolBar {" + "background-color: #a1afc9;" + "border-right: 1px solid #065279;" + "padding: 5px}" + "QToolBar > QToolButton:hover {" + "background-color: #f0f0f4;" + "border-radius:3px}") configButton = QToolButton() configButton.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) configButton.setIcon(QIcon("Image/Icon/file.png")) configButton.setText("Configure") configButton.setFixedSize(120, 30) envAnaButton = QToolButton() envAnaButton.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) envAnaButton.setIcon(QIcon("Image/Icon/file.png")) envAnaButton.setText("Environment") envAnaButton.setFixedSize(120, 30) stoHisButton = QToolButton() stoHisButton.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) stoHisButton.setIcon(QIcon("Image/Icon/file.png")) stoHisButton.setText("Stock History") stoHisButton.setFixedSize(120, 30) stoAnaButton = QToolButton() stoAnaButton.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) stoAnaButton.setIcon(QIcon("Image/Icon/file.png")) stoAnaButton.setText("Stocks Analysis") stoAnaButton.setFixedSize(120, 30) autoTrButton = QToolButton() autoTrButton.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) autoTrButton.setIcon(QIcon("Image/Icon/file.png")) autoTrButton.setText("Auto Trading") autoTrButton.setFixedSize(120, 30) configButton.clicked.connect(self.configPage) envAnaButton.clicked.connect(self.envAnaPage) stoHisButton.clicked.connect(self.stoHisPage) stoAnaButton.clicked.connect(self.stoAnaPage) autoTrButton.clicked.connect(self.autoTrPage) self.toolBar.addWidget(configButton) self.toolBar.addWidget(envAnaButton) self.toolBar.addWidget(stoHisButton) self.toolBar.addWidget(stoAnaButton) self.toolBar.addWidget(autoTrButton) def initMenuBar(self): exitAction = QAction('&Exit', self) exitAction.setShortcut('Ctrl+Q') exitAction.triggered.connect(qApp.quit) launchAction = QAction('&Launch', self) launchAction.setShortcut('Ctrl+L') launchAction.triggered.connect(self.execute) menubar = self.menuBar() menubar.setNativeMenuBar(False) fannsMenu = menubar.addMenu('&App') fannsMenu.addAction(exitAction) editMenu = menubar.addMenu('&Func') editMenu.addAction(launchAction) def execute(self): print 'have a lunch!' def configPage(self): print "in config page" ci = 0 page = self.pages[ci] if self.pagesStatus[ci] == 0: self.label = QLabel("This is configure page.", page) self.label.show() self.pagesStatus[ci] = 1 for pi in range(0,len(self.pages)): self.pages[pi].hide() page.show() def envAnaPage(self): print "in environment page" ci = 1 page = self.pages[ci] if self.pagesStatus[ci] == 0: self.label = QLabel("This is environment page.", page) self.label.show() self.pagesStatus[ci] = 1 for pi in range(0,len(self.pages)): self.pages[pi].hide() page.show() def stoHisPage(self): print "in stock history page" ci = 2 page = self.pages[ci] if self.pagesStatus[ci] == 0: self.label = QLabel("This is stock history page.", page) self.label.show() self.pagesStatus[ci] = 1 for pi in range(0,len(self.pages)): self.pages[pi].hide() page.show() def stoAnaPage(self): print "in stock analysis page" ci = 3 page = self.pages[ci] if self.pagesStatus[ci] == 0: self.label = QLabel("This is stock analysis page.", page) self.label.show() self.pagesStatus[ci] = 1 for pi in range(0,len(self.pages)): self.pages[pi].hide() page.show() def autoTrPage(self): print "in auto trading page" ci = 4 page = self.pages[ci] if self.pagesStatus[ci] == 0: self.label = QLabel("This is auto trading page.", page) self.label.show() self.pagesStatus[ci] = 1 for pi in range(0,len(self.pages)): self.pages[pi].hide() page.show()
class MainWindow(QMainWindow): def __init__(self, autoTradingSystem): super(MainWindow, self).__init__() self.ATM = autoTradingSystem self.initUI() def initUI(self): self.setStyle() self.setWindowTitle('Auto Trading System') self.initToolBar() self.initMenuBar() self.initMainBoard() self.techAnPage() #self.pairTrPage() # make window in center point self.setFixedSize(1000, 700) qr = self.frameGeometry() qr.moveCenter(QDesktopWidget().availableGeometry().center()) self.move(qr.topLeft()) self.show() def initMainBoard(self): self.mainBoard = QWidget() self.setCentralWidget(self.mainBoard) self.pagesStatus = [0]*5 self.pages = [QWidget(self.mainBoard) for i in self.pagesStatus] self.toolButtons self.mainBoard.setStyleSheet(self.mainBoardQSS) for page in self.pages: page.setStyleSheet(self.pagesQSS) def initToolBar(self): self.toolBar = QToolBar("Tools") self.toolBar.setMovable(False) self.addToolBar(Qt.LeftToolBarArea, self.toolBar) self.toolBar.setIconSize(QSize(20, 20)) self.techAnButton = QToolButton() self.techAnButton.setText("Technical analysis") self.techAnButton.setFixedSize(130, 25) self.pairTrButton = QToolButton() self.pairTrButton.setText("Pair Trading") self.pairTrButton.setFixedSize(130, 25) self.atoTrdButton = QToolButton() self.atoTrdButton.setText("Monitor") self.atoTrdButton.setFixedSize(130, 25) self.trdPnlButton = QToolButton() self.trdPnlButton.setText("PnL Report") self.trdPnlButton.setFixedSize(130, 25) self.trdHisButton = QToolButton() self.trdHisButton.setText("Trade History") self.trdHisButton.setFixedSize(130, 25) self.techAnButton.clicked.connect(self.techAnPage) self.pairTrButton.clicked.connect(self.pairTrPage) self.atoTrdButton.clicked.connect(self.atoTrdPage) self.trdPnlButton.clicked.connect(self.trdPnlPage) self.trdHisButton.clicked.connect(self.trdHisPage) self.toolBar.addWidget(self.techAnButton) self.toolBar.addWidget(self.pairTrButton) self.toolBar.addWidget(self.atoTrdButton) self.toolBar.addWidget(self.trdPnlButton) self.toolBar.addWidget(self.trdHisButton) self.toolButtons = [self.techAnButton, self.pairTrButton, self.atoTrdButton, self.trdPnlButton, self.trdHisButton] def initMenuBar(self): exitAction = QAction('&Exit', self) exitAction.setShortcut('Ctrl+Q') exitAction.triggered.connect(qApp.quit) techAnAction = QAction('&Technical Analysis', self) techAnAction.setShortcut('Ctrl+T') techAnAction.triggered.connect(self.techAnPage) pairTrAction = QAction('&Pair Trading', self) pairTrAction.setShortcut('Ctrl+P') pairTrAction.triggered.connect(self.pairTrPage) atoTrdAction = QAction('&Monitor', self) atoTrdAction.setShortcut('Ctrl+M') atoTrdAction.triggered.connect(self.atoTrdPage) trdPnlAction = QAction('&Profit And Loss Report', self) trdPnlAction.setShortcut('Ctrl+R') trdPnlAction.triggered.connect(self.trdPnlPage) trdHisAction = QAction('&Trade History', self) trdHisAction.setShortcut('Ctrl+H') trdHisAction.triggered.connect(self.trdHisPage) menubar = self.menuBar() menubar.setNativeMenuBar(False) fannsMenu = menubar.addMenu('&App') fannsMenu.addAction(exitAction) naviMenu = menubar.addMenu('&Navigate') naviMenu.addAction(techAnAction) naviMenu.addAction(pairTrAction) naviMenu.addAction(atoTrdAction) naviMenu.addAction(trdPnlAction) naviMenu.addAction(trdHisAction) # The technical analysis page def techAnPage(self): # hide all pages to show self page for pi in range(0,len(self.pages)): self.toolButtons[pi].setStyleSheet(self.toolButtonHideQSS) self.pages[pi].hide() print "in technical analysis page" ci = 0 page = self.pages[ci] self.toolButtons[ci].setStyleSheet(self.toolButtonFocusQSS) if self.pagesStatus[ci] == 0: self.pageTechAnConfigWidget = QWidget(page) self.pageTechAnConfigWidget.setFixedSize(860, 700) pageMainVerticalBox = QVBoxLayout() pageMainVerticalBox.setContentsMargins(0, 5, 0, 0) self.pageTechAnTitleLabel = QLabel("Technical Analysis", page) self.pageTechAnTitleLabel.setFixedSize(860, 25) self.pageTechAnTitleLabel.setStyleSheet(self.pageTitleQSS) pageMainVerticalBox.addWidget(self.pageTechAnTitleLabel) # capital config components self.pageTechAnCapitalLabel = QLabel("Capital Config", page) self.pageTechAnCapitalLabel.setFixedSize(860, 25) self.pageTechAnCapitalLabel.setStyleSheet(self.pageSubTitleQSS) pageMainVerticalBox.addWidget(self.pageTechAnCapitalLabel) capitalHbox = QHBoxLayout() capitalHbox.setContentsMargins(30, 10, 0, 10) self.pageTechAnCapitalInputLabel = QLabel("Capital: ", page) self.pageTechAnCapitalInputLabel.setFont(self.contentFont) self.pageTechAnCapitalInputLabel.setFixedSize(100, 25) self.pageTechAnCapitalInputLabel.setStyleSheet(self.itemNameQSS) capitalHbox.addWidget(self.pageTechAnCapitalInputLabel) self.pageTechAnCapitalEdit = QLineEdit("$ 100,000,000") self.pageTechAnCapitalEdit.setStyleSheet(self.lineEditQSS) self.pageTechAnCapitalEdit.setFixedSize(300, 25) self.pageTechAnCapitalEdit.setEnabled(False) capitalHbox.addWidget(self.pageTechAnCapitalEdit) capitalHbox.addStretch(1) pageMainVerticalBox.addLayout(capitalHbox) # security code select components self.pageTechAnSecurityLabel = QLabel("Security Select", page) self.pageTechAnSecurityLabel.setFixedSize(860, 25) self.pageTechAnSecurityLabel.setStyleSheet(self.pageSubTitleQSS) pageMainVerticalBox.addWidget(self.pageTechAnSecurityLabel) securityHbox = QHBoxLayout() securityHbox.setContentsMargins(30, 10, 0, 10) self.pageTechAnSecurityCode= QLabel("Security Code: ", page) self.pageTechAnSecurityCode.setFont(self.contentFont) self.pageTechAnSecurityCode.setFixedSize(100, 25) self.pageTechAnSecurityCode.setStyleSheet(self.itemNameQSS) securityHbox.addWidget(self.pageTechAnSecurityCode) self.pageTechAnSecurityCombo = QComboBox(page) self.pageTechAnSecurityCombo.setFixedSize(300, 25) self.pageTechAnSecurityCombo.setStyleSheet(self.comboQSS) self.pageTechAnSecurityCombo.addItem("hsi_futures_jan") for item in self.ATM.data.getAssetList("./dataManager/data/hsi_futures"): self.pageTechAnSecurityCombo.addItem(item) securityHbox.addWidget(self.pageTechAnSecurityCombo) securityHbox.addStretch(1) pageMainVerticalBox.addLayout(securityHbox) # investment strategies select components self.pageTechAnStrategiesLabel = QLabel("Investment Strategies Select", page) self.pageTechAnStrategiesLabel.setFixedSize(860, 25) self.pageTechAnStrategiesLabel.setStyleSheet(self.pageSubTitleQSS) pageMainVerticalBox.addWidget(self.pageTechAnStrategiesLabel) self.pageTechAnStrategiesWidget = QWidget(page) self.pageTechAnStrategiesWidget.setFixedSize(700, 80) strategiesGrid = QGridLayout() strategiesGrid.setContentsMargins(30, 10, 0, 10) self.pageTechAnStrategyCheckBoxACOscillator = QCheckBox(" ACOscillator") self.pageTechAnStrategyCheckBoxCCICorrection= QCheckBox(" CCI Correction") self.pageTechAnStrategyCheckBoxDMRSIADX = QCheckBox(" DM RSI ADX") self.pageTechAnStrategyCheckBoxMACD = QCheckBox(" MACD") self.pageTechAnStrategyCheckBoxBreakoutsSwing=QCheckBox(" Breakouts Swing") self.pageTechAnStrategyCheckBoxOscillator313= QCheckBox(" Oscillator3 13") self.pageTechAnStrategyCheckBoxACOscillator.setChecked(False) self.pageTechAnStrategyCheckBoxCCICorrection.setChecked(False) self.pageTechAnStrategyCheckBoxDMRSIADX.setChecked(False) self.pageTechAnStrategyCheckBoxMACD.setChecked(True) self.pageTechAnStrategyCheckBoxBreakoutsSwing.setChecked(False) self.pageTechAnStrategyCheckBoxOscillator313.setChecked(False) strategiesGrid.addWidget(self.pageTechAnStrategyCheckBoxACOscillator, *(1, 1)) strategiesGrid.addWidget(self.pageTechAnStrategyCheckBoxCCICorrection, *(1, 2)) strategiesGrid.addWidget(self.pageTechAnStrategyCheckBoxDMRSIADX, *(1, 3)) strategiesGrid.addWidget(self.pageTechAnStrategyCheckBoxMACD, *(2,1)) strategiesGrid.addWidget(self.pageTechAnStrategyCheckBoxBreakoutsSwing, *(2,2)) strategiesGrid.addWidget(self.pageTechAnStrategyCheckBoxOscillator313, *(2,3)) self.pageTechAnStrategiesWidget.setLayout(strategiesGrid) pageMainVerticalBox.addWidget(self.pageTechAnStrategiesWidget) # trading time config components self.pageTechAnTimeSpanLabel = QLabel("Trading Time Config", page) self.pageTechAnTimeSpanLabel.setFixedSize(860, 25) self.pageTechAnTimeSpanLabel.setStyleSheet(self.pageSubTitleQSS) pageMainVerticalBox.addWidget(self.pageTechAnTimeSpanLabel) timeVbox = QVBoxLayout() timeVbox.setContentsMargins(30, 10, 0, 10) startTimeHbox = QHBoxLayout() self.pageTechAnStartTimeName = QLabel("Start Time: ", page) self.pageTechAnStartTimeName.setFont(self.contentFont) self.pageTechAnStartTimeName.setFixedSize(100, 25) self.pageTechAnStartTimeName.setStyleSheet(self.itemNameQSS) startTimeHbox.addWidget(self.pageTechAnStartTimeName) self.pageTechAnStartTimeEdit = QLineEdit("2016-02-10 16:00:00") self.pageTechAnStartTimeEdit.setEnabled(False) self.pageTechAnStartTimeEdit.setStyleSheet(self.lineEditQSS) self.pageTechAnStartTimeEdit.setFixedSize(300, 25) startTimeHbox.addWidget(self.pageTechAnStartTimeEdit) startTimeHbox.addStretch(1) timeVbox.addLayout(startTimeHbox) endTimeHbox = QHBoxLayout() self.pageTechAnEndTimeName = QLabel("End Time: ", page) self.pageTechAnEndTimeName.setFont(self.contentFont) self.pageTechAnEndTimeName.setFixedSize(100, 25) self.pageTechAnEndTimeName.setStyleSheet(self.itemNameQSS) endTimeHbox.addWidget(self.pageTechAnEndTimeName) self.pageTechAnEndTimeEdit = QLineEdit("2016-02-26 16:00:00") self.pageTechAnEndTimeEdit.setEnabled(False) self.pageTechAnEndTimeEdit.setStyleSheet(self.lineEditQSS) self.pageTechAnEndTimeEdit.setFixedSize(300, 25) endTimeHbox.addWidget(self.pageTechAnEndTimeEdit) endTimeHbox.addStretch(1) timeVbox.addLayout(endTimeHbox) pageMainVerticalBox.addLayout(timeVbox) # trading strategies select components self.pageTechAnTStrSpanLabel = QLabel("Trading Strategies Select", page) self.pageTechAnTStrSpanLabel.setFixedSize(860, 25) self.pageTechAnTStrSpanLabel.setStyleSheet(self.pageSubTitleQSS) pageMainVerticalBox.addWidget(self.pageTechAnTStrSpanLabel) self.pageTechAnTradeStrateWidget = QWidget(page) self.pageTechAnTradeStrateWidget.setFixedSize(700, 40) tradeStratGrid = QGridLayout() tradeStratGrid.setContentsMargins(30, 5, 0, 5) self.pageTechAnTStrRadioButtonVWAP = QRadioButton(" VWAP") self.pageTechAnTStrRadioButtonVWAP.setCheckable(False) self.pageTechAnTStrRadioButtonTWAP = QRadioButton(" TWAP") self.pageTechAnTStrRadioButtonTWAP.setChecked(True) self.pageTechAnTStrRadioButtonNONE = QRadioButton(" NONE") tradeStratGrid.addWidget(self.pageTechAnTStrRadioButtonVWAP, *(1, 1)) tradeStratGrid.addWidget(self.pageTechAnTStrRadioButtonTWAP, *(1, 2)) tradeStratGrid.addWidget(self.pageTechAnTStrRadioButtonNONE, *(1, 3)) tradeStratGrid.addWidget(QLabel(), *(1, 4)) tradeStratGrid.addWidget(QLabel(), *(1, 5)) self.pageTechAnTradeStrateWidget.setLayout(tradeStratGrid) pageMainVerticalBox.addWidget(self.pageTechAnTradeStrateWidget) # position management method select components self.pageTechAnPManSpanLabel = QLabel("Position Management Method Select", page) self.pageTechAnPManSpanLabel.setFixedSize(860, 25) self.pageTechAnPManSpanLabel.setStyleSheet(self.pageSubTitleQSS) pageMainVerticalBox.addWidget(self.pageTechAnPManSpanLabel) self.pageTechAnPManageMthdWidget = QWidget(page) self.pageTechAnPManageMthdWidget.setFixedSize(700, 40) pManageMtdGrid = QGridLayout() pManageMtdGrid.setContentsMargins(30, 10, 0, 10) self.pageTechAnPMtdRadioButtonFixedFraction = QRadioButton(" Fixed Fraction") self.pageTechAnPMtdRadioButtonFixedFraction.setChecked(True) self.pageTechAnPMtdRadioButtonMaximDrawDown = QRadioButton(" Max Draw Down") pManageMtdGrid.addWidget(self.pageTechAnPMtdRadioButtonFixedFraction, *(1, 1)) pManageMtdGrid.addWidget(self.pageTechAnPMtdRadioButtonMaximDrawDown, *(1, 2)) pManageMtdGrid.addWidget(QLabel(), *(1, 3)) pManageMtdGrid.addWidget(QLabel(), *(1, 4)) pManageMtdGrid.addWidget(QLabel(), *(1, 5)) self.pageTechAnPManageMthdWidget.setLayout(pManageMtdGrid) pageMainVerticalBox.addWidget(self.pageTechAnPManageMthdWidget) space = QWidget() space.setFixedSize(0, 0) pageMainVerticalBox.addWidget(space) self.pageTechAnLaunchButton = QPushButton("Launch") self.pageTechAnLaunchButton.setFont(self.contentFont) self.pageTechAnLaunchButton.setFixedSize(860, 40) self.pageTechAnLaunchButton.setStyleSheet(self.launchWdgtReadyQSS) self.pageTechAnLaunchButton.clicked.connect(self.pageTechAnLaunch) pageMainVerticalBox.addWidget(self.pageTechAnLaunchButton) page.setLayout(pageMainVerticalBox) self.pagesStatus[ci] = 1 page.show() def pageTechAnLaunch(self): capital = int("".join(re.split("\$| |,", self.pageTechAnCapitalEdit.text()))) securityCode = self.pageTechAnSecurityCombo.currentText() investmentStrategies = [] if self.pageTechAnStrategyCheckBoxACOscillator.isChecked() : investmentStrategies.append("ACOscillator") if self.pageTechAnStrategyCheckBoxCCICorrection.isChecked() : investmentStrategies.append("CCI_Correction") if self.pageTechAnStrategyCheckBoxDMRSIADX.isChecked() : investmentStrategies.append("DM_RSI_ADX") if self.pageTechAnStrategyCheckBoxMACD.isChecked() : investmentStrategies.append("MACD") if self.pageTechAnStrategyCheckBoxBreakoutsSwing.isChecked(): investmentStrategies.append("breakouts_swing") if self.pageTechAnStrategyCheckBoxOscillator313.isChecked() : investmentStrategies.append("oscillator3_13") startTime = self.pageTechAnStartTimeEdit.text() endTime = self.pageTechAnEndTimeEdit.text() tradeStrategy = None if self.pageTechAnTStrRadioButtonVWAP.isChecked() : tradeStrategy = "VWAP" if self.pageTechAnTStrRadioButtonTWAP.isChecked() : tradeStrategy = "TWAP" # if self.pageTechAnTStrRadioButtonPOV.isChecked() : tradeStrategy = "POV" # if self.pageTechAnTStrRadioButtonSimple.isChecked() : tradeStrategy = "Simple" if self.pageTechAnTStrRadioButtonNONE.isChecked() : tradeStrategy = "Default" positionManagement = None if self.pageTechAnPMtdRadioButtonFixedFraction.isChecked() : positionManagement = "FixedFraction" if self.pageTechAnPMtdRadioButtonMaximDrawDown.isChecked() : positionManagement = "MaximumDrawDown" thread.start_new_thread(self.ATM.launchTechnicalAnalysis, (capital, securityCode, investmentStrategies, startTime, endTime, tradeStrategy, positionManagement)) def pageTechAnLaunchProcess(self): self.pageTechAnLaunchButton.setStyleSheet(self.launchWdgtProcesQSS) self.pageTechAnLaunchButton.setText("Processing") def pageTechAnLaunchFinish(self): self.pageTechAnLaunchButton.setStyleSheet(self.launchWdgtReadyQSS) self.pageTechAnLaunchButton.setText("Re-Launch") # The pair trading page def pairTrPage(self): # hide all pages to show self page for pi in range(0, len(self.pages)): self.toolButtons[pi].setStyleSheet(self.toolButtonHideQSS) self.pages[pi].hide() print "in pair trading page" ci = 1 page = self.pages[ci] self.toolButtons[ci].setStyleSheet(self.toolButtonFocusQSS) if self.pagesStatus[ci] == 0: self.pagePairTrConfigWidget = QWidget(page) self.pagePairTrConfigWidget.setFixedSize(860, 700) pageMainVerticalBox = QVBoxLayout() pageMainVerticalBox.setContentsMargins(0, 5, 0, 0) self.pagePairTrTitleLabel = QLabel("Pair Trading") self.pagePairTrTitleLabel.setFixedSize(860, 25) self.pagePairTrTitleLabel.setStyleSheet(self.pageTitleQSS) pageMainVerticalBox.addWidget(self.pagePairTrTitleLabel) self.pagePairTrCapitalLabel = QLabel("Capital Config", page) self.pagePairTrCapitalLabel.setFixedSize(860, 25) self.pagePairTrCapitalLabel.setStyleSheet(self.pageSubTitleQSS) pageMainVerticalBox.addWidget(self.pagePairTrCapitalLabel) capitalHbox = QHBoxLayout() capitalHbox.setContentsMargins(30, 10, 0, 10) self.pagePairTrCapitalInputLabel = QLabel("Capital: ", page) self.pagePairTrCapitalInputLabel.setFont(self.contentFont) self.pagePairTrCapitalInputLabel.setFixedSize(100, 25) self.pagePairTrCapitalInputLabel.setStyleSheet(self.itemNameQSS) capitalHbox.addWidget(self.pagePairTrCapitalInputLabel) self.pagePairTrCapitalEdit = QLineEdit("$ 100,000,000") self.pagePairTrCapitalEdit.setStyleSheet(self.lineEditQSS) self.pagePairTrCapitalEdit.setFixedSize(300, 25) self.pagePairTrCapitalEdit.setEnabled(False) capitalHbox.addWidget(self.pagePairTrCapitalEdit) capitalHbox.addStretch(1) pageMainVerticalBox.addLayout(capitalHbox) self.pagePairTrSecurityLabel = QLabel("Security Select", page) self.pagePairTrSecurityLabel.setFixedSize(860, 25) self.pagePairTrSecurityLabel.setStyleSheet(self.pageSubTitleQSS) pageMainVerticalBox.addWidget(self.pagePairTrSecurityLabel) securityHbox = QHBoxLayout() securityHbox.setContentsMargins(30, 10, 0, 10) self.pagePairTrSecurityCode = QLabel("Security Basket: ", page) self.pagePairTrSecurityCode.setFont(self.contentFont) self.pagePairTrSecurityCode.setFixedSize(100, 25) self.pagePairTrSecurityCode.setStyleSheet(self.itemNameQSS) securityHbox.addWidget(self.pagePairTrSecurityCode) self.pagePairTrSecurities = QTextEdit(page) self.pagePairTrSecurities.setEnabled(False) self.pagePairTrSecurities.setFixedSize(600, 148) securities = "" for item in self.ATM.data.getAssetList("./dataManager/data/hsi_stocks"): securities += item + ' ' self.pagePairTrSecurities.setText(securities) securityHbox.addWidget(self.pagePairTrSecurities) securityHbox.addStretch(1) pageMainVerticalBox.addLayout(securityHbox) self.pagePairTrTimeSpanLabel = QLabel("Trading Time Config", page) self.pagePairTrTimeSpanLabel.setFixedSize(860, 25) self.pagePairTrTimeSpanLabel.setStyleSheet(self.pageSubTitleQSS) pageMainVerticalBox.addWidget(self.pagePairTrTimeSpanLabel) timeVbox = QVBoxLayout() timeVbox.setContentsMargins(30, 10, 0, 10) startTimeHbox = QHBoxLayout() self.pagePairTrStartTimeName = QLabel("Start Time: ", page) self.pagePairTrStartTimeName.setFont(self.contentFont) self.pagePairTrStartTimeName.setFixedSize(100, 25) self.pagePairTrStartTimeName.setStyleSheet(self.itemNameQSS) startTimeHbox.addWidget(self.pagePairTrStartTimeName) self.pagePairTrStartTimeEdit = QLineEdit("2016-02-10 16:00:00") self.pagePairTrStartTimeEdit.setEnabled(False) self.pagePairTrStartTimeEdit.setStyleSheet(self.lineEditQSS) self.pagePairTrStartTimeEdit.setFixedSize(300, 25) startTimeHbox.addWidget(self.pagePairTrStartTimeEdit) startTimeHbox.addStretch(1) timeVbox.addLayout(startTimeHbox) endTimeHbox = QHBoxLayout() self.pagePairTrEndTimeName = QLabel("End Time: ", page) self.pagePairTrEndTimeName.setFont(self.contentFont) self.pagePairTrEndTimeName.setFixedSize(100, 25) self.pagePairTrEndTimeName.setStyleSheet(self.itemNameQSS) endTimeHbox.addWidget(self.pagePairTrEndTimeName) self.pagePairTrEndTimeEdit = QLineEdit("2016-02-26 16:00:00") self.pagePairTrEndTimeEdit.setEnabled(False) self.pagePairTrEndTimeEdit.setStyleSheet(self.lineEditQSS) self.pagePairTrEndTimeEdit.setFixedSize(300, 25) endTimeHbox.addWidget(self.pagePairTrEndTimeEdit) endTimeHbox.addStretch(1) timeVbox.addLayout(endTimeHbox) pageMainVerticalBox.addLayout(timeVbox) self.pagePairTrTStrSpanLabel = QLabel("Trading Strategies Select", page) self.pagePairTrTStrSpanLabel.setFixedSize(860, 25) self.pagePairTrTStrSpanLabel.setStyleSheet(self.pageSubTitleQSS) pageMainVerticalBox.addWidget(self.pagePairTrTStrSpanLabel) self.pagePairTrTradeStrateWidget = QWidget(page) self.pagePairTrTradeStrateWidget.setFixedSize(700, 40) tradeStratGrid = QGridLayout() tradeStratGrid.setContentsMargins(30, 5, 0, 5) self.pagePairTrTStrRadioButtonVWAP = QRadioButton(" VWAP") self.pagePairTrTStrRadioButtonVWAP.setCheckable(False) self.pagePairTrTStrRadioButtonTWAP = QRadioButton(" TWAP") self.pagePairTrTStrRadioButtonTWAP.setChecked(True) self.pagePairTrTStrRadioButtonNONE = QRadioButton(" NONE") tradeStratGrid.addWidget(self.pagePairTrTStrRadioButtonVWAP, *(1, 1)) tradeStratGrid.addWidget(self.pagePairTrTStrRadioButtonTWAP, *(1, 2)) tradeStratGrid.addWidget(self.pagePairTrTStrRadioButtonNONE, *(1, 3)) tradeStratGrid.addWidget(QLabel(), *(1, 4)) tradeStratGrid.addWidget(QLabel(), *(1, 5)) self.pagePairTrTradeStrateWidget.setLayout(tradeStratGrid) pageMainVerticalBox.addWidget(self.pagePairTrTradeStrateWidget) self.pagePairTrPManSpanLabel = QLabel("Position Management Method Select", page) self.pagePairTrPManSpanLabel.setFixedSize(860, 25) self.pagePairTrPManSpanLabel.setStyleSheet(self.pageSubTitleQSS) pageMainVerticalBox.addWidget(self.pagePairTrPManSpanLabel) self.pagePairTrPManageMthdWidget = QWidget(page) self.pagePairTrPManageMthdWidget.setFixedSize(700, 40) pManageMtdGrid = QGridLayout() pManageMtdGrid.setContentsMargins(30, 10, 0, 10) self.pagePairTrPMtdRadioButtonFixedFraction = QRadioButton(" Fixed Fraction") self.pagePairTrPMtdRadioButtonFixedFraction.setChecked(True) self.pagePairTrPMtdRadioButtonMaximDrawDown = QRadioButton(" Max Draw Down") pManageMtdGrid.addWidget(self.pagePairTrPMtdRadioButtonFixedFraction, *(1, 1)) pManageMtdGrid.addWidget(self.pagePairTrPMtdRadioButtonMaximDrawDown, *(1, 2)) pManageMtdGrid.addWidget(QLabel(), *(1, 3)) pManageMtdGrid.addWidget(QLabel(), *(1, 4)) pManageMtdGrid.addWidget(QLabel(), *(1, 5)) self.pagePairTrPManageMthdWidget.setLayout(pManageMtdGrid) pageMainVerticalBox.addWidget(self.pagePairTrPManageMthdWidget) space = QWidget() space.setFixedSize(0, 0) pageMainVerticalBox.addWidget(space) self.pagePairTrLaunchButton = QPushButton("Launch") self.pagePairTrLaunchButton.setFont(self.contentFont) self.pagePairTrLaunchButton.setFixedSize(860, 40) self.pagePairTrLaunchButton.setStyleSheet(self.launchWdgtReadyQSS) self.pagePairTrLaunchButton.clicked.connect(self.pagePairTrdLaunch) pageMainVerticalBox.addWidget(self.pagePairTrLaunchButton) page.setLayout(pageMainVerticalBox) self.pagesStatus[ci] = 1 page.show() def pagePairTrdLaunch(self): capital = int("".join(re.split("\$| |,", self.pagePairTrCapitalEdit.text()))) investmentStrategies = ["pairstrading", ] startTime = self.pagePairTrStartTimeEdit.text() endTime = self.pagePairTrEndTimeEdit.text() tradeStrategy = None if self.pagePairTrTStrRadioButtonVWAP.isChecked(): tradeStrategy = "VWAP" if self.pagePairTrTStrRadioButtonTWAP.isChecked(): tradeStrategy = "TWAP" if self.pagePairTrTStrRadioButtonNONE.isChecked(): tradeStrategy = "Default" positionManagement = None if self.pagePairTrPMtdRadioButtonFixedFraction.isChecked(): positionManagement = "FixedFraction" if self.pagePairTrPMtdRadioButtonMaximDrawDown.isChecked(): positionManagement = "MaximumDrawDown" thread.start_new_thread(self.ATM.launchPairTradingAnalysis, (capital, investmentStrategies, startTime, endTime, tradeStrategy, positionManagement)) def pagePairTrdLaunchProcess(self): self.pagePairTrLaunchButton.setStyleSheet(self.launchWdgtProcesQSS) self.pagePairTrLaunchButton.setText("Processing") def pagePairTrdLaunchFinish(self): self.pagePairTrLaunchButton.setStyleSheet(self.launchWdgtReadyQSS) self.pagePairTrLaunchButton.setText("Re-Launch") def atoTrdPage(self): for pi in range(0,len(self.pages)): self.toolButtons[pi].setStyleSheet(self.toolButtonHideQSS) self.pages[pi].hide() print "in monitor page" ci = 2 page = self.pages[ci] self.toolButtons[ci].setStyleSheet(self.toolButtonFocusQSS) if self.pagesStatus[ci] == 0: if not page.layout() == None: while page.layout().count() > 0: page.layout().takeAt(0).widget().setParent(None) if page.layout() == None: self.pageAutoTrdPageMainVerticalBox = QVBoxLayout() self.pageAutoTrdPageMainVerticalBox.setContentsMargins(0, 5, 0, 0) page.setLayout(self.pageAutoTrdPageMainVerticalBox) self.pageAutoTrdTitleLabel = QLabel("Monitor", page) self.pageAutoTrdTitleLabel.setFixedSize(860, 25) self.pageAutoTrdTitleLabel.setStyleSheet(self.pageTitleQSS) self.pageAutoTrdPageMainVerticalBox.addWidget(self.pageAutoTrdTitleLabel) pnlReport = self.ATM.report if not len(self.ATM.strategies.strategiesPool.keys()) == 0: self.pageAtoTrdPageScroll = QScrollArea(page) self.pageAtoTrdPageScroll.setWidgetResizable(True) self.pageAtoTrdPageScroll.setBackgroundRole(QPalette.NoRole) self.pageAtoTrdPageScroll.setStyleSheet("background: transparent") self.pageAtoTrdPageScroll.setFixedSize(860, 635) self.pageAtoTrdScrollContentsWidget = QWidget(page) scrollContentVBox = QVBoxLayout() scrollContentVBox.setAlignment(Qt.AlignTop) scrollContentVBox.setContentsMargins(0, 0, 0, 0) self.pageAtoTrdSignalPlotLabel = QLabel("Signals Plots", page) self.pageAtoTrdSignalPlotLabel.setFixedSize(860, 25) self.pageAtoTrdSignalPlotLabel.setStyleSheet(self.pageSubTitleQSS) scrollContentVBox.addWidget(self.pageAtoTrdSignalPlotLabel) path = "./strategies/image/" for file in os.listdir(path): if file.endswith(".png") and file.split('.')[0] in self.ATM.strategies.strategiesPool.keys(): pageAtoTrdSignalPlotStrategyLabel = QLabel(file.split('.')[0], page) pageAtoTrdSignalPlotStrategyLabel.setFixedSize(860, 25) pageAtoTrdSignalPlotStrategyLabel.setStyleSheet(self.pageSubSubTitleQSS) scrollContentVBox.addWidget(pageAtoTrdSignalPlotStrategyLabel) widget = QWidget() widget.setFixedHeight(300) hbox = QHBoxLayout() hbox.setContentsMargins(0, 0, 0, 0) hbox.setAlignment(Qt.AlignCenter) lbl = QLabel() pixmap = QPixmap(path + file) scaled_pixmap = pixmap.scaled(860, 330, Qt.KeepAspectRatio) lbl.setPixmap(scaled_pixmap) hbox.addWidget(lbl) widget.setLayout(hbox) scrollContentVBox.addWidget(widget) self.pageAtoTrdAllSignalsLabel = QLabel("All Signals", page) self.pageAtoTrdAllSignalsLabel.setFixedSize(860, 25) self.pageAtoTrdAllSignalsLabel.setStyleSheet(self.pageSubTitleQSS) scrollContentVBox.addWidget(self.pageAtoTrdAllSignalsLabel) self.pageAtoTrdAllSignalsTitle = QWidget(page) self.pageAtoTrdAllSignalsTitle.setFixedSize(860, 25) self.pageAtoTrdAllSignalsTitle.setStyleSheet(self.pageSubSubTitleQSS) titlesHBox = QHBoxLayout() titlesHBox.setContentsMargins(10, 0, 20, 0) titlesHBox.addWidget(QLabel("Code")) titlesHBox.addWidget(QLabel("Time")) titlesHBox.addWidget(QLabel("Action")) titlesHBox.addWidget(QLabel("Qnt")) titlesHBox.addWidget(QLabel("Price")) titlesHBox.addWidget(QLabel("Volumn")) titlesHBox.addWidget(QLabel("Strategy")) self.pageAtoTrdAllSignalsTitle.setLayout(titlesHBox) scrollContentVBox.addWidget(self.pageAtoTrdAllSignalsTitle) signals = self.ATM.strategies.signals if not len(signals) == 0: for i in xrange(len(signals)): widget = QWidget(page) widget.setFixedHeight(15) widget.setStyleSheet("color:#ffffff") signalHBox = QHBoxLayout() signalHBox.setContentsMargins(20, 0, 10, 0) signalHBox.addWidget(QLabel(signals.ix[i]["Code"])) signalHBox.addWidget(QLabel(str(signals.ix[i]["Time"]))) signalHBox.addWidget(QLabel(signals.ix[i]["Action"])) signalHBox.addWidget(QLabel(str(signals.ix[i]["Qnt"]))) signalHBox.addWidget(QLabel(str(signals.ix[i]["Price"]))) signalHBox.addWidget(QLabel(str(signals.ix[i]["Volume"]))) signalHBox.addWidget(QLabel(signals.ix[i]["Strategy"])) widget.setLayout(signalHBox) scrollContentVBox.addWidget(widget) else: widget = QLabel("No Data.") widget.setFixedSize(860, 550) widget.setStyleSheet(self.noDataLabelQSS) widget.setAlignment(Qt.AlignCenter) scrollContentVBox.addWidget(widget) self.pageAtoTrdScrollContentsWidget.setLayout(scrollContentVBox) self.pageAtoTrdPageScroll.setWidget(self.pageAtoTrdScrollContentsWidget) self.pageAutoTrdPageMainVerticalBox.addWidget(self.pageAtoTrdPageScroll) else: widget = QLabel("No Data.") widget.setFixedSize(860, 550) widget.setStyleSheet(self.noDataLabelQSS) widget.setAlignment(Qt.AlignCenter) self.pageAutoTrdPageMainVerticalBox.addWidget(widget) self.pagesStatus[ci] = 1 page.show() def trdPnlPage(self): for pi in range(0,len(self.pages)): self.toolButtons[pi].setStyleSheet(self.toolButtonHideQSS) self.pages[pi].hide() print "in profit and loss report page" ci = 3 page = self.pages[ci] self.toolButtons[ci].setStyleSheet(self.toolButtonFocusQSS) if self.pagesStatus[ci] == 0: if not page.layout() == None: while page.layout().count() > 0: page.layout().takeAt(0).widget().setParent(None) if page.layout() == None: self.pageTrdPnlPageMainVerticalBox = QVBoxLayout() self.pageTrdPnlPageMainVerticalBox.setContentsMargins(0, 5, 0, 0) page.setLayout(self.pageTrdPnlPageMainVerticalBox) self.pageTrdHisTitleLabel = QLabel("Profit And Loss Report", page) self.pageTrdHisTitleLabel.setFixedSize(860, 25) self.pageTrdHisTitleLabel.setStyleSheet(self.pageTitleQSS) self.pageTrdPnlPageMainVerticalBox.addWidget(self.pageTrdHisTitleLabel) pnlReport = self.ATM.report if not len(pnlReport) == 0: self.pageTrdHisBookTitles = QWidget(page) self.pageTrdHisBookTitles.setFixedSize(860, 25) self.pageTrdHisBookTitles.setStyleSheet(self.pageSubTitleQSS) titlesHBox = QHBoxLayout() titlesHBox.setContentsMargins(10, 0, 20, 0) strategy = QLabel("Strategy") titlesHBox.addWidget(QLabel("Strategy")) titlesHBox.addWidget(QLabel("Realized PnL")) titlesHBox.addWidget(QLabel("Return")) areturn = QLabel("Annual Return") areturn.setFixedWidth(130) titlesHBox.addWidget(areturn) titlesHBox.addWidget(QLabel("Volatility")) titlesHBox.addWidget(QLabel("Sharpe Ratio")) mdd = QLabel("Maximum Draw Down") mdd.setFixedWidth(155) titlesHBox.addWidget(mdd) self.pageTrdHisBookTitles.setLayout(titlesHBox) self.pageTrdPnlPageMainVerticalBox.addWidget(self.pageTrdHisBookTitles) self.pageTrdHisPageScroll = QScrollArea(page) self.pageTrdHisPageScroll.setWidgetResizable(True) self.pageTrdHisPageScroll.setBackgroundRole(QPalette.NoRole) self.pageTrdHisPageScroll.setStyleSheet("background: transparent") self.pageTrdHisPageScroll.setFixedSize(860, 600) self.pageTrdHisScrollContentsWidget = QWidget(page) scrollContentVBox = QVBoxLayout() scrollContentVBox.setAlignment(Qt.AlignTop) scrollContentVBox.setContentsMargins(0, 0, 0, 0) for i in xrange(0, len(pnlReport)): widget = QWidget() widget.setFixedHeight(15) if pnlReport.ix[i]["realized PnL"] > 0: widget.setStyleSheet("color:#fa2020") if pnlReport.ix[i]["realized PnL"] < 0: widget.setStyleSheet("color:#27AE60") hbox = QHBoxLayout() hbox.setContentsMargins(20, 0, 10, 0) strategy = QLabel(pnlReport.ix[i]["Strategy"]) strategy.setFixedWidth(100) hbox.addWidget(strategy) hbox.addWidget(QLabel(str("{0:.2f}".format(pnlReport.ix[i]["realized PnL"])))) hbox.addWidget(QLabel(str("{0:.4f}".format(pnlReport.ix[i]["Return"])))) areturn = QLabel(str("{0:.4f}".format(pnlReport.ix[i]["Annualized Return"]))) # hbox.addWidget(QLabel(str("{0:.2f}".format(tradeHistory.ix[i]["QntPer"] * 100)))) areturn.setFixedWidth(130) hbox.addWidget(areturn) hbox.addWidget(QLabel(str("{0:.4f}".format(pnlReport.ix[i]["Volatility"])))) hbox.addWidget(QLabel(str("{0:.4f}".format(pnlReport.ix[i]["Sharpe Ratio"])))) mdd = QLabel(str("{0:.6f}".format(pnlReport.ix[i]["MDD"]))) mdd.setFixedWidth(155) hbox.addWidget(mdd) widget.setLayout(hbox) scrollContentVBox.addWidget(widget) self.pageTrdHisScrollContentsWidget.setLayout(scrollContentVBox) self.pageTrdHisPageScroll.setWidget(self.pageTrdHisScrollContentsWidget) self.pageTrdPnlPageMainVerticalBox.addWidget(self.pageTrdHisPageScroll) else: widget = QLabel("No Data.") widget.setFixedSize(860, 550) widget.setStyleSheet(self.noDataLabelQSS) widget.setAlignment(Qt.AlignCenter) self.pageTrdPnlPageMainVerticalBox.addWidget(widget) self.pagesStatus[ci] = 1 page.show() def trdHisPage(self): for pi in range(0,len(self.pages)): self.toolButtons[pi].setStyleSheet(self.toolButtonHideQSS) self.pages[pi].hide() print "in trade history page" ci = 4 page = self.pages[ci] self.toolButtons[ci].setStyleSheet(self.toolButtonFocusQSS) if self.pagesStatus[ci] == 0: if not page.layout() == None: while page.layout().count() > 0: page.layout().takeAt(0).widget().setParent(None) if page.layout() == None: self.pageTrdHisPageMainVerticalBox = QVBoxLayout() self.pageTrdHisPageMainVerticalBox.setContentsMargins(0, 5, 0, 0) page.setLayout(self.pageTrdHisPageMainVerticalBox) self.pageTrdHisTitleLabel = QLabel("Trade History", page) self.pageTrdHisTitleLabel.setFixedSize(860, 25) self.pageTrdHisTitleLabel.setStyleSheet(self.pageTitleQSS) self.pageTrdHisPageMainVerticalBox.addWidget(self.pageTrdHisTitleLabel) tradeHistory = self.ATM.account.queryTradeHistory() if not len(tradeHistory) == 0: self.pageTrdHisBookTitles = QWidget(page) self.pageTrdHisBookTitles.setFixedSize(860, 25) self.pageTrdHisBookTitles.setStyleSheet(self.pageSubTitleQSS) titlesHBox = QHBoxLayout() titlesHBox.setContentsMargins(10, 0, 20, 0) code = QLabel("Code") code.setFixedWidth(100) titlesHBox.addWidget(code) time = QLabel("Time") time.setFixedWidth(145) titlesHBox.addWidget(time) titlesHBox.addWidget(QLabel("Action")) qnt = QLabel("Qnt") qnt.setFixedWidth(50) titlesHBox.addWidget(qnt) titlesHBox.addWidget(QLabel("Occupy")) titlesHBox.addWidget(QLabel("Price")) titlesHBox.addWidget(QLabel("PnL")) titlesHBox.addWidget(QLabel("Equity")) strategy = QLabel("Strategy") strategy.setFixedWidth(100) titlesHBox.addWidget(strategy) self.pageTrdHisBookTitles.setLayout(titlesHBox) self.pageTrdHisPageMainVerticalBox.addWidget(self.pageTrdHisBookTitles) self.pageTrdHisPageScroll = QScrollArea(page) self.pageTrdHisPageScroll.setWidgetResizable(True) self.pageTrdHisPageScroll.setBackgroundRole(QPalette.NoRole) self.pageTrdHisPageScroll.setStyleSheet("background: transparent") self.pageTrdHisPageScroll.setFixedSize(860, 600) self.pageTrdHisScrollContentsWidget = QWidget(page) scrollContentVBox = QVBoxLayout() scrollContentVBox.setContentsMargins(0, 0, 0, 0) scrollContentVBox.setAlignment(Qt.AlignTop) for i in xrange(0, len(tradeHistory)): widget = QWidget() widget.setFixedHeight(15) if tradeHistory.ix[i]["Action"] == "Short" : widget.setStyleSheet("color:#27AE60") if tradeHistory.ix[i]["Action"] == "SellToCover" : widget.setStyleSheet("color:#27AE60") if tradeHistory.ix[i]["Action"] == "Long" : widget.setStyleSheet("color:#fa2020") if tradeHistory.ix[i]["Action"] == "BuyToCover" : widget.setStyleSheet("color:#fa2020") hbox = QHBoxLayout() hbox.setContentsMargins(20, 0, 10, 0) code = QLabel(str(tradeHistory.ix[i]["Code"])); code.setFixedWidth(100); hbox.addWidget(code); time = QLabel(str(tradeHistory.ix[i]["Time"])); time.setFixedWidth(145); hbox.addWidget(time); hbox.addWidget(QLabel(tradeHistory.ix[i]["Action"])) qnt = QLabel(str(tradeHistory.ix[i]["Qnt"])); qnt.setFixedWidth(50); hbox.addWidget(qnt); hbox.addWidget(QLabel(str("{0:.2f}".format(tradeHistory.ix[i]["QntPer"] * 100))+"%")) hbox.addWidget(QLabel(str(round(tradeHistory.ix[i]["Price"])))) pnl = QLabel() if not tradeHistory.ix[i]["PnL"] == "": pnl = QLabel(str(round(float(tradeHistory.ix[i]["PnL"])))); hbox.addWidget(pnl) hbox.addWidget(QLabel(str(round(tradeHistory.ix[i]["Equity"])))) strategy = QLabel(tradeHistory.ix[i]["Strategy"]); strategy.setFixedWidth(100); hbox.addWidget(strategy); widget.setLayout(hbox) scrollContentVBox.addWidget(widget) self.pageTrdHisScrollContentsWidget.setLayout(scrollContentVBox) self.pageTrdHisPageScroll.setWidget(self.pageTrdHisScrollContentsWidget) self.pageTrdHisPageMainVerticalBox.addWidget(self.pageTrdHisPageScroll) else: widget = QLabel("No Data.") widget.setFixedSize(860, 550) widget.setStyleSheet(self.noDataLabelQSS) widget.setAlignment(Qt.AlignCenter) self.pageTrdHisPageMainVerticalBox.addWidget(widget) self.pagesStatus[ci] = 1 page.show() def setStyle(self): self.setStyleSheet( "QToolBar {" + "background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #203138, stop: 1.0 #000000);" + "border-right: 1px solid #065279;" + "padding: 5px}" + "QToolBar > QToolButton {" + "color: #ffffff;" + "font-family:'ArialRegular';" + "font-size: 14px}" + "QToolBar > QToolButton:hover {" + "background: qlineargradient(x1: 0, y1: 1, x2: 0, y2: 0, stop: 0 #ffcb06, stop: 1.0 #ff9c28);" + "border-radius:3px}" + "QLabel {" + "font-family:'ArialRegular';" + "padding: 10px;}" + "QPushButton {" + "height: 20px}" + "QComboBox {" + "border-radius: 1px; " + "border-top-right-radius:11px;" + "border-bottom-right-radius:11px;" + "font-family:'ArialRegular'}" + "QComboBox::drop-down {" + "width:15px;" + "background-color: #ff9c28;" + "border-top-right-radius:10px;" + "border-bottom-right-radius:10px;}" + "QCheckBox {" + "color: #ffffff;" + "font-family:'ArialRegular'}" + "QCheckBox::indicator {" + "background-color:#ffffff;" + "border-radius: 1px}" + "QCheckBox::indicator:checked {" + "background-color:#ff9c28}" + "QLineEdit {" + "background:#ff9c28;" + "border-radius:1px}" + "QLineEdit:focus {" + "border-radius:1px;}" + "QRadioButton {color: #ffffff}" + "QScrollArea {border:0px; background:transparent}" + "QTextEdit {padding-left: 5px; border: 0px; font-family: 'ArialRegular'; font-weight:20; font-size:14px; background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #ec2f4b, stop: 1.0 #85030f);}" ) self.mainBoardQSS = "padding:0px; background:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #203138, stop: 1.0 #000000);" self.pagesQSS = "background:none; padding: 0px" self.pageTitleQSS = "padding-left:5px; background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #ec2f4b, stop: 1.0 #85030f); color: #ffffff; font-family: 'ArialRegular'; font-weight:20; font-size: 16px" self.pageSubTitleQSS = "padding-left:5px; background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #495d76, stop: 1.0 #1f4e7c); color: #dddddd; font-family: 'ArialRegular'; font-weight:20; font-size: 14px" self.pageSubSubTitleQSS = "padding-left:5px; background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #646464, stop: 1.0 #838683); color: #dddddd; font-family: 'ArialRegular'; font-weight:20; font-size: 14px" self.toolButtonHideQSS = "background:none; font-size: 14px; font-family:'ArialRegular'" self.toolButtonFocusQSS = "background:qlineargradient(x1: 0, y1: 1, x2: 0, y2: 0, stop: 0 #ffcb06, stop: 1.0 #ff9c28);border-radius:3px; color:#000000" self.itemNameQSS = "color: #ffffff; font-family: 'ArialRegular'" self.comboQSS = "padding-left:5px;background:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #eeeeee, stop: 1.0 #dddddd);" self.lineEditQSS = "background:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #eeeeee, stop: 1.0 #dddddd);border: 0px; padding-left:5px; font-family:'ArialRegular'; font-weight:20; font-size: 14px" self.launchWdgtReadyQSS = "background:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #004900, stop: 1.0 #033502);border: 0px; color:#ffffff" self.launchWdgtProcesQSS= "background:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #006602, stop: 1.0 #007b03);border: 0px; color:#ffffff" self.tableTitleQSS = "padding-left:5px; background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #495d76, stop: 1.0 #1f4e7c); color: #dddddd; font-family: 'ArialRegular'; font-weight:20; font-size: 14px" self.noDataLabelQSS = "color: #ffffff; font-family: ArialRegular; font-weight: 20; font-size: 14px" self.pageTitleFont = QFont('ArialRegular') self.titleFont = QFont('ArialRegular') self.contentFont = QFont('ArialRegular') self.pageTitleColor = "#ffffff" self.titleColor = "#ffffff" self.contentColor = "#ffffff"
class MainWindow_Ui(QMainWindow): def __init__(self, persepolis_setting): super().__init__() # MainWindow self.persepolis_setting = persepolis_setting # add support for other languages locale = str(self.persepolis_setting.value('settings/locale')) QLocale.setDefault(QLocale(locale)) self.translator = QTranslator() if self.translator.load(':/translations/locales/ui_' + locale, 'ts'): QCoreApplication.installTranslator(self.translator) # set ui direction ui_direction = self.persepolis_setting.value('ui_direction') if ui_direction == 'rtl': self.setLayoutDirection(Qt.RightToLeft) elif ui_direction in 'ltr': self.setLayoutDirection(Qt.LeftToRight) icons = ':/' + \ str(self.persepolis_setting.value('settings/icons')) + '/' self.setWindowTitle(QCoreApplication.translate("mainwindow_ui_tr", "Persepolis Download Manager")) self.setWindowIcon(QIcon.fromTheme('persepolis', QIcon(':/persepolis.svg'))) self.centralwidget = QWidget(self) self.verticalLayout = QVBoxLayout(self.centralwidget) # enable drag and drop self.setAcceptDrops(True) # frame self.frame = QFrame(self.centralwidget) # download_table_horizontalLayout download_table_horizontalLayout = QHBoxLayout() tabels_splitter = QSplitter(Qt.Horizontal) # category_tree self.category_tree_qwidget = QWidget(self) category_tree_verticalLayout = QVBoxLayout() self.category_tree = CategoryTreeView(self) category_tree_verticalLayout.addWidget(self.category_tree) self.category_tree_model = QStandardItemModel() self.category_tree.setModel(self.category_tree_model) category_table_header = [QCoreApplication.translate("mainwindow_ui_tr", 'Category')] self.category_tree_model.setHorizontalHeaderLabels( category_table_header) self.category_tree.header().setStretchLastSection(True) self.category_tree.header().setDefaultAlignment(Qt.AlignCenter) # queue_panel self.queue_panel_widget = QWidget(self) queue_panel_verticalLayout_main = QVBoxLayout(self.queue_panel_widget) # queue_panel_show_button self.queue_panel_show_button = QPushButton(self) queue_panel_verticalLayout_main.addWidget(self.queue_panel_show_button) # queue_panel_widget_frame self.queue_panel_widget_frame = QFrame(self) self.queue_panel_widget_frame.setFrameShape(QFrame.StyledPanel) self.queue_panel_widget_frame.setFrameShadow(QFrame.Raised) queue_panel_verticalLayout_main.addWidget( self.queue_panel_widget_frame) queue_panel_verticalLayout = QVBoxLayout(self.queue_panel_widget_frame) queue_panel_verticalLayout_main.setContentsMargins(50, -1, 50, -1) # start_end_frame self.start_end_frame = QFrame(self) # start time start_verticalLayout = QVBoxLayout(self.start_end_frame) self.start_checkBox = QCheckBox(self) start_verticalLayout.addWidget(self.start_checkBox) self.start_frame = QFrame(self) self.start_frame.setFrameShape(QFrame.StyledPanel) self.start_frame.setFrameShadow(QFrame.Raised) start_frame_verticalLayout = QVBoxLayout(self.start_frame) self.start_time_qDataTimeEdit = QDateTimeEdit(self.start_frame) self.start_time_qDataTimeEdit.setDisplayFormat('H:mm') start_frame_verticalLayout.addWidget(self.start_time_qDataTimeEdit) start_verticalLayout.addWidget(self.start_frame) # end time self.end_checkBox = QCheckBox(self) start_verticalLayout.addWidget(self.end_checkBox) self.end_frame = QFrame(self) self.end_frame.setFrameShape(QFrame.StyledPanel) self.end_frame.setFrameShadow(QFrame.Raised) end_frame_verticalLayout = QVBoxLayout(self.end_frame) self.end_time_qDateTimeEdit = QDateTimeEdit(self.end_frame) self.end_time_qDateTimeEdit.setDisplayFormat('H:mm') end_frame_verticalLayout.addWidget(self.end_time_qDateTimeEdit) start_verticalLayout.addWidget(self.end_frame) self.reverse_checkBox = QCheckBox(self) start_verticalLayout.addWidget(self.reverse_checkBox) queue_panel_verticalLayout.addWidget(self.start_end_frame) # limit_after_frame self.limit_after_frame = QFrame(self) # limit_checkBox limit_verticalLayout = QVBoxLayout(self.limit_after_frame) self.limit_checkBox = QCheckBox(self) limit_verticalLayout.addWidget(self.limit_checkBox) # limit_frame self.limit_frame = QFrame(self) self.limit_frame.setFrameShape(QFrame.StyledPanel) self.limit_frame.setFrameShadow(QFrame.Raised) limit_verticalLayout.addWidget(self.limit_frame) limit_frame_verticalLayout = QVBoxLayout(self.limit_frame) # limit_spinBox limit_frame_horizontalLayout = QHBoxLayout() self.limit_spinBox = QDoubleSpinBox(self) self.limit_spinBox.setMinimum(1) self.limit_spinBox.setMaximum(1023) limit_frame_horizontalLayout.addWidget(self.limit_spinBox) # limit_comboBox self.limit_comboBox = QComboBox(self) self.limit_comboBox.addItem("") self.limit_comboBox.addItem("") limit_frame_horizontalLayout.addWidget(self.limit_comboBox) limit_frame_verticalLayout.addLayout(limit_frame_horizontalLayout) # limit_pushButton self.limit_pushButton = QPushButton(self) limit_frame_verticalLayout.addWidget(self.limit_pushButton) # after_checkBox self.after_checkBox = QtWidgets.QCheckBox(self) limit_verticalLayout.addWidget(self.after_checkBox) # after_frame self.after_frame = QtWidgets.QFrame(self) self.after_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) self.after_frame.setFrameShadow(QtWidgets.QFrame.Raised) limit_verticalLayout.addWidget(self.after_frame) after_frame_verticalLayout = QVBoxLayout(self.after_frame) # after_comboBox self.after_comboBox = QComboBox(self) self.after_comboBox.addItem("") after_frame_verticalLayout.addWidget(self.after_comboBox) # after_pushButton self.after_pushButton = QPushButton(self) after_frame_verticalLayout.addWidget(self.after_pushButton) queue_panel_verticalLayout.addWidget(self.limit_after_frame) category_tree_verticalLayout.addWidget(self.queue_panel_widget) # keep_awake_checkBox self.keep_awake_checkBox = QCheckBox(self) queue_panel_verticalLayout.addWidget(self.keep_awake_checkBox) self.category_tree_qwidget.setLayout(category_tree_verticalLayout) tabels_splitter.addWidget(self.category_tree_qwidget) # download table widget self.download_table_content_widget = QWidget(self) download_table_content_widget_verticalLayout = QVBoxLayout( self.download_table_content_widget) self.download_table = DownloadTableWidget(self) download_table_content_widget_verticalLayout.addWidget( self.download_table) tabels_splitter.addWidget(self.download_table_content_widget) self.download_table.setColumnCount(13) self.download_table.setSelectionBehavior(QAbstractItemView.SelectRows) self.download_table.setEditTriggers(QAbstractItemView.NoEditTriggers) self.download_table.verticalHeader().hide() # hide gid and download dictioanry section self.download_table.setColumnHidden(8, True) self.download_table.setColumnHidden(9, True) download_table_header = [QCoreApplication.translate("mainwindow_ui_tr", 'File Name'), QCoreApplication.translate("mainwindow_ui_tr",'Status'), QCoreApplication.translate("mainwindow_ui_tr", 'Size'), QCoreApplication.translate("mainwindow_ui_tr", 'Downloaded'), QCoreApplication.translate("mainwindow_ui_tr", 'Percentage'), QCoreApplication.translate("mainwindow_ui_tr", 'Connections'), QCoreApplication.translate("mainwindow_ui_tr", 'Transfer rate'), QCoreApplication.translate("mainwindow_ui_tr",'Estimated time left'), 'Gid', QCoreApplication.translate("mainwindow_ui_tr",'Link'), QCoreApplication.translate("mainwindow_ui_tr", 'First try date'), QCoreApplication.translate("mainwindow_ui_tr", 'Last try date'), QCoreApplication.translate("mainwindow_ui_tr",'Category')] self.download_table.setHorizontalHeaderLabels(download_table_header) # fixing the size of download_table when window is Maximized! self.download_table.horizontalHeader().setSectionResizeMode(0) self.download_table.horizontalHeader().setStretchLastSection(True) tabels_splitter.setStretchFactor(0, 3) # category_tree width tabels_splitter.setStretchFactor(1, 10) # ratio of tables's width download_table_horizontalLayout.addWidget(tabels_splitter) self.frame.setLayout(download_table_horizontalLayout) self.verticalLayout.addWidget(self.frame) self.setCentralWidget(self.centralwidget) # menubar self.menubar = QMenuBar(self) self.menubar.setGeometry(QRect(0, 0, 600, 24)) self.setMenuBar(self.menubar) fileMenu = self.menubar.addMenu(QCoreApplication.translate("mainwindow_ui_tr", '&File')) editMenu = self.menubar.addMenu(QCoreApplication.translate("mainwindow_ui_tr", '&Edit')) viewMenu = self.menubar.addMenu(QCoreApplication.translate("mainwindow_ui_tr", '&View')) downloadMenu = self.menubar.addMenu(QCoreApplication.translate("mainwindow_ui_tr", '&Download')) queueMenu = self.menubar.addMenu(QCoreApplication.translate("mainwindow_ui_tr", '&Queue')) videoFinderMenu = self.menubar.addMenu(QCoreApplication.translate("mainwindow_ui_tr", 'V&ideo Finder')) helpMenu = self.menubar.addMenu(QCoreApplication.translate("mainwindow_ui_tr", '&Help')) # viewMenu submenus sortMenu = viewMenu.addMenu(QCoreApplication.translate("mainwindow_ui_tr", 'Sort by')) # statusbar self.statusbar = QStatusBar(self) self.setStatusBar(self.statusbar) self.statusbar.showMessage(QCoreApplication.translate("mainwindow_ui_tr", "Persepolis Download Manager")) # toolBar self.toolBar2 = QToolBar(self) self.addToolBar(QtCore.Qt.TopToolBarArea, self.toolBar2) self.toolBar2.setWindowTitle(QCoreApplication.translate("mainwindow_ui_tr", 'Menu')) self.toolBar2.setFloatable(False) self.toolBar2.setMovable(False) self.toolBar = QToolBar(self) self.addToolBar(QtCore.Qt.TopToolBarArea, self.toolBar) self.toolBar.setWindowTitle(QCoreApplication.translate("mainwindow_ui_tr", 'Toolbar')) self.toolBar.setFloatable(False) self.toolBar.setMovable(False) #toolBar and menubar and actions self.videoFinderAddLinkAction = QAction(QIcon(icons + 'video_finder'), QCoreApplication.translate("mainwindow_ui_tr", 'Find Video Links'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Download video or audio from Youtube, Vimeo, etc...'), triggered=self.showVideoFinderAddLinkWindow) QShortcut(QKeySequence('Ctrl+I'), self, self.showVideoFinderAddLinkWindow) videoFinderMenu.addAction(self.videoFinderAddLinkAction) self.stopAllAction = QAction(QIcon(icons + 'stop_all'), QCoreApplication.translate("mainwindow_ui_tr", 'Stop all active downloads'), self, statusTip='Stop all active downloads', triggered=self.stopAllDownloads) downloadMenu.addAction(self.stopAllAction) self.sort_file_name_Action = QAction( QCoreApplication.translate("mainwindow_ui_tr", 'File name'), self, triggered=self.sortByName) sortMenu.addAction(self.sort_file_name_Action) self.sort_file_size_Action = QAction( QCoreApplication.translate("mainwindow_ui_tr", 'File size'), self, triggered=self.sortBySize) sortMenu.addAction(self.sort_file_size_Action) self.sort_first_try_date_Action = QAction( QCoreApplication.translate("mainwindow_ui_tr", 'First try date'), self, triggered=self.sortByFirstTry) sortMenu.addAction(self.sort_first_try_date_Action) self.sort_last_try_date_Action = QAction( QCoreApplication.translate("mainwindow_ui_tr", 'Last try date'), self, triggered=self.sortByLastTry) sortMenu.addAction(self.sort_last_try_date_Action) self.sort_download_status_Action = QAction( QCoreApplication.translate("mainwindow_ui_tr", 'Download status'), self, triggered=self.sortByStatus) sortMenu.addAction(self.sort_download_status_Action) self.trayAction = QAction(QCoreApplication.translate("mainwindow_ui_tr", 'Show system tray icon'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", "Show/Hide system tray icon"), triggered=self.showTray) self.trayAction.setCheckable(True) viewMenu.addAction(self.trayAction) self.showMenuBarAction = QAction( QCoreApplication.translate("mainwindow_ui_tr", 'Show menubar'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Show menubar'), triggered=self.showMenuBar) self.showMenuBarAction.setCheckable(True) viewMenu.addAction(self.showMenuBarAction) self.showSidePanelAction = QAction( QCoreApplication.translate("mainwindow_ui_tr", 'Show side panel'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Show side panel'), triggered=self.showSidePanel) self.showSidePanelAction.setCheckable(True) viewMenu.addAction(self.showSidePanelAction) self.minimizeAction = QAction(QIcon(icons + 'minimize'), QCoreApplication.translate("mainwindow_ui_tr", 'Minimize to system tray'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", "Minimize to system tray"), triggered=self.minMaxTray) QShortcut(QKeySequence('Ctrl+W'), self, self.minMaxTray) viewMenu.addAction(self.minimizeAction) self.addlinkAction = QAction(QIcon(icons + 'add'), QCoreApplication.translate("mainwindow_ui_tr", 'Add New Download Link'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", "Add New Download Link"), triggered=self.addLinkButtonPressed) QShortcut(QKeySequence('Ctrl+N'), self, self.addLinkButtonPressed) fileMenu.addAction(self.addlinkAction) self.addtextfileAction = QAction(QIcon(icons + 'file'), QCoreApplication.translate("mainwindow_ui_tr", 'Import links from text file'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Create a Text file and put links in it.line by line!'), triggered=self.importText) fileMenu.addAction(self.addtextfileAction) self.resumeAction = QAction(QIcon(icons + 'play'), QCoreApplication.translate("mainwindow_ui_tr", 'Resume Download'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", "Resume Download"), triggered=self.resumeButtonPressed) QShortcut(QKeySequence('Ctrl+R'), self, self.resumeButtonPressed) downloadMenu.addAction(self.resumeAction) self.pauseAction = QAction(QIcon(icons + 'pause'), QCoreApplication.translate("mainwindow_ui_tr", 'Pause Download'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", "Pause Download"), triggered=self.pauseButtonPressed) QShortcut(QKeySequence('Ctrl+C'), self, self.pauseButtonPressed) downloadMenu.addAction(self.pauseAction) self.stopAction = QAction(QIcon(icons + 'stop'), QCoreApplication.translate("mainwindow_ui_tr", 'Stop Download'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", "Stop/Cancel Download"), triggered=self.stopButtonPressed) QShortcut(QKeySequence('Ctrl+S'), self, self.stopButtonPressed) downloadMenu.addAction(self.stopAction) self.propertiesAction = QAction(QIcon(icons + 'setting'), QCoreApplication.translate("mainwindow_ui_tr", 'Properties'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", "Properties"), triggered=self.propertiesButtonPressed) QShortcut(QKeySequence('Ctrl+P'), self, self.propertiesButtonPressed) downloadMenu.addAction(self.propertiesAction) self.progressAction = QAction(QIcon(icons + 'window'), QCoreApplication.translate("mainwindow_ui_tr", 'Progress'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", "Progress"), triggered=self.progressButtonPressed) QShortcut(QKeySequence('Ctrl+Z'), self, self.progressButtonPressed) downloadMenu.addAction(self.progressAction) self.openFileAction = QAction(QIcon( icons + 'file'), QCoreApplication.translate("mainwindow_ui_tr", 'Open file'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Open file'), triggered=self.openFile) fileMenu.addAction(self.openFileAction) self.openDownloadFolderAction = QAction(QIcon( icons + 'folder'), QCoreApplication.translate("mainwindow_ui_tr", 'Open download folder'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Open download folder'), triggered=self.openDownloadFolder) fileMenu.addAction(self.openDownloadFolderAction) self.openDefaultDownloadFolderAction = QAction(QIcon( icons + 'folder'), QCoreApplication.translate("mainwindow_ui_tr", 'Open default download folder'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Open default download folder'), triggered=self.openDefaultDownloadFolder) fileMenu.addAction(self.openDefaultDownloadFolderAction) self.exitAction = QAction(QIcon(icons + 'exit'), QCoreApplication.translate("mainwindow_ui_tr", 'Exit'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", "Exit"), triggered=self.closeEvent) QShortcut(QKeySequence('Ctrl+Q'), self, self.closeEvent) fileMenu.addAction(self.exitAction) self.clearAction = QAction(QIcon(icons + 'multi_remove'), QCoreApplication.translate("mainwindow_ui_tr", 'Clear download list'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Clear all items in download list'), triggered=self.clearDownloadList) editMenu.addAction(self.clearAction) self.removeSelectedAction = QAction(QIcon(icons + 'remove'), QCoreApplication.translate("mainwindow_ui_tr", 'Remove selected downloads from list'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Remove selected downloads form list'), triggered=self.removeSelected) editMenu.addAction(self.removeSelectedAction) self.removeSelectedAction.setEnabled(False) self.deleteSelectedAction = QAction(QIcon(icons + 'trash'), QCoreApplication.translate("mainwindow_ui_tr", 'Delete selected download files'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Delete selected download files'), triggered=self.deleteSelected) editMenu.addAction(self.deleteSelectedAction) self.deleteSelectedAction.setEnabled(False) self.createQueueAction = QAction(QIcon(icons + 'add_queue'), QCoreApplication.translate("mainwindow_ui_tr", 'Create new queue'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Create new download queue'), triggered=self.createQueue) queueMenu.addAction(self.createQueueAction) self.removeQueueAction = QAction(QIcon(icons + 'remove_queue'), QCoreApplication.translate("mainwindow_ui_tr", 'Remove this queue'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Remove this queue'), triggered=self.removeQueue) queueMenu.addAction(self.removeQueueAction) self.startQueueAction = QAction(QIcon( icons + 'start_queue'), QCoreApplication.translate("mainwindow_ui_tr", 'Start this queue'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Start this queue'), triggered=self.startQueue) queueMenu.addAction(self.startQueueAction) self.stopQueueAction = QAction(QIcon( icons + 'stop_queue'), QCoreApplication.translate("mainwindow_ui_tr", 'Stop this queue'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Stop this queue'), triggered=self.stopQueue) queueMenu.addAction(self.stopQueueAction) self.moveUpSelectedAction = QAction(QIcon(icons + 'multi_up'), QCoreApplication.translate("mainwindow_ui_tr", 'Move up selected items'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Move currently selected items up by one row'), triggered=self.moveUpSelected) queueMenu.addAction(self.moveUpSelectedAction) self.moveDownSelectedAction = QAction(QIcon(icons + 'multi_down'), QCoreApplication.translate("mainwindow_ui_tr", 'Move down selected items'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Move currently selected items down by one row'), triggered=self.moveDownSelected) queueMenu.addAction(self.moveDownSelectedAction) self.preferencesAction = QAction(QIcon(icons + 'preferences'), QCoreApplication.translate("mainwindow_ui_tr", 'Preferences'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Preferences'), triggered=self.openPreferences, menuRole=5) editMenu.addAction(self.preferencesAction) self.aboutAction = QAction(QIcon( icons + 'about'), QCoreApplication.translate("mainwindow_ui_tr", 'About'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'About'), triggered=self.openAbout, menuRole=4) helpMenu.addAction(self.aboutAction) self.issueAction = QAction(QIcon(icons + 'about'), QCoreApplication.translate("mainwindow_ui_tr", 'Report an issue'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Report an issue'), triggered=self.reportIssue) helpMenu.addAction(self.issueAction) self.updateAction = QAction(QIcon(icons + 'about'), QCoreApplication.translate("mainwindow_ui_tr", 'Check for newer version'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Check for newer release'), triggered=self.newUpdate) helpMenu.addAction(self.updateAction) self.logAction = QAction(QIcon(icons + 'about'), QCoreApplication.translate("mainwindow_ui_tr", 'Show log file'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Help'), triggered=self.showLog) helpMenu.addAction(self.logAction) self.helpAction = QAction(QIcon(icons + 'about'), QCoreApplication.translate("mainwindow_ui_tr", 'Help'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Help'), triggered=self.persepolisHelp) helpMenu.addAction(self.helpAction) self.qmenu = MenuWidget(self) self.toolBar2.addWidget(self.qmenu) # labels self.queue_panel_show_button.setText(QCoreApplication.translate("mainwindow_ui_tr", "Hide options")) self.start_checkBox.setText(QCoreApplication.translate("mainwindow_ui_tr", "Start Time")) self.end_checkBox.setText(QCoreApplication.translate("mainwindow_ui_tr", "End Time")) self.reverse_checkBox.setText(QCoreApplication.translate("mainwindow_ui_tr", "Download bottom of\n the list first")) self.limit_checkBox.setText(QCoreApplication.translate("mainwindow_ui_tr", "Limit Speed")) self.limit_comboBox.setItemText(0, "KiB/s") self.limit_comboBox.setItemText(1, "MiB/s") self.limit_pushButton.setText(QCoreApplication.translate("mainwindow_ui_tr", "Apply")) self.after_checkBox.setText(QCoreApplication.translate("mainwindow_ui_tr", "After download")) self.after_comboBox.setItemText(0, QCoreApplication.translate("mainwindow_ui_tr", "Shut Down")) self.keep_awake_checkBox.setText(QCoreApplication.translate("mainwindow_ui_tr", "Keep system awake!")) self.keep_awake_checkBox.setToolTip( QCoreApplication.translate("mainwindow_ui_tr", "<html><head/><body><p>This option is preventing system from going to sleep.\ This is necessary if your power manager is suspending system automatically. </p></body></html>")) self.after_pushButton.setText(QCoreApplication.translate("mainwindow_ui_tr", "Apply")) def changeIcon(self, icons): icons = ':/' + str(icons) + '/' action_icon_dict = {self.stopAllAction: 'stop_all', self.minimizeAction: 'minimize', self.addlinkAction: 'add', self.addtextfileAction: 'file', self.resumeAction: 'play', self.pauseAction: 'pause', self.stopAction: 'stop', self.propertiesAction: 'setting', self.progressAction: 'window', self.openFileAction: 'file', self.openDownloadFolderAction: 'folder', self.openDefaultDownloadFolderAction: 'folder', self.exitAction: 'exit', self.removeSelectedAction: 'multi_remove', self.deleteSelectedAction: 'multi_trash', self.createQueueAction: 'add_queue', self.removeQueueAction: 'remove_queue', self.startQueueAction: 'start_queue', self.stopQueueAction: 'stop_queue', self.preferencesAction: 'preferences', self.aboutAction: 'about', self.issueAction: 'about', self.updateAction: 'about', self.videoFinderAddLinkAction: 'video_finder', self.qmenu: 'menu'} for key in action_icon_dict.keys(): key.setIcon(QIcon(icons + str(action_icon_dict[key])))
class AppWindow(QMainWindow): "The application's main window" move_listener = pyqtSignal() db_activity_checker = pyqtSignal() graphics_blur = QGraphicsBlurEffect() def __init__(self): super().__init__() app_constants.GENERAL_THREAD = QThread(self) app_constants.GENERAL_THREAD.finished.connect(app_constants.GENERAL_THREAD.deleteLater) app_constants.GENERAL_THREAD.start() self.setAcceptDrops(True) self.initUI() self.start_up() QTimer.singleShot(3000, self._check_update) self.setFocusPolicy(Qt.NoFocus) self.set_shortcuts() self.graphics_blur.setParent(self) #ex = settings.ExProperties() #d = pewnet.ExHenManager(ex.ipb_id, ex.ipb_pass) #item = d.from_gallery_url('http://exhentai.org/g/861957/02741dc584/') #def a(): print(item.file) #if not item.file: # item.file_rdy.connect(a) #else: # a() def set_shortcuts(self): quit = QShortcut(QKeySequence('Ctrl+Q'), self, self.close) def init_watchers(self): def remove_gallery(g): index = self.manga_list_view.find_index(g.id, True) if index: self.manga_list_view.remove_gallery([index]) def create_gallery(path): g_dia = gallerydialog.GalleryDialog(self, path) g_dia.SERIES.connect(self.manga_list_view.gallery_model.addRows) g_dia.show() def update_gallery(g): index = self.manga_list_view.find_index(g.id) if index: self.manga_list_view.replace_edit_gallery([g], index.row()) else: log_e('Could not find gallery to update from watcher') def created(path): c_popup = io_misc.CreatedPopup(path, self) c_popup.ADD_SIGNAL.connect(create_gallery) def modified(path, gallery): mod_popup = io_misc.ModifiedPopup(path, gallery, self) def deleted(path, gallery): d_popup = io_misc.DeletedPopup(path, gallery, self) d_popup.UPDATE_SIGNAL.connect(update_gallery) d_popup.REMOVE_SIGNAL.connect(remove_gallery) def moved(new_path, gallery): mov_popup = io_misc.MovedPopup(new_path, gallery, self) mov_popup.UPDATE_SIGNAL.connect(update_gallery) self.watchers = io_misc.Watchers() self.watchers.gallery_handler.CREATE_SIGNAL.connect(created) self.watchers.gallery_handler.MODIFIED_SIGNAL.connect(modified) self.watchers.gallery_handler.MOVED_SIGNAL.connect(moved) self.watchers.gallery_handler.DELETED_SIGNAL.connect(deleted) admin_db_method_invoker = pyqtSignal(str) def start_up(self): hello = ["Hello!", "Hi!", "Onii-chan!", "Senpai!", "Hisashiburi!", "Welcome!", "Okaerinasai!", "Welcome back!", "Hajimemashite!"] self.notification_bar.add_text("{} Please don't hesitate to report any bugs you find.".format(hello[random.randint(0, len(hello)-1)])+ " Go to Settings -> About -> Bug Reporting for more info!") level = 5 def normalize_first_time(): settings.set(level, 'Application', 'first time level') settings.save() def done(status=True): gallerydb.DatabaseEmitter.RUN = True if app_constants.FIRST_TIME_LEVEL != level: normalize_first_time() if app_constants.ENABLE_MONITOR and\ app_constants.MONITOR_PATHS and all(app_constants.MONITOR_PATHS): self.init_watchers() if app_constants.LOOK_NEW_GALLERY_STARTUP: if self.manga_list_view.gallery_model.db_emitter.count == app_constants.GALLERY_DATA: self.scan_for_new_galleries() else: self.manga_list_view.gallery_model.db_emitter.DONE.connect(self.scan_for_new_galleries) self.download_manager = pewnet.Downloader() app_constants.DOWNLOAD_MANAGER = self.download_manager self.download_manager.start_manager(4) if app_constants.FIRST_TIME_LEVEL < 4: log_i('Invoking first time level {}'.format(4)) settings.set([], 'Application', 'monitor paths') settings.set([], 'Application', 'ignore paths') app_constants.MONITOR_PATHS = [] app_constants.IGNORE_PATHS = [] settings.save() done() elif app_constants.FIRST_TIME_LEVEL < 5: log_i('Invoking first time level {}'.format(5)) app_widget = misc.ApplicationPopup(self) app_widget.note_info.setText("<font color='red'>IMPORTANT:</font> Application restart is required when done") app_widget.restart_info.hide() self.admin_db = gallerydb.AdminDB() self.admin_db.moveToThread(app_constants.GENERAL_THREAD) self.admin_db.DONE.connect(done) self.admin_db.DONE.connect(lambda: app_constants.NOTIF_BAR.add_text("Application requires a restart")) self.admin_db.DONE.connect(self.admin_db.deleteLater) self.admin_db.DATA_COUNT.connect(app_widget.prog.setMaximum) self.admin_db.PROGRESS.connect(app_widget.prog.setValue) self.admin_db_method_invoker.connect(self.admin_db.rebuild_db) self.admin_db_method_invoker.connect(app_widget.show) app_widget.adjustSize() db_p = os.path.join(os.path.split(database.db_constants.DB_PATH)[0], 'sadpanda.db') self.admin_db_method_invoker.emit(db_p) else: done() def initUI(self): self.center = QWidget() self.display = QStackedLayout() self._main_layout = QVBoxLayout() self._main_layout.setSpacing(0) self._main_layout.setContentsMargins(0,0,0,0) self._main_layout.addLayout(self.display) self.center.setLayout(self._main_layout) # init the manga view variables self.manga_display() log_d('Create manga display: OK') # init the chapter view variables #self.chapter_display() self.m_l_view_index = self.display.addWidget(self.manga_list_view) self.m_t_view_index = self.display.addWidget(self.manga_table_view) self.download_window = io_misc.GalleryDownloader(self) self.download_window.hide() # init toolbar self.init_toolbar() log_d('Create toolbar: OK') # init status bar self.init_stat_bar() log_d('Create statusbar: OK') self.tags_treeview = None if app_constants.TAGS_TREEVIEW_ON_START: def tags_tree_none(): self.tags_treeview = None self.tags_treeview = misc_db.DBOverview(self, True) self.tags_treeview.about_to_close.connect(tags_tree_none) self.tags_treeview.show() self.system_tray = misc.SystemTray(QIcon(app_constants.APP_ICO_PATH), self) app_constants.SYSTEM_TRAY = self.system_tray tray_menu = QMenu(self) self.system_tray.setContextMenu(tray_menu) self.system_tray.setToolTip('Happypanda {}'.format(app_constants.vs)) tray_quit = QAction('Quit', tray_menu) tray_update = tray_menu.addAction('Check for update') tray_update.triggered.connect(self._check_update) tray_menu.addAction(tray_quit) tray_quit.triggered.connect(self.close) self.system_tray.show() def tray_activate(r=None): if not r or r == QSystemTrayIcon.Trigger: self.showNormal() self.activateWindow() self.system_tray.messageClicked.connect(tray_activate) self.system_tray.activated.connect(tray_activate) log_d('Create system tray: OK') #self.display.addWidget(self.chapter_main) self.setCentralWidget(self.center) self.setWindowIcon(QIcon(app_constants.APP_ICO_PATH)) props = settings.win_read(self, 'AppWindow') if props.resize: x, y = props.resize self.resize(x, y) else: self.resize(app_constants.MAIN_W, app_constants.MAIN_H) posx, posy = props.pos self.move(posx, posy) self.init_spinners() self.show() log_d('Show window: OK') self.notification_bar = misc.NotificationOverlay(self) p = self.toolbar.pos() self.notification_bar.move(p.x(), p.y()+self.toolbar.height()) self.notification_bar.resize(self.width()) app_constants.NOTIF_BAR = self.notification_bar log_d('Create notificationbar: OK') log_d('Window Create: OK') def _check_update(self): class upd_chk(QObject): UPDATE_CHECK = pyqtSignal(str) def __init__(self, **kwargs): super().__init__(**kwargs) def fetch_vs(self): import requests import time log_d('Checking Update') time.sleep(1.5) try: if os.path.exists('cacert.pem'): r = requests.get("https://raw.githubusercontent.com/Pewpews/happypanda/master/VS.txt", verify='cacert.pem') else: r = requests.get("https://raw.githubusercontent.com/Pewpews/happypanda/master/VS.txt") a = r.text vs = a.strip() self.UPDATE_CHECK.emit(vs) except: log.exception('Checking Update: FAIL') self.UPDATE_CHECK.emit('this is a very long text which is sure to be over limit') def check_update(vs): log_i('Received version: {}\nCurrent version: {}'.format(vs, app_constants.vs)) if vs != app_constants.vs: if len(vs) < 10: self.notification_bar.begin_show() self.notification_bar.add_text("Version {} of Happypanda is".format(vs)+ " available. Click here to update!", False) self.notification_bar.clicked.connect(lambda: utils.open_web_link( 'https://github.com/Pewpews/happypanda/releases')) self.notification_bar.set_clickable(True) else: self.notification_bar.add_text("An error occurred while checking for new version") self.update_instance = upd_chk() thread = QThread(self) self.update_instance.moveToThread(thread) thread.started.connect(self.update_instance.fetch_vs) self.update_instance.UPDATE_CHECK.connect(check_update) self.update_instance.UPDATE_CHECK.connect(self.update_instance.deleteLater) thread.finished.connect(thread.deleteLater) thread.start() def _web_metadata_picker(self, gallery, title_url_list, queue, parent=None): if not parent: parent = self text = "Which gallery do you want to extract metadata from?" s_gallery_popup = misc.SingleGalleryChoices(gallery, title_url_list, text, parent) s_gallery_popup.USER_CHOICE.connect(queue.put) def get_metadata(self, gal=None): metadata_spinner = misc.Spinner(self) def move_md_spinner(): metadata_spinner.update_move( QPoint( self.pos().x()+self.width()-65, self.pos().y()+self.toolbar.height()+55)) metadata_spinner.set_text("Metadata") metadata_spinner.set_size(55) metadata_spinner.move(QPoint(self.pos().x()+self.width()-65, self.pos().y()+self.toolbar.height()+55)) self.move_listener.connect(move_md_spinner) thread = QThread(self) thread.setObjectName('App.get_metadata') fetch_instance = fetch.Fetch() if gal: if not isinstance(gal, list): galleries = [gal] else: galleries = gal else: if app_constants.CONTINUE_AUTO_METADATA_FETCHER: galleries = [g for g in self.manga_list_view.gallery_model._data if not g.exed] else: galleries = self.manga_list_view.gallery_model._data if not galleries: self.notification_bar.add_text('Looks like we\'ve already gone through all galleries!') return None fetch_instance.galleries = galleries self.notification_bar.begin_show() fetch_instance.moveToThread(thread) def done(status): self.notification_bar.end_show() fetch_instance.deleteLater() if not isinstance(status, bool): galleries = [] for tup in status: galleries.append(tup[0]) class GalleryContextMenu(QMenu): app_instance = self def __init__(self, parent=None): super().__init__(parent) show_in_library_act = self.addAction('Show in library') show_in_library_act.triggered.connect(self.show_in_library) def show_in_library(self): viewer = self.app_instance.manga_list_view index = viewer.find_index(self.gallery_widget.gallery.id, True) if index: self.app_instance.manga_table_view.scroll_to_index(index) self.app_instance.manga_list_view.scroll_to_index(index) g_popup = io_misc.GalleryPopup(('Fecthing metadata for these galleries failed.'+ ' Check happypanda.log for details.', galleries), self, menu=GalleryContextMenu()) #errors = {g[0].id: g[1] for g in status} #for g_item in g_popup.get_all_items(): # g_item.setToolTip(errors[g_item.gallery.id]) g_popup.graphics_blur.setEnabled(False) close_button = g_popup.add_buttons('Close')[0] close_button.clicked.connect(g_popup.close) fetch_instance.GALLERY_PICKER.connect(self._web_metadata_picker) fetch_instance.GALLERY_EMITTER.connect(self.manga_list_view.replace_edit_gallery) fetch_instance.AUTO_METADATA_PROGRESS.connect(self.notification_bar.add_text) thread.started.connect(fetch_instance.auto_web_metadata) fetch_instance.FINISHED.connect(done) fetch_instance.FINISHED.connect(metadata_spinner.close) fetch_instance.FINISHED.connect(lambda: self.move_listener.disconnect(move_md_spinner)) thread.finished.connect(thread.deleteLater) thread.start() metadata_spinner.show() def init_stat_bar(self): self.status_bar = self.statusBar() self.status_bar.setMaximumHeight(20) self.status_bar.setSizeGripEnabled(False) self.stat_info = QLabel() self.stat_info.setIndent(5) self.sort_main = QAction("Asc", self) sort_menu = QMenu() self.sort_main.setMenu(sort_menu) s_by_title = QAction("Title", sort_menu) s_by_artist = QAction("Artist", sort_menu) sort_menu.addAction(s_by_title) sort_menu.addAction(s_by_artist) self.status_bar.addPermanentWidget(self.stat_info) #self.status_bar.addAction(self.sort_main) self.temp_msg = QLabel() self.temp_timer = QTimer() self.manga_list_view.gallery_model.ROWCOUNT_CHANGE.connect(self.stat_row_info) self.manga_list_view.gallery_model.db_emitter.COUNT_CHANGE.connect(self.stat_row_info) self.manga_list_view.gallery_model.STATUSBAR_MSG.connect(self.stat_temp_msg) self.manga_list_view.STATUS_BAR_MSG.connect(self.stat_temp_msg) self.manga_table_view.STATUS_BAR_MSG.connect(self.stat_temp_msg) self.stat_row_info() app_constants.STAT_MSG_METHOD = self.stat_temp_msg def stat_temp_msg(self, msg): self.temp_timer.stop() self.temp_msg.setText(msg) self.status_bar.addWidget(self.temp_msg) self.temp_timer.timeout.connect(self.temp_msg.clear) self.temp_timer.setSingleShot(True) self.temp_timer.start(5000) def stat_row_info(self): r = self.manga_list_view.model().rowCount() t = self.manga_list_view.gallery_model.db_emitter.count self.stat_info.setText("Loaded {} of {} ".format(r, t)) def manga_display(self): "initiates the manga view and related things" #list view self.manga_list_view = gallery.MangaView(self) #table view self.manga_table_view = gallery.MangaTableView(self) self.manga_table_view.gallery_model = self.manga_list_view.gallery_model self.manga_table_view.sort_model = self.manga_list_view.sort_model self.manga_table_view.setModel(self.manga_table_view.sort_model) self.manga_table_view.sort_model.change_model(self.manga_table_view.gallery_model) self.manga_table_view.setColumnWidth(app_constants.FAV, 20) self.manga_table_view.setColumnWidth(app_constants.ARTIST, 200) self.manga_table_view.setColumnWidth(app_constants.TITLE, 400) self.manga_table_view.setColumnWidth(app_constants.TAGS, 300) self.manga_table_view.setColumnWidth(app_constants.TYPE, 60) self.manga_table_view.setColumnWidth(app_constants.CHAPTERS, 60) self.manga_table_view.setColumnWidth(app_constants.LANGUAGE, 100) self.manga_table_view.setColumnWidth(app_constants.LINK, 400) def init_spinners(self): # fetching spinner self.data_fetch_spinner = misc.Spinner(self) self.data_fetch_spinner.set_size(60) self.move_listener.connect( lambda: self.data_fetch_spinner.update_move( QPoint(self.pos().x()+self.width()//2, self.pos().y()+self.height()//2))) self.manga_list_view.gallery_model.ADD_MORE.connect(self.data_fetch_spinner.show) self.manga_list_view.gallery_model.db_emitter.START.connect(self.data_fetch_spinner.show) self.manga_list_view.gallery_model.ADDED_ROWS.connect(self.data_fetch_spinner.before_hide) self.manga_list_view.gallery_model.db_emitter.CANNOT_FETCH_MORE.connect(self.data_fetch_spinner.before_hide) ## deleting spinner #self.gallery_delete_spinner = misc.Spinner(self) #self.gallery_delete_spinner.set_size(40,40) ##self.gallery_delete_spinner.set_text('Removing...') #self.manga_list_view.gallery_model.rowsAboutToBeRemoved.connect(self.gallery_delete_spinner.show) #self.manga_list_view.gallery_model.rowsRemoved.connect(self.gallery_delete_spinner.before_hide) def search(self, srch_string): self.search_bar.setText(srch_string) self.search_backward.setVisible(True) self.manga_list_view.sort_model.init_search(srch_string) old_cursor_pos = self._search_cursor_pos[0] self.search_bar.end(False) if self.search_bar.cursorPosition() != old_cursor_pos+1: self.search_bar.setCursorPosition(old_cursor_pos) def favourite_display(self): "Switches to favourite display" self.manga_table_view.sort_model.fav_view() self.favourite_btn.selected = True self.library_btn.selected = False def catalog_display(self): "Switches to catalog display" self.manga_table_view.sort_model.catalog_view() self.library_btn.selected = True self.favourite_btn.selected = False def settings(self): sett = settingsdialog.SettingsDialog(self) sett.scroll_speed_changed.connect(self.manga_list_view.updateGeometries) #sett.show() def init_toolbar(self): self.toolbar = QToolBar() self.toolbar.setFixedHeight(25) self.toolbar.setWindowTitle("Show") # text for the contextmenu #self.toolbar.setStyleSheet("QToolBar {border:0px}") # make it user defined? self.toolbar.setMovable(False) self.toolbar.setFloatable(False) #self.toolbar.setIconSize(QSize(20,20)) self.toolbar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) self.toolbar.setIconSize(QSize(20,20)) spacer_start = QWidget() # aligns the first actions properly spacer_start.setFixedSize(QSize(10, 1)) self.toolbar.addWidget(spacer_start) self.favourite_btn = misc.ToolbarButton(self.toolbar, 'Favorites') self.toolbar.addWidget(self.favourite_btn) self.favourite_btn.clicked.connect(self.favourite_display) #need lambda to pass extra args self.library_btn = misc.ToolbarButton(self.toolbar, 'Library') self.toolbar.addWidget(self.library_btn) self.library_btn.clicked.connect(self.catalog_display) #need lambda to pass extra args self.library_btn.selected = True self.toolbar.addSeparator() gallery_menu = QMenu() gallery_action = QToolButton() gallery_action.setText('Gallery ') gallery_action.setPopupMode(QToolButton.InstantPopup) gallery_action.setToolTip('Contains various gallery related features') gallery_action.setMenu(gallery_menu) add_gallery_icon = QIcon(app_constants.PLUS_PATH) gallery_action_add = QAction(add_gallery_icon, "Add single gallery...", self) gallery_action_add.triggered.connect(self.manga_list_view.SERIES_DIALOG.emit) gallery_action_add.setToolTip('Add a single gallery thoroughly') gallery_menu.addAction(gallery_action_add) add_more_action = QAction(add_gallery_icon, "Add galleries...", self) add_more_action.setStatusTip('Add galleries from different folders') add_more_action.triggered.connect(lambda: self.populate(True)) gallery_menu.addAction(add_more_action) populate_action = QAction(add_gallery_icon, "Populate from directory/archive...", self) populate_action.setStatusTip('Populates the DB with galleries from a single folder or archive') populate_action.triggered.connect(self.populate) gallery_menu.addAction(populate_action) gallery_menu.addSeparator() metadata_action = QAction('Get metadata for all galleries', self) metadata_action.triggered.connect(self.get_metadata) gallery_menu.addAction(metadata_action) scan_galleries_action = QAction('Scan for new galleries', self) scan_galleries_action.triggered.connect(self.scan_for_new_galleries) scan_galleries_action.setStatusTip('Scan monitored folders for new galleries') gallery_menu.addAction(scan_galleries_action) gallery_action_random = gallery_menu.addAction("Open random gallery") gallery_action_random.triggered.connect(self.manga_list_view.open_random_gallery) self.toolbar.addWidget(gallery_action) misc_action = QToolButton() misc_action.setText('Tools ') misc_action_menu = QMenu() misc_action.setMenu(misc_action_menu) misc_action.setPopupMode(QToolButton.InstantPopup) misc_action.setToolTip("Contains misc. features") gallery_downloader = QAction("Gallery Downloader", misc_action_menu) gallery_downloader.triggered.connect(self.download_window.show) misc_action_menu.addAction(gallery_downloader) duplicate_check_simple = QAction("Simple Duplicate Finder", misc_action_menu) duplicate_check_simple.triggered.connect(lambda: self.manga_list_view.duplicate_check()) misc_action_menu.addAction(duplicate_check_simple) self.toolbar.addWidget(misc_action) spacer_middle = QWidget() # aligns buttons to the right spacer_middle.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.toolbar.addWidget(spacer_middle) sort_action = QToolButton() sort_action.setIcon(QIcon(app_constants.SORT_PATH)) sort_action.setMenu(misc.SortMenu(self.toolbar, self.manga_list_view)) sort_action.setPopupMode(QToolButton.InstantPopup) self.toolbar.addWidget(sort_action) self.grid_toggle_g_icon = QIcon(app_constants.GRID_PATH) self.grid_toggle_l_icon = QIcon(app_constants.LIST_PATH) self.grid_toggle = QToolButton() if self.display.currentIndex() == self.m_l_view_index: self.grid_toggle.setIcon(self.grid_toggle_l_icon) else: self.grid_toggle.setIcon(self.grid_toggle_g_icon) self.grid_toggle.setObjectName('gridtoggle') self.grid_toggle.clicked.connect(self.toggle_view) self.toolbar.addWidget(self.grid_toggle) spacer_mid2 = QWidget() spacer_mid2.setFixedSize(QSize(5, 1)) self.toolbar.addWidget(spacer_mid2) def set_search_case(b): app_constants.GALLERY_SEARCH_CASE = b settings.set(b, 'Application', 'gallery search case') settings.save() def set_search_strict(b): app_constants.GALLERY_SEARCH_STRICT = b settings.set(b, 'Application', 'gallery search strict') settings.save() self.search_bar = misc.LineEdit() search_options = self.search_bar.addAction(QIcon(app_constants.SEARCH_OPTIONS_PATH), QLineEdit.TrailingPosition) search_options_menu = QMenu(self) search_options.triggered.connect(lambda: search_options_menu.popup(QCursor.pos())) search_options.setMenu(search_options_menu) case_search_option = search_options_menu.addAction('Case Sensitive') case_search_option.setCheckable(True) case_search_option.setChecked(app_constants.GALLERY_SEARCH_CASE) case_search_option.toggled.connect(set_search_case) strict_search_option = search_options_menu.addAction('Match whole terms') strict_search_option.setCheckable(True) strict_search_option.setChecked(app_constants.GALLERY_SEARCH_STRICT) strict_search_option.toggled.connect(set_search_strict) self.search_bar.setObjectName('search_bar') self.search_timer = QTimer(self) self.search_timer.setSingleShot(True) self.search_timer.timeout.connect(lambda: self.search(self.search_bar.text())) self._search_cursor_pos = [0, 0] def set_cursor_pos(old, new): self._search_cursor_pos[0] = old self._search_cursor_pos[1] = new self.search_bar.cursorPositionChanged.connect(set_cursor_pos) if app_constants.SEARCH_AUTOCOMPLETE: completer = QCompleter(self) completer_view = misc.CompleterPopupView() completer.setPopup(completer_view) completer_view._setup() completer.setModel(self.manga_list_view.gallery_model) completer.setCaseSensitivity(Qt.CaseInsensitive) completer.setCompletionMode(QCompleter.PopupCompletion) completer.setCompletionRole(Qt.DisplayRole) completer.setCompletionColumn(app_constants.TITLE) completer.setFilterMode(Qt.MatchContains) self.search_bar.setCompleter(completer) self.search_bar.returnPressed.connect(lambda: self.search(self.search_bar.text())) if not app_constants.SEARCH_ON_ENTER: self.search_bar.textEdited.connect(lambda: self.search_timer.start(800)) self.search_bar.setPlaceholderText("Search title, artist, namespace & tags") self.search_bar.setMinimumWidth(150) self.search_bar.setMaximumWidth(500) self.search_bar.setFixedHeight(19) self.manga_list_view.sort_model.HISTORY_SEARCH_TERM.connect(lambda a: self.search_bar.setText(a)) self.toolbar.addWidget(self.search_bar) def search_history(_, back=True): # clicked signal passes a bool sort_model = self.manga_list_view.sort_model nav = sort_model.PREV if back else sort_model.NEXT history_term = sort_model.navigate_history(nav) if back: self.search_forward.setVisible(True) back = QShortcut(QKeySequence(QKeySequence.Back), self, lambda: search_history(None)) forward = QShortcut(QKeySequence(QKeySequence.Forward), self, lambda: search_history(None, False)) search_backbutton = QToolButton(self.toolbar) search_backbutton.setText(u'\u25C0') search_backbutton.setFixedWidth(15) search_backbutton.clicked.connect(search_history) self.search_backward = self.toolbar.addWidget(search_backbutton) self.search_backward.setVisible(False) search_forwardbutton = QToolButton(self.toolbar) search_forwardbutton.setText(u'\u25B6') search_forwardbutton.setFixedWidth(15) search_forwardbutton.clicked.connect(lambda: search_history(None, False)) self.search_forward = self.toolbar.addWidget(search_forwardbutton) self.search_forward.setVisible(False) spacer_end = QWidget() # aligns settings action properly spacer_end.setFixedSize(QSize(10, 1)) self.toolbar.addWidget(spacer_end) settings_act = QToolButton(self.toolbar) settings_act.setIcon(QIcon(app_constants.SETTINGS_PATH)) settings_act.clicked.connect(self.settings) self.toolbar.addWidget(settings_act) spacer_end2 = QWidget() # aligns About action properly spacer_end2.setFixedSize(QSize(5, 1)) self.toolbar.addWidget(spacer_end2) self.addToolBar(self.toolbar) def toggle_view(self): """ Toggles the current display view """ if self.display.currentIndex() == self.m_l_view_index: self.display.setCurrentIndex(self.m_t_view_index) self.grid_toggle.setIcon(self.grid_toggle_g_icon) else: self.display.setCurrentIndex(self.m_l_view_index) self.grid_toggle.setIcon(self.grid_toggle_l_icon) # TODO: Improve this so that it adds to the gallery dialog, # so user can edit data before inserting (make it a choice) def populate(self, mixed=None): "Populates the database with gallery from local drive'" if mixed: gallery_view = misc.GalleryListView(self, True) gallery_view.SERIES.connect(self.gallery_populate) gallery_view.show() else: msg_box = misc.BasePopup(self) l = QVBoxLayout() msg_box.main_widget.setLayout(l) l.addWidget(QLabel('Directory or Archive?')) l.addLayout(msg_box.buttons_layout) def from_dir(): path = QFileDialog.getExistingDirectory(self, "Choose a directory containing your galleries") if not path: return msg_box.close() app_constants.OVERRIDE_SUBFOLDER_AS_GALLERY = True self.gallery_populate(path, True) def from_arch(): path = QFileDialog.getOpenFileName(self, 'Choose an archive containing your galleries', filter=utils.FILE_FILTER) path = [path[0]] if not all(path) or not path: return msg_box.close() app_constants.OVERRIDE_SUBFOLDER_AS_GALLERY = True self.gallery_populate(path, True) buttons = msg_box.add_buttons('Directory', 'Archive', 'Close') buttons[2].clicked.connect(msg_box.close) buttons[0].clicked.connect(from_dir) buttons[1].clicked.connect(from_arch) msg_box.adjustSize() msg_box.show() def gallery_populate(self, path, validate=False): "Scans the given path for gallery to add into the DB" if len(path) is not 0: data_thread = QThread(self) data_thread.setObjectName('General gallery populate') loading = misc.Loading(self) self.g_populate_inst = fetch.Fetch() self.g_populate_inst.series_path = path loading.show() def finished(status): def hide_loading(): loading.hide() if status: if len(status) != 0: def add_gallery(gallery_list): def append_to_model(x): self.manga_list_view.sort_model.insertRows(x, None, len(x)) self.manga_list_view.sort_model.init_search( self.manga_list_view.sort_model.current_term) class A(QObject): done = pyqtSignal() prog = pyqtSignal(int) def __init__(self, obj, parent=None): super().__init__(parent) self.obj = obj self.galleries = [] def add_to_db(self): for y, x in enumerate(self.obj): gallerydb.add_method_queue( gallerydb.GalleryDB.add_gallery_return, False, x) self.galleries.append(x) y += 1 self.prog.emit(y) append_to_model(self.galleries) self.done.emit() loading.progress.setMaximum(len(gallery_list)) self.a_instance = A(gallery_list) thread = QThread(self) thread.setObjectName('Database populate') def loading_show(numb): if loading.isHidden(): loading.show() loading.setText('Populating database ({}/{})\nPlease wait...'.format( numb, loading.progress.maximum())) loading.progress.setValue(numb) loading.show() def loading_hide(): loading.close() self.manga_list_view.gallery_model.ROWCOUNT_CHANGE.emit() self.a_instance.moveToThread(thread) self.a_instance.prog.connect(loading_show) thread.started.connect(self.a_instance.add_to_db) self.a_instance.done.connect(loading_hide) self.a_instance.done.connect(self.a_instance.deleteLater) #a_instance.add_to_db() thread.finished.connect(thread.deleteLater) thread.start() #data_thread.quit hide_loading() log_i('Populating DB from gallery folder: OK') if validate: gallery_list = misc.GalleryListView(self) gallery_list.SERIES.connect(add_gallery) for ser in status: if ser.is_archive and app_constants.SUBFOLDER_AS_GALLERY: p = os.path.split(ser.path)[1] if ser.chapters[0].path: pt_in_arch = os.path.split(ser.path_in_archive) pt_in_arch = pt_in_arch[1] or pt_in_arch[0] text = '{}: {}'.format(p, pt_in_arch) else: text = p gallery_list.add_gallery(ser, text) else: gallery_list.add_gallery(ser, os.path.split(ser.path)[1]) #self.manga_list_view.gallery_model.populate_data() gallery_list.update_count() gallery_list.show() else: add_gallery(status) else: log_d('No new gallery was found') loading.setText("No new gallery found") #data_thread.quit else: log_e('Populating DB from gallery folder: Nothing was added!') loading.setText("<font color=red>Nothing was added. Check happypanda_log for details..</font>") loading.progress.setStyleSheet("background-color:red;") data_thread.quit QTimer.singleShot(8000, loading.close) def skipped_gs(s_list): "Skipped galleries" msg_box = QMessageBox(self) msg_box.setIcon(QMessageBox.Question) msg_box.setText('Do you want to view skipped paths?') msg_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No) msg_box.setDefaultButton(QMessageBox.No) if msg_box.exec() == QMessageBox.Yes: list_wid = QTableWidget(self) list_wid.setAttribute(Qt.WA_DeleteOnClose) list_wid.setRowCount(len(s_list)) list_wid.setColumnCount(2) list_wid.setAlternatingRowColors(True) list_wid.setEditTriggers(list_wid.NoEditTriggers) list_wid.setHorizontalHeaderLabels(['Reason', 'Path']) list_wid.setSelectionBehavior(list_wid.SelectRows) list_wid.setSelectionMode(list_wid.SingleSelection) list_wid.setSortingEnabled(True) list_wid.verticalHeader().hide() list_wid.setAutoScroll(False) for x, g in enumerate(s_list): list_wid.setItem(x, 0, QTableWidgetItem(g[1])) list_wid.setItem(x, 1, QTableWidgetItem(g[0])) list_wid.resizeColumnsToContents() list_wid.setWindowTitle('{} skipped paths'.format(len(s_list))) list_wid.setWindowFlags(Qt.Window) list_wid.resize(900,400) list_wid.doubleClicked.connect(lambda i: utils.open_path( list_wid.item(i.row(), 1).text(), list_wid.item(i.row(), 1).text())) list_wid.show() def a_progress(prog): loading.progress.setValue(prog) loading.setText("Preparing galleries...") self.g_populate_inst.moveToThread(data_thread) self.g_populate_inst.DATA_COUNT.connect(loading.progress.setMaximum) self.g_populate_inst.PROGRESS.connect(a_progress) self.g_populate_inst.FINISHED.connect(finished) self.g_populate_inst.FINISHED.connect(self.g_populate_inst.deleteLater) self.g_populate_inst.SKIPPED.connect(skipped_gs) data_thread.finished.connect(data_thread.deleteLater) data_thread.started.connect(self.g_populate_inst.local) data_thread.start() #.g_populate_inst.local() log_i('Populating DB from directory/archive') def scan_for_new_galleries(self): available_folders = app_constants.ENABLE_MONITOR and\ app_constants.MONITOR_PATHS and all(app_constants.MONITOR_PATHS) if available_folders and not app_constants.SCANNING_FOR_GALLERIES: app_constants.SCANNING_FOR_GALLERIES = True self.notification_bar.add_text("Scanning for new galleries...") log_i('Scanning for new galleries...') try: class ScanDir(QObject): final_paths_and_galleries = pyqtSignal(list, list) finished = pyqtSignal() def __init__(self, parent=None): super().__init__(parent) self.scanned_data = [] def scan_dirs(self): paths = [] for p in app_constants.MONITOR_PATHS: if os.path.exists(p): dir_content = scandir.scandir(p) for d in dir_content: paths.append(d.path) else: log_e("Monitored path does not exists: {}".format(p.encode(errors='ignore'))) fetch_inst = fetch.Fetch(self) fetch_inst.series_path = paths def set_scanned_d(d): self.scanned_data = d fetch_inst.FINISHED.connect(set_scanned_d) fetch_inst.local() #contents = [] #for g in self.scanned_data: # contents.append(g) #paths = sorted(paths) #new_galleries = [] #for x in contents: # y = utils.b_search(paths, os.path.normcase(x.path)) # if not y: # new_galleries.append(x) galleries = [] final_paths = [] if self.scanned_data: for g in self.scanned_data: try: if g.is_archive: g.profile = utils.get_gallery_img(g.chapters[0].path, g.path) else: g.profile = utils.get_gallery_img(g.chapters[0].path) if not g.profile: raise Exception except: g.profile = app_constants.NO_IMAGE_PATH galleries.append(g) final_paths.append(g.path) self.final_paths_and_galleries.emit(final_paths, galleries) self.finished.emit() self.deleteLater() #if app_constants.LOOK_NEW_GALLERY_AUTOADD: # QTimer.singleShot(10000, self.gallery_populate(final_paths)) # return def show_new_galleries(final_paths, galleries): if final_paths and galleries: app_constants.OVERRIDE_MOVE_IMPORTED_IN_FETCH = True if app_constants.LOOK_NEW_GALLERY_AUTOADD: self.gallery_populate(final_paths) else: class NewGalleryMenu(QMenu): def __init__(self, parent=None): super().__init__(parent) ignore_act = self.addAction('Add to ignore list') ignore_act.triggered.connect(self.add_to_ignore) def add_to_ignore(self): gallery = self.gallery_widget.gallery app_constants.IGNORE_PATHS.append(gallery.path) settings.set(app_constants.IGNORE_PATHS, 'Application', 'ignore paths') if self.gallery_widget.parent_widget.gallery_layout.count() == 1: self.gallery_widget.parent_widget.close() else: self.gallery_widget.close() if len(galleries) == 1: self.notification_bar.add_text("{} new gallery was discovered in one of your monitored directories".format(len(galleries))) else: self.notification_bar.add_text("{} new galleries were discovered in one of your monitored directories".format(len(galleries))) text = "These new galleries were discovered! Do you want to add them?"\ if len(galleries) > 1 else "This new gallery was discovered! Do you want to add it?" g_popup = io_misc.GalleryPopup((text, galleries), self, NewGalleryMenu()) buttons = g_popup.add_buttons('Add', 'Close') def populate_n_close(): g_popup.close() self.gallery_populate(final_paths) buttons[0].clicked.connect(populate_n_close) buttons[1].clicked.connect(g_popup.close) def finished(): app_constants.SCANNING_FOR_GALLERIES = False thread = QThread(self) self.scan_inst = ScanDir() self.scan_inst.moveToThread(thread) self.scan_inst.final_paths_and_galleries.connect(show_new_galleries) self.scan_inst.finished.connect(finished) thread.started.connect(self.scan_inst.scan_dirs) #self.scan_inst.scan_dirs() thread.finished.connect(thread.deleteLater) thread.start() except: self.notification_bar.add_text('An error occured while attempting to scan for new galleries. Check happypanda.log.') log.exception('An error occured while attempting to scan for new galleries.') app_constants.SCANNING_FOR_GALLERIES = False def dragEnterEvent(self, event): if event.mimeData().hasUrls(): event.acceptProposedAction() else: self.notification_bar.add_text('File is not supported') def dropEvent(self, event): acceptable = [] unaccept = [] for u in event.mimeData().urls(): path = u.toLocalFile() if os.path.isdir(path) or path.endswith(utils.ARCHIVE_FILES): acceptable.append(path) else: unaccept.append(path) log_i('Acceptable dropped items: {}'.format(len(acceptable))) log_i('Unacceptable dropped items: {}'.format(len(unaccept))) log_d('Dropped items: {}\n{}'.format(acceptable, unaccept).encode(errors='ignore')) if acceptable: self.notification_bar.add_text('Adding dropped items...') log_i('Adding dropped items') l = len(acceptable) == 1 f_item = acceptable[0] if f_item.endswith(utils.ARCHIVE_FILES): f_item = utils.check_archive(f_item) else: f_item = utils.recursive_gallery_check(f_item) f_item_l = len(f_item) < 2 subfolder_as_c = not app_constants.SUBFOLDER_AS_GALLERY if l and subfolder_as_c or l and f_item_l: g_d = gallerydialog.GalleryDialog(self, acceptable[0]) g_d.SERIES.connect(self.manga_list_view.gallery_model.addRows) g_d.show() else: self.gallery_populate(acceptable, True) else: text = 'File not supported' if len(unaccept) < 2 else 'Files not supported' self.notification_bar.add_text(text) if unaccept: self.notification_bar.add_text('Some unsupported files did not get added') def resizeEvent(self, event): try: self.notification_bar.resize(event.size().width()) except AttributeError: pass self.move_listener.emit() return super().resizeEvent(event) def moveEvent(self, event): self.move_listener.emit() return super().moveEvent(event) def showEvent(self, event): return super().showEvent(event) def cleanup_exit(self): self.system_tray.hide() # watchers try: self.watchers.stop_all() except AttributeError: pass # settings settings.set(self.manga_list_view.current_sort, 'General', 'current sort') settings.set(app_constants.IGNORE_PATHS, 'Application', 'ignore paths') settings.win_save(self, 'AppWindow') # temp dir try: for root, dirs, files in scandir.walk('temp', topdown=False): for name in files: os.remove(os.path.join(root, name)) for name in dirs: os.rmdir(os.path.join(root, name)) log_d('Flush temp on exit: OK') except: log.exception('Flush temp on exit: FAIL') if self.tags_treeview: self.tags_treeview.close() self.download_window.close() # check if there is db activity if not gallerydb.method_queue.empty(): class DBActivityChecker(QObject): FINISHED = pyqtSignal() def __init__(self, **kwargs): super().__init__(**kwargs) def check(self): gallerydb.method_queue.join() self.FINISHED.emit() self.deleteLater() db_activity = DBActivityChecker() db_spinner = misc.Spinner(self) self.db_activity_checker.connect(db_activity.check) db_activity.moveToThread(app_constants.GENERAL_THREAD) db_activity.FINISHED.connect(db_spinner.close) db_spinner.set_size(50) db_spinner.set_text('Activity') db_spinner.move(QPoint(self.pos().x()+self.width()-70, self.pos().y()+self.height()-70)) self.move_listener.connect(lambda: db_spinner.update_move(QPoint(self.pos().x()+self.width()-70, self.pos().y()+self.height()-70))) db_spinner.show() self.db_activity_checker.emit() msg_box = QMessageBox(self) msg_box.setText('Database activity detected!') msg_box.setInformativeText("Closing now might result in data loss." + " Do you still want to close?\n(Wait for the activity spinner to hide before closing)") msg_box.setIcon(QMessageBox.Critical) msg_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No) msg_box.setDefaultButton(QMessageBox.No) if msg_box.exec() == QMessageBox.Yes: return 1 else: return 2 else: return 0 def closeEvent(self, event): r_code = self.cleanup_exit() if r_code == 1: log_d('Force Exit App: OK') super().closeEvent(event) elif r_code == 2: log_d('Ignore Exit App') event.ignore() else: log_d('Normal Exit App: OK') super().closeEvent(event)
def __init__(self, stampManager, parent = None): super().__init__(parent) self.mTileStampManager = stampManager self.mTileStampModel = stampManager.tileStampModel() self.mProxyModel = QSortFilterProxyModel(self.mTileStampModel) self.mFilterEdit = QLineEdit(self) self.mNewStamp = QAction(self) self.mAddVariation = QAction(self) self.mDuplicate = QAction(self) self.mDelete = QAction(self) self.mChooseFolder = QAction(self) self.setObjectName("TileStampsDock") self.mProxyModel.setSortLocaleAware(True) self.mProxyModel.setSortCaseSensitivity(Qt.CaseInsensitive) self.mProxyModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.mProxyModel.setSourceModel(self.mTileStampModel) self.mProxyModel.sort(0) self.mTileStampView = TileStampView(self) self.mTileStampView.setModel(self.mProxyModel) self.mTileStampView.setVerticalScrollMode(QAbstractItemView.ScrollPerPixel) self.mTileStampView.header().setStretchLastSection(False) self.mTileStampView.header().setSectionResizeMode(0, QHeaderView.Stretch) self.mTileStampView.header().setSectionResizeMode(1, QHeaderView.ResizeToContents) self.mTileStampView.setContextMenuPolicy(Qt.CustomContextMenu) self.mTileStampView.customContextMenuRequested.connect(self.showContextMenu) self.mNewStamp.setIcon(QIcon(":images/16x16/document-new.png")) self.mAddVariation.setIcon(QIcon(":/images/16x16/add.png")) self.mDuplicate.setIcon(QIcon(":/images/16x16/stock-duplicate-16.png")) self.mDelete.setIcon(QIcon(":images/16x16/edit-delete.png")) self.mChooseFolder.setIcon(QIcon(":images/16x16/document-open.png")) Utils.setThemeIcon(self.mNewStamp, "document-new") Utils.setThemeIcon(self.mAddVariation, "add") Utils.setThemeIcon(self.mDelete, "edit-delete") Utils.setThemeIcon(self.mChooseFolder, "document-open") self.mFilterEdit.setClearButtonEnabled(True) self.mFilterEdit.textChanged.connect(self.mProxyModel.setFilterFixedString) self.mTileStampModel.stampRenamed.connect(self.ensureStampVisible) self.mNewStamp.triggered.connect(self.newStamp) self.mAddVariation.triggered.connect(self.addVariation) self.mDuplicate.triggered.connect(self.duplicate) self.mDelete.triggered.connect(self.delete_) self.mChooseFolder.triggered.connect(self.chooseFolder) self.mDuplicate.setEnabled(False) self.mDelete.setEnabled(False) self.mAddVariation.setEnabled(False) widget = QWidget(self) layout = QVBoxLayout(widget) layout.setContentsMargins(5, 5, 5, 5) buttonContainer = QToolBar() buttonContainer.setFloatable(False) buttonContainer.setMovable(False) buttonContainer.setIconSize(QSize(16, 16)) buttonContainer.addAction(self.mNewStamp) buttonContainer.addAction(self.mAddVariation) buttonContainer.addAction(self.mDuplicate) buttonContainer.addAction(self.mDelete) stretch = QWidget() stretch.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum) buttonContainer.addWidget(stretch) buttonContainer.addAction(self.mChooseFolder) listAndToolBar = QVBoxLayout() listAndToolBar.setSpacing(0) listAndToolBar.addWidget(self.mFilterEdit) listAndToolBar.addWidget(self.mTileStampView) listAndToolBar.addWidget(buttonContainer) layout.addLayout(listAndToolBar) selectionModel = self.mTileStampView.selectionModel() selectionModel.currentRowChanged.connect(self.currentRowChanged) self.setWidget(widget) self.retranslateUi()