class MainWindow(QMainWindow): # class EventFilter(QObject): # def __init__(self, parent): # super().__init__(parent) # # def eventFilter(self, obj, e): # #print(obj.metaObject().className()) # # if e.type() == QEvent.KeyPress or e.type() == QEvent.ShortcutOverride: # key = e.key() # mod = e.modifiers() # # print(str(e) + ' ' + str(e.type()) ) # # if mod == Qt.AltModifier: # print('*'*30) # print('alt pressed') # if key == Qt.Key_Left or key == Qt.Key_Right: # print('alt-left') if key == Qt.Key_Left else print('alt-right') ## action = QAbstractItemView.MoveLeft if key == Qt.Key_Left else QAbstractItemView.MoveRight ## idx = obj.moveCursor(action, Qt.NoModifier) ## item = obj.itemFromIndex(idx) ## obj.setCurrentItem(item) # return True # # return False PROGRAM_NAME = 'KiCad Schematic Component Manager' #-------------------------------------------------------------------------------- class EventFilter(QObject): def __init__(self, parent): super().__init__(parent) def eventFilter(self, obj, e): if e.type() == QEvent.KeyPress or e.type() == QEvent.ShortcutOverride: key = e.key() mod = e.modifiers() #print(obj.focusWidget().metaObject().className()) return False #-------------------------------------------------------------------------------- def scroll_left(self): print('alt-left') if self.ToolIndex == 3 or self.ToolIndex == 2: self.ToolList[self.ToolIndex].finish_edit() self.ToolIndex -= 1 if self.ToolIndex < 0: self.ToolIndex = len(self.ToolList) - 1 print('Tool Index: ' + str(self.ToolIndex)) self.ToolList[self.ToolIndex].setFocus() #-------------------------------------------------------------------------------- def scroll_right(self): print('alt-right') if self.ToolIndex == 3 or self.ToolIndex == 2: self.ToolList[self.ToolIndex].finish_edit() self.ToolIndex += 1 if self.ToolIndex == len(self.ToolList): self.ToolIndex = 0 print('Tool Index: ' + str(self.ToolIndex)) self.ToolList[self.ToolIndex].setFocus() #-------------------------------------------------------------------------------- def mouse_change_tool(self, s): print('Tool ' + s) if s == 'CmpTable': self.ToolIndex = 0 elif s == 'Selector': self.ToolIndex = 1 elif s == 'Inspector': self.ToolIndex = 2 elif s == 'FieldInspector': self.ToolIndex = 3 if self.ToolIndex != 3: self.ToolList[3].finish_edit() # save field properties when leave field inspector #-------------------------------------------------------------------------------- def add_user_property(self): self.Inspector.save_cmps() self.FieldInspector.save_fields() self.Inspector.add_property() #-------------------------------------------------------------------------------- def remove_user_property(self): #self.Inspector.save_cmps() self.FieldInspector.save_fields() self.Inspector.remove_property() #-------------------------------------------------------------------------------- def rename_user_property(self): #self.Inspector.save_cmps() self.FieldInspector.save_fields() self.Inspector.rename_property() #-------------------------------------------------------------------------------- def __init__(self): super().__init__() self.initUI() self.installEventFilter(self.EventFilter(self)) self.setFocusPolicy(Qt.WheelFocus) self.setTabOrder(self.CmpTable, self.Inspector ) self.setTabOrder(self.Inspector, self.Selector) self.setTabOrder(self.Selector, self.FieldInspector) #self.setTabOrder(self.FieldInspector, self.CmpTable) #---------------------------------------------------- # # Application Hotkeys # self.shortcutLeft = QShortcut(QKeySequence(Qt.ALT + Qt.Key_Left), self) self.shortcutRight = QShortcut(QKeySequence(Qt.ALT + Qt.Key_Right), self) self.shortcutLeft.setContext(Qt.ApplicationShortcut) self.shortcutRight.setContext(Qt.ApplicationShortcut) self.shortcutLeft.activated.connect(self.scroll_left) self.shortcutRight.activated.connect(self.scroll_right) #-------------------------------------------------------------------------------- def initUI(self): #---------------------------------------------------- # # Main Window # work_zone = QWidget(self) Layout = QHBoxLayout(work_zone) self.setCentralWidget(work_zone) openAction = QAction(QIcon( os.path.join(resources_path, 'open24.png') ), 'Open', self) openAction.setShortcut('Ctrl+O') openAction.setStatusTip('Open Schematic File') openAction.triggered.connect(self.open_file) saveAction = QAction(QIcon( os.path.join(resources_path, 'save24.png') ), 'Save', self) saveAction.setShortcut('Ctrl+S') saveAction.setStatusTip('Save Schematic File') saveAction.triggered.connect(self.save_file) saveAsAction = QAction(QIcon( os.path.join(resources_path, 'save-as24.png') ), 'Save As...', self) saveAsAction.setShortcut('Ctrl+Shift+S') saveAsAction.setStatusTip('Save Schematic File As...') saveAsAction.triggered.connect(self.save_file_as) exitAction = QAction(QIcon( os.path.join(resources_path, 'exit24.png') ), 'Exit', self) exitAction.setShortcut('Ctrl+Q') exitAction.setStatusTip('Exit application') exitAction.triggered.connect(self.close) settingsAction = QAction(QIcon( os.path.join(resources_path, 'settings24.png') ), 'Settings', self) settingsAction.setShortcut('Ctrl+Alt+S') settingsAction.setStatusTip('Edit settings') settingsAction.triggered.connect(self.edit_settings) helpAction = QAction(QIcon( os.path.join(resources_path, 'help_book24.png') ), 'User\'s Manual', self) helpAction.setShortcut('F1') helpAction.setStatusTip('User\'s Manual') helpAction.triggered.connect(self.show_user_manual_slot) helpSDAction = QAction(QIcon( os.path.join(resources_path, 'gear24.png') ), 'Settings Dialog', self) helpSDAction.setShortcut('Ctrl+F1') helpSDAction.setStatusTip('Settings Dialog Help') helpSDAction.triggered.connect(self.show_setting_dialog_help_slot) helpHKAction = QAction(QIcon( os.path.join(resources_path, 'rocket24.png') ), 'Hotkeys', self) helpHKAction.setShortcut('Shift+F1') helpHKAction.setStatusTip('Hotkeys Help') helpHKAction.triggered.connect(self.show_hotkeys_help_slot) self.statusBar().showMessage('Ready') #-------------------------------------------- # # Main Menu # menubar = self.menuBar() fileMenu = menubar.addMenu('&File') fileMenu.addAction(openAction) fileMenu.addAction(saveAction) fileMenu.addAction(saveAsAction) fileMenu.addAction(exitAction) #-------------------------------------------- # # Options Menu # optionsMenu = menubar.addMenu('&Options') optionsMenu.addAction(settingsAction) #-------------------------------------------- # # Help Menu # helpMenu = menubar.addMenu('&Help') helpMenu.addAction(helpAction) helpMenu.addAction(helpSDAction) helpMenu.addAction(helpHKAction) #-------------------------------------------- # # Toolbar # toolbar = self.addToolBar('Exit') toolbar.addAction(exitAction) toolbar.addAction(openAction) toolbar.addAction(saveAction) toolbar.addAction(saveAsAction) toolbar.addAction(settingsAction) toolbar.addAction(helpAction) #---------------------------------------------------- # # Settings Dialog # #---------------------------------------------------- # # Components Table # self.CmpTabBox = QGroupBox('Components', self) self.CmpTabLayout = QVBoxLayout(self.CmpTabBox) self.CmpTabLayout.setContentsMargins(4,10,4,4) self.CmpTabLayout.setSpacing(10) self.CmpTabLayout.setSizeConstraint(QVBoxLayout.SetMaximumSize) self.CmpTable = ComponentsTable(self) #self.CmpChooseButton = QPushButton('Choose', self) self.CmpTabLayout.addWidget(self.CmpTable) #self.CmpTabLayout.addWidget(self.CmpChooseButton) #---------------------------------------------------- # # Selector # self.SelectorBox = QGroupBox('Selector', self) self.SelectorLayout = QVBoxLayout(self.SelectorBox) self.SelectorLayout.setContentsMargins(4,10,4,4) self.SelectorLayout.setSpacing(2) self.SelectorBtnWidget = QWidget(self) self.SelectorBtnLayout = QHBoxLayout(self.SelectorBtnWidget) self.SelectorBtnLayout.setContentsMargins(4,10,4,4) self.SelectorBtnLayout.setSpacing(10) self.Selector = Selector(self) self.SelApplyButton = QPushButton('Apply', self) self.SelApplyButton.setToolTip('Alt+S: Apply selection patterns to components') self.SelClearButton = QPushButton('Clear', self) self.SelClearButton.setToolTip('Alt+C: Clear selection patterns') self.SelTemplateButton = QPushButton('Use Component', self) self.SelTemplateButton.setToolTip('Alt+T: Use Selected Component As Template') self.SelectorLayout.addWidget(self.Selector) self.SelectorBtnLayout.addWidget(self.SelTemplateButton) self.SelectorBtnLayout.addWidget(self.SelApplyButton) self.SelectorBtnLayout.addWidget(self.SelClearButton) self.SelectorLayout.addWidget(self.SelectorBtnWidget) self.shortcutSelApply = QShortcut(QKeySequence(Qt.ALT + Qt.Key_S), self) self.shortcutSelApply.activated.connect(self.Selector.apply_slot) self.shortcutSelClear = QShortcut(QKeySequence(Qt.ALT + Qt.Key_C), self) self.shortcutSelClear.activated.connect(self.Selector.clear_slot) self.shortcutSelTemplate = QShortcut(QKeySequence(Qt.ALT + Qt.Key_T), self) self.shortcutSelTemplate.activated.connect(self.Selector.use_comp_as_template_slot) #---------------------------------------------------- # # Inspector # self.Inspector = Inspector(self) self.FieldInspector = FieldInspector(self) self.InspectorBtnWidget = QWidget(self) self.InspectorBtnLayout = QHBoxLayout(self.InspectorBtnWidget) self.InspectorBtnLayout.setContentsMargins(4,10,4,4) self.InspectorBtnLayout.setSpacing(10) self.AddUserProperty = QPushButton('Add Property', self) self.AddUserProperty.setToolTip('Alt+A: Add new user property') self.DeleteUserProperty = QPushButton('Delete Property', self) self.DeleteUserProperty.setToolTip('Alt+Delete: Delete user property') self.RenameUserProperty = QPushButton('Rename Property', self) self.RenameUserProperty.setToolTip('Alt+R: Rename user property') self.InspectorBox = QGroupBox('Inspector', self) self.InspectorSplit = QSplitter(Qt.Vertical, self) self.InspectorLayout = QVBoxLayout(self.InspectorBox) self.InspectorLayout.setContentsMargins(4,10,4,4) self.InspectorLayout.setSpacing(2) self.InspectorSplit.addWidget(self.Inspector) self.InspectorSplit.addWidget(self.FieldInspector) self.InspectorLayout.addWidget(self.InspectorSplit) self.InspectorBtnLayout.addWidget(self.AddUserProperty) self.InspectorBtnLayout.addWidget(self.DeleteUserProperty) self.InspectorBtnLayout.addWidget(self.RenameUserProperty) self.InspectorLayout.addWidget(self.InspectorBtnWidget) self.shortcutSelApply = QShortcut(QKeySequence(Qt.ALT + Qt.Key_A), self) self.shortcutSelApply.activated.connect(self.add_user_property) self.shortcutSelApply = QShortcut(QKeySequence(Qt.ALT + Qt.Key_Delete), self) self.shortcutSelApply.activated.connect(self.remove_user_property) self.shortcutSelApply = QShortcut(QKeySequence(Qt.ALT + Qt.Key_R), self) self.shortcutSelApply.activated.connect(self.rename_user_property) #---------------------------------------------------- self.Splitter = QSplitter(self) self.Splitter.addWidget(self.CmpTabBox) self.Splitter.addWidget(self.SelectorBox) self.Splitter.addWidget(self.InspectorBox) self.centralWidget().layout().addWidget(self.Splitter) #---------------------------------------------------- # # Signals and Slots connections # self.CmpTable.cells_chosen.connect(self.Inspector.load_cmp) self.CmpTable.cells_chosen.connect(self.Selector.comp_template_slot) self.CmpTable.file_load.connect(self.file_loaded_slot) self.CmpTable.cmps_updated.connect(self.Selector.process_comps_slot) self.CmpTable.cmps_selected.connect(self.set_status_text_slot) self.SelApplyButton.clicked.connect(self.Selector.apply_slot) self.SelClearButton.clicked.connect(self.Selector.clear_slot) self.SelTemplateButton.clicked.connect(self.Selector.use_comp_as_template_slot) self.Selector.select_comps_signal.connect(self.CmpTable.select_comps_slot) self.Inspector.load_field.connect(self.FieldInspector.load_field_slot) self.Inspector.update_comps.connect(self.data_changed_slot) self.Inspector.update_comps.connect(self.CmpTable.update_cmp_list_slot) self.FieldInspector.data_changed.connect(self.data_changed_slot) CmpMgr.file_saved.connect(self.file_saved_slot) self.CmpTable.mouse_click.connect(self.mouse_change_tool) self.Inspector.mouse_click.connect(self.mouse_change_tool) self.FieldInspector.mouse_click.connect(self.mouse_change_tool) self.Inspector.header().sectionResized.connect(self.FieldInspector.column_resize) self.AddUserProperty.clicked.connect(self.add_user_property) self.DeleteUserProperty.clicked.connect(self.remove_user_property) self.RenameUserProperty.clicked.connect(self.rename_user_property) #---------------------------------------------------- self.ToolList = [] self.ToolList.append(self.CmpTable) self.ToolList.append(self.Selector) self.ToolList.append(self.Inspector) self.ToolList.append(self.FieldInspector) self.ToolIndex = 0 #---------------------------------------------------- # # Window # self.setWindowTitle(self.PROGRAM_NAME) Settings = QSettings('kicad-tools', 'Schematic Component Manager') #print(Settings.allKeys()) if Settings.contains('geometry'): self.restoreGeometry( Settings.value('geometry') ) else: self.setGeometry(100, 100, 1024, 768) if Settings.contains('cmptable'): w0, w1 = Settings.value('cmptable') self.CmpTable.setColumnWidth( 0, int(w0) ) self.CmpTable.setColumnWidth( 1, int(w1) ) if Settings.contains('selector'): w0, w1 = Settings.value('selector') self.Selector.setColumnWidth( 0, int(w0) ) self.Selector.setColumnWidth( 1, int(w1) ) if Settings.contains('inspector'): w0, w1 = Settings.value('inspector') self.Inspector.setColumnWidth( 0, int(w0) ) self.Inspector.setColumnWidth( 1, int(w1) ) self.FieldInspector.setColumnWidth( 0, int(w0) ) self.FieldInspector.setColumnWidth( 1, int(w1) ) #self.Inspector.setColumnWidth( 2, int(w2) ) if Settings.contains('splitter'): self.Splitter.restoreState( Settings.value('splitter') ) if Settings.contains('inssplitter'): self.InspectorSplit.restoreState( Settings.value('inssplitter') ) #---------------------------------------------------- # # Process command line arguments # if len(sys.argv) > 1: fname = sys.argv[1] if os.path.exists(fname): self.CmpTable.load_file(fname) else: print('E: input file "' + fname + '"does not exist') self.show() #--------------------------------------------------------------------------- def closeEvent(self, event): Settings = QSettings('kicad-tools', 'Schematic Component Manager') Settings.setValue( 'geometry', self.saveGeometry() ) Settings.setValue( 'cmptable', [self.CmpTable.columnWidth(0), self.CmpTable.columnWidth(1)] ) Settings.setValue( 'selector', [self.Selector.columnWidth(0), self.Selector.columnWidth(1)] ) Settings.setValue( 'inspector', [self.Inspector.columnWidth(0), self.Inspector.columnWidth(1)] ) Settings.setValue( 'splitter', self.Splitter.saveState() ) Settings.setValue( 'inssplitter', self.InspectorSplit.saveState() ) QWidget.closeEvent(self, event) #--------------------------------------------------------------------------- def open_file(self): #filename = QFileDialog.getOpenFileName(self, 'Open schematic file', '/opt/cad/kicad', 'KiCad Schematic Files (*.sch)') dialog = QFileDialog(self) dialog.setFileMode(QFileDialog.ExistingFile) dialog.setNameFilter('KiCad Schematic Files (*.sch)') filenames = [] if dialog.exec_(): filenames = dialog.selectedFiles() if len(filenames) == 0: return CmpMgr.set_curr_file_path( filenames[0] ) self.CmpTable.load_file( filenames[0] ) #--------------------------------------------------------------------------- def save_file(self): self.FieldInspector.save_fields() self.Inspector.save_cmps() curr_file = CmpMgr.curr_file_path() message = 'Save File "' + curr_file + '"' print(message) self.statusBar().showMessage(message) CmpMgr.save_file(curr_file) #--------------------------------------------------------------------------- def save_file_as(self): self.Inspector.save_cmps() self.FieldInspector.save_fields() filenames = QFileDialog.getSaveFileName(self, 'Save File As...', '', 'KiCad Schematic Files (*.sch)') if filenames[0] == '': return print('Save File As "' + filenames[0] + '"') CmpMgr.save_file(filenames[0]) CmpMgr.set_curr_file_path( filenames[0] ) #--------------------------------------------------------------------------- def file_loaded_slot(self): text = CmpMgr.curr_file_path() self.set_title(text) #--------------------------------------------------------------------------- def data_changed_slot(self): text = CmpMgr.curr_file_path() + ' *' self.set_title(text) #--------------------------------------------------------------------------- def file_saved_slot(self): text = CmpMgr.curr_file_path() self.set_title(text) #--------------------------------------------------------------------------- def set_title(self, text = ''): text = ' - ' + text if len(text) > 0 else '' self.setWindowTitle(self.PROGRAM_NAME + ' v' + VERSION + text) #--------------------------------------------------------------------------- def set_status_text_slot(self, text): self.statusBar().showMessage(text) #--------------------------------------------------------------------------- def edit_settings(self): print('edit settings') SettingsDialog = TSettingsDialog(self) SettingsDialog.resize(400, 400) SettingsDialog.Tabs.setMinimumWidth(800) SettingsDialog.show() #--------------------------------------------------------------------------- def show_user_manual_slot(self): help = THelpForm(self, 'User\'s Manual', 'main.html') #--------------------------------------------------------------------------- def show_setting_dialog_help_slot(self): help = THelpForm(self, 'Settings Dialog', 'settings.html') #--------------------------------------------------------------------------- def show_hotkeys_help_slot(self): help = THelpForm(self, 'Hotkeys', 'hotkeys.html')
class Runner(): _window = None _application = None def __init__(self): self._application = QApplication(sys.argv) self._window = None # Main Window Initialized.. try: self._window = yali.gui.YaliWindow.Widget() except yali.Error, msg: ctx.logger.debug(msg) sys.exit(1) self._translator = QTranslator() self._translator.load( "qt_" + QLocale.system().name(), QLibraryInfo.location(QLibraryInfo.TranslationsPath)) ctx.mainScreen = self._window screens = self._get_screens(ctx.flags.install_type) self._set_steps(screens) # These shorcuts for developers :) prevScreenShortCut = QShortcut(QKeySequence(Qt.SHIFT + Qt.Key_F1), self._window) nextScreenShortCut = QShortcut(QKeySequence(Qt.SHIFT + Qt.Key_F2), self._window) prevScreenShortCut.activated.connect(self._window.slotBack) nextScreenShortCut.activated.connect(self._window.slotNext) # VBox utils ctx.logger.debug("Starting VirtualBox tools..") #FIXME:sh /etc/X11/Xsession.d/98-vboxclient.sh yali.util.run_batch("VBoxClient", ["--autoresize"]) yali.util.run_batch("VBoxClient", ["--clipboard"]) # Cp Reboot, ShutDown yali.util.run_batch("cp", ["/sbin/reboot", "/tmp/reboot"]) yali.util.run_batch("cp", ["/sbin/shutdown", "/tmp/shutdown"]) # base connections self._application.lastWindowClosed.connect(self._application.quit) self._window.signalProcessEvents.connect( self._application.processEvents) #hata olabilir self._application.desktop().resized[int].connect( self._reinit_screen) #hata olabilir # Font Resize fontMinusShortCut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Minus), self._window) fontPlusShortCut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Plus), self._window) fontMinusShortCut.activated.connect(self._window.setFontMinus) fontPlusShortCut.activated.connect(self._window.setFontPlus)
def setupUi(self, *args): # {{{ self.resize(990, 670) self.download_shortcut = QShortcut(self) self.download_shortcut.setKey(QKeySequence('Ctrl+D', QKeySequence.PortableText)) p = self.parent() if hasattr(p, 'keyboard'): kname = u'Interface Action: Edit Metadata (Edit Metadata) : menu action : download' sc = p.keyboard.keys_map.get(kname, None) if sc: self.download_shortcut.setKey(sc[0]) self.swap_title_author_shortcut = s = QShortcut(self) s.setKey(QKeySequence('Alt+Down', QKeySequence.PortableText)) self.button_box = bb = QDialogButtonBox(self) self.button_box.accepted.connect(self.accept) self.button_box.rejected.connect(self.reject) self.next_button = QPushButton(QIcon(I('forward.png')), _('Next'), self) self.next_button.setShortcut(QKeySequence('Alt+Right')) self.next_button.clicked.connect(self.next_clicked) self.prev_button = QPushButton(QIcon(I('back.png')), _('Previous'), self) self.prev_button.setShortcut(QKeySequence('Alt+Left')) self.button_box.addButton(self.prev_button, bb.ActionRole) self.button_box.addButton(self.next_button, bb.ActionRole) self.prev_button.clicked.connect(self.prev_clicked) bb.setStandardButtons(bb.Ok|bb.Cancel) bb.button(bb.Ok).setDefault(True) self.scroll_area = QScrollArea(self) self.scroll_area.setFrameShape(QScrollArea.NoFrame) self.scroll_area.setWidgetResizable(True) self.central_widget = QTabWidget(self) self.scroll_area.setWidget(self.central_widget) self.l = QVBoxLayout(self) self.setLayout(self.l) self.l.addWidget(self.scroll_area) ll = self.button_box_layout = QHBoxLayout() self.l.addLayout(ll) ll.addSpacing(10) ll.addWidget(self.button_box) self.setWindowIcon(QIcon(I('edit_input.png'))) self.setWindowTitle(BASE_TITLE) self.create_basic_metadata_widgets() if len(self.db.custom_column_label_map): self.create_custom_metadata_widgets() self.do_layout() geom = gprefs.get('metasingle_window_geometry3', None) if geom is not None: self.restoreGeometry(bytes(geom))
def setupUi(self, *args): # {{{ self.resize(990, 670) self.download_shortcut = QShortcut(self) self.download_shortcut.setKey(QKeySequence('Ctrl+D', QKeySequence.PortableText)) p = self.parent() if hasattr(p, 'keyboard'): kname = u'Interface Action: Edit Metadata (Edit Metadata) : menu action : download' sc = p.keyboard.keys_map.get(kname, None) if sc: self.download_shortcut.setKey(sc[0]) self.swap_title_author_shortcut = s = QShortcut(self) s.setKey(QKeySequence('Alt+Down', QKeySequence.PortableText)) self.button_box = bb = QDialogButtonBox(self) self.button_box.accepted.connect(self.accept) self.button_box.rejected.connect(self.reject) self.next_button = QPushButton(QIcon(I('forward.png')), _('Next'), self) self.next_button.setShortcut(QKeySequence('Alt+Right')) self.next_button.clicked.connect(self.next_clicked) self.prev_button = QPushButton(QIcon(I('back.png')), _('Previous'), self) self.prev_button.setShortcut(QKeySequence('Alt+Left')) self.button_box.addButton(self.prev_button, bb.ActionRole) self.button_box.addButton(self.next_button, bb.ActionRole) self.prev_button.clicked.connect(self.prev_clicked) bb.setStandardButtons(bb.Ok|bb.Cancel) bb.button(bb.Ok).setDefault(True) self.scroll_area = QScrollArea(self) self.scroll_area.setFrameShape(QScrollArea.NoFrame) self.scroll_area.setWidgetResizable(True) self.central_widget = QTabWidget(self) self.scroll_area.setWidget(self.central_widget) self.l = QVBoxLayout(self) self.setLayout(self.l) self.l.addWidget(self.scroll_area) ll = self.button_box_layout = QHBoxLayout() self.l.addLayout(ll) ll.addSpacing(10) ll.addWidget(self.button_box) self.setWindowIcon(QIcon(I('edit_input.png'))) self.setWindowTitle(BASE_TITLE) self.create_basic_metadata_widgets() if len(self.db.custom_column_label_map): self.create_custom_metadata_widgets() self.comments_edit_state_at_apply = {self.comments:None} self.do_layout() geom = gprefs.get('metasingle_window_geometry3', None) if geom is not None: self.restoreGeometry(bytes(geom))
def initUI(self): #self._layout = QHBoxLayout(self) self._widget = QWidget(self) #self._layout.addWidget(self._widget) self._menuBar = QMenuBar(self._widget) exitAct = QAction(QIcon('exit.png'), '&Exit', self) exitAct.setShortcut('Ctrl+Q') exitAct.setStatusTip('Exit application') exitAct.triggered.connect(qApp.quit) self.statusBar() #menubar = self.menuBar() menubar = self._menuBar fileMenu = menubar.addMenu('&File') fileMenu.addAction(exitAct) act = self._menuBar.addAction('test') act.setShortcut(QKeySequence('Ctrl+M')) def testCb(): print('hello') act.triggered.connect(testCb) act.setShortcutContext(Qt.WidgetShortcut) print('_menuBar parent', self._menuBar.parent()) print('act parent', act.parent()) print('_widget parent', self._widget.parent()) #act.setParent(self) #print(act.parent()) self.addAction(act) # This is important for Qt.WidgetShortcut # the Example is focused, and it's menu's act can trigger # Qt.WidgetShortcut context action #self.setFocus() #act.parent().setFocus() print('focusWidget', QApplication.focusWidget()) def cb(): tt = QApplication.focusWidget() print(tt) from PyQt5.Qt import QShortcut self.short0 = QShortcut(QKeySequence('Ctrl+N'), self) self.short0.activated.connect(cb) self.setGeometry(300, 300, 300, 200) self.setWindowTitle('Simple menu') self.show()
def __init__(self): super().__init__() self.initUI() self.installEventFilter(self.EventFilter(self)) self.setFocusPolicy(Qt.WheelFocus) self.setTabOrder(self.CmpTable, self.Inspector ) self.setTabOrder(self.Inspector, self.Selector) self.setTabOrder(self.Selector, self.FieldInspector) #self.setTabOrder(self.FieldInspector, self.CmpTable) #---------------------------------------------------- # # Application Hotkeys # self.shortcutLeft = QShortcut(QKeySequence(Qt.ALT + Qt.Key_Left), self) self.shortcutRight = QShortcut(QKeySequence(Qt.ALT + Qt.Key_Right), self) self.shortcutLeft.setContext(Qt.ApplicationShortcut) self.shortcutRight.setContext(Qt.ApplicationShortcut) self.shortcutLeft.activated.connect(self.scroll_left) self.shortcutRight.activated.connect(self.scroll_right)
def __init__(self, title, widget=None, closeButton=True, keySequence=None, isDialog=False, icon=None): QDialog.__init__(self, ctx.mainScreen) self.setObjectName("dialog") self.isDialog = isDialog self.layout = QVBoxLayout() self.setLayout(self.layout) self.wlayout = QHBoxLayout() if icon: self.setStyleSheet( """QDialog QLabel{ margin-left:16px;margin-right:10px} QDialog#dialog {background-image:url(':/images/%s.png'); background-repeat:no-repeat; background-position: top left; padding-left:500px;} """ % icon) self.windowTitle = windowTitle(self, closeButton) self.setTitle(title) #self.layout.setMargin(0) self.layout.addWidget(self.windowTitle) if widget: self.addWidget(widget) try: widget.finished[int].connect(self.reject) except: pass finally: try: widget.resizeDialog[int, int].connect(self.resize) except: pass if closeButton: self.windowTitle.pushButton.clicked.connect(self.reject) if keySequence: shortCut = QShortcut(keySequence, self) shortCut.activated.connect(self.reject) QMetaObject.connectSlotsByName(self) self.resize(10, 10)
def __init__(self, view, parent=None): ''' @param: view WebView @param: parent QWidget ''' super().__init__(parent) self._ui = uic.loadUi('mc/webtab/SearchToolBar.ui', self) self._view = view # WebView self._findFlags = QWebEnginePage.FindCaseSensitively # QWebEnginePage.FindFlags self._searchRequests = 0 self._ui.closeButton.clicked.connect(self.close) self._ui.lineEdit.textEdited.connect(self.findNext) self._ui.next.clicked.connect(self.findNext) self._ui.previous.clicked.connect(self.findPrevious) self._ui.caseSensitive.clicked.connect(self.caseSensitivityChanged) findNextAction = QShortcut(QKeySequence('F3'), self) findNextAction.activated.connect(self.findNext) findPreviousAction = QShortcut(QKeySequence('Shift+F3'), self) findPreviousAction.activated.connect(self.findPrevious) parent.installEventFilter(self)
def __init__(self): super().__init__() self.initUI() self.installEventFilter(self.EventFilter(self)) self.setFocusPolicy(Qt.WheelFocus) self.setTabOrder(self.CmpTable, self.Inspector) self.setTabOrder(self.Inspector, self.Selector) self.setTabOrder(self.Selector, self.FieldInspector) #self.setTabOrder(self.FieldInspector, self.CmpTable) #---------------------------------------------------- # # Application Hotkeys # self.shortcutLeft = QShortcut(QKeySequence(Qt.ALT + Qt.Key_Left), self) self.shortcutRight = QShortcut(QKeySequence(Qt.ALT + Qt.Key_Right), self) self.shortcutLeft.setContext(Qt.ApplicationShortcut) self.shortcutRight.setContext(Qt.ApplicationShortcut) self.shortcutLeft.activated.connect(self.scroll_left) self.shortcutRight.activated.connect(self.scroll_right)
def __init__(self, parent, title, path): #super().__init__(parent, Qt.WA_DeleteOnClose ) super().__init__(parent, Qt.Window) self.text_browser = QTextBrowser(self) #self.text_browser = QWebEngineView(self) self.back_button = QPushButton('Back', self) self.forward_button = QPushButton('Forward', self) self.close_button = QPushButton('Close', self) self.layout = QVBoxLayout(self) self.btn_widget = QWidget(self) self.btn_layout = QHBoxLayout(self.btn_widget) self.btn_layout.addWidget(self.back_button) self.btn_layout.addWidget(self.forward_button) self.btn_layout.addStretch(1) self.btn_layout.addWidget(self.close_button) self.shortcutEscape = QShortcut(QKeySequence(Qt.Key_Escape), self) self.shortcutEscape.activated.connect(self.close) self.layout.addWidget(self.btn_widget) self.layout.addWidget(self.text_browser) self.back_button.clicked.connect(self.text_browser.backward) self.forward_button.clicked.connect(self.text_browser.forward) self.close_button.clicked.connect(self.close) self.text_browser.setSearchPaths([os.path.join(resources_path, 'doc')]) self.text_browser.setSource(QUrl(path)) # f = QFont() # f.setPointSize(14) # self.text_browser.setFont(f) Settings = QSettings('kicad-tools', 'Schematic Component Manager') if Settings.contains('help-window'): self.restoreGeometry(Settings.value('help-window')) #pos_x, pos_y, width, height = Settings.value('help-window') else: pos_x, pos_y, width, height = 0, 0, 640, 640 self.setGeometry(pos_x, pos_y, width, height) self.window().setWindowTitle(title) self.show()
def _setupMenu(self): if const.OS_MACOS: macMainMenu = None if not macMainMenu: macMainMenu = MainMenu(self, 0) macMainMenu.initMenuBar(QMenuBar(0)) gVar.app.activeWindowChanged.connect(macMainMenu.setWindow) else: macMainMenu.setWindow(self) else: self.setMenuBar(MenuBar(self)) self._mainMenu = MainMenu(self, self) self._mainMenu.initMenuBar(self.menuBar()) self._mainMenu.initSuperMenu(self._superMenu) # Setup other shortcuts reloadBypassCacheAction = QShortcut(QKeySequence('Ctrl+F5'), self) reloadBypassCacheAction2 = QShortcut(QKeySequence('Ctrl+Shift+R'), self) reloadBypassCacheAction.activated.connect(self.reloadBypassCache) reloadBypassCacheAction2.activated.connect(self.reloadBypassCache) closeTabAction = QShortcut(QKeySequence('Ctrl+W'), self) closeTabAction2 = QShortcut(QKeySequence('Ctrl+F4'), self) closeTabAction.activated.connect(self._closeTab) closeTabAction2.activated.connect(self._closeTab) reloadAction = QShortcut(QKeySequence('Ctrl+R'), self) reloadAction.activated.connect(self.reload) openLocationAction = QShortcut(QKeySequence('Alt+D'), self) openLocationAction.activated.connect(self._openLocation) inspectorAction = QShortcut(QKeySequence('F12'), self) inspectorAction.activated.connect(self.toggleWebInspector) restoreClosedWindow = QShortcut(QKeySequence('Ctrl+Shift+N'), self) restoreClosedWindow.activated.connect( gVar.app.closedWindowsManager().restoreClosedWindow)
def __init__(self, parent): #--------------------------------------------------- super().__init__(parent) self.Parent = parent #--------------------------------------------------- Settings = QSettings('kicad-tools', 'Schematic Component Manager') if Settings.contains('component-view'): CmpViewDict = Settings.value('component-view') else: CmpViewDict = { 'C': '$Value, $Footprint', 'D': '$LibRef', 'R': '$Value, $Footprint' } if Settings.contains('component-ignore'): IgnoreCmpRefsList = Settings.value('component-ignore') else: IgnoreCmpRefsList = [] #--------------------------------------------------- self.CmpViewTable = self.TCmpViewTable(self, CmpViewDict) self.IgnoreCmpList = self.TIgnoreCmpList(self, IgnoreCmpRefsList) #--------------------------------------------------- self.Tabs = QTabWidget(self) self.Tabs.addTab(self.CmpViewTable, 'Component View') self.Tabs.addTab(self.IgnoreCmpList, 'Ignore Component List') #--------------------------------------------------- self.ButtonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.ButtonBox.accepted.connect(self.save_settings) self.ButtonBox.rejected.connect(self.cancel) #--------------------------------------------------- self.Layout = QVBoxLayout(self) self.Layout.addWidget(self.Tabs) self.Layout.addWidget(self.ButtonBox) #--------------------------------------------------- self.setWindowTitle('Settings') self.setModal(True) #--------------------------------------------------- self.shortcutHelp = QShortcut(QKeySequence(Qt.Key_F1), self) self.shortcutHelp.activated.connect(self.show_help)
def __init__(self, parent=None): super().__init__(parent) self.ui = None # Ui::DownloadManager self._timer = QBasicTimer() self._lastDownloadPath = '' self._downloadPath = '' self._useNativeDialog = False self._isClosing = False self._closeOnFinish = False self._activeDownloadsCount = 0 self._useExternalManager = False self._externalExecutable = '' self._externalArguments = '' self._lastDownloadOption = self.NoOption # DownloadOption self._taskbarButton = None # QPointer<QWinTaskbarButton> self._ui = uic.loadUi('mc/downloads/DownloadManager.ui', self) self.setWindowFlags(self.windowFlags() ^ Qt.WindowMaximizeButtonHint) if const.OS_WIN: if QtWin.isCompositionEnabled(): # TODO: ? QtWin.extendFrameIntoClientArea(self, -1, -1, -1, -1) self._ui.clearButton.setIcon(QIcon.fromTheme('edit-clear')) gVar.appTools.centerWidgetOnScreen(self) self._ui.clearButton.clicked.connect(self._clearList) clearShortcut = QShortcut(QKeySequence('CTRL+L'), self) clearShortcut.activated.connect(self._clearList) self.loadSettings() gVar.appTools.setWmClass('Download Manager', self)
class MetadataSingleDialogBase(ResizableDialog): view_format = pyqtSignal(object, object) cc_two_column = tweaks['metadata_single_use_2_cols_for_custom_fields'] one_line_comments_toolbar = False use_toolbutton_for_config_metadata = True def __init__(self, db, parent=None): self.db = db self.changed = set() self.books_to_refresh = set() self.rows_to_refresh = set() self.metadata_before_fetch = None ResizableDialog.__init__(self, parent) def setupUi(self, *args): # {{{ self.resize(990, 670) self.download_shortcut = QShortcut(self) self.download_shortcut.setKey( QKeySequence('Ctrl+D', QKeySequence.PortableText)) p = self.parent() if hasattr(p, 'keyboard'): kname = u'Interface Action: Edit Metadata (Edit Metadata) : menu action : download' sc = p.keyboard.keys_map.get(kname, None) if sc: self.download_shortcut.setKey(sc[0]) self.button_box = bb = QDialogButtonBox(self) self.button_box.accepted.connect(self.accept) self.button_box.rejected.connect(self.reject) self.next_button = QPushButton(QIcon(I('forward.png')), _('Next'), self) self.next_button.setShortcut(QKeySequence('Alt+Right')) self.next_button.clicked.connect(self.next_clicked) self.prev_button = QPushButton(QIcon(I('back.png')), _('Previous'), self) self.prev_button.setShortcut(QKeySequence('Alt+Left')) self.button_box.addButton(self.prev_button, bb.ActionRole) self.button_box.addButton(self.next_button, bb.ActionRole) self.prev_button.clicked.connect(self.prev_clicked) bb.setStandardButtons(bb.Ok | bb.Cancel) bb.button(bb.Ok).setDefault(True) self.scroll_area = QScrollArea(self) self.scroll_area.setFrameShape(QScrollArea.NoFrame) self.scroll_area.setWidgetResizable(True) self.central_widget = QTabWidget(self) self.scroll_area.setWidget(self.central_widget) self.l = QVBoxLayout(self) self.setLayout(self.l) self.l.addWidget(self.scroll_area) ll = self.button_box_layout = QHBoxLayout() self.l.addLayout(ll) ll.addSpacing(10) ll.addWidget(self.button_box) self.setWindowIcon(QIcon(I('edit_input.png'))) self.setWindowTitle(BASE_TITLE) self.create_basic_metadata_widgets() if len(self.db.custom_column_label_map): self.create_custom_metadata_widgets() self.do_layout() geom = gprefs.get('metasingle_window_geometry3', None) if geom is not None: self.restoreGeometry(bytes(geom)) # }}} def create_basic_metadata_widgets(self): # {{{ self.basic_metadata_widgets = [] self.languages = LanguagesEdit(self) self.basic_metadata_widgets.append(self.languages) self.title = TitleEdit(self) self.title.textChanged.connect(self.update_window_title) self.deduce_title_sort_button = QToolButton(self) self.deduce_title_sort_button.setToolTip( _('Automatically create the title sort entry based on the current ' 'title entry.\nUsing this button to create title sort will ' 'change title sort from red to green.')) self.deduce_title_sort_button.setWhatsThis( self.deduce_title_sort_button.toolTip()) self.title_sort = TitleSortEdit(self, self.title, self.deduce_title_sort_button, self.languages) self.basic_metadata_widgets.extend([self.title, self.title_sort]) self.deduce_author_sort_button = b = QToolButton(self) b.setToolTip('<p>' + _( 'Automatically create the author sort entry based on the current ' 'author entry. Using this button to create author sort will ' 'change author sort from red to green. There is a menu of ' 'functions available under this button. Click and hold ' 'on the button to see it.') + '</p>') if isosx: # Workaround for https://bugreports.qt-project.org/browse/QTBUG-41017 class Menu(QMenu): def mouseReleaseEvent(self, ev): ac = self.actionAt(ev.pos()) if ac is not None: ac.trigger() return QMenu.mouseReleaseEvent(self, ev) b.m = m = Menu() else: b.m = m = QMenu() ac = m.addAction(QIcon(I('forward.png')), _('Set author sort from author')) ac2 = m.addAction(QIcon(I('back.png')), _('Set author from author sort')) ac3 = m.addAction(QIcon(I('user_profile.png')), _('Manage authors')) ac4 = m.addAction(QIcon(I('next.png')), _('Copy author to author sort')) ac5 = m.addAction(QIcon(I('previous.png')), _('Copy author sort to author')) b.setMenu(m) self.authors = AuthorsEdit(self, ac3) self.author_sort = AuthorSortEdit(self, self.authors, b, self.db, ac, ac2, ac4, ac5) self.basic_metadata_widgets.extend([self.authors, self.author_sort]) self.swap_title_author_button = QToolButton(self) self.swap_title_author_button.setIcon(QIcon(I('swap.png'))) self.swap_title_author_button.setToolTip( _('Swap the author and title')) self.swap_title_author_button.clicked.connect(self.swap_title_author) self.manage_authors_button = QToolButton(self) self.manage_authors_button.setIcon(QIcon(I('user_profile.png'))) self.manage_authors_button.setToolTip( '<p>' + _('Manage authors. Use to rename authors and correct ' 'individual author\'s sort values') + '</p>') self.manage_authors_button.clicked.connect(self.authors.manage_authors) self.series = SeriesEdit(self) self.clear_series_button = QToolButton(self) self.clear_series_button.setToolTip(_('Clear series')) self.clear_series_button.clicked.connect(self.series.clear) self.series_index = SeriesIndexEdit(self, self.series) self.basic_metadata_widgets.extend([self.series, self.series_index]) self.formats_manager = FormatsManager(self, self.copy_fmt) # We want formats changes to be committed before title/author, as # otherwise we could have data loss if the title/author changed and the # user was trying to add an extra file from the old books directory. self.basic_metadata_widgets.insert(0, self.formats_manager) self.formats_manager.metadata_from_format_button.clicked.connect( self.metadata_from_format) self.formats_manager.cover_from_format_button.clicked.connect( self.cover_from_format) self.cover = Cover(self) self.cover.download_cover.connect(self.download_cover) self.basic_metadata_widgets.append(self.cover) self.comments = CommentsEdit(self, self.one_line_comments_toolbar) self.basic_metadata_widgets.append(self.comments) self.rating = RatingEdit(self) self.clear_ratings_button = QToolButton(self) self.clear_ratings_button.setToolTip(_('Clear rating')) self.clear_ratings_button.setIcon(QIcon(I('trash.png'))) self.clear_ratings_button.clicked.connect(self.rating.zero) self.basic_metadata_widgets.append(self.rating) self.tags = TagsEdit(self) self.tags_editor_button = QToolButton(self) self.tags_editor_button.setToolTip(_('Open Tag Editor')) self.tags_editor_button.setIcon(QIcon(I('chapters.png'))) self.tags_editor_button.clicked.connect(self.tags_editor) self.clear_tags_button = QToolButton(self) self.clear_tags_button.setToolTip(_('Clear all tags')) self.clear_tags_button.setIcon(QIcon(I('trash.png'))) self.clear_tags_button.clicked.connect(self.tags.clear) self.basic_metadata_widgets.append(self.tags) self.identifiers = IdentifiersEdit(self) self.basic_metadata_widgets.append(self.identifiers) self.clear_identifiers_button = QToolButton(self) self.clear_identifiers_button.setIcon(QIcon(I('trash.png'))) self.clear_identifiers_button.setToolTip(_('Clear Ids')) self.clear_identifiers_button.clicked.connect(self.identifiers.clear) self.paste_isbn_button = QToolButton(self) self.paste_isbn_button.setToolTip( '<p>' + _('Paste the contents of the clipboard into the ' 'identifiers box prefixed with isbn:') + '</p>') self.paste_isbn_button.setIcon(QIcon(I('edit-paste.png'))) self.paste_isbn_button.clicked.connect(self.identifiers.paste_isbn) self.publisher = PublisherEdit(self) self.basic_metadata_widgets.append(self.publisher) self.timestamp = DateEdit(self) self.pubdate = PubdateEdit(self) self.basic_metadata_widgets.extend([self.timestamp, self.pubdate]) self.fetch_metadata_button = b = QToolButton(self) b.setText(_('&Download metadata')), b.setPopupMode(b.DelayedPopup) b.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)) self.fetch_metadata_button.clicked.connect(self.fetch_metadata) self.fetch_metadata_menu = m = QMenu(self.fetch_metadata_button) m.addAction(QIcon(I('edit-undo.png')), _('Undo last metadata download'), self.undo_fetch_metadata) self.fetch_metadata_button.setMenu(m) self.download_shortcut.activated.connect( self.fetch_metadata_button.click) font = self.fmb_font = QFont() font.setBold(True) self.fetch_metadata_button.setFont(font) if self.use_toolbutton_for_config_metadata: self.config_metadata_button = QToolButton(self) self.config_metadata_button.setIcon(QIcon(I('config.png'))) else: self.config_metadata_button = QPushButton(self) self.config_metadata_button.setText( _('Configure download metadata')) self.config_metadata_button.setIcon(QIcon(I('config.png'))) self.config_metadata_button.clicked.connect(self.configure_metadata) self.config_metadata_button.setToolTip( _('Change how calibre downloads metadata')) # }}} def create_custom_metadata_widgets(self): # {{{ self.custom_metadata_widgets_parent = w = QWidget(self) layout = QGridLayout() w.setLayout(layout) self.custom_metadata_widgets, self.__cc_spacers = \ populate_metadata_page(layout, self.db, None, parent=w, bulk=False, two_column=self.cc_two_column) self.__custom_col_layouts = [layout] # }}} def set_custom_metadata_tab_order(self, before=None, after=None): # {{{ sto = QWidget.setTabOrder if getattr(self, 'custom_metadata_widgets', []): ans = self.custom_metadata_widgets for i in range(len(ans) - 1): if before is not None and i == 0: pass if len(ans[i + 1].widgets) == 2: sto(ans[i].widgets[-1], ans[i + 1].widgets[1]) else: sto(ans[i].widgets[-1], ans[i + 1].widgets[0]) for c in range(2, len(ans[i].widgets), 2): sto(ans[i].widgets[c - 1], ans[i].widgets[c + 1]) if after is not None: pass # }}} def do_view_format(self, path, fmt): if path: self.view_format.emit(None, path) else: self.view_format.emit(self.book_id, fmt) def copy_fmt(self, fmt, f): self.db.copy_format_to(self.book_id, fmt, f, index_is_id=True) def do_layout(self): raise NotImplementedError() def __call__(self, id_): self.book_id = id_ self.books_to_refresh = set([]) self.metadata_before_fetch = None for widget in self.basic_metadata_widgets: widget.initialize(self.db, id_) for widget in getattr(self, 'custom_metadata_widgets', []): widget.initialize(id_) if callable(self.set_current_callback): self.set_current_callback(id_) # Commented out as it doesn't play nice with Next, Prev buttons # self.fetch_metadata_button.setFocus(Qt.OtherFocusReason) # Miscellaneous interaction methods {{{ def update_window_title(self, *args): title = self.title.current_val if len(title) > 50: title = title[:50] + u'\u2026' self.setWindowTitle( BASE_TITLE + ' - ' + title + ' - ' + _(' [%(num)d of %(tot)d]') % dict(num=self.current_row + 1, tot=len(self.row_list))) def swap_title_author(self, *args): title = self.title.current_val self.title.current_val = authors_to_string(self.authors.current_val) self.authors.current_val = string_to_authors(title) self.title_sort.auto_generate() self.author_sort.auto_generate() def tags_editor(self, *args): self.tags.edit(self.db, self.book_id) def metadata_from_format(self, *args): mi, ext = self.formats_manager.get_selected_format_metadata( self.db, self.book_id) if mi is not None: self.update_from_mi(mi) def get_pdf_cover(self): pdfpath = self.formats_manager.get_format_path(self.db, self.book_id, 'pdf') from calibre.gui2.metadata.pdf_covers import PDFCovers d = PDFCovers(pdfpath, parent=self) if d.exec_() == d.Accepted: cpath = d.cover_path if cpath: with open(cpath, 'rb') as f: self.update_cover(f.read(), 'PDF') d.cleanup() def cover_from_format(self, *args): ext = self.formats_manager.get_selected_format() if ext is None: return if ext == 'pdf': return self.get_pdf_cover() try: mi, ext = self.formats_manager.get_selected_format_metadata( self.db, self.book_id) except (IOError, OSError) as err: if getattr(err, 'errno', None) == errno.EACCES: # Permission denied import traceback fname = err.filename if err.filename else 'file' error_dialog(self, _('Permission denied'), _('Could not open %s. Is it being used by another' ' program?') % fname, det_msg=traceback.format_exc(), show=True) return raise if mi is None: return cdata = None if mi.cover and os.access(mi.cover, os.R_OK): cdata = open(mi.cover).read() elif mi.cover_data[1] is not None: cdata = mi.cover_data[1] if cdata is None: error_dialog(self, _('Could not read cover'), _('Could not read cover from %s format') % ext).exec_() return self.update_cover(cdata, ext) def update_cover(self, cdata, fmt): orig = self.cover.current_val self.cover.current_val = cdata if self.cover.current_val is None: self.cover.current_val = orig return error_dialog(self, _('Could not read cover'), _('The cover in the %s format is invalid') % fmt, show=True) return def update_from_mi(self, mi, update_sorts=True, merge_tags=True, merge_comments=False): fw = self.focusWidget() if not mi.is_null('title'): self.title.set_value(mi.title) if update_sorts: self.title_sort.auto_generate() if not mi.is_null('authors'): self.authors.set_value(mi.authors) if not mi.is_null('author_sort'): self.author_sort.set_value(mi.author_sort) elif update_sorts: self.author_sort.auto_generate() if not mi.is_null('rating'): try: self.rating.set_value(mi.rating) except: pass if not mi.is_null('publisher'): self.publisher.set_value(mi.publisher) if not mi.is_null('tags'): old_tags = self.tags.current_val tags = mi.tags if mi.tags else [] if old_tags and merge_tags: ltags, lotags = {t.lower() for t in tags}, {t.lower() for t in old_tags} tags = [t for t in tags if t.lower() in ltags - lotags ] + old_tags self.tags.set_value(tags) if not mi.is_null('identifiers'): current = self.identifiers.current_val current.update(mi.identifiers) self.identifiers.set_value(current) if not mi.is_null('pubdate'): self.pubdate.set_value(mi.pubdate) if not mi.is_null('series') and mi.series.strip(): self.series.set_value(mi.series) if mi.series_index is not None: self.series_index.reset_original() self.series_index.set_value(float(mi.series_index)) if not mi.is_null('languages'): langs = [canonicalize_lang(x) for x in mi.languages] langs = [x for x in langs if x is not None] if langs: self.languages.set_value(langs) if mi.comments and mi.comments.strip(): val = mi.comments if val and merge_comments: cval = self.comments.current_val if cval: val = merge_two_comments(cval, val) self.comments.set_value(val) if fw is not None: fw.setFocus(Qt.OtherFocusReason) def fetch_metadata(self, *args): d = FullFetch(self.cover.pixmap(), self) ret = d.start(title=self.title.current_val, authors=self.authors.current_val, identifiers=self.identifiers.current_val) if ret == d.Accepted: self.metadata_before_fetch = { f: getattr(self, f).current_val for f in fetched_fields } from calibre.ebooks.metadata.sources.prefs import msprefs mi = d.book dummy = Metadata(_('Unknown')) for f in msprefs['ignore_fields']: if ':' not in f: setattr(mi, f, getattr(dummy, f)) if mi is not None: pd = mi.pubdate if pd is not None: # Put the downloaded published date into the local timezone # as we discard time info and the date is timezone # invariant. This prevents the as_local_timezone() call in # update_from_mi from changing the pubdate mi.pubdate = datetime(pd.year, pd.month, pd.day, tzinfo=local_tz) self.update_from_mi(mi, merge_comments=msprefs['append_comments']) if d.cover_pixmap is not None: self.metadata_before_fetch['cover'] = self.cover.current_val self.cover.current_val = pixmap_to_data(d.cover_pixmap) def undo_fetch_metadata(self): if self.metadata_before_fetch is None: return error_dialog(self, _('No downloaded metadata'), _('There is no downloaded metadata to undo'), show=True) for field, val in self.metadata_before_fetch.iteritems(): getattr(self, field).current_val = val self.metadata_before_fetch = None def configure_metadata(self): from calibre.gui2.preferences import show_config_widget gui = self.parent() show_config_widget('Sharing', 'Metadata download', parent=self, gui=gui, never_shutdown=True) def download_cover(self, *args): from calibre.gui2.metadata.single_download import CoverFetch d = CoverFetch(self.cover.pixmap(), self) ret = d.start(self.title.current_val, self.authors.current_val, self.identifiers.current_val) if ret == d.Accepted: if d.cover_pixmap is not None: self.cover.current_val = pixmap_to_data(d.cover_pixmap) # }}} def to_book_metadata(self): mi = Metadata(_('Unknown')) if self.db is None: return mi mi.set_all_user_metadata( self.db.field_metadata.custom_field_metadata()) for widget in self.basic_metadata_widgets: widget.apply_to_metadata(mi) for widget in getattr(self, 'custom_metadata_widgets', []): widget.apply_to_metadata(mi) return mi def apply_changes(self): self.changed.add(self.book_id) if self.db is None: # break_cycles has already been called, don't know why this should # happen but a user reported it return True for widget in self.basic_metadata_widgets: try: if hasattr(widget, 'validate_for_commit'): title, msg, det_msg = widget.validate_for_commit() if title is not None: error_dialog(self, title, msg, det_msg=det_msg, show=True) return False widget.commit(self.db, self.book_id) self.books_to_refresh |= getattr(widget, 'books_to_refresh', set()) except (IOError, OSError) as err: if getattr(err, 'errno', None) == errno.EACCES: # Permission denied import traceback fname = getattr(err, 'filename', None) p = 'Locked file: %s\n\n' % fname if fname else '' error_dialog( self, _('Permission denied'), _('Could not change the on disk location of this' ' book. Is it open in another program?'), det_msg=p + traceback.format_exc(), show=True) return False raise for widget in getattr(self, 'custom_metadata_widgets', []): self.books_to_refresh |= widget.commit(self.book_id) self.db.commit() rows = self.db.refresh_ids(list(self.books_to_refresh)) if rows: self.rows_to_refresh |= set(rows) return True def accept(self): self.save_state() if not self.apply_changes(): return ResizableDialog.accept(self) def reject(self): self.save_state() ResizableDialog.reject(self) def save_state(self): try: gprefs['metasingle_window_geometry3'] = bytearray( self.saveGeometry()) except: # Weird failure, see https://bugs.launchpad.net/bugs/995271 import traceback traceback.print_exc() # Dialog use methods {{{ def start(self, row_list, current_row, view_slot=None, set_current_callback=None): self.row_list = row_list self.current_row = current_row if view_slot is not None: self.view_format.connect(view_slot) self.set_current_callback = set_current_callback self.do_one(apply_changes=False) ret = self.exec_() self.break_cycles() return ret def next_clicked(self): if not self.apply_changes(): return self.do_one(delta=1, apply_changes=False) def prev_clicked(self): if not self.apply_changes(): return self.do_one(delta=-1, apply_changes=False) def do_one(self, delta=0, apply_changes=True): if apply_changes: self.apply_changes() self.current_row += delta prev = next_ = None if self.current_row > 0: prev = self.db.title(self.row_list[self.current_row - 1]) if self.current_row < len(self.row_list) - 1: next_ = self.db.title(self.row_list[self.current_row + 1]) if next_ is not None: tip = (_('Save changes and edit the metadata of %s') + ' [Alt+Right]') % next_ self.next_button.setToolTip(tip) self.next_button.setEnabled(next_ is not None) if prev is not None: tip = (_('Save changes and edit the metadata of %s') + ' [Alt+Left]') % prev self.prev_button.setToolTip(tip) self.prev_button.setEnabled(prev is not None) self.button_box.button(self.button_box.Ok).setDefault(True) self.button_box.button(self.button_box.Ok).setFocus( Qt.OtherFocusReason) self(self.db.id(self.row_list[self.current_row])) def break_cycles(self): # Break any reference cycles that could prevent python # from garbage collecting this dialog self.set_current_callback = self.db = None self.metadata_before_fetch = None def disconnect(signal): try: signal.disconnect() except: pass # Fails if view format was never connected disconnect(self.view_format) for b in ('next_button', 'prev_button'): x = getattr(self, b, None) if x is not None: disconnect(x.clicked) for widget in self.basic_metadata_widgets: bc = getattr(widget, 'break_cycles', None) if bc is not None and callable(bc): bc() for widget in getattr(self, 'custom_metadata_widgets', []): widget.break_cycles()
def __init_shortcuts(self): self.__keyF11 = QShortcut(self) self.__keyF11.setKey(Qt.Key_F11) self.__keyF11.activated.connect(self.__action_full_screen) self.__keyCtrlQ = QShortcut(self) self.__keyCtrlQ.setKey(Qt.CTRL + Qt.Key_Q) self.__keyCtrlQ.activated.connect(self.__action_quit) self.__keyCtrlH = QShortcut(self) self.__keyCtrlH.setKey(Qt.CTRL + Qt.Key_H) self.__keyCtrlH.activated.connect(self.__action_home) self.__keyCtrlR = QShortcut(self) self.__keyCtrlR.setKey(Qt.CTRL + Qt.Key_R) self.__keyCtrlR.activated.connect(self.__action_reload) self.__keyF5 = QShortcut(self) self.__keyF5.setKey(Qt.Key_F5) self.__keyF5.activated.connect(self.__action_reload) self.__keyAltLeft = QShortcut(self) self.__keyAltLeft.setKey(Qt.ALT + Qt.Key_Left) self.__keyAltLeft.activated.connect(self.__action_back)
def __init__(self, parent=None): ''' @param parent QWidget ''' super().__init__(parent) self._ui = uic.loadUi('mc/cookies/CookieManager.ui', self) self._domainHash = {} # QHash<QString, QTreeWidgetItem> self._itemHash = {} # QHash<QTreeWidgetItem, QNetworkCookie> self.setAttribute(Qt.WA_DeleteOnClose) gVar.appTools.centerWidgetOnScreen(self) if self.isRightToLeft(): self._ui.cookieTree.headerItem().setTextAlignment( 0, Qt.AlignRight | Qt.AlignVCenter) self._ui.cookieTree.headerItem().setTextAlignment( 1, Qt.AlignRight | Qt.AlignVCenter) self._ui.cookieTree.setLayoutDirection(Qt.LeftToRight) self._ui.whiteList.setLayoutDirection(Qt.LeftToRight) self._ui.blackList.setLayoutDirection(Qt.LeftToRight) # Stored Cookies self._ui.cookieTree.currentItemChanged.connect( self._currentItemChanged) self._ui.removeAll.clicked.connect(self._removeAll) self._ui.removeOne.clicked.connect(self._remove) self._ui.close.clicked.connect(lambda: self._close()) self._ui.close2.clicked.connect(lambda: self._close()) self._ui.close3.clicked.connect(lambda: self._close()) self._ui.search.textChanged.connect(self._filterString) # Cookie Filtering self._ui.whiteAdd.clicked.connect(self._addWhitelist) self._ui.whiteRemove.clicked.connect(self._removeWhitelist) self._ui.blackAdd.clicked.connect(self._addBlacklist) self._ui.blackRemove.clicked.connect(self._removeBlacklist) # Cookie Settings settings = Settings() settings.beginGroup('Cookie-Settings') self._ui.saveCookies.setChecked(settings.value('allCookies', True)) self._ui.filter3rdParty.setChecked( settings.value('filterThirdPartyCookies', False)) self._ui.filterTracking.setChecked( settings.value('filterTrackingCookie', False)) self._ui.deleteCookiesOnClose.setChecked( settings.value('deleteCookiesOnClose', False)) self._ui.whiteList.addItems(settings.value('whitelist', [])) self._ui.blackList.addItems(settings.value('blacklist', [])) settings.endGroup() if const.QTWEBENGINEWIDGETS_VERSION < const.QT_VERSION_CHECK(5, 11, 0): self._ui.filter3rdParty.hide() self._ui.search.setPlaceholderText(_('Search')) self._ui.cookieTree.setDefaultItemShowMode(TreeWidget.ItemsCollapsed) self._ui.cookieTree.sortItems(0, Qt.AscendingOrder) self._ui.cookieTree.header().setDefaultSectionSize(220) self._ui.cookieTree.setFocus() self._ui.whiteList.setSortingEnabled(True) self._ui.blackList.setSortingEnabled(True) self._removeShortcut = QShortcut(QKeySequence('Del'), self) self._removeShortcut.activated.connect(self._deletePressed) self._ui.search.textChanged.connect(self._filterString) cookieJar = gVar.app.cookieJar() cookieJar.cookieAdded.connect(self._addCookie) cookieJar.cookieRemoved.connect(self._removeCookie) # Load cookies for cookie in cookieJar.getAllCookies(): self._addCookie(cookie) gVar.appTools.setWmClass('Cookies', self)
def set_shortcuts(lista, context=Qt.WidgetWithChildrenShortcut): """ Creates QShortcuts from a list of (key, owner, callback) 3-tuples """ for (shortcut, owner, callback) in lista: QShortcut(shortcut, owner, callback).setContext(context)
def initUI(self): #---------------------------------------------------- # # Main Window # work_zone = QWidget(self) Layout = QHBoxLayout(work_zone) self.setCentralWidget(work_zone) openAction = QAction(QIcon(os.path.join(resources_path, 'open24.png')), 'Open', self) openAction.setShortcut('Ctrl+O') openAction.setStatusTip('Open Schematic File') openAction.triggered.connect(self.open_file) saveAction = QAction(QIcon(os.path.join(resources_path, 'save24.png')), 'Save', self) saveAction.setShortcut('Ctrl+S') saveAction.setStatusTip('Save Schematic File') saveAction.triggered.connect(self.save_file) saveAsAction = QAction( QIcon(os.path.join(resources_path, 'save-as24.png')), 'Save As...', self) saveAsAction.setShortcut('Ctrl+Shift+S') saveAsAction.setStatusTip('Save Schematic File As...') saveAsAction.triggered.connect(self.save_file_as) exitAction = QAction(QIcon(os.path.join(resources_path, 'exit24.png')), 'Exit', self) exitAction.setShortcut('Ctrl+Q') exitAction.setStatusTip('Exit application') exitAction.triggered.connect(self.close) settingsAction = QAction( QIcon(os.path.join(resources_path, 'settings24.png')), 'Settings', self) settingsAction.setShortcut('Ctrl+Alt+S') settingsAction.setStatusTip('Edit settings') settingsAction.triggered.connect(self.edit_settings) helpAction = QAction( QIcon(os.path.join(resources_path, 'help_book24.png')), 'User\'s Manual', self) helpAction.setShortcut('F1') helpAction.setStatusTip('User\'s Manual') helpAction.triggered.connect(self.show_user_manual_slot) helpSDAction = QAction( QIcon(os.path.join(resources_path, 'gear24.png')), 'Settings Dialog', self) helpSDAction.setShortcut('Ctrl+F1') helpSDAction.setStatusTip('Settings Dialog Help') helpSDAction.triggered.connect(self.show_setting_dialog_help_slot) helpHKAction = QAction( QIcon(os.path.join(resources_path, 'rocket24.png')), 'Hotkeys', self) helpHKAction.setShortcut('Shift+F1') helpHKAction.setStatusTip('Hotkeys Help') helpHKAction.triggered.connect(self.show_hotkeys_help_slot) self.statusBar().showMessage('Ready') #-------------------------------------------- # # Main Menu # menubar = self.menuBar() fileMenu = menubar.addMenu('&File') fileMenu.addAction(openAction) fileMenu.addAction(saveAction) fileMenu.addAction(saveAsAction) fileMenu.addAction(exitAction) #-------------------------------------------- # # Options Menu # optionsMenu = menubar.addMenu('&Options') optionsMenu.addAction(settingsAction) #-------------------------------------------- # # Help Menu # helpMenu = menubar.addMenu('&Help') helpMenu.addAction(helpAction) helpMenu.addAction(helpSDAction) helpMenu.addAction(helpHKAction) #-------------------------------------------- # # Toolbar # toolbar = self.addToolBar('Exit') toolbar.addAction(exitAction) toolbar.addAction(openAction) toolbar.addAction(saveAction) toolbar.addAction(saveAsAction) toolbar.addAction(settingsAction) toolbar.addAction(helpAction) #---------------------------------------------------- # # Settings Dialog # #---------------------------------------------------- # # Components Table # self.CmpTabBox = QGroupBox('Components', self) self.CmpTabLayout = QVBoxLayout(self.CmpTabBox) self.CmpTabLayout.setContentsMargins(4, 10, 4, 4) self.CmpTabLayout.setSpacing(10) self.CmpTabLayout.setSizeConstraint(QVBoxLayout.SetMaximumSize) self.CmpTable = ComponentsTable(self) #self.CmpChooseButton = QPushButton('Choose', self) self.CmpTabLayout.addWidget(self.CmpTable) #self.CmpTabLayout.addWidget(self.CmpChooseButton) #---------------------------------------------------- # # Selector # self.SelectorBox = QGroupBox('Selector', self) self.SelectorLayout = QVBoxLayout(self.SelectorBox) self.SelectorLayout.setContentsMargins(4, 10, 4, 4) self.SelectorLayout.setSpacing(2) self.SelectorBtnWidget = QWidget(self) self.SelectorBtnLayout = QHBoxLayout(self.SelectorBtnWidget) self.SelectorBtnLayout.setContentsMargins(4, 10, 4, 4) self.SelectorBtnLayout.setSpacing(10) self.Selector = Selector(self) self.SelApplyButton = QPushButton('Apply', self) self.SelApplyButton.setToolTip( 'Alt+S: Apply selection patterns to components') self.SelClearButton = QPushButton('Clear', self) self.SelClearButton.setToolTip('Alt+C: Clear selection patterns') self.SelTemplateButton = QPushButton('Use Component', self) self.SelTemplateButton.setToolTip( 'Alt+T: Use Selected Component As Template') self.SelectorLayout.addWidget(self.Selector) self.SelectorBtnLayout.addWidget(self.SelTemplateButton) self.SelectorBtnLayout.addWidget(self.SelApplyButton) self.SelectorBtnLayout.addWidget(self.SelClearButton) self.SelectorLayout.addWidget(self.SelectorBtnWidget) self.shortcutSelApply = QShortcut(QKeySequence(Qt.ALT + Qt.Key_S), self) self.shortcutSelApply.activated.connect(self.Selector.apply_slot) self.shortcutSelClear = QShortcut(QKeySequence(Qt.ALT + Qt.Key_C), self) self.shortcutSelClear.activated.connect(self.Selector.clear_slot) self.shortcutSelTemplate = QShortcut(QKeySequence(Qt.ALT + Qt.Key_T), self) self.shortcutSelTemplate.activated.connect( self.Selector.use_comp_as_template_slot) #---------------------------------------------------- # # Inspector # self.Inspector = Inspector(self) self.FieldInspector = FieldInspector(self) self.InspectorBtnWidget = QWidget(self) self.InspectorBtnLayout = QHBoxLayout(self.InspectorBtnWidget) self.InspectorBtnLayout.setContentsMargins(4, 10, 4, 4) self.InspectorBtnLayout.setSpacing(10) self.AddUserProperty = QPushButton('Add Property', self) self.AddUserProperty.setToolTip('Alt+A: Add new user property') self.DeleteUserProperty = QPushButton('Delete Property', self) self.DeleteUserProperty.setToolTip('Alt+Delete: Delete user property') self.RenameUserProperty = QPushButton('Rename Property', self) self.RenameUserProperty.setToolTip('Alt+R: Rename user property') self.InspectorBox = QGroupBox('Inspector', self) self.InspectorSplit = QSplitter(Qt.Vertical, self) self.InspectorLayout = QVBoxLayout(self.InspectorBox) self.InspectorLayout.setContentsMargins(4, 10, 4, 4) self.InspectorLayout.setSpacing(2) self.InspectorSplit.addWidget(self.Inspector) self.InspectorSplit.addWidget(self.FieldInspector) self.InspectorLayout.addWidget(self.InspectorSplit) self.InspectorBtnLayout.addWidget(self.AddUserProperty) self.InspectorBtnLayout.addWidget(self.DeleteUserProperty) self.InspectorBtnLayout.addWidget(self.RenameUserProperty) self.InspectorLayout.addWidget(self.InspectorBtnWidget) self.shortcutSelApply = QShortcut(QKeySequence(Qt.ALT + Qt.Key_A), self) self.shortcutSelApply.activated.connect(self.add_user_property) self.shortcutSelApply = QShortcut(QKeySequence(Qt.ALT + Qt.Key_Delete), self) self.shortcutSelApply.activated.connect(self.remove_user_property) self.shortcutSelApply = QShortcut(QKeySequence(Qt.ALT + Qt.Key_R), self) self.shortcutSelApply.activated.connect(self.rename_user_property) #---------------------------------------------------- self.Splitter = QSplitter(self) self.Splitter.addWidget(self.CmpTabBox) self.Splitter.addWidget(self.SelectorBox) self.Splitter.addWidget(self.InspectorBox) self.centralWidget().layout().addWidget(self.Splitter) #---------------------------------------------------- # # Signals and Slots connections # self.CmpTable.cells_chosen.connect(self.Inspector.load_cmp) self.CmpTable.cells_chosen.connect(self.Selector.comp_template_slot) self.CmpTable.file_load.connect(self.file_loaded_slot) self.CmpTable.cmps_updated.connect(self.Selector.process_comps_slot) self.CmpTable.cmps_selected.connect(self.set_status_text_slot) self.SelApplyButton.clicked.connect(self.Selector.apply_slot) self.SelClearButton.clicked.connect(self.Selector.clear_slot) self.SelTemplateButton.clicked.connect( self.Selector.use_comp_as_template_slot) self.Selector.select_comps_signal.connect( self.CmpTable.select_comps_slot) self.Inspector.load_field.connect(self.FieldInspector.load_field_slot) self.Inspector.update_comps.connect(self.data_changed_slot) self.Inspector.update_comps.connect(self.CmpTable.update_cmp_list_slot) self.FieldInspector.data_changed.connect(self.data_changed_slot) CmpMgr.file_saved.connect(self.file_saved_slot) self.CmpTable.mouse_click.connect(self.mouse_change_tool) self.Inspector.mouse_click.connect(self.mouse_change_tool) self.FieldInspector.mouse_click.connect(self.mouse_change_tool) self.Inspector.header().sectionResized.connect( self.FieldInspector.column_resize) self.AddUserProperty.clicked.connect(self.add_user_property) self.DeleteUserProperty.clicked.connect(self.remove_user_property) self.RenameUserProperty.clicked.connect(self.rename_user_property) #---------------------------------------------------- self.ToolList = [] self.ToolList.append(self.CmpTable) self.ToolList.append(self.Selector) self.ToolList.append(self.Inspector) self.ToolList.append(self.FieldInspector) self.ToolIndex = 0 #---------------------------------------------------- # # Window # self.setWindowTitle(self.PROGRAM_NAME) Settings = QSettings('kicad-tools', 'Schematic Component Manager') #print(Settings.allKeys()) if Settings.contains('geometry'): self.restoreGeometry(Settings.value('geometry')) else: self.setGeometry(100, 100, 1024, 768) if Settings.contains('cmptable'): w0, w1 = Settings.value('cmptable') self.CmpTable.setColumnWidth(0, int(w0)) self.CmpTable.setColumnWidth(1, int(w1)) if Settings.contains('selector'): w0, w1 = Settings.value('selector') self.Selector.setColumnWidth(0, int(w0)) self.Selector.setColumnWidth(1, int(w1)) if Settings.contains('inspector'): w0, w1 = Settings.value('inspector') self.Inspector.setColumnWidth(0, int(w0)) self.Inspector.setColumnWidth(1, int(w1)) self.FieldInspector.setColumnWidth(0, int(w0)) self.FieldInspector.setColumnWidth(1, int(w1)) #self.Inspector.setColumnWidth( 2, int(w2) ) if Settings.contains('splitter'): self.Splitter.restoreState(Settings.value('splitter')) if Settings.contains('inssplitter'): self.InspectorSplit.restoreState(Settings.value('inssplitter')) #---------------------------------------------------- # # Process command line arguments # if len(sys.argv) > 1: fname = sys.argv[1] if os.path.exists(fname): self.CmpTable.load_file(fname) else: print('E: input file "' + fname + '"does not exist') self.show()
def __init__(self, parent): QWidget.__init__(self, parent) self.parent = parent self._layout = QVBoxLayout() self.setLayout(self._layout) self._layout.setContentsMargins(0, 0, 0, 0) # Set up the find box & button search_layout = QHBoxLayout() self._layout.addLayout(search_layout) self.item_search = HistoryLineEdit(parent) self.item_search.setMinimumContentsLength(5) self.item_search.setSizeAdjustPolicy( self.item_search.AdjustToMinimumContentsLengthWithIcon) try: self.item_search.lineEdit().setPlaceholderText( _('Find item in tag browser')) except: pass # Using Qt < 4.7 self.item_search.setToolTip( _('Search for items. This is a "contains" search; items containing the\n' 'text anywhere in the name will be found. You can limit the search\n' 'to particular categories using syntax similar to search. For example,\n' 'tags:foo will find foo in any tag, but not in authors etc. Entering\n' '*foo will filter all categories at once, showing only those items\n' 'containing the text "foo"')) search_layout.addWidget(self.item_search) # Not sure if the shortcut should be translatable ... sc = QShortcut(QKeySequence(_('ALT+f')), parent) sc.activated.connect(self.set_focus_to_find_box) self.search_button = QToolButton() self.search_button.setText(_('F&ind')) self.search_button.setToolTip(_('Find the first/next matching item')) search_layout.addWidget(self.search_button) self.expand_button = QToolButton() self.expand_button.setText('-') self.expand_button.setToolTip(_('Collapse all categories')) search_layout.addWidget(self.expand_button) search_layout.setStretch(0, 10) search_layout.setStretch(1, 1) search_layout.setStretch(2, 1) self.current_find_position = None self.search_button.clicked.connect(self.find) self.item_search.initialize('tag_browser_search') self.item_search.lineEdit().returnPressed.connect(self.do_find) self.item_search.lineEdit().textEdited.connect(self.find_text_changed) self.item_search.activated[str].connect(self.do_find) self.item_search.completer().setCaseSensitivity(Qt.CaseSensitive) parent.tags_view = TagsView(parent) self.tags_view = parent.tags_view self.expand_button.clicked.connect(self.tags_view.collapseAll) self._layout.addWidget(parent.tags_view) # Now the floating 'not found' box l = QLabel(self.tags_view) self.not_found_label = l l.setFrameStyle(QFrame.StyledPanel) l.setAutoFillBackground(True) l.setText( '<p><b>' + _('No More Matches.</b><p> Click Find again to go to first match')) l.setAlignment(Qt.AlignVCenter) l.setWordWrap(True) l.resize(l.sizeHint()) l.move(10, 20) l.setVisible(False) self.not_found_label_timer = QTimer() self.not_found_label_timer.setSingleShot(True) self.not_found_label_timer.timeout.connect( self.not_found_label_timer_event, type=Qt.QueuedConnection) parent.alter_tb = l = QPushButton(parent) l.setText(_('Alter Tag Browser')) l.setIcon(QIcon(I('tags.png'))) l.m = QMenu() l.setMenu(l.m) self._layout.addWidget(l) sb = l.m.addAction(_('Sort by')) sb.m = l.sort_menu = QMenu(l.m) sb.setMenu(sb.m) sb.bg = QActionGroup(sb) # Must be in the same order as db2.CATEGORY_SORTS for i, x in enumerate((_('Sort by name'), _('Sort by number of books'), _('Sort by average rating'))): a = sb.m.addAction(x) sb.bg.addAction(a) a.setCheckable(True) if i == 0: a.setChecked(True) sb.setToolTip(_('Set the sort order for entries in the Tag Browser')) sb.setStatusTip(sb.toolTip()) ma = l.m.addAction(_('Search type when selecting multiple items')) ma.m = l.match_menu = QMenu(l.m) ma.setMenu(ma.m) ma.ag = QActionGroup(ma) # Must be in the same order as db2.MATCH_TYPE for i, x in enumerate( (_('Match any of the items'), _('Match all of the items'))): a = ma.m.addAction(x) ma.ag.addAction(a) a.setCheckable(True) if i == 0: a.setChecked(True) ma.setToolTip( _('When selecting multiple entries in the Tag Browser ' 'match any or all of them')) ma.setStatusTip(ma.toolTip()) mt = l.m.addAction(_('Manage authors, tags, etc.')) mt.setToolTip( _('All of these category_managers are available by right-clicking ' 'on items in the tag browser above')) mt.m = l.manage_menu = QMenu(l.m) mt.setMenu(mt.m)
class BookInfo(QDialog): closed = pyqtSignal(object) def __init__(self, parent, view, row, link_delegate): QDialog.__init__(self, parent) self.normal_brush = QBrush(Qt.white) self.marked_brush = QBrush(Qt.lightGray) self.marked = None self.gui = parent self.splitter = QSplitter(self) self._l = l = QVBoxLayout(self) self.setLayout(l) l.addWidget(self.splitter) self.cover = CoverView(self) self.cover.resizeEvent = self.cover_view_resized self.cover.cover_changed.connect(self.cover_changed) self.cover_pixmap = None self.cover.sizeHint = self.details_size_hint self.splitter.addWidget(self.cover) self.details = Details(parent.book_details.book_info, self) self.details.page().setLinkDelegationPolicy(self.details.page().DelegateAllLinks) self.details.linkClicked.connect(self.link_clicked) s = self.details.page().settings() s.setAttribute(s.JavascriptEnabled, False) self.css = css() self.link_delegate = link_delegate self.details.setAttribute(Qt.WA_OpaquePaintEvent, False) palette = self.details.palette() self.details.setAcceptDrops(False) palette.setBrush(QPalette.Base, Qt.transparent) self.details.page().setPalette(palette) self.c = QWidget(self) self.c.l = l2 = QGridLayout(self.c) self.c.setLayout(l2) l2.addWidget(self.details, 0, 0, 1, -1) self.splitter.addWidget(self.c) self.fit_cover = QCheckBox(_('Fit &cover within view'), self) self.fit_cover.setChecked(gprefs.get('book_info_dialog_fit_cover', True)) l2.addWidget(self.fit_cover, l2.rowCount(), 0, 1, -1) self.previous_button = QPushButton(QIcon(I('previous.png')), _('&Previous'), self) self.previous_button.clicked.connect(self.previous) l2.addWidget(self.previous_button, l2.rowCount(), 0) self.next_button = QPushButton(QIcon(I('next.png')), _('&Next'), self) self.next_button.clicked.connect(self.next) l2.addWidget(self.next_button, l2.rowCount() - 1, 1) self.view = view self.current_row = None self.refresh(row) self.view.model().new_bookdisplay_data.connect(self.slave) self.fit_cover.stateChanged.connect(self.toggle_cover_fit) self.ns = QShortcut(QKeySequence('Alt+Right'), self) self.ns.activated.connect(self.next) self.ps = QShortcut(QKeySequence('Alt+Left'), self) self.ps.activated.connect(self.previous) self.next_button.setToolTip(_('Next [%s]')% unicode(self.ns.key().toString(QKeySequence.NativeText))) self.previous_button.setToolTip(_('Previous [%s]')% unicode(self.ps.key().toString(QKeySequence.NativeText))) geom = QCoreApplication.instance().desktop().availableGeometry(self) screen_height = geom.height() - 100 screen_width = geom.width() - 100 self.resize(max(int(screen_width/2), 700), screen_height) saved_layout = gprefs.get('book_info_dialog_layout', None) if saved_layout is not None: try: self.restoreGeometry(saved_layout[0]) self.splitter.restoreState(saved_layout[1]) except Exception: pass def link_clicked(self, qurl): link = unicode(qurl.toString(NO_URL_FORMATTING)) self.link_delegate(link) def done(self, r): saved_layout = (bytearray(self.saveGeometry()), bytearray(self.splitter.saveState())) gprefs.set('book_info_dialog_layout', saved_layout) ret = QDialog.done(self, r) self.view.model().new_bookdisplay_data.disconnect(self.slave) self.view = self.link_delegate = self.gui = None self.closed.emit(self) return ret def cover_changed(self, data): if self.current_row is not None: id_ = self.view.model().id(self.current_row) self.view.model().db.set_cover(id_, data) self.gui.refresh_cover_browser() ci = self.view.currentIndex() if ci.isValid(): self.view.model().current_changed(ci, ci) def details_size_hint(self): return QSize(350, 550) def toggle_cover_fit(self, state): gprefs.set('book_info_dialog_fit_cover', self.fit_cover.isChecked()) self.resize_cover() def cover_view_resized(self, event): QTimer.singleShot(1, self.resize_cover) def slave(self, mi): self.refresh(mi.row_number, mi) def move(self, delta=1): idx = self.view.currentIndex() if idx.isValid(): m = self.view.model() ni = m.index(idx.row() + delta, idx.column()) if ni.isValid(): if self.view.isVisible(): self.view.scrollTo(ni) self.view.setCurrentIndex(ni) def next(self): self.move() def previous(self): self.move(-1) def resize_cover(self): if self.cover_pixmap is None: return pixmap = self.cover_pixmap if self.fit_cover.isChecked(): scaled, new_width, new_height = fit_image(pixmap.width(), pixmap.height(), self.cover.size().width()-10, self.cover.size().height()-10) if scaled: try: dpr = self.devicePixelRatioF() except AttributeError: dpr = self.devicePixelRatio() pixmap = pixmap.scaled(int(dpr * new_width), int(dpr * new_height), Qt.KeepAspectRatio, Qt.SmoothTransformation) pixmap.setDevicePixelRatio(dpr) self.cover.set_pixmap(pixmap) self.update_cover_tooltip() def update_cover_tooltip(self): tt = '' if self.marked: tt = _('This book is marked') if self.marked in {True, 'true'} else _( 'This book is marked as: %s') % self.marked tt += '\n\n' if self.cover_pixmap is not None: sz = self.cover_pixmap.size() tt += _('Cover size: %(width)d x %(height)d pixels')%dict(width=sz.width(), height=sz.height()) self.cover.setToolTip(tt) def refresh(self, row, mi=None): if isinstance(row, QModelIndex): row = row.row() if row == self.current_row and mi is None: return mi = self.view.model().get_book_display_info(row) if mi is None else mi if mi is None: # Indicates books was deleted from library, or row numbers have # changed return self.previous_button.setEnabled(False if row == 0 else True) self.next_button.setEnabled(False if row == self.view.model().rowCount(QModelIndex())-1 else True) self.current_row = row self.setWindowTitle(mi.title) self.cover_pixmap = QPixmap.fromImage(mi.cover_data[1]) try: dpr = self.devicePixelRatioF() except AttributeError: dpr = self.devicePixelRatio() self.cover_pixmap.setDevicePixelRatio(dpr) self.resize_cover() html = render_html(mi, self.css, True, self, all_fields=True) self.details.setHtml(html) self.marked = mi.marked self.cover.setBackgroundBrush(self.marked_brush if mi.marked else self.normal_brush) self.update_cover_tooltip()
class QTWSMainWindow(QWidget): def __init__(self, app_id, config_filename: str, url: str, app_chooser: AppChooser = None): super().__init__() self.config = QTWSConfig(config_filename, app_id) self.app_chooser = app_chooser self.app_settings = QSettings(self.config.name, "Save State", self) QTWSPluginManager.instance().load_plugins(self.config) self.__init_ui(url) self.__init_web_view() self.__read_settings() self.__init_shortcuts() QTWSPluginManager.instance().each( lambda plugin: plugin.window_setup(self)) def closeEvent(self, event: QCloseEvent): self.__write_settings() if self.app_chooser: self.app_chooser.stop_serving() super().closeEvent(event) def __init_ui(self, url: str = None): self.setWindowTitle(self.config.name) if not url: url = self.config.home self.web = QTWSWebView(self.config) self.web.load(QUrl(url)) layout = QVBoxLayout() layout.addWidget(self.web) layout.setContentsMargins(0, 0, 0, 0) self.setLayout(layout) self.setWindowIcon(QIcon(os.path.join(__home__, self.config.icon))) self.show() def __init_web_view(self): profile = QWebEngineProfile.defaultProfile() profile.setCachePath(profile.cachePath() + "/" + self.config.name) profile.setPersistentStoragePath(profile.persistentStoragePath() + "/" + self.config.name) profile.setHttpCacheMaximumSize(self.config.cache_mb * 1024 * 1024) self.web.page().fullScreenRequested.connect( self.__full_screen_requested) if self.config.always_on_top: self.setWindowFlags(Qt.WindowStaysOnTopHint) def __full_screen_requested(self, request: QWebEngineFullScreenRequest): if request.toggleOn(): self.maximized = self.isMaximized() self.showFullScreen() else: self.web.triggerPageAction(QTWSWebPage.ExitFullScreen) if self.maximized: self.showNormal() self.showMaximized() else: self.showNormal() request.accept() def __write_settings(self): self.app_settings.setValue("geometry/mainWindowGeometry", self.saveGeometry()) if not self.config.save_session: return site = self.web.url().toString() self.app_settings.setValue("site", site) def __read_settings(self): if not self.config.save_session or self.app_settings.value( "state/mainWindowState"): return if self.config.save_session: geometry_data = self.app_settings.value( "geometry/mainWindowGeometry") if geometry_data: self.restoreGeometry(geometry_data) site_data = self.app_settings.value("site") if site_data and site_data != "": self.web.setUrl(QUrl(site_data)) def __init_shortcuts(self): self.__keyF11 = QShortcut(self) self.__keyF11.setKey(Qt.Key_F11) self.__keyF11.activated.connect(self.__action_full_screen) self.__keyCtrlQ = QShortcut(self) self.__keyCtrlQ.setKey(Qt.CTRL + Qt.Key_Q) self.__keyCtrlQ.activated.connect(self.__action_quit) self.__keyCtrlH = QShortcut(self) self.__keyCtrlH.setKey(Qt.CTRL + Qt.Key_H) self.__keyCtrlH.activated.connect(self.__action_home) self.__keyCtrlR = QShortcut(self) self.__keyCtrlR.setKey(Qt.CTRL + Qt.Key_R) self.__keyCtrlR.activated.connect(self.__action_reload) self.__keyF5 = QShortcut(self) self.__keyF5.setKey(Qt.Key_F5) self.__keyF5.activated.connect(self.__action_reload) self.__keyAltLeft = QShortcut(self) self.__keyAltLeft.setKey(Qt.ALT + Qt.Key_Left) self.__keyAltLeft.activated.connect(self.__action_back) def __action_back(self): self.web.back() def __action_full_screen(self): if not self.isFullScreen(): self.maximized = self.isMaximized() self.showFullScreen() else: self.web.triggerPageAction(QTWSWebPage.ExitFullScreen) if self.maximized: self.showNormal() self.showMaximized() else: self.showNormal() def __action_home(self): self.web.setUrl(self.config.home) def __action_quit(self): self.__write_settings() QApplication.quit() def __action_reload(self): self.web.reload()
class MetadataSingleDialogBase(ResizableDialog): view_format = pyqtSignal(object, object) cc_two_column = tweaks["metadata_single_use_2_cols_for_custom_fields"] one_line_comments_toolbar = False use_toolbutton_for_config_metadata = True def __init__(self, db, parent=None): self.db = db self.changed = set() self.books_to_refresh = set() self.rows_to_refresh = set() ResizableDialog.__init__(self, parent) def setupUi(self, *args): # {{{ self.resize(990, 670) self.download_shortcut = QShortcut(self) self.download_shortcut.setKey(QKeySequence("Ctrl+D", QKeySequence.PortableText)) p = self.parent() if hasattr(p, "keyboard"): kname = "Interface Action: Edit Metadata (Edit Metadata) : menu action : download" sc = p.keyboard.keys_map.get(kname, None) if sc: self.download_shortcut.setKey(sc[0]) self.button_box = bb = QDialogButtonBox(self) self.button_box.accepted.connect(self.accept) self.button_box.rejected.connect(self.reject) self.next_button = QPushButton(QIcon(I("forward.png")), _("Next"), self) self.next_button.setShortcut(QKeySequence("Alt+Right")) self.next_button.clicked.connect(self.next_clicked) self.prev_button = QPushButton(QIcon(I("back.png")), _("Previous"), self) self.prev_button.setShortcut(QKeySequence("Alt+Left")) self.button_box.addButton(self.prev_button, bb.ActionRole) self.button_box.addButton(self.next_button, bb.ActionRole) self.prev_button.clicked.connect(self.prev_clicked) bb.setStandardButtons(bb.Ok | bb.Cancel) bb.button(bb.Ok).setDefault(True) self.scroll_area = QScrollArea(self) self.scroll_area.setFrameShape(QScrollArea.NoFrame) self.scroll_area.setWidgetResizable(True) self.central_widget = QTabWidget(self) self.scroll_area.setWidget(self.central_widget) self.l = QVBoxLayout(self) self.setLayout(self.l) self.l.addWidget(self.scroll_area) ll = self.button_box_layout = QHBoxLayout() self.l.addLayout(ll) ll.addSpacing(10) ll.addWidget(self.button_box) self.setWindowIcon(QIcon(I("edit_input.png"))) self.setWindowTitle(BASE_TITLE) self.create_basic_metadata_widgets() if len(self.db.custom_column_label_map): self.create_custom_metadata_widgets() self.do_layout() geom = gprefs.get("metasingle_window_geometry3", None) if geom is not None: self.restoreGeometry(bytes(geom)) # }}} def create_basic_metadata_widgets(self): # {{{ self.basic_metadata_widgets = [] self.languages = LanguagesEdit(self) self.basic_metadata_widgets.append(self.languages) self.title = TitleEdit(self) self.title.textChanged.connect(self.update_window_title) self.deduce_title_sort_button = QToolButton(self) self.deduce_title_sort_button.setToolTip( _( "Automatically create the title sort entry based on the current " "title entry.\nUsing this button to create title sort will " "change title sort from red to green." ) ) self.deduce_title_sort_button.setWhatsThis(self.deduce_title_sort_button.toolTip()) self.title_sort = TitleSortEdit(self, self.title, self.deduce_title_sort_button, self.languages) self.basic_metadata_widgets.extend([self.title, self.title_sort]) self.deduce_author_sort_button = b = QToolButton(self) b.setToolTip( "<p>" + _( "Automatically create the author sort entry based on the current " "author entry. Using this button to create author sort will " "change author sort from red to green. There is a menu of " "functions available under this button. Click and hold " "on the button to see it." ) + "</p>" ) b.m = m = QMenu() ac = m.addAction(QIcon(I("forward.png")), _("Set author sort from author")) ac2 = m.addAction(QIcon(I("back.png")), _("Set author from author sort")) ac3 = m.addAction(QIcon(I("user_profile.png")), _("Manage authors")) ac4 = m.addAction(QIcon(I("next.png")), _("Copy author to author sort")) ac5 = m.addAction(QIcon(I("previous.png")), _("Copy author sort to author")) b.setMenu(m) self.authors = AuthorsEdit(self, ac3) self.author_sort = AuthorSortEdit(self, self.authors, b, self.db, ac, ac2, ac4, ac5) self.basic_metadata_widgets.extend([self.authors, self.author_sort]) self.swap_title_author_button = QToolButton(self) self.swap_title_author_button.setIcon(QIcon(I("swap.png"))) self.swap_title_author_button.setToolTip(_("Swap the author and title")) self.swap_title_author_button.clicked.connect(self.swap_title_author) self.manage_authors_button = QToolButton(self) self.manage_authors_button.setIcon(QIcon(I("user_profile.png"))) self.manage_authors_button.setToolTip( "<p>" + _("Manage authors. Use to rename authors and correct " "individual author's sort values") + "</p>" ) self.manage_authors_button.clicked.connect(self.authors.manage_authors) self.series = SeriesEdit(self) self.clear_series_button = QToolButton(self) self.clear_series_button.setToolTip(_("Clear series")) self.clear_series_button.clicked.connect(self.series.clear) self.series_index = SeriesIndexEdit(self, self.series) self.basic_metadata_widgets.extend([self.series, self.series_index]) self.formats_manager = FormatsManager(self, self.copy_fmt) # We want formats changes to be committed before title/author, as # otherwise we could have data loss if the title/author changed and the # user was trying to add an extra file from the old books directory. self.basic_metadata_widgets.insert(0, self.formats_manager) self.formats_manager.metadata_from_format_button.clicked.connect(self.metadata_from_format) self.formats_manager.cover_from_format_button.clicked.connect(self.cover_from_format) self.cover = Cover(self) self.cover.download_cover.connect(self.download_cover) self.basic_metadata_widgets.append(self.cover) self.comments = CommentsEdit(self, self.one_line_comments_toolbar) self.basic_metadata_widgets.append(self.comments) self.rating = RatingEdit(self) self.clear_ratings_button = QToolButton(self) self.clear_ratings_button.setToolTip(_("Clear rating")) self.clear_ratings_button.setIcon(QIcon(I("trash.png"))) self.clear_ratings_button.clicked.connect(self.rating.zero) self.basic_metadata_widgets.append(self.rating) self.tags = TagsEdit(self) self.tags_editor_button = QToolButton(self) self.tags_editor_button.setToolTip(_("Open Tag Editor")) self.tags_editor_button.setIcon(QIcon(I("chapters.png"))) self.tags_editor_button.clicked.connect(self.tags_editor) self.clear_tags_button = QToolButton(self) self.clear_tags_button.setToolTip(_("Clear all tags")) self.clear_tags_button.setIcon(QIcon(I("trash.png"))) self.clear_tags_button.clicked.connect(self.tags.clear) self.basic_metadata_widgets.append(self.tags) self.identifiers = IdentifiersEdit(self) self.basic_metadata_widgets.append(self.identifiers) self.clear_identifiers_button = QToolButton(self) self.clear_identifiers_button.setIcon(QIcon(I("trash.png"))) self.clear_identifiers_button.setToolTip(_("Clear Ids")) self.clear_identifiers_button.clicked.connect(self.identifiers.clear) self.paste_isbn_button = QToolButton(self) self.paste_isbn_button.setToolTip( "<p>" + _("Paste the contents of the clipboard into the " "identifiers box prefixed with isbn:") + "</p>" ) self.paste_isbn_button.setIcon(QIcon(I("edit-paste.png"))) self.paste_isbn_button.clicked.connect(self.identifiers.paste_isbn) self.publisher = PublisherEdit(self) self.basic_metadata_widgets.append(self.publisher) self.timestamp = DateEdit(self) self.pubdate = PubdateEdit(self) self.basic_metadata_widgets.extend([self.timestamp, self.pubdate]) self.fetch_metadata_button = QPushButton(_("&Download metadata"), self) self.fetch_metadata_button.clicked.connect(self.fetch_metadata) self.download_shortcut.activated.connect(self.fetch_metadata_button.click) font = self.fmb_font = QFont() font.setBold(True) self.fetch_metadata_button.setFont(font) if self.use_toolbutton_for_config_metadata: self.config_metadata_button = QToolButton(self) self.config_metadata_button.setIcon(QIcon(I("config.png"))) else: self.config_metadata_button = QPushButton(self) self.config_metadata_button.setText(_("Configure download metadata")) self.config_metadata_button.setIcon(QIcon(I("config.png"))) self.config_metadata_button.clicked.connect(self.configure_metadata) self.config_metadata_button.setToolTip(_("Change how calibre downloads metadata")) # }}} def create_custom_metadata_widgets(self): # {{{ self.custom_metadata_widgets_parent = w = QWidget(self) layout = QGridLayout() w.setLayout(layout) self.custom_metadata_widgets, self.__cc_spacers = populate_metadata_page( layout, self.db, None, parent=w, bulk=False, two_column=self.cc_two_column ) self.__custom_col_layouts = [layout] # }}} def set_custom_metadata_tab_order(self, before=None, after=None): # {{{ sto = QWidget.setTabOrder if getattr(self, "custom_metadata_widgets", []): ans = self.custom_metadata_widgets for i in range(len(ans) - 1): if before is not None and i == 0: pass if len(ans[i + 1].widgets) == 2: sto(ans[i].widgets[-1], ans[i + 1].widgets[1]) else: sto(ans[i].widgets[-1], ans[i + 1].widgets[0]) for c in range(2, len(ans[i].widgets), 2): sto(ans[i].widgets[c - 1], ans[i].widgets[c + 1]) if after is not None: pass # }}} def do_view_format(self, path, fmt): if path: self.view_format.emit(None, path) else: self.view_format.emit(self.book_id, fmt) def copy_fmt(self, fmt, f): self.db.copy_format_to(self.book_id, fmt, f, index_is_id=True) def do_layout(self): raise NotImplementedError() def __call__(self, id_): self.book_id = id_ self.books_to_refresh = set([]) for widget in self.basic_metadata_widgets: widget.initialize(self.db, id_) for widget in getattr(self, "custom_metadata_widgets", []): widget.initialize(id_) if callable(self.set_current_callback): self.set_current_callback(id_) # Commented out as it doesn't play nice with Next, Prev buttons # self.fetch_metadata_button.setFocus(Qt.OtherFocusReason) # Miscellaneous interaction methods {{{ def update_window_title(self, *args): title = self.title.current_val if len(title) > 50: title = title[:50] + "\u2026" self.setWindowTitle( BASE_TITLE + " - " + title + " - " + _(" [%(num)d of %(tot)d]") % dict(num=self.current_row + 1, tot=len(self.row_list)) ) def swap_title_author(self, *args): title = self.title.current_val self.title.current_val = authors_to_string(self.authors.current_val) self.authors.current_val = string_to_authors(title) self.title_sort.auto_generate() self.author_sort.auto_generate() def tags_editor(self, *args): self.tags.edit(self.db, self.book_id) def metadata_from_format(self, *args): mi, ext = self.formats_manager.get_selected_format_metadata(self.db, self.book_id) if mi is not None: self.update_from_mi(mi) def get_pdf_cover(self): pdfpath = self.formats_manager.get_format_path(self.db, self.book_id, "pdf") from calibre.gui2.metadata.pdf_covers import PDFCovers d = PDFCovers(pdfpath, parent=self) if d.exec_() == d.Accepted: cpath = d.cover_path if cpath: with open(cpath, "rb") as f: self.update_cover(f.read(), "PDF") d.cleanup() def cover_from_format(self, *args): ext = self.formats_manager.get_selected_format() if ext is None: return if ext == "pdf": return self.get_pdf_cover() try: mi, ext = self.formats_manager.get_selected_format_metadata(self.db, self.book_id) except (IOError, OSError) as err: if getattr(err, "errno", None) == errno.EACCES: # Permission denied import traceback fname = err.filename if err.filename else "file" error_dialog( self, _("Permission denied"), _("Could not open %s. Is it being used by another" " program?") % fname, det_msg=traceback.format_exc(), show=True, ) return raise if mi is None: return cdata = None if mi.cover and os.access(mi.cover, os.R_OK): cdata = open(mi.cover).read() elif mi.cover_data[1] is not None: cdata = mi.cover_data[1] if cdata is None: error_dialog(self, _("Could not read cover"), _("Could not read cover from %s format") % ext).exec_() return self.update_cover(cdata, ext) def update_cover(self, cdata, fmt): orig = self.cover.current_val self.cover.current_val = cdata if self.cover.current_val is None: self.cover.current_val = orig return error_dialog( self, _("Could not read cover"), _("The cover in the %s format is invalid") % fmt, show=True ) return def update_from_mi(self, mi, update_sorts=True, merge_tags=True, merge_comments=False): if not mi.is_null("title"): self.title.current_val = mi.title if update_sorts: self.title_sort.auto_generate() if not mi.is_null("authors"): self.authors.current_val = mi.authors if not mi.is_null("author_sort"): self.author_sort.current_val = mi.author_sort elif update_sorts: self.author_sort.auto_generate() if not mi.is_null("rating"): try: self.rating.current_val = mi.rating except: pass if not mi.is_null("publisher"): self.publisher.current_val = mi.publisher if not mi.is_null("tags"): old_tags = self.tags.current_val tags = mi.tags if mi.tags else [] if old_tags and merge_tags: ltags, lotags = {t.lower() for t in tags}, {t.lower() for t in old_tags} tags = [t for t in tags if t.lower() in ltags - lotags] + old_tags self.tags.current_val = tags if not mi.is_null("identifiers"): current = self.identifiers.current_val current.update(mi.identifiers) self.identifiers.current_val = current if not mi.is_null("pubdate"): self.pubdate.current_val = mi.pubdate if not mi.is_null("series") and mi.series.strip(): self.series.current_val = mi.series if mi.series_index is not None: self.series_index.reset_original() self.series_index.current_val = float(mi.series_index) if not mi.is_null("languages"): langs = [canonicalize_lang(x) for x in mi.languages] langs = [x for x in langs if x is not None] if langs: self.languages.current_val = langs if mi.comments and mi.comments.strip(): val = mi.comments if val and merge_comments: cval = self.comments.current_val if cval: val = merge_two_comments(cval, val) self.comments.current_val = val def fetch_metadata(self, *args): d = FullFetch(self.cover.pixmap(), self) ret = d.start( title=self.title.current_val, authors=self.authors.current_val, identifiers=self.identifiers.current_val ) if ret == d.Accepted: from calibre.ebooks.metadata.sources.prefs import msprefs mi = d.book dummy = Metadata(_("Unknown")) for f in msprefs["ignore_fields"]: if ":" not in f: setattr(mi, f, getattr(dummy, f)) if mi is not None: pd = mi.pubdate if pd is not None: # Put the downloaded published date into the local timezone # as we discard time info and the date is timezone # invariant. This prevents the as_local_timezone() call in # update_from_mi from changing the pubdate mi.pubdate = datetime(pd.year, pd.month, pd.day, tzinfo=local_tz) self.update_from_mi(mi, merge_comments=msprefs["append_comments"]) if d.cover_pixmap is not None: self.cover.current_val = pixmap_to_data(d.cover_pixmap) def configure_metadata(self): from calibre.gui2.preferences import show_config_widget gui = self.parent() show_config_widget("Sharing", "Metadata download", parent=self, gui=gui, never_shutdown=True) def download_cover(self, *args): from calibre.gui2.metadata.single_download import CoverFetch d = CoverFetch(self.cover.pixmap(), self) ret = d.start(self.title.current_val, self.authors.current_val, self.identifiers.current_val) if ret == d.Accepted: if d.cover_pixmap is not None: self.cover.current_val = pixmap_to_data(d.cover_pixmap) # }}} def apply_changes(self): self.changed.add(self.book_id) if self.db is None: # break_cycles has already been called, don't know why this should # happen but a user reported it return True for widget in self.basic_metadata_widgets: try: if hasattr(widget, "validate_for_commit"): title, msg, det_msg = widget.validate_for_commit() if title is not None: error_dialog(self, title, msg, det_msg=det_msg, show=True) return False widget.commit(self.db, self.book_id) self.books_to_refresh |= getattr(widget, "books_to_refresh", set()) except (IOError, OSError) as err: if getattr(err, "errno", None) == errno.EACCES: # Permission denied import traceback fname = getattr(err, "filename", None) p = "Locked file: %s\n\n" % fname if fname else "" error_dialog( self, _("Permission denied"), _("Could not change the on disk location of this" " book. Is it open in another program?"), det_msg=p + traceback.format_exc(), show=True, ) return False raise for widget in getattr(self, "custom_metadata_widgets", []): self.books_to_refresh |= widget.commit(self.book_id) self.db.commit() rows = self.db.refresh_ids(list(self.books_to_refresh)) if rows: self.rows_to_refresh |= set(rows) return True def accept(self): self.save_state() if not self.apply_changes(): return ResizableDialog.accept(self) def reject(self): self.save_state() ResizableDialog.reject(self) def save_state(self): try: gprefs["metasingle_window_geometry3"] = bytearray(self.saveGeometry()) except: # Weird failure, see https://bugs.launchpad.net/bugs/995271 import traceback traceback.print_exc() # Dialog use methods {{{ def start(self, row_list, current_row, view_slot=None, set_current_callback=None): self.row_list = row_list self.current_row = current_row if view_slot is not None: self.view_format.connect(view_slot) self.set_current_callback = set_current_callback self.do_one(apply_changes=False) ret = self.exec_() self.break_cycles() return ret def next_clicked(self): if not self.apply_changes(): return self.do_one(delta=1, apply_changes=False) def prev_clicked(self): if not self.apply_changes(): return self.do_one(delta=-1, apply_changes=False) def do_one(self, delta=0, apply_changes=True): if apply_changes: self.apply_changes() self.current_row += delta prev = next_ = None if self.current_row > 0: prev = self.db.title(self.row_list[self.current_row - 1]) if self.current_row < len(self.row_list) - 1: next_ = self.db.title(self.row_list[self.current_row + 1]) if next_ is not None: tip = (_("Save changes and edit the metadata of %s") + " [Alt+Right]") % next_ self.next_button.setToolTip(tip) self.next_button.setEnabled(next_ is not None) if prev is not None: tip = (_("Save changes and edit the metadata of %s") + " [Alt+Left]") % prev self.prev_button.setToolTip(tip) self.prev_button.setEnabled(prev is not None) self.button_box.button(self.button_box.Ok).setDefault(True) self.button_box.button(self.button_box.Ok).setFocus(Qt.OtherFocusReason) self(self.db.id(self.row_list[self.current_row])) def break_cycles(self): # Break any reference cycles that could prevent python # from garbage collecting this dialog self.set_current_callback = self.db = None def disconnect(signal): try: signal.disconnect() except: pass # Fails if view format was never connected disconnect(self.view_format) for b in ("next_button", "prev_button"): x = getattr(self, b, None) if x is not None: disconnect(x.clicked) for widget in self.basic_metadata_widgets: bc = getattr(widget, "break_cycles", None) if bc is not None and callable(bc): bc() for widget in getattr(self, "custom_metadata_widgets", []): widget.break_cycles()
def __init__(self, parent, view, row, link_delegate): QDialog.__init__(self, parent) self.normal_brush = QBrush(Qt.white) self.marked_brush = QBrush(Qt.lightGray) self.marked = None self.gui = parent self.splitter = QSplitter(self) self._l = l = QVBoxLayout(self) self.setLayout(l) l.addWidget(self.splitter) self.cover = CoverView(self, show_size=gprefs['bd_overlay_cover_size']) self.cover.resizeEvent = self.cover_view_resized self.cover.cover_changed.connect(self.cover_changed) self.cover_pixmap = None self.cover.sizeHint = self.details_size_hint self.splitter.addWidget(self.cover) self.details = Details(parent.book_details.book_info, self) self.details.anchor_clicked.connect(self.on_link_clicked) self.link_delegate = link_delegate self.details.setAttribute(Qt.WA_OpaquePaintEvent, False) palette = self.details.palette() self.details.setAcceptDrops(False) palette.setBrush(QPalette.Base, Qt.transparent) self.details.setPalette(palette) self.c = QWidget(self) self.c.l = l2 = QGridLayout(self.c) l2.setContentsMargins(0, 0, 0, 0) self.c.setLayout(l2) l2.addWidget(self.details, 0, 0, 1, -1) self.splitter.addWidget(self.c) self.fit_cover = QCheckBox(_('Fit &cover within view'), self) self.fit_cover.setChecked( gprefs.get('book_info_dialog_fit_cover', True)) self.hl = hl = QHBoxLayout() hl.setContentsMargins(0, 0, 0, 0) l2.addLayout(hl, l2.rowCount(), 0, 1, -1) hl.addWidget(self.fit_cover), hl.addStretch() self.clabel = QLabel( '<div style="text-align: right"><a href="calibre:conf" title="{}" style="text-decoration: none">{}</a>' .format(_('Configure this view'), _('Configure'))) self.clabel.linkActivated.connect(self.configure) hl.addWidget(self.clabel) self.previous_button = QPushButton(QIcon(I('previous.png')), _('&Previous'), self) self.previous_button.clicked.connect(self.previous) l2.addWidget(self.previous_button, l2.rowCount(), 0) self.next_button = QPushButton(QIcon(I('next.png')), _('&Next'), self) self.next_button.clicked.connect(self.next) l2.addWidget(self.next_button, l2.rowCount() - 1, 1) self.view = view self.current_row = None self.refresh(row) self.view.model().new_bookdisplay_data.connect(self.slave) self.fit_cover.stateChanged.connect(self.toggle_cover_fit) self.ns = QShortcut(QKeySequence('Alt+Right'), self) self.ns.activated.connect(self.next) self.ps = QShortcut(QKeySequence('Alt+Left'), self) self.ps.activated.connect(self.previous) self.next_button.setToolTip( _('Next [%s]') % unicode_type(self.ns.key().toString(QKeySequence.NativeText))) self.previous_button.setToolTip( _('Previous [%s]') % unicode_type(self.ps.key().toString(QKeySequence.NativeText))) geom = QCoreApplication.instance().desktop().availableGeometry(self) screen_height = geom.height() - 100 screen_width = geom.width() - 100 self.resize(max(int(screen_width / 2), 700), screen_height) saved_layout = gprefs.get('book_info_dialog_layout', None) if saved_layout is not None: try: self.restoreGeometry(saved_layout[0]) self.splitter.restoreState(saved_layout[1]) except Exception: pass
class QTWSMainWindow(QWidget): def __init__(self, app_id, config_filename: str, url: str = None, profile: str = None): super().__init__() self.config = QTWSConfig(config_filename, app_id) self.app_settings = QSettings(self.config.name, "Save State", self) self._profile_id = profile QTWSPluginManager.instance().load_plugins(self.config) self.__init_ui(url) self.__init_web_view() self.__read_settings() self.__init_shortcuts() self.enter_event_handler = EnterEventHandler() self.setMouseTracking(True) self.installEventFilter(self.enter_event_handler) self.default_flags = self.windowFlags() QTWSPluginManager.instance().each( lambda plugin: plugin.window_setup(self)) def closeEvent(self, event: QCloseEvent): self.__write_settings() QTWSPluginManager.instance().each( lambda plugin: plugin.close_event(self, event)) def quit(self): self.__action_quit() def profile_id(self): return self._profile_id def set_always_on_top(self, always_on_top: bool): self.setWindowFlag(Qt.WindowStaysOnTopHint, always_on_top) self.show() def set_maximizable(self, maximizable: bool): self.setWindowFlag(Qt.WindowMaximizeButtonHint, maximizable) self.show() def reset_flags(self): self.setWindowFlags(self.default_flags) self.show() def activate_fullscreen(self): if not self.isFullScreen(): self.maximized = self.isMaximized() self.showFullScreen() def deactivate_fullscreen(self): if self.isFullScreen(): self.web.triggerPageAction(QTWSWebPage.ExitFullScreen) if self.maximized: self.showNormal() self.showMaximized() else: self.showNormal() def set_mouse_enter_callback(self, callback): self.enter_event_handler.set_callback(callback) def __init_ui(self, url: str = None): self.setWindowTitle(f"{self.config.name}") if not url or not self.config.in_scope(url): url = self.config.home if type(url) == QUrl: url = url.toString() url = url.replace('silo://', 'https://') self.web = QTWSWebView(self.config, self, self._profile_id) self.web.load(QUrl(url)) layout = QVBoxLayout() layout.addWidget(self.web) layout.setContentsMargins(0, 0, 0, 0) self.setLayout(layout) self.setWindowIcon(QIcon(self.config.icon)) QApplication.instance().setDesktopFileName("silo-" + self.config.app_id) self.show() self.maximized = self.isMaximized() if self.config.always_on_top: self.set_always_on_top(True) def __init_web_view(self): self.web.page().fullScreenRequested.connect( self.__full_screen_requested) def __full_screen_requested(self, request: QWebEngineFullScreenRequest): if request.toggleOn(): self.maximized = self.isMaximized() self.showFullScreen() else: self.web.triggerPageAction(QTWSWebPage.ExitFullScreen) if self.maximized: self.showNormal() self.showMaximized() else: self.showNormal() request.accept() def __write_settings(self): self.app_settings.setValue("geometry/mainWindowGeometry", self.saveGeometry()) if not self.config.save_session: return site = self.web.url().toString() self.app_settings.setValue("site", site) def __read_settings(self): if not self.config.save_session: return if self.config.save_session: geometry_data = self.app_settings.value( "geometry/mainWindowGeometry") if geometry_data: self.restoreGeometry(geometry_data) site_data = self.app_settings.value("site") if site_data and site_data != "": self.web.setUrl(QUrl(site_data)) def __init_shortcuts(self): self.__keyF11 = QShortcut(self) self.__keyF11.setKey(Qt.Key_F11) self.__keyF11.activated.connect(self.__action_full_screen) self.__keyCtrlQ = QShortcut(self) self.__keyCtrlQ.setKey(Qt.CTRL + Qt.Key_Q) self.__keyCtrlQ.activated.connect(self.__action_quit) self.__keyCtrlH = QShortcut(self) self.__keyCtrlH.setKey(Qt.CTRL + Qt.Key_H) self.__keyCtrlH.activated.connect(self.__action_home) self.__keyCtrlR = QShortcut(self) self.__keyCtrlR.setKey(Qt.CTRL + Qt.Key_R) self.__keyCtrlR.activated.connect(self.__action_reload) self.__keyF5 = QShortcut(self) self.__keyF5.setKey(Qt.Key_F5) self.__keyF5.activated.connect(self.__action_reload) self.__keyAltLeft = QShortcut(self) self.__keyAltLeft.setKey(Qt.ALT + Qt.Key_Left) self.__keyAltLeft.activated.connect(self.__action_back) QTWSPluginManager.instance().each( lambda plugin: plugin.register_shortcuts(self)) def __action_back(self): self.web.back() def __action_full_screen(self): if not self.isFullScreen(): self.activate_fullscreen() else: self.deactivate_fullscreen() def __action_home(self): self.web.setUrl(self.config.home) def __action_quit(self): self.__write_settings() QApplication.quit() def __action_reload(self): self.web.reload()
def __init_shortcuts(self): self.__keyF11 = QShortcut(self) self.__keyF11.setKey(Qt.Key_F11) self.__keyF11.activated.connect(self.__action_full_screen) self.__keyCtrlQ = QShortcut(self) self.__keyCtrlQ.setKey(Qt.CTRL + Qt.Key_Q) self.__keyCtrlQ.activated.connect(self.__action_quit) self.__keyCtrlH = QShortcut(self) self.__keyCtrlH.setKey(Qt.CTRL + Qt.Key_H) self.__keyCtrlH.activated.connect(self.__action_home) self.__keyCtrlR = QShortcut(self) self.__keyCtrlR.setKey(Qt.CTRL + Qt.Key_R) self.__keyCtrlR.activated.connect(self.__action_reload) self.__keyF5 = QShortcut(self) self.__keyF5.setKey(Qt.Key_F5) self.__keyF5.activated.connect(self.__action_reload) self.__keyAltLeft = QShortcut(self) self.__keyAltLeft.setKey(Qt.ALT + Qt.Key_Left) self.__keyAltLeft.activated.connect(self.__action_back) QTWSPluginManager.instance().each( lambda plugin: plugin.register_shortcuts(self))
def setup_widgets(self): # file editing and tabs self.editor_pane = gui_widgets.TabController(self, self.ontologies) self.editor_pane.currentChanged.connect(self.__on_tab_change) # project navigation self.explorer_tab = QTabWidget(self) self.project_explorer = gui_widgets.ProjectExplorer(self, self.root_path, self.editor_pane) self.explorer_tab.addTab(self.project_explorer, "Directory") self.import_explorer = gui_widgets.ImportSidebar(self, self.root_path, self.editor_pane) self.explorer_tab.addTab(self.import_explorer, "Imports") # informational sidebar self.info_bar = gui_widgets.InformationSidebar(self, self.root_path) # output self.console = gui_widgets.Console(self) main_menu = self.menuBar() # file menu and associated actions file_menu = main_menu.addMenu('File') # Create a new tab new_action = QAction("New File", self) file_menu.addAction(new_action) new_action.triggered.connect(self.new_command) # Open a file open_action = QAction("Open", self) file_menu.addAction(open_action) open_action.triggered.connect(self.open_command) open_shortcut = QShortcut(QKeySequence("Ctrl+O"), self) open_shortcut.activated.connect(self.open_command) # Save file; if no file, open dialog save_action = QAction("Save", self) file_menu.addAction(save_action) save_action.triggered.connect(self.save_command) save_shortcut = QShortcut(QKeySequence("Ctrl+S"), self) save_shortcut.activated.connect(self.save_command) # Open Save dialog saveas_action = QAction("Save As..", self) file_menu.addAction(saveas_action) saveas_action.triggered.connect(self.saveas_command) # Open settings dialog settings_action = QAction("Settings..", self) file_menu.addAction(settings_action) settings_action.triggered.connect(self.settings_command) # Open Export dialog export_action = QAction("Export.. ", self) file_menu.addAction(export_action) export_action.triggered.connect(self.export_command) # Run menu and associated actions run_menu = main_menu.addMenu('Run') # Run the parse w/out resolving imports parse_action = QAction("Parse (No Imports)", self) run_menu.addAction(parse_action) parse_action.triggered.connect(self.parse_command) # Run the parse w/ imports parse_imports_action = QAction("Parse (w/ Imports)", self) run_menu.addAction(parse_imports_action) parse_imports_action.triggered.connect(self.parse_imports_command) run_menu.addSeparator() # Run the check consistency dialog check_consistency_action = QAction("Check Consistency..", self) run_menu.addAction(check_consistency_action) check_consistency_action.triggered.connect(self.check_consistency_command) # Threads self.parse_thread = gui_threads.ParseThread() self.parse_thread.finished.connect(self.__on_parse_done)
def __init__(self, gui, row, toggle_shortcut): self.is_pane = gprefs.get('quickview_is_pane', False) if not self.is_pane: QDialog.__init__(self, gui, flags=Qt.WindowType.Widget) else: QDialog.__init__(self, gui) Ui_Quickview.__init__(self) self.setupUi(self) self.isClosed = False self.current_book = None self.closed_by_button = False if self.is_pane: self.main_grid_layout.setContentsMargins(0, 0, 0, 0) else: self.setWindowIcon(self.windowIcon()) self.books_table_column_widths = None try: self.books_table_column_widths = \ gprefs.get('quickview_dialog_books_table_widths', None) if not self.is_pane: geom = gprefs.get('quickview_dialog_geometry', None) if geom: QApplication.instance().safe_restore_geometry( self, QByteArray(geom)) except: pass self.view = gui.library_view self.db = self.view.model().db self.gui = gui self.is_closed = False self.current_book_id = None # the db id of the book used to fill the lh pane self.current_column = None # current logical column in books list self.current_key = None # current lookup key in books list self.last_search = None self.no_valid_items = False self.follow_library_view = True self.apply_vls.setCheckState( Qt.CheckState.Checked if gprefs['qv_respects_vls'] else Qt. CheckState.Unchecked) self.apply_vls.stateChanged.connect(self.vl_box_changed) self.fm = self.db.field_metadata self.items.setSelectionMode( QAbstractItemView.SelectionMode.SingleSelection) self.items.currentTextChanged.connect(self.item_selected) self.items.setProperty('highlight_current_item', 150) self.items.itemDoubleClicked.connect(self.item_doubleclicked) self.items.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) self.items.customContextMenuRequested.connect( self.show_item_context_menu) focus_filter = WidgetFocusFilter(self.items) focus_filter.focus_entered_signal.connect(self.focus_entered) self.items.installEventFilter(focus_filter) self.tab_pressed_signal.connect(self.tab_pressed) return_filter = BooksTableFilter(self.books_table) return_filter.return_pressed_signal.connect(self.return_pressed) self.books_table.installEventFilter(return_filter) focus_filter = WidgetFocusFilter(self.books_table) focus_filter.focus_entered_signal.connect(self.focus_entered) self.books_table.installEventFilter(focus_filter) self.close_button.clicked.connect(self.close_button_clicked) self.refresh_button.clicked.connect(self.refill) self.tab_order_widgets = [ self.items, self.books_table, self.lock_qv, self.dock_button, self.refresh_button, self.close_button ] for idx, widget in enumerate(self.tab_order_widgets): widget.installEventFilter( WidgetTabFilter(widget, idx, self.tab_pressed_signal)) self.books_table.setSelectionBehavior( QAbstractItemView.SelectionBehavior.SelectRows) self.books_table.setSelectionMode( QAbstractItemView.SelectionMode.SingleSelection) self.books_table.setProperty('highlight_current_item', 150) # Set up the books table columns self.add_columns_to_widget() self.books_table_header_height = self.books_table.height() self.books_table.cellDoubleClicked.connect(self.book_doubleclicked) self.books_table.currentCellChanged.connect( self.books_table_cell_changed) self.books_table.cellClicked.connect( self.books_table_set_search_string) self.books_table.cellActivated.connect( self.books_table_set_search_string) self.books_table.sortByColumn(0, Qt.SortOrder.AscendingOrder) # get the standard table row height. Do this here because calling # resizeRowsToContents can word wrap long cell contents, creating # double-high rows self.books_table.setRowCount(1) self.books_table.setItem(0, 0, TableItem('A', '')) self.books_table.resizeRowsToContents() self.books_table_row_height = self.books_table.rowHeight(0) self.books_table.setRowCount(0) # Add the data self.refresh(row) self.view.clicked.connect(self.slave) self.view.selectionModel().currentColumnChanged.connect( self.column_slave) QCoreApplication.instance().aboutToQuit.connect(self.save_state) self.view.model().new_bookdisplay_data.connect(self.book_was_changed) self.close_button.setDefault(False) self.close_button_tooltip = _( 'The Quickview shortcut ({0}) shows/hides the Quickview panel') if self.is_pane: self.dock_button.setText(_('Undock')) self.dock_button.setToolTip( _('Show the Quickview panel in its own floating window')) self.dock_button.setIcon(QIcon(I('arrow-up.png'))) # Remove the ampersands from the buttons because shortcuts exist. self.lock_qv.setText(_('Lock Quickview contents')) self.refresh_button.setText(_('Refresh')) self.gui.quickview_splitter.add_quickview_dialog(self) self.close_button.setVisible(False) else: self.dock_button.setToolTip( _('Embed the quickview panel into the main calibre window')) self.dock_button.setIcon(QIcon(I('arrow-down.png'))) self.set_focus() self.books_table.horizontalHeader().sectionResized.connect( self.section_resized) self.dock_button.clicked.connect(self.show_as_pane_changed) self.view.model().search_done.connect(self.check_for_no_items) # Enable the refresh button only when QV is locked self.refresh_button.setEnabled(False) self.lock_qv.stateChanged.connect(self.lock_qv_changed) self.view_icon = QIcon(I('view.png')) self.view_plugin = self.gui.iactions['View'] self.edit_metadata_icon = QIcon(I('edit_input.png')) self.quickview_icon = QIcon(I('quickview.png')) self.select_book_icon = QIcon(I('library.png')) self.search_icon = QIcon(I('search.png')) self.books_table.setContextMenuPolicy( Qt.ContextMenuPolicy.CustomContextMenu) self.books_table.customContextMenuRequested.connect( self.show_context_menu) # Add the quickview toggle as a shortcut for the close button # Don't add it if it identical to the current &X shortcut because that # breaks &X if (not self.is_pane and toggle_shortcut and self.close_button.shortcut() != toggle_shortcut): toggle_sc = QShortcut(toggle_shortcut, self.close_button) toggle_sc.activated.connect(lambda: self.close_button_clicked()) toggle_sc.setEnabled(True) self.close_button.setToolTip( _('Alternate shortcut: ') + toggle_shortcut.toString())
class MainWindow(QMainWindow): # class EventFilter(QObject): # def __init__(self, parent): # super().__init__(parent) # # def eventFilter(self, obj, e): # #print(obj.metaObject().className()) # # if e.type() == QEvent.KeyPress or e.type() == QEvent.ShortcutOverride: # key = e.key() # mod = e.modifiers() # # print(str(e) + ' ' + str(e.type()) ) # # if mod == Qt.AltModifier: # print('*'*30) # print('alt pressed') # if key == Qt.Key_Left or key == Qt.Key_Right: # print('alt-left') if key == Qt.Key_Left else print('alt-right') ## action = QAbstractItemView.MoveLeft if key == Qt.Key_Left else QAbstractItemView.MoveRight ## idx = obj.moveCursor(action, Qt.NoModifier) ## item = obj.itemFromIndex(idx) ## obj.setCurrentItem(item) # return True # # return False PROGRAM_NAME = 'KiCad Schematic Component Manager' #-------------------------------------------------------------------------------- class EventFilter(QObject): def __init__(self, parent): super().__init__(parent) def eventFilter(self, obj, e): if e.type() == QEvent.KeyPress or e.type( ) == QEvent.ShortcutOverride: key = e.key() mod = e.modifiers() #print(obj.focusWidget().metaObject().className()) return False #-------------------------------------------------------------------------------- def scroll_left(self): print('alt-left') if self.ToolIndex == 3 or self.ToolIndex == 2: self.ToolList[self.ToolIndex].finish_edit() self.ToolIndex -= 1 if self.ToolIndex < 0: self.ToolIndex = len(self.ToolList) - 1 print('Tool Index: ' + str(self.ToolIndex)) self.ToolList[self.ToolIndex].setFocus() #-------------------------------------------------------------------------------- def scroll_right(self): print('alt-right') if self.ToolIndex == 3 or self.ToolIndex == 2: self.ToolList[self.ToolIndex].finish_edit() self.ToolIndex += 1 if self.ToolIndex == len(self.ToolList): self.ToolIndex = 0 print('Tool Index: ' + str(self.ToolIndex)) self.ToolList[self.ToolIndex].setFocus() #-------------------------------------------------------------------------------- def mouse_change_tool(self, s): print('Tool ' + s) if s == 'CmpTable': self.ToolIndex = 0 elif s == 'Selector': self.ToolIndex = 1 elif s == 'Inspector': self.ToolIndex = 2 elif s == 'FieldInspector': self.ToolIndex = 3 if self.ToolIndex != 3: self.ToolList[3].finish_edit( ) # save field properties when leave field inspector #-------------------------------------------------------------------------------- def add_user_property(self): self.Inspector.save_cmps() self.FieldInspector.save_fields() self.Inspector.add_property() #-------------------------------------------------------------------------------- def remove_user_property(self): #self.Inspector.save_cmps() self.FieldInspector.save_fields() self.Inspector.remove_property() #-------------------------------------------------------------------------------- def rename_user_property(self): #self.Inspector.save_cmps() self.FieldInspector.save_fields() self.Inspector.rename_property() #-------------------------------------------------------------------------------- def __init__(self): super().__init__() self.initUI() self.installEventFilter(self.EventFilter(self)) self.setFocusPolicy(Qt.WheelFocus) self.setTabOrder(self.CmpTable, self.Inspector) self.setTabOrder(self.Inspector, self.Selector) self.setTabOrder(self.Selector, self.FieldInspector) #self.setTabOrder(self.FieldInspector, self.CmpTable) #---------------------------------------------------- # # Application Hotkeys # self.shortcutLeft = QShortcut(QKeySequence(Qt.ALT + Qt.Key_Left), self) self.shortcutRight = QShortcut(QKeySequence(Qt.ALT + Qt.Key_Right), self) self.shortcutLeft.setContext(Qt.ApplicationShortcut) self.shortcutRight.setContext(Qt.ApplicationShortcut) self.shortcutLeft.activated.connect(self.scroll_left) self.shortcutRight.activated.connect(self.scroll_right) #-------------------------------------------------------------------------------- def initUI(self): #---------------------------------------------------- # # Main Window # work_zone = QWidget(self) Layout = QHBoxLayout(work_zone) self.setCentralWidget(work_zone) openAction = QAction(QIcon(os.path.join(resources_path, 'open24.png')), 'Open', self) openAction.setShortcut('Ctrl+O') openAction.setStatusTip('Open Schematic File') openAction.triggered.connect(self.open_file) saveAction = QAction(QIcon(os.path.join(resources_path, 'save24.png')), 'Save', self) saveAction.setShortcut('Ctrl+S') saveAction.setStatusTip('Save Schematic File') saveAction.triggered.connect(self.save_file) saveAsAction = QAction( QIcon(os.path.join(resources_path, 'save-as24.png')), 'Save As...', self) saveAsAction.setShortcut('Ctrl+Shift+S') saveAsAction.setStatusTip('Save Schematic File As...') saveAsAction.triggered.connect(self.save_file_as) exitAction = QAction(QIcon(os.path.join(resources_path, 'exit24.png')), 'Exit', self) exitAction.setShortcut('Ctrl+Q') exitAction.setStatusTip('Exit application') exitAction.triggered.connect(self.close) settingsAction = QAction( QIcon(os.path.join(resources_path, 'settings24.png')), 'Settings', self) settingsAction.setShortcut('Ctrl+Alt+S') settingsAction.setStatusTip('Edit settings') settingsAction.triggered.connect(self.edit_settings) helpAction = QAction( QIcon(os.path.join(resources_path, 'help_book24.png')), 'User\'s Manual', self) helpAction.setShortcut('F1') helpAction.setStatusTip('User\'s Manual') helpAction.triggered.connect(self.show_user_manual_slot) helpSDAction = QAction( QIcon(os.path.join(resources_path, 'gear24.png')), 'Settings Dialog', self) helpSDAction.setShortcut('Ctrl+F1') helpSDAction.setStatusTip('Settings Dialog Help') helpSDAction.triggered.connect(self.show_setting_dialog_help_slot) helpHKAction = QAction( QIcon(os.path.join(resources_path, 'rocket24.png')), 'Hotkeys', self) helpHKAction.setShortcut('Shift+F1') helpHKAction.setStatusTip('Hotkeys Help') helpHKAction.triggered.connect(self.show_hotkeys_help_slot) self.statusBar().showMessage('Ready') #-------------------------------------------- # # Main Menu # menubar = self.menuBar() fileMenu = menubar.addMenu('&File') fileMenu.addAction(openAction) fileMenu.addAction(saveAction) fileMenu.addAction(saveAsAction) fileMenu.addAction(exitAction) #-------------------------------------------- # # Options Menu # optionsMenu = menubar.addMenu('&Options') optionsMenu.addAction(settingsAction) #-------------------------------------------- # # Help Menu # helpMenu = menubar.addMenu('&Help') helpMenu.addAction(helpAction) helpMenu.addAction(helpSDAction) helpMenu.addAction(helpHKAction) #-------------------------------------------- # # Toolbar # toolbar = self.addToolBar('Exit') toolbar.addAction(exitAction) toolbar.addAction(openAction) toolbar.addAction(saveAction) toolbar.addAction(saveAsAction) toolbar.addAction(settingsAction) toolbar.addAction(helpAction) #---------------------------------------------------- # # Settings Dialog # #---------------------------------------------------- # # Components Table # self.CmpTabBox = QGroupBox('Components', self) self.CmpTabLayout = QVBoxLayout(self.CmpTabBox) self.CmpTabLayout.setContentsMargins(4, 10, 4, 4) self.CmpTabLayout.setSpacing(10) self.CmpTabLayout.setSizeConstraint(QVBoxLayout.SetMaximumSize) self.CmpTable = ComponentsTable(self) #self.CmpChooseButton = QPushButton('Choose', self) self.CmpTabLayout.addWidget(self.CmpTable) #self.CmpTabLayout.addWidget(self.CmpChooseButton) #---------------------------------------------------- # # Selector # self.SelectorBox = QGroupBox('Selector', self) self.SelectorLayout = QVBoxLayout(self.SelectorBox) self.SelectorLayout.setContentsMargins(4, 10, 4, 4) self.SelectorLayout.setSpacing(2) self.SelectorBtnWidget = QWidget(self) self.SelectorBtnLayout = QHBoxLayout(self.SelectorBtnWidget) self.SelectorBtnLayout.setContentsMargins(4, 10, 4, 4) self.SelectorBtnLayout.setSpacing(10) self.Selector = Selector(self) self.SelApplyButton = QPushButton('Apply', self) self.SelApplyButton.setToolTip( 'Alt+S: Apply selection patterns to components') self.SelClearButton = QPushButton('Clear', self) self.SelClearButton.setToolTip('Alt+C: Clear selection patterns') self.SelTemplateButton = QPushButton('Use Component', self) self.SelTemplateButton.setToolTip( 'Alt+T: Use Selected Component As Template') self.SelectorLayout.addWidget(self.Selector) self.SelectorBtnLayout.addWidget(self.SelTemplateButton) self.SelectorBtnLayout.addWidget(self.SelApplyButton) self.SelectorBtnLayout.addWidget(self.SelClearButton) self.SelectorLayout.addWidget(self.SelectorBtnWidget) self.shortcutSelApply = QShortcut(QKeySequence(Qt.ALT + Qt.Key_S), self) self.shortcutSelApply.activated.connect(self.Selector.apply_slot) self.shortcutSelClear = QShortcut(QKeySequence(Qt.ALT + Qt.Key_C), self) self.shortcutSelClear.activated.connect(self.Selector.clear_slot) self.shortcutSelTemplate = QShortcut(QKeySequence(Qt.ALT + Qt.Key_T), self) self.shortcutSelTemplate.activated.connect( self.Selector.use_comp_as_template_slot) #---------------------------------------------------- # # Inspector # self.Inspector = Inspector(self) self.FieldInspector = FieldInspector(self) self.InspectorBtnWidget = QWidget(self) self.InspectorBtnLayout = QHBoxLayout(self.InspectorBtnWidget) self.InspectorBtnLayout.setContentsMargins(4, 10, 4, 4) self.InspectorBtnLayout.setSpacing(10) self.AddUserProperty = QPushButton('Add Property', self) self.AddUserProperty.setToolTip('Alt+A: Add new user property') self.DeleteUserProperty = QPushButton('Delete Property', self) self.DeleteUserProperty.setToolTip('Alt+Delete: Delete user property') self.RenameUserProperty = QPushButton('Rename Property', self) self.RenameUserProperty.setToolTip('Alt+R: Rename user property') self.InspectorBox = QGroupBox('Inspector', self) self.InspectorSplit = QSplitter(Qt.Vertical, self) self.InspectorLayout = QVBoxLayout(self.InspectorBox) self.InspectorLayout.setContentsMargins(4, 10, 4, 4) self.InspectorLayout.setSpacing(2) self.InspectorSplit.addWidget(self.Inspector) self.InspectorSplit.addWidget(self.FieldInspector) self.InspectorLayout.addWidget(self.InspectorSplit) self.InspectorBtnLayout.addWidget(self.AddUserProperty) self.InspectorBtnLayout.addWidget(self.DeleteUserProperty) self.InspectorBtnLayout.addWidget(self.RenameUserProperty) self.InspectorLayout.addWidget(self.InspectorBtnWidget) self.shortcutSelApply = QShortcut(QKeySequence(Qt.ALT + Qt.Key_A), self) self.shortcutSelApply.activated.connect(self.add_user_property) self.shortcutSelApply = QShortcut(QKeySequence(Qt.ALT + Qt.Key_Delete), self) self.shortcutSelApply.activated.connect(self.remove_user_property) self.shortcutSelApply = QShortcut(QKeySequence(Qt.ALT + Qt.Key_R), self) self.shortcutSelApply.activated.connect(self.rename_user_property) #---------------------------------------------------- self.Splitter = QSplitter(self) self.Splitter.addWidget(self.CmpTabBox) self.Splitter.addWidget(self.SelectorBox) self.Splitter.addWidget(self.InspectorBox) self.centralWidget().layout().addWidget(self.Splitter) #---------------------------------------------------- # # Signals and Slots connections # self.CmpTable.cells_chosen.connect(self.Inspector.load_cmp) self.CmpTable.cells_chosen.connect(self.Selector.comp_template_slot) self.CmpTable.file_load.connect(self.file_loaded_slot) self.CmpTable.cmps_updated.connect(self.Selector.process_comps_slot) self.CmpTable.cmps_selected.connect(self.set_status_text_slot) self.SelApplyButton.clicked.connect(self.Selector.apply_slot) self.SelClearButton.clicked.connect(self.Selector.clear_slot) self.SelTemplateButton.clicked.connect( self.Selector.use_comp_as_template_slot) self.Selector.select_comps_signal.connect( self.CmpTable.select_comps_slot) self.Inspector.load_field.connect(self.FieldInspector.load_field_slot) self.Inspector.update_comps.connect(self.data_changed_slot) self.Inspector.update_comps.connect(self.CmpTable.update_cmp_list_slot) self.FieldInspector.data_changed.connect(self.data_changed_slot) CmpMgr.file_saved.connect(self.file_saved_slot) self.CmpTable.mouse_click.connect(self.mouse_change_tool) self.Inspector.mouse_click.connect(self.mouse_change_tool) self.FieldInspector.mouse_click.connect(self.mouse_change_tool) self.Inspector.header().sectionResized.connect( self.FieldInspector.column_resize) self.AddUserProperty.clicked.connect(self.add_user_property) self.DeleteUserProperty.clicked.connect(self.remove_user_property) self.RenameUserProperty.clicked.connect(self.rename_user_property) #---------------------------------------------------- self.ToolList = [] self.ToolList.append(self.CmpTable) self.ToolList.append(self.Selector) self.ToolList.append(self.Inspector) self.ToolList.append(self.FieldInspector) self.ToolIndex = 0 #---------------------------------------------------- # # Window # self.setWindowTitle(self.PROGRAM_NAME) Settings = QSettings('kicad-tools', 'Schematic Component Manager') #print(Settings.allKeys()) if Settings.contains('geometry'): self.restoreGeometry(Settings.value('geometry')) else: self.setGeometry(100, 100, 1024, 768) if Settings.contains('cmptable'): w0, w1 = Settings.value('cmptable') self.CmpTable.setColumnWidth(0, int(w0)) self.CmpTable.setColumnWidth(1, int(w1)) if Settings.contains('selector'): w0, w1 = Settings.value('selector') self.Selector.setColumnWidth(0, int(w0)) self.Selector.setColumnWidth(1, int(w1)) if Settings.contains('inspector'): w0, w1 = Settings.value('inspector') self.Inspector.setColumnWidth(0, int(w0)) self.Inspector.setColumnWidth(1, int(w1)) self.FieldInspector.setColumnWidth(0, int(w0)) self.FieldInspector.setColumnWidth(1, int(w1)) #self.Inspector.setColumnWidth( 2, int(w2) ) if Settings.contains('splitter'): self.Splitter.restoreState(Settings.value('splitter')) if Settings.contains('inssplitter'): self.InspectorSplit.restoreState(Settings.value('inssplitter')) #---------------------------------------------------- # # Process command line arguments # if len(sys.argv) > 1: fname = sys.argv[1] if os.path.exists(fname): self.CmpTable.load_file(fname) else: print('E: input file "' + fname + '"does not exist') self.show() #--------------------------------------------------------------------------- def closeEvent(self, event): Settings = QSettings('kicad-tools', 'Schematic Component Manager') Settings.setValue('geometry', self.saveGeometry()) Settings.setValue( 'cmptable', [self.CmpTable.columnWidth(0), self.CmpTable.columnWidth(1)]) Settings.setValue( 'selector', [self.Selector.columnWidth(0), self.Selector.columnWidth(1)]) Settings.setValue( 'inspector', [self.Inspector.columnWidth(0), self.Inspector.columnWidth(1)]) Settings.setValue('splitter', self.Splitter.saveState()) Settings.setValue('inssplitter', self.InspectorSplit.saveState()) QWidget.closeEvent(self, event) #--------------------------------------------------------------------------- def open_file(self): #filename = QFileDialog.getOpenFileName(self, 'Open schematic file', '/opt/cad/kicad', 'KiCad Schematic Files (*.sch)') dialog = QFileDialog(self) dialog.setFileMode(QFileDialog.ExistingFile) dialog.setNameFilter('KiCad Schematic Files (*.sch)') filenames = [] if dialog.exec_(): filenames = dialog.selectedFiles() if len(filenames) == 0: return CmpMgr.set_curr_file_path(filenames[0]) self.CmpTable.load_file(filenames[0]) #--------------------------------------------------------------------------- def save_file(self): self.FieldInspector.save_fields() self.Inspector.save_cmps() curr_file = CmpMgr.curr_file_path() message = 'Save File "' + curr_file + '"' print(message) self.statusBar().showMessage(message) CmpMgr.save_file(curr_file) #--------------------------------------------------------------------------- def save_file_as(self): self.Inspector.save_cmps() self.FieldInspector.save_fields() filenames = QFileDialog.getSaveFileName( self, 'Save File As...', '', 'KiCad Schematic Files (*.sch)') if filenames[0] == '': return print('Save File As "' + filenames[0] + '"') CmpMgr.save_file(filenames[0]) CmpMgr.set_curr_file_path(filenames[0]) #--------------------------------------------------------------------------- def file_loaded_slot(self): text = CmpMgr.curr_file_path() self.set_title(text) #--------------------------------------------------------------------------- def data_changed_slot(self): text = CmpMgr.curr_file_path() + ' *' self.set_title(text) #--------------------------------------------------------------------------- def file_saved_slot(self): text = CmpMgr.curr_file_path() self.set_title(text) #--------------------------------------------------------------------------- def set_title(self, text=''): text = ' - ' + text if len(text) > 0 else '' self.setWindowTitle(self.PROGRAM_NAME + ' v' + VERSION + text) #--------------------------------------------------------------------------- def set_status_text_slot(self, text): self.statusBar().showMessage(text) #--------------------------------------------------------------------------- def edit_settings(self): print('edit settings') SettingsDialog = TSettingsDialog(self) SettingsDialog.resize(400, 400) SettingsDialog.Tabs.setMinimumWidth(800) SettingsDialog.show() #--------------------------------------------------------------------------- def show_user_manual_slot(self): help = THelpForm(self, 'User\'s Manual', 'main.html') #--------------------------------------------------------------------------- def show_setting_dialog_help_slot(self): help = THelpForm(self, 'Settings Dialog', 'settings.html') #--------------------------------------------------------------------------- def show_hotkeys_help_slot(self): help = THelpForm(self, 'Hotkeys', 'hotkeys.html')
def __init__(self, parent, view, row, link_delegate): QDialog.__init__(self, parent) self.normal_brush = QBrush(Qt.white) self.marked_brush = QBrush(Qt.lightGray) self.marked = None self.gui = parent self.splitter = QSplitter(self) self._l = l = QVBoxLayout(self) self.setLayout(l) l.addWidget(self.splitter) self.cover = CoverView(self) self.cover.resizeEvent = self.cover_view_resized self.cover.cover_changed.connect(self.cover_changed) self.cover_pixmap = None self.cover.sizeHint = self.details_size_hint self.splitter.addWidget(self.cover) self.details = Details(parent.book_details.book_info, self) self.details.page().setLinkDelegationPolicy( self.details.page().DelegateAllLinks) self.details.linkClicked.connect(self.link_clicked) s = self.details.page().settings() s.setAttribute(s.JavascriptEnabled, False) self.css = css() self.link_delegate = link_delegate self.details.setAttribute(Qt.WA_OpaquePaintEvent, False) palette = self.details.palette() self.details.setAcceptDrops(False) palette.setBrush(QPalette.Base, Qt.transparent) self.details.page().setPalette(palette) self.c = QWidget(self) self.c.l = l2 = QGridLayout(self.c) self.c.setLayout(l2) l2.addWidget(self.details, 0, 0, 1, -1) self.splitter.addWidget(self.c) self.fit_cover = QCheckBox(_('Fit &cover within view'), self) self.fit_cover.setChecked( gprefs.get('book_info_dialog_fit_cover', True)) l2.addWidget(self.fit_cover, l2.rowCount(), 0, 1, -1) self.previous_button = QPushButton(QIcon(I('previous.png')), _('&Previous'), self) self.previous_button.clicked.connect(self.previous) l2.addWidget(self.previous_button, l2.rowCount(), 0) self.next_button = QPushButton(QIcon(I('next.png')), _('&Next'), self) self.next_button.clicked.connect(self.next) l2.addWidget(self.next_button, l2.rowCount() - 1, 1) self.view = view self.current_row = None self.refresh(row) self.view.model().new_bookdisplay_data.connect(self.slave) self.fit_cover.stateChanged.connect(self.toggle_cover_fit) self.ns = QShortcut(QKeySequence('Alt+Right'), self) self.ns.activated.connect(self.next) self.ps = QShortcut(QKeySequence('Alt+Left'), self) self.ps.activated.connect(self.previous) self.next_button.setToolTip( _('Next [%s]') % unicode(self.ns.key().toString(QKeySequence.NativeText))) self.previous_button.setToolTip( _('Previous [%s]') % unicode(self.ps.key().toString(QKeySequence.NativeText))) geom = QCoreApplication.instance().desktop().availableGeometry(self) screen_height = geom.height() - 100 screen_width = geom.width() - 100 self.resize(max(int(screen_width / 2), 700), screen_height) saved_layout = gprefs.get('book_info_dialog_layout', None) if saved_layout is not None: try: self.restoreGeometry(saved_layout[0]) self.splitter.restoreState(saved_layout[1]) except Exception: pass
def __init__(self, parent, view, row, link_delegate): QDialog.__init__(self, parent) self.normal_brush = QBrush(Qt.white) self.marked_brush = QBrush(Qt.lightGray) self.marked = None self.gui = parent self.splitter = QSplitter(self) self._l = l = QVBoxLayout(self) self.setLayout(l) l.addWidget(self.splitter) self.cover = CoverView(self) self.cover.resizeEvent = self.cover_view_resized self.cover.cover_changed.connect(self.cover_changed) self.cover_pixmap = None self.cover.sizeHint = self.details_size_hint self.splitter.addWidget(self.cover) self.details = Details(parent.book_details.book_info, self) self.details.page().setLinkDelegationPolicy(self.details.page().DelegateAllLinks) self.details.linkClicked.connect(self.link_clicked) s = self.details.page().settings() s.setAttribute(s.JavascriptEnabled, False) self.css = css() self.link_delegate = link_delegate self.details.setAttribute(Qt.WA_OpaquePaintEvent, False) palette = self.details.palette() self.details.setAcceptDrops(False) palette.setBrush(QPalette.Base, Qt.transparent) self.details.page().setPalette(palette) self.c = QWidget(self) self.c.l = l2 = QGridLayout(self.c) self.c.setLayout(l2) l2.addWidget(self.details, 0, 0, 1, -1) self.splitter.addWidget(self.c) self.fit_cover = QCheckBox(_('Fit &cover within view'), self) self.fit_cover.setChecked(gprefs.get('book_info_dialog_fit_cover', True)) l2.addWidget(self.fit_cover, l2.rowCount(), 0, 1, -1) self.previous_button = QPushButton(QIcon(I('previous.png')), _('&Previous'), self) self.previous_button.clicked.connect(self.previous) l2.addWidget(self.previous_button, l2.rowCount(), 0) self.next_button = QPushButton(QIcon(I('next.png')), _('&Next'), self) self.next_button.clicked.connect(self.next) l2.addWidget(self.next_button, l2.rowCount() - 1, 1) self.view = view self.current_row = None self.refresh(row) self.view.model().new_bookdisplay_data.connect(self.slave) self.fit_cover.stateChanged.connect(self.toggle_cover_fit) self.ns = QShortcut(QKeySequence('Alt+Right'), self) self.ns.activated.connect(self.next) self.ps = QShortcut(QKeySequence('Alt+Left'), self) self.ps.activated.connect(self.previous) self.next_button.setToolTip(_('Next [%s]')% unicode(self.ns.key().toString(QKeySequence.NativeText))) self.previous_button.setToolTip(_('Previous [%s]')% unicode(self.ps.key().toString(QKeySequence.NativeText))) geom = QCoreApplication.instance().desktop().availableGeometry(self) screen_height = geom.height() - 100 screen_width = geom.width() - 100 self.resize(max(int(screen_width/2), 700), screen_height) saved_layout = gprefs.get('book_info_dialog_layout', None) if saved_layout is not None: try: self.restoreGeometry(saved_layout[0]) self.splitter.restoreState(saved_layout[1]) except Exception: pass
def register_shortcuts(self, window): self.__keyCtrlF = QShortcut(window) self.__keyCtrlF.setKey(Qt.CTRL + Qt.Key_F) self.__keyCtrlF.activated.connect(lambda: self.__activate_floating())
class MetadataSingleDialogBase(ResizableDialog): view_format = pyqtSignal(object, object) cc_two_column = tweaks['metadata_single_use_2_cols_for_custom_fields'] one_line_comments_toolbar = False use_toolbutton_for_config_metadata = True def __init__(self, db, parent=None, editing_multiple=False): self.db = db self.changed = set() self.books_to_refresh = set() self.rows_to_refresh = set() self.metadata_before_fetch = None self.editing_multiple = editing_multiple self.comments_edit_state_at_apply = {} ResizableDialog.__init__(self, parent) def setupUi(self, *args): # {{{ self.resize(990, 670) self.download_shortcut = QShortcut(self) self.download_shortcut.setKey(QKeySequence('Ctrl+D', QKeySequence.PortableText)) p = self.parent() if hasattr(p, 'keyboard'): kname = u'Interface Action: Edit Metadata (Edit Metadata) : menu action : download' sc = p.keyboard.keys_map.get(kname, None) if sc: self.download_shortcut.setKey(sc[0]) self.swap_title_author_shortcut = s = QShortcut(self) s.setKey(QKeySequence('Alt+Down', QKeySequence.PortableText)) self.button_box = bb = QDialogButtonBox(self) self.button_box.accepted.connect(self.accept) self.button_box.rejected.connect(self.reject) self.next_button = QPushButton(QIcon(I('forward.png')), _('Next'), self) self.next_button.setShortcut(QKeySequence('Alt+Right')) self.next_button.clicked.connect(self.next_clicked) self.prev_button = QPushButton(QIcon(I('back.png')), _('Previous'), self) self.prev_button.setShortcut(QKeySequence('Alt+Left')) self.button_box.addButton(self.prev_button, bb.ActionRole) self.button_box.addButton(self.next_button, bb.ActionRole) self.prev_button.clicked.connect(self.prev_clicked) bb.setStandardButtons(bb.Ok|bb.Cancel) bb.button(bb.Ok).setDefault(True) self.scroll_area = QScrollArea(self) self.scroll_area.setFrameShape(QScrollArea.NoFrame) self.scroll_area.setWidgetResizable(True) self.central_widget = QTabWidget(self) self.scroll_area.setWidget(self.central_widget) self.l = QVBoxLayout(self) self.setLayout(self.l) self.l.addWidget(self.scroll_area) ll = self.button_box_layout = QHBoxLayout() self.l.addLayout(ll) ll.addSpacing(10) ll.addWidget(self.button_box) self.setWindowIcon(QIcon(I('edit_input.png'))) self.setWindowTitle(BASE_TITLE) self.create_basic_metadata_widgets() if len(self.db.custom_column_label_map): self.create_custom_metadata_widgets() self.comments_edit_state_at_apply = {self.comments:None} self.do_layout() geom = gprefs.get('metasingle_window_geometry3', None) if geom is not None: self.restoreGeometry(bytes(geom)) # }}} def create_basic_metadata_widgets(self): # {{{ self.basic_metadata_widgets = [] self.languages = LanguagesEdit(self) self.basic_metadata_widgets.append(self.languages) self.title = TitleEdit(self) self.title.textChanged.connect(self.update_window_title) self.deduce_title_sort_button = QToolButton(self) self.deduce_title_sort_button.setToolTip( _('Automatically create the title sort entry based on the current ' 'title entry.\nUsing this button to create title sort will ' 'change title sort from red to green.')) self.deduce_title_sort_button.setWhatsThis( self.deduce_title_sort_button.toolTip()) self.title_sort = TitleSortEdit(self, self.title, self.deduce_title_sort_button, self.languages) self.basic_metadata_widgets.extend([self.title, self.title_sort]) self.deduce_author_sort_button = b = RightClickButton(self) b.setToolTip('<p>' + _('Automatically create the author sort entry based on the current ' 'author entry. Using this button to create author sort will ' 'change author sort from red to green. There is a menu of ' 'functions available under this button. Click and hold ' 'on the button to see it.') + '</p>') if isosx: # Workaround for https://bugreports.qt-project.org/browse/QTBUG-41017 class Menu(QMenu): def mouseReleaseEvent(self, ev): ac = self.actionAt(ev.pos()) if ac is not None: ac.trigger() return QMenu.mouseReleaseEvent(self, ev) b.m = m = Menu() else: b.m = m = QMenu() ac = m.addAction(QIcon(I('forward.png')), _('Set author sort from author')) ac2 = m.addAction(QIcon(I('back.png')), _('Set author from author sort')) ac3 = m.addAction(QIcon(I('user_profile.png')), _('Manage authors')) ac4 = m.addAction(QIcon(I('next.png')), _('Copy author to author sort')) ac5 = m.addAction(QIcon(I('previous.png')), _('Copy author sort to author')) b.setMenu(m) self.authors = AuthorsEdit(self, ac3) self.author_sort = AuthorSortEdit(self, self.authors, b, self.db, ac, ac2, ac4, ac5) self.basic_metadata_widgets.extend([self.authors, self.author_sort]) self.swap_title_author_button = QToolButton(self) self.swap_title_author_button.setIcon(QIcon(I('swap.png'))) self.swap_title_author_button.setToolTip(_( 'Swap the author and title') + ' [%s]' % self.swap_title_author_shortcut.key().toString(QKeySequence.NativeText)) self.swap_title_author_button.clicked.connect(self.swap_title_author) self.swap_title_author_shortcut.activated.connect(self.swap_title_author_button.click) self.manage_authors_button = QToolButton(self) self.manage_authors_button.setIcon(QIcon(I('user_profile.png'))) self.manage_authors_button.setToolTip('<p>' + _( 'Manage authors. Use to rename authors and correct ' 'individual author\'s sort values') + '</p>') self.manage_authors_button.clicked.connect(self.authors.manage_authors) self.series = SeriesEdit(self) self.clear_series_button = QToolButton(self) self.clear_series_button.setToolTip( _('Clear series')) self.clear_series_button.clicked.connect(self.series.clear) self.series_index = SeriesIndexEdit(self, self.series) self.basic_metadata_widgets.extend([self.series, self.series_index]) self.formats_manager = FormatsManager(self, self.copy_fmt) # We want formats changes to be committed before title/author, as # otherwise we could have data loss if the title/author changed and the # user was trying to add an extra file from the old books directory. self.basic_metadata_widgets.insert(0, self.formats_manager) self.formats_manager.metadata_from_format_button.clicked.connect( self.metadata_from_format) self.formats_manager.cover_from_format_button.clicked.connect( self.cover_from_format) self.cover = Cover(self) self.cover.download_cover.connect(self.download_cover) self.basic_metadata_widgets.append(self.cover) self.comments = CommentsEdit(self, self.one_line_comments_toolbar) self.basic_metadata_widgets.append(self.comments) self.rating = RatingEdit(self) self.clear_ratings_button = QToolButton(self) self.clear_ratings_button.setToolTip(_('Clear rating')) self.clear_ratings_button.setIcon(QIcon(I('trash.png'))) self.clear_ratings_button.clicked.connect(self.rating.zero) self.basic_metadata_widgets.append(self.rating) self.tags = TagsEdit(self) self.tags_editor_button = QToolButton(self) self.tags_editor_button.setToolTip(_('Open Tag Editor')) self.tags_editor_button.setIcon(QIcon(I('chapters.png'))) self.tags_editor_button.clicked.connect(self.tags_editor) self.clear_tags_button = QToolButton(self) self.clear_tags_button.setToolTip(_('Clear all tags')) self.clear_tags_button.setIcon(QIcon(I('trash.png'))) self.clear_tags_button.clicked.connect(self.tags.clear) self.basic_metadata_widgets.append(self.tags) self.identifiers = IdentifiersEdit(self) self.basic_metadata_widgets.append(self.identifiers) self.clear_identifiers_button = QToolButton(self) self.clear_identifiers_button.setIcon(QIcon(I('trash.png'))) self.clear_identifiers_button.setToolTip(_('Clear Ids')) self.clear_identifiers_button.clicked.connect(self.identifiers.clear) self.paste_isbn_button = QToolButton(self) self.paste_isbn_button.setToolTip('<p>' + _('Paste the contents of the clipboard into the ' 'identifiers box prefixed with isbn:') + '</p>') self.paste_isbn_button.setIcon(QIcon(I('edit-paste.png'))) self.paste_isbn_button.clicked.connect(self.identifiers.paste_isbn) self.publisher = PublisherEdit(self) self.basic_metadata_widgets.append(self.publisher) self.timestamp = DateEdit(self) self.pubdate = PubdateEdit(self) self.basic_metadata_widgets.extend([self.timestamp, self.pubdate]) self.fetch_metadata_button = b = RightClickButton(self) # The following rigmarole is needed so that Qt gives the button the # same height as the other buttons in the dialog. There is no way to # center the text in a QToolButton with an icon, so we cant just set an # icon b.setIcon(QIcon(I('download-metadata.png'))) b.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) b.setMinimumHeight(b.sizeHint().height()) b.setIcon(QIcon()) b.setText(_('&Download metadata')), b.setPopupMode(b.DelayedPopup) b.setToolTip(_('Download metadata for this book [%s]') % self.download_shortcut.key().toString(QKeySequence.NativeText)) b.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)) self.fetch_metadata_button.clicked.connect(self.fetch_metadata) self.fetch_metadata_menu = m = QMenu(self.fetch_metadata_button) m.addAction(QIcon(I('edit-undo.png')), _('Undo last metadata download'), self.undo_fetch_metadata) self.fetch_metadata_button.setMenu(m) self.download_shortcut.activated.connect(self.fetch_metadata_button.click) font = self.fmb_font = QFont() font.setBold(True) self.fetch_metadata_button.setFont(font) if self.use_toolbutton_for_config_metadata: self.config_metadata_button = QToolButton(self) self.config_metadata_button.setIcon(QIcon(I('config.png'))) else: self.config_metadata_button = QPushButton(self) self.config_metadata_button.setText(_('Configure download metadata')) self.config_metadata_button.setIcon(QIcon(I('config.png'))) self.config_metadata_button.clicked.connect(self.configure_metadata) self.config_metadata_button.setToolTip( _('Change how calibre downloads metadata')) # }}} def create_custom_metadata_widgets(self): # {{{ self.custom_metadata_widgets_parent = w = QWidget(self) layout = QGridLayout() w.setLayout(layout) self.custom_metadata_widgets, self.__cc_spacers = \ populate_metadata_page(layout, self.db, None, parent=w, bulk=False, two_column=self.cc_two_column) self.__custom_col_layouts = [layout] for widget in self.custom_metadata_widgets: if isinstance(widget, Comments): self.comments_edit_state_at_apply[widget] = None # }}} def set_custom_metadata_tab_order(self, before=None, after=None): # {{{ sto = QWidget.setTabOrder if getattr(self, 'custom_metadata_widgets', []): ans = self.custom_metadata_widgets for i in range(len(ans)-1): if before is not None and i == 0: pass if len(ans[i+1].widgets) == 2: sto(ans[i].widgets[-1], ans[i+1].widgets[1]) else: sto(ans[i].widgets[-1], ans[i+1].widgets[0]) for c in range(2, len(ans[i].widgets), 2): sto(ans[i].widgets[c-1], ans[i].widgets[c+1]) if after is not None: pass # }}} def do_view_format(self, path, fmt): if path: self.view_format.emit(None, path) else: self.view_format.emit(self.book_id, fmt) def copy_fmt(self, fmt, f): self.db.copy_format_to(self.book_id, fmt, f, index_is_id=True) def do_layout(self): raise NotImplementedError() def __call__(self, id_): self.book_id = id_ self.books_to_refresh = set([]) self.metadata_before_fetch = None for widget in self.basic_metadata_widgets: widget.initialize(self.db, id_) for widget in getattr(self, 'custom_metadata_widgets', []): widget.initialize(id_) if callable(self.set_current_callback): self.set_current_callback(id_) # Commented out as it doesn't play nice with Next, Prev buttons # self.fetch_metadata_button.setFocus(Qt.OtherFocusReason) # Miscellaneous interaction methods {{{ def update_window_title(self, *args): title = self.title.current_val if len(title) > 50: title = title[:50] + u'\u2026' self.setWindowTitle(BASE_TITLE + ' - ' + title + ' - ' + _(' [%(num)d of %(tot)d]')%dict(num=self.current_row+1, tot=len(self.row_list))) def swap_title_author(self, *args): title = self.title.current_val self.title.current_val = authors_to_string(self.authors.current_val) self.authors.current_val = string_to_authors(title) self.title_sort.auto_generate() self.author_sort.auto_generate() def tags_editor(self, *args): self.tags.edit(self.db, self.book_id) def metadata_from_format(self, *args): mi, ext = self.formats_manager.get_selected_format_metadata(self.db, self.book_id) if mi is not None: self.update_from_mi(mi) def get_pdf_cover(self): pdfpath = self.formats_manager.get_format_path(self.db, self.book_id, 'pdf') from calibre.gui2.metadata.pdf_covers import PDFCovers d = PDFCovers(pdfpath, parent=self) if d.exec_() == d.Accepted: cpath = d.cover_path if cpath: with open(cpath, 'rb') as f: self.update_cover(f.read(), 'PDF') d.cleanup() def cover_from_format(self, *args): ext = self.formats_manager.get_selected_format() if ext is None: return if ext == 'pdf': return self.get_pdf_cover() try: mi, ext = self.formats_manager.get_selected_format_metadata(self.db, self.book_id) except (IOError, OSError) as err: if getattr(err, 'errno', None) == errno.EACCES: # Permission denied import traceback fname = err.filename if err.filename else 'file' error_dialog(self, _('Permission denied'), _('Could not open %s. Is it being used by another' ' program?')%fname, det_msg=traceback.format_exc(), show=True) return raise if mi is None: return cdata = None if mi.cover and os.access(mi.cover, os.R_OK): cdata = open(mi.cover).read() elif mi.cover_data[1] is not None: cdata = mi.cover_data[1] if cdata is None: error_dialog(self, _('Could not read cover'), _('Could not read cover from %s format')%ext).exec_() return self.update_cover(cdata, ext) def update_cover(self, cdata, fmt): orig = self.cover.current_val self.cover.current_val = cdata if self.cover.current_val is None: self.cover.current_val = orig return error_dialog(self, _('Could not read cover'), _('The cover in the %s format is invalid')%fmt, show=True) return def update_from_mi(self, mi, update_sorts=True, merge_tags=True, merge_comments=False): fw = self.focusWidget() if not mi.is_null('title'): self.title.set_value(mi.title) if update_sorts: self.title_sort.auto_generate() if not mi.is_null('authors'): self.authors.set_value(mi.authors) if not mi.is_null('author_sort'): self.author_sort.set_value(mi.author_sort) elif update_sorts and not mi.is_null('authors'): self.author_sort.auto_generate() if not mi.is_null('rating'): try: self.rating.set_value(mi.rating) except: pass if not mi.is_null('publisher'): self.publisher.set_value(mi.publisher) if not mi.is_null('tags'): old_tags = self.tags.current_val tags = mi.tags if mi.tags else [] if old_tags and merge_tags: ltags, lotags = {t.lower() for t in tags}, {t.lower() for t in old_tags} tags = [t for t in tags if t.lower() in ltags-lotags] + old_tags self.tags.set_value(tags) if not mi.is_null('identifiers'): current = self.identifiers.current_val current.update(mi.identifiers) self.identifiers.set_value(current) if not mi.is_null('pubdate'): self.pubdate.set_value(mi.pubdate) if not mi.is_null('series') and mi.series.strip(): self.series.set_value(mi.series) if mi.series_index is not None: self.series_index.reset_original() self.series_index.set_value(float(mi.series_index)) if not mi.is_null('languages'): langs = [canonicalize_lang(x) for x in mi.languages] langs = [x for x in langs if x is not None] if langs: self.languages.set_value(langs) if mi.comments and mi.comments.strip(): val = mi.comments if val and merge_comments: cval = self.comments.current_val if cval: val = merge_two_comments(cval, val) self.comments.set_value(val) if fw is not None: fw.setFocus(Qt.OtherFocusReason) def fetch_metadata(self, *args): d = FullFetch(self.cover.pixmap(), self) ret = d.start(title=self.title.current_val, authors=self.authors.current_val, identifiers=self.identifiers.current_val) if ret == d.Accepted: self.metadata_before_fetch = {f:getattr(self, f).current_val for f in fetched_fields} from calibre.ebooks.metadata.sources.prefs import msprefs mi = d.book dummy = Metadata(_('Unknown')) for f in msprefs['ignore_fields']: if ':' not in f: setattr(mi, f, getattr(dummy, f)) if mi is not None: pd = mi.pubdate if pd is not None: # Put the downloaded published date into the local timezone # as we discard time info and the date is timezone # invariant. This prevents the as_local_timezone() call in # update_from_mi from changing the pubdate mi.pubdate = datetime(pd.year, pd.month, pd.day, tzinfo=local_tz) self.update_from_mi(mi, merge_comments=msprefs['append_comments']) if d.cover_pixmap is not None: self.metadata_before_fetch['cover'] = self.cover.current_val self.cover.current_val = pixmap_to_data(d.cover_pixmap) def undo_fetch_metadata(self): if self.metadata_before_fetch is None: return error_dialog(self, _('No downloaded metadata'), _( 'There is no downloaded metadata to undo'), show=True) for field, val in self.metadata_before_fetch.iteritems(): getattr(self, field).current_val = val self.metadata_before_fetch = None def configure_metadata(self): from calibre.gui2.preferences import show_config_widget gui = self.parent() show_config_widget('Sharing', 'Metadata download', parent=self, gui=gui, never_shutdown=True) def download_cover(self, *args): from calibre.gui2.metadata.single_download import CoverFetch d = CoverFetch(self.cover.pixmap(), self) ret = d.start(self.title.current_val, self.authors.current_val, self.identifiers.current_val) if ret == d.Accepted: if d.cover_pixmap is not None: self.cover.current_val = pixmap_to_data(d.cover_pixmap) # }}} def to_book_metadata(self): mi = Metadata(_('Unknown')) if self.db is None: return mi mi.set_all_user_metadata(self.db.field_metadata.custom_field_metadata()) for widget in self.basic_metadata_widgets: widget.apply_to_metadata(mi) for widget in getattr(self, 'custom_metadata_widgets', []): widget.apply_to_metadata(mi) return mi def apply_changes(self): self.changed.add(self.book_id) if self.db is None: # break_cycles has already been called, don't know why this should # happen but a user reported it return True self.comments_edit_state_at_apply = {w:w.tab for w in self.comments_edit_state_at_apply} for widget in self.basic_metadata_widgets: try: if hasattr(widget, 'validate_for_commit'): title, msg, det_msg = widget.validate_for_commit() if title is not None: error_dialog(self, title, msg, det_msg=det_msg, show=True) return False widget.commit(self.db, self.book_id) self.books_to_refresh |= getattr(widget, 'books_to_refresh', set()) except (IOError, OSError) as err: if getattr(err, 'errno', None) == errno.EACCES: # Permission denied import traceback fname = getattr(err, 'filename', None) p = 'Locked file: %s\n\n'%fname if fname else '' error_dialog(self, _('Permission denied'), _('Could not change the on disk location of this' ' book. Is it open in another program?'), det_msg=p+traceback.format_exc(), show=True) return False raise for widget in getattr(self, 'custom_metadata_widgets', []): self.books_to_refresh |= widget.commit(self.book_id) self.db.commit() rows = self.db.refresh_ids(list(self.books_to_refresh)) if rows: self.rows_to_refresh |= set(rows) return True def accept(self): self.save_state() if not self.apply_changes(): return if self.editing_multiple and self.current_row != len(self.row_list) - 1: num = len(self.row_list) - 1 - self.current_row from calibre.gui2 import question_dialog if not question_dialog( self, _('Are you sure?'), _('There are still %d more books to edit in this set.' ' Are you sure you want to stop? Use the Next button' ' instead of the OK button to move through books in the set.') % num, yes_text=_('&Stop editing'), no_text=_('&Continue editing'), yes_icon='dot_red.png', no_icon='dot_green.png', default_yes=False, skip_dialog_name='edit-metadata-single-confirm-ok-on-multiple'): return self.do_one(delta=1, apply_changes=False) ResizableDialog.accept(self) def reject(self): self.save_state() ResizableDialog.reject(self) def save_state(self): try: gprefs['metasingle_window_geometry3'] = bytearray(self.saveGeometry()) except: # Weird failure, see https://bugs.launchpad.net/bugs/995271 import traceback traceback.print_exc() # Dialog use methods {{{ def start(self, row_list, current_row, view_slot=None, set_current_callback=None): self.row_list = row_list self.current_row = current_row if view_slot is not None: self.view_format.connect(view_slot) self.set_current_callback = set_current_callback self.do_one(apply_changes=False) ret = self.exec_() self.break_cycles() return ret def next_clicked(self): if not self.apply_changes(): return self.do_one(delta=1, apply_changes=False) def prev_clicked(self): if not self.apply_changes(): return self.do_one(delta=-1, apply_changes=False) def do_one(self, delta=0, apply_changes=True): if apply_changes: self.apply_changes() self.current_row += delta self.update_window_title() prev = next_ = None if self.current_row > 0: prev = self.db.title(self.row_list[self.current_row-1]) if self.current_row < len(self.row_list) - 1: next_ = self.db.title(self.row_list[self.current_row+1]) if next_ is not None: tip = (_('Save changes and edit the metadata of %s')+ ' [Alt+Right]')%next_ self.next_button.setToolTip(tip) self.next_button.setEnabled(next_ is not None) if prev is not None: tip = (_('Save changes and edit the metadata of %s')+ ' [Alt+Left]')%prev self.prev_button.setToolTip(tip) self.prev_button.setEnabled(prev is not None) self.button_box.button(self.button_box.Ok).setDefault(True) self.button_box.button(self.button_box.Ok).setFocus(Qt.OtherFocusReason) self(self.db.id(self.row_list[self.current_row])) for w, state in self.comments_edit_state_at_apply.iteritems(): if state == 'code': w.tab = 'code' def break_cycles(self): # Break any reference cycles that could prevent python # from garbage collecting this dialog self.set_current_callback = self.db = None self.metadata_before_fetch = None def disconnect(signal): try: signal.disconnect() except: pass # Fails if view format was never connected disconnect(self.view_format) for b in ('next_button', 'prev_button'): x = getattr(self, b, None) if x is not None: disconnect(x.clicked) for widget in self.basic_metadata_widgets: bc = getattr(widget, 'break_cycles', None) if bc is not None and callable(bc): bc() for widget in getattr(self, 'custom_metadata_widgets', []): widget.break_cycles()
class FloatingWindow(QTWSPlugin): def __init__(self, config): super().__init__("FloatingWindow") self.window = None self.config = config self.floating_toggle = None self.is_window_floating = False self.message_box_shown = False def window_setup(self, window: QTWSMainWindow): self.window = window def register_shortcuts(self, window): self.__keyCtrlF = QShortcut(window) self.__keyCtrlF.setKey(Qt.CTRL + Qt.Key_F) self.__keyCtrlF.activated.connect(lambda: self.__activate_floating()) def add_menu_items(self, menu: QMenu): self.floating_toggle = None self.floating_toggle = QAction(QIcon.fromTheme("file-zoom-out"), "Enable floating window") self.floating_toggle.triggered.connect( lambda: self.__activate_floating()) menu.addAction(self.floating_toggle) def close_event(self, window: QTWSMainWindow, event): if self.is_window_floating: self.__deactivate_floating() event.ignore() def __activate_floating(self): if self.is_window_floating: return self.screen = QApplication.instance().primaryScreen().geometry() self.previous_x = self.window.x() self.previous_y = self.window.y() self.previous_width = self.window.width() self.previous_height = self.window.height() self.previous_max_width = self.window.maximumWidth() self.previous_max_height = self.window.maximumHeight() self.previously_maximized = self.window.isMaximized() self.previously_fullscreen = self.window.isFullScreen() self.window.set_always_on_top(True) self.window.setMaximumWidth(self.screen.width() / 2) self.window.setMaximumHeight(self.screen.height() / 2) self.window.resize(self.screen.width() / 3, self.screen.height() / 3) self.window.set_mouse_enter_callback(lambda e: self.__on_focus(e)) self.window.set_maximizable(False) self.__on_focus(None) self.is_window_floating = True if not self.message_box_shown: box = QMessageBox() box.setWindowTitle("Floating mode activated") box.setText("Just close the window to disable the floating mode.") box.setIcon(QMessageBox.Information) box.exec_() self.message_box_shown = True def __deactivate_floating(self): self.window.setMaximumWidth(self.previous_max_width) self.window.setMaximumHeight(self.previous_max_height) self.window.resize(self.previous_width, self.previous_height) self.window.move(self.previous_x, self.previous_y) if not self.config.always_on_top: self.window.set_always_on_top(False) self.window.set_maximizable(True) self.window.set_mouse_enter_callback(None) self.is_window_floating = False self.window.showNormal() if self.previously_maximized: self.window.showMaximized() if self.previously_fullscreen: self.window.activate_fullscreen() def __on_focus(self, event): self.screen = QApplication.instance().primaryScreen().geometry() y = self.screen.height() - self.window.height() if event is not None and event.globalX() < self.window.width(): x = self.screen.width() - self.window.width() self.window.move(x, y) else: self.window.move(0, y)
def __init__(self): QWidget.__init__(self, None) # Set pixmaps resource before Main Window initialized self._resource = os.path.join(ctx.consts.theme_dir, ctx.flags.theme, ctx.consts.pixmaps_resource_file) if os.path.exists(self._resource): resource = QResource() resource.registerResource(self._resource) else: raise yali.Error, _("Pixmaps resources file doesn't exists") self.ui = Ui_YaliMain() self.ui.setupUi(self) self.font = 10 self.animation_type = None self.screens = None self.screens_content = None self.pds_helper = HelpWidget(self.ui.scrollAreaWidgetContents) # shortcut to open help self.help_shortcut = QShortcut(QKeySequence(Qt.Key_F1), self) # shortcut to open debug window #self.debugShortCut = QtWidgets.QShortcut(QtWidgets.QKeySequence(Qt.Key_F2),self) # something funny self.tetris_shortcut = QShortcut(QKeySequence(Qt.Key_F6), self) self.cursor_shortcut = QShortcut(QKeySequence(Qt.Key_F7), self) self.theme_shortcut = QShortcut(QKeySequence(Qt.Key_F8), self) # shortcut to open a console self.console_shortcut = QShortcut(QKeySequence(Qt.Key_F11), self) # set style self._style = os.path.join(ctx.consts.theme_dir, ctx.flags.theme, ctx.consts.style_file) if os.path.exists(self._style): self.updateStyle() else: raise yali.Error, _("Style file doesn't exists") # set screens content release_file = os.path.join(ctx.consts.branding_dir, ctx.flags.branding, ctx.consts.release_file) if os.path.exists(release_file): self.screens_content = yali.util.parse_branding_screens( release_file) else: raise yali.Error, _("Release file doesn't exists") # move one step at a time self.step_increment = 1 # ToolButton Popup Menu self.menu = QMenu() self.shutdown = self.menu.addAction( QIcon(QPixmap(":/images/system-shutdown.png")), _("Turn Off Computer")) self.reboot = self.menu.addAction( QIcon(QPixmap(":/images/system-reboot.png")), _("Restart Computer")) self.restart = self.menu.addAction( QIcon(QPixmap(":/images/system-yali-reboot.png")), _("Restart YALI")) #self.menu.setDefaultAction(self.shutdown) self.ui.system_menu.setMenu(self.menu) self.ui.system_menu.setDefaultAction(self.shutdown) # Main Slots self.help_shortcut.activated.connect(self.pds_helper.toggleHelp) #self.debugShortCut.activated.connect(self.toggleDebug) self.console_shortcut.activated.connect(self.toggleConsole) self.cursor_shortcut.activated.connect(self.toggleCursor) self.theme_shortcut.activated.connect(self.toggleTheme) self.tetris_shortcut.activated.connect(self.toggleTetris) self.ui.buttonNext.clicked.connect(self.slotNext) self.ui.buttonBack.clicked.connect(self.slotBack) self.ui.toggleHelp.clicked.connect(self.pds_helper.toggleHelp) if not ctx.flags.install_type == ctx.STEP_FIRST_BOOT: self.ui.releaseNotes.clicked.connect(self.showReleaseNotes) else: self.ui.releaseNotes.hide() self.menu.triggered[QAction].connect(self.slotMenu) self.cmb = _("right") self.dont_ask_again = False self.terminal = None self.tetris = None self.ui.helpContentFrame.hide() self.effect = QGraphicsOpacityEffect(self) self.ui.mainStack.setGraphicsEffect(self.effect) self.effect.setOpacity(1.0) self.anime = QTimer(self) self.anime.timeout.connect(self.animate)
class BookInfo(QDialog): closed = pyqtSignal(object) def __init__(self, parent, view, row, link_delegate): QDialog.__init__(self, parent) self.normal_brush = QBrush(Qt.white) self.marked_brush = QBrush(Qt.lightGray) self.marked = None self.gui = parent self.splitter = QSplitter(self) self._l = l = QVBoxLayout(self) self.setLayout(l) l.addWidget(self.splitter) self.cover = CoverView(self) self.cover.resizeEvent = self.cover_view_resized self.cover.cover_changed.connect(self.cover_changed) self.cover_pixmap = None self.cover.sizeHint = self.details_size_hint self.splitter.addWidget(self.cover) self.details = QWebView(self) self.details.sizeHint = self.details_size_hint self.details.page().setLinkDelegationPolicy( self.details.page().DelegateAllLinks) self.details.linkClicked.connect(self.link_clicked) s = self.details.page().settings() s.setAttribute(s.JavascriptEnabled, False) self.css = css() self.link_delegate = link_delegate self.details.setAttribute(Qt.WA_OpaquePaintEvent, False) palette = self.details.palette() self.details.setAcceptDrops(False) palette.setBrush(QPalette.Base, Qt.transparent) self.details.page().setPalette(palette) self.c = QWidget(self) self.c.l = l2 = QGridLayout(self.c) self.c.setLayout(l2) l2.addWidget(self.details, 0, 0, 1, -1) self.splitter.addWidget(self.c) self.fit_cover = QCheckBox(_('Fit &cover within view'), self) self.fit_cover.setChecked( gprefs.get('book_info_dialog_fit_cover', True)) l2.addWidget(self.fit_cover, l2.rowCount(), 0, 1, -1) self.previous_button = QPushButton(QIcon(I('previous.png')), _('&Previous'), self) self.previous_button.clicked.connect(self.previous) l2.addWidget(self.previous_button, l2.rowCount(), 0) self.next_button = QPushButton(QIcon(I('next.png')), _('&Next'), self) self.next_button.clicked.connect(self.next) l2.addWidget(self.next_button, l2.rowCount() - 1, 1) self.view = view self.current_row = None self.refresh(row) self.view.model().new_bookdisplay_data.connect(self.slave) self.fit_cover.stateChanged.connect(self.toggle_cover_fit) self.ns = QShortcut(QKeySequence('Alt+Right'), self) self.ns.activated.connect(self.next) self.ps = QShortcut(QKeySequence('Alt+Left'), self) self.ps.activated.connect(self.previous) self.next_button.setToolTip( _('Next [%s]') % unicode(self.ns.key().toString(QKeySequence.NativeText))) self.previous_button.setToolTip( _('Previous [%s]') % unicode(self.ps.key().toString(QKeySequence.NativeText))) geom = QCoreApplication.instance().desktop().availableGeometry(self) screen_height = geom.height() - 100 screen_width = geom.width() - 100 self.resize(max(int(screen_width / 2), 700), screen_height) saved_layout = gprefs.get('book_info_dialog_layout', None) if saved_layout is not None: try: self.restoreGeometry(saved_layout[0]) self.splitter.restoreState(saved_layout[1]) except Exception: pass def link_clicked(self, qurl): link = unicode(qurl.toString(QUrl.None)) self.link_delegate(link) def done(self, r): saved_layout = (bytearray(self.saveGeometry()), bytearray(self.splitter.saveState())) gprefs.set('book_info_dialog_layout', saved_layout) ret = QDialog.done(self, r) self.view.model().new_bookdisplay_data.disconnect(self.slave) self.view = self.link_delegate = self.gui = None self.closed.emit(self) return ret def cover_changed(self, data): if self.current_row is not None: id_ = self.view.model().id(self.current_row) self.view.model().db.set_cover(id_, data) if self.gui.cover_flow: self.gui.cover_flow.dataChanged() ci = self.view.currentIndex() if ci.isValid(): self.view.model().current_changed(ci, ci) def details_size_hint(self): return QSize(350, 550) def toggle_cover_fit(self, state): gprefs.set('book_info_dialog_fit_cover', self.fit_cover.isChecked()) self.resize_cover() def cover_view_resized(self, event): QTimer.singleShot(1, self.resize_cover) def slave(self, mi): self.refresh(mi.row_number, mi) def move(self, delta=1): idx = self.view.currentIndex() if idx.isValid(): m = self.view.model() ni = m.index(idx.row() + delta, idx.column()) if ni.isValid(): if self.view.isVisible(): self.view.scrollTo(ni) self.view.setCurrentIndex(ni) def next(self): self.move() def previous(self): self.move(-1) def resize_cover(self): if self.cover_pixmap is None: return pixmap = self.cover_pixmap if self.fit_cover.isChecked(): scaled, new_width, new_height = fit_image( pixmap.width(), pixmap.height(), self.cover.size().width() - 10, self.cover.size().height() - 10) if scaled: pixmap = pixmap.scaled(new_width, new_height, Qt.KeepAspectRatio, Qt.SmoothTransformation) self.cover.set_pixmap(pixmap) self.update_cover_tooltip() def update_cover_tooltip(self): tt = '' if self.marked: tt = _('This book is marked') if self.marked in { True, 'true' } else _('This book is marked as: %s') % self.marked tt += '\n\n' if self.cover_pixmap is not None: sz = self.cover_pixmap.size() tt += _('Cover size: %(width)d x %(height)d') % dict( width=sz.width(), height=sz.height()) self.cover.setToolTip(tt) def refresh(self, row, mi=None): if isinstance(row, QModelIndex): row = row.row() if row == self.current_row and mi is None: return mi = self.view.model().get_book_display_info(row) if mi is None else mi if mi is None: # Indicates books was deleted from library, or row numbers have # changed return self.previous_button.setEnabled(False if row == 0 else True) self.next_button.setEnabled(False if row == self.view.model().rowCount(QModelIndex()) - 1 else True) self.current_row = row self.setWindowTitle(mi.title) self.cover_pixmap = QPixmap.fromImage(mi.cover_data[1]) self.resize_cover() html = render_html(mi, self.css, True, self, all_fields=True) self.details.setHtml(html) self.marked = mi.marked self.cover.setBackgroundBrush( self.marked_brush if mi.marked else self.normal_brush) self.update_cover_tooltip()
class BookInfo(QDialog): closed = pyqtSignal(object) def __init__(self, parent, view, row, link_delegate): QDialog.__init__(self, parent) self.normal_brush = QBrush(Qt.white) self.marked_brush = QBrush(Qt.lightGray) self.marked = None self.gui = parent self.splitter = QSplitter(self) self._l = l = QVBoxLayout(self) self.setLayout(l) l.addWidget(self.splitter) self.cover = CoverView(self, show_size=gprefs['bd_overlay_cover_size']) self.cover.resizeEvent = self.cover_view_resized self.cover.cover_changed.connect(self.cover_changed) self.cover_pixmap = None self.cover.sizeHint = self.details_size_hint self.splitter.addWidget(self.cover) self.details = Details(parent.book_details.book_info, self) self.details.anchor_clicked.connect(self.on_link_clicked) self.link_delegate = link_delegate self.details.setAttribute(Qt.WA_OpaquePaintEvent, False) palette = self.details.palette() self.details.setAcceptDrops(False) palette.setBrush(QPalette.Base, Qt.transparent) self.details.setPalette(palette) self.c = QWidget(self) self.c.l = l2 = QGridLayout(self.c) l2.setContentsMargins(0, 0, 0, 0) self.c.setLayout(l2) l2.addWidget(self.details, 0, 0, 1, -1) self.splitter.addWidget(self.c) self.fit_cover = QCheckBox(_('Fit &cover within view'), self) self.fit_cover.setChecked( gprefs.get('book_info_dialog_fit_cover', True)) self.hl = hl = QHBoxLayout() hl.setContentsMargins(0, 0, 0, 0) l2.addLayout(hl, l2.rowCount(), 0, 1, -1) hl.addWidget(self.fit_cover), hl.addStretch() self.clabel = QLabel( '<div style="text-align: right"><a href="calibre:conf" title="{}" style="text-decoration: none">{}</a>' .format(_('Configure this view'), _('Configure'))) self.clabel.linkActivated.connect(self.configure) hl.addWidget(self.clabel) self.previous_button = QPushButton(QIcon(I('previous.png')), _('&Previous'), self) self.previous_button.clicked.connect(self.previous) l2.addWidget(self.previous_button, l2.rowCount(), 0) self.next_button = QPushButton(QIcon(I('next.png')), _('&Next'), self) self.next_button.clicked.connect(self.next) l2.addWidget(self.next_button, l2.rowCount() - 1, 1) self.view = view self.current_row = None self.refresh(row) self.view.model().new_bookdisplay_data.connect(self.slave) self.fit_cover.stateChanged.connect(self.toggle_cover_fit) self.ns = QShortcut(QKeySequence('Alt+Right'), self) self.ns.activated.connect(self.next) self.ps = QShortcut(QKeySequence('Alt+Left'), self) self.ps.activated.connect(self.previous) self.next_button.setToolTip( _('Next [%s]') % unicode_type(self.ns.key().toString(QKeySequence.NativeText))) self.previous_button.setToolTip( _('Previous [%s]') % unicode_type(self.ps.key().toString(QKeySequence.NativeText))) geom = QCoreApplication.instance().desktop().availableGeometry(self) screen_height = geom.height() - 100 screen_width = geom.width() - 100 self.resize(max(int(screen_width / 2), 700), screen_height) saved_layout = gprefs.get('book_info_dialog_layout', None) if saved_layout is not None: try: self.restoreGeometry(saved_layout[0]) self.splitter.restoreState(saved_layout[1]) except Exception: pass def configure(self): d = Configure(get_gui().current_db, self) if d.exec_() == d.Accepted: if self.current_row is not None: mi = self.view.model().get_book_display_info(self.current_row) if mi is not None: self.refresh(self.current_row, mi=mi) def on_link_clicked(self, qurl): link = unicode_type(qurl.toString(NO_URL_FORMATTING)) self.link_delegate(link) def done(self, r): saved_layout = (bytearray(self.saveGeometry()), bytearray(self.splitter.saveState())) gprefs.set('book_info_dialog_layout', saved_layout) ret = QDialog.done(self, r) self.view.model().new_bookdisplay_data.disconnect(self.slave) self.view = self.link_delegate = self.gui = None self.closed.emit(self) return ret def cover_changed(self, data): if self.current_row is not None: id_ = self.view.model().id(self.current_row) self.view.model().db.set_cover(id_, data) self.gui.refresh_cover_browser() ci = self.view.currentIndex() if ci.isValid(): self.view.model().current_changed(ci, ci) def details_size_hint(self): return QSize(350, 550) def toggle_cover_fit(self, state): gprefs.set('book_info_dialog_fit_cover', self.fit_cover.isChecked()) self.resize_cover() def cover_view_resized(self, event): QTimer.singleShot(1, self.resize_cover) def slave(self, mi): self.refresh(mi.row_number, mi) def move(self, delta=1): idx = self.view.currentIndex() if idx.isValid(): m = self.view.model() ni = m.index(idx.row() + delta, idx.column()) if ni.isValid(): if self.view.isVisible(): self.view.scrollTo(ni) self.view.setCurrentIndex(ni) def next(self): self.move() def previous(self): self.move(-1) def resize_cover(self): if self.cover_pixmap is None: return pixmap = self.cover_pixmap if self.fit_cover.isChecked(): scaled, new_width, new_height = fit_image( pixmap.width(), pixmap.height(), self.cover.size().width() - 10, self.cover.size().height() - 10) if scaled: try: dpr = self.devicePixelRatioF() except AttributeError: dpr = self.devicePixelRatio() pixmap = pixmap.scaled(int(dpr * new_width), int(dpr * new_height), Qt.KeepAspectRatio, Qt.SmoothTransformation) pixmap.setDevicePixelRatio(dpr) self.cover.set_pixmap(pixmap) self.update_cover_tooltip() def update_cover_tooltip(self): tt = '' if self.marked: tt = _('This book is marked') if self.marked in { True, 'true' } else _('This book is marked as: %s') % self.marked tt += '\n\n' if self.cover_pixmap is not None: sz = self.cover_pixmap.size() tt += _('Cover size: %(width)d x %(height)d pixels') % dict( width=sz.width(), height=sz.height()) self.cover.setToolTip(tt) self.cover.pixmap_size = sz.width(), sz.height() def refresh(self, row, mi=None): if isinstance(row, QModelIndex): row = row.row() if row == self.current_row and mi is None: return mi = self.view.model().get_book_display_info(row) if mi is None else mi if mi is None: # Indicates books was deleted from library, or row numbers have # changed return self.previous_button.setEnabled(False if row == 0 else True) self.next_button.setEnabled(False if row == self.view.model().rowCount(QModelIndex()) - 1 else True) self.current_row = row self.setWindowTitle(mi.title) self.cover_pixmap = QPixmap.fromImage(mi.cover_data[1]) try: dpr = self.devicePixelRatioF() except AttributeError: dpr = self.devicePixelRatio() self.cover_pixmap.setDevicePixelRatio(dpr) self.resize_cover() html = render_html(mi, True, self, pref_name='popup_book_display_fields') set_html(mi, html, self.details) self.marked = mi.marked self.cover.setBackgroundBrush( self.marked_brush if mi.marked else self.normal_brush) self.update_cover_tooltip()
def register_shortcuts(self, window): self.__keyCtrlP = QShortcut(window) self.__keyCtrlP.setKey(Qt.CTRL + Qt.Key_P) self.__keyCtrlP.activated.connect( lambda: self.__show_profile_manager())