class ConfigWidget(QWidget): def __init__(self, plugin_action): QWidget.__init__(self) self.plugin_action = plugin_action self.gui = plugin_action.gui self.library = get_library_config(self.gui.current_db) self.views = self.library[KEY_VIEWS] self.all_columns = self.get_current_columns() self.view_name = None self.has_pin_view = hasattr(self.gui.library_view, 'pin_view') toplayout = QVBoxLayout(self) self.setLayout(toplayout) ## wrap config in a scrollable area for smaller displays. scrollable = QScrollArea() scrollcontent = QWidget() scrollable.setWidget(scrollcontent) scrollable.setWidgetResizable(True) toplayout.addWidget(scrollable) layout = QVBoxLayout() scrollcontent.setLayout(layout) select_view_layout = QHBoxLayout() layout.addLayout(select_view_layout) select_view_label = QLabel('Select view to customize:', self) select_view_layout.addWidget(select_view_label) self.select_view_combo = ViewComboBox(self, self.views) self.select_view_combo.setMinimumSize(150, 20) select_view_layout.addWidget(self.select_view_combo) self.add_view_button = QtGui.QToolButton(self) self.add_view_button.setToolTip('Add view') self.add_view_button.setIcon(QIcon(I('plus.png'))) self.add_view_button.clicked.connect(self.add_view) select_view_layout.addWidget(self.add_view_button) self.delete_view_button = QtGui.QToolButton(self) self.delete_view_button.setToolTip('Delete view') self.delete_view_button.setIcon(QIcon(I('minus.png'))) self.delete_view_button.clicked.connect(self.delete_view) select_view_layout.addWidget(self.delete_view_button) self.rename_view_button = QtGui.QToolButton(self) self.rename_view_button.setToolTip('Rename view') self.rename_view_button.setIcon(QIcon(I('edit-undo.png'))) self.rename_view_button.clicked.connect(self.rename_view) select_view_layout.addWidget(self.rename_view_button) select_view_layout.insertStretch(-1) view_group_box = QGroupBox('Column Options', self) layout.addWidget(view_group_box) view_group_box_layout = QVBoxLayout() view_group_box.setLayout(view_group_box_layout) customise_layout = QGridLayout() view_group_box_layout.addLayout(customise_layout, 1) if self.has_pin_view: columns_label = 'Columns in Default (Left) pane' else: columns_label = 'Columns in view' self.columns_label = QLabel(columns_label, self) self.columns_list = ColumnListWidget(self, self.gui) self.move_column_up_button = QtGui.QToolButton(self) self.move_column_up_button.setToolTip('Move column up') self.move_column_up_button.setIcon(QIcon(I('arrow-up.png'))) self.move_column_down_button = QtGui.QToolButton(self) self.move_column_down_button.setToolTip('Move column down') self.move_column_down_button.setIcon(QIcon(I('arrow-down.png'))) self.move_column_up_button.clicked.connect( self.columns_list.move_column_up) self.move_column_down_button.clicked.connect( self.columns_list.move_column_down) if self.has_pin_view: self.apply_pin_columns_checkbox = QCheckBox( 'Columns in Split (Right) Pane', self) self.apply_pin_columns_checkbox.setToolTip( 'Split Book List will <i>only</i> be shown if this is checked. This will be checked if you save a Split View.' ) self.pin_columns_list = ColumnListWidget(self, self.gui) self.move_pin_column_up_button = QtGui.QToolButton(self) self.move_pin_column_up_button.setToolTip('Move column up') self.move_pin_column_up_button.setIcon(QIcon(I('arrow-up.png'))) self.move_pin_column_down_button = QtGui.QToolButton(self) self.move_pin_column_down_button.setToolTip('Move column down') self.move_pin_column_down_button.setIcon(QIcon( I('arrow-down.png'))) self.move_pin_column_up_button.clicked.connect( self.pin_columns_list.move_column_up) self.move_pin_column_down_button.clicked.connect( self.pin_columns_list.move_column_down) def group_abled(elems, cb): for el in elems: el.setEnabled(cb.isChecked()) pin_abled = partial(group_abled, [ self.pin_columns_list, self.move_pin_column_up_button, self.move_pin_column_down_button ], self.apply_pin_columns_checkbox) pin_abled() self.apply_pin_columns_checkbox.stateChanged.connect(pin_abled) self.sort_label = QLabel('Sort order', self) self.sort_list = SortColumnListWidget(self, self.gui) self.move_sort_up_button = QtGui.QToolButton(self) self.move_sort_up_button.setToolTip('Move sort column up') self.move_sort_up_button.setIcon(QIcon(I('arrow-up.png'))) self.move_sort_down_button = QtGui.QToolButton(self) self.move_sort_down_button.setToolTip('Move sort down') self.move_sort_down_button.setIcon(QIcon(I('arrow-down.png'))) self.move_sort_up_button.clicked.connect(self.sort_list.move_column_up) self.move_sort_down_button.clicked.connect( self.sort_list.move_column_down) layout_col = 0 # calculate layout because pin column only shown if available. customise_layout.addWidget(self.columns_label, 0, layout_col, 1, 1) customise_layout.addWidget(self.columns_list, 1, layout_col, 3, 1) layout_col = layout_col + 1 customise_layout.addWidget(self.move_column_up_button, 1, layout_col, 1, 1) customise_layout.addWidget(self.move_column_down_button, 3, layout_col, 1, 1) layout_col = layout_col + 1 if self.has_pin_view: customise_layout.addWidget(self.apply_pin_columns_checkbox, 0, layout_col, 1, 1) customise_layout.addWidget(self.pin_columns_list, 1, layout_col, 3, 1) layout_col = layout_col + 1 customise_layout.addWidget(self.move_pin_column_up_button, 1, layout_col, 1, 1) customise_layout.addWidget(self.move_pin_column_down_button, 3, layout_col, 1, 1) layout_col = layout_col + 1 customise_layout.addWidget(self.sort_label, 0, layout_col, 1, 1) customise_layout.addWidget(self.sort_list, 1, layout_col, 3, 1) layout_col = layout_col + 1 customise_layout.addWidget(self.move_sort_up_button, 1, layout_col, 1, 1) customise_layout.addWidget(self.move_sort_down_button, 3, layout_col, 1, 1) layout_col = layout_col + 1 search_group_box = QGroupBox("Search and Virtual Library Options", self) layout.addWidget(search_group_box) search_group_box_layout = QVBoxLayout() search_group_box.setLayout(search_group_box_layout) other_layout = QGridLayout() search_group_box_layout.addLayout(other_layout) self.apply_search_checkbox = QCheckBox('Apply saved &search', self) self.apply_search_checkbox.setToolTip( "Apply the selected saved search when the View is activated.") self.saved_search_combo = SearchComboBox( self, entries=saved_searches().names(), empty="(Clear Search)") self.saved_search_combo.setToolTip("Saved search to apply.") # enable/disable combo based on check. self.saved_search_combo.setEnabled( self.apply_search_checkbox.isChecked()) self.apply_search_checkbox.stateChanged.connect( lambda x: self.saved_search_combo.setEnabled( self.apply_search_checkbox.isChecked())) self.apply_virtlib_checkbox = QCheckBox('Switch to &Virtual library', self) self.apply_virtlib_checkbox.setToolTip( "Switch to the selected Virtual library when the View is activated." ) self.virtlib_combo = SearchComboBox( self, entries=self.gui.library_view.model().db.prefs.get( 'virtual_libraries', {}), empty="(No Virtual library)") self.virtlib_combo.setToolTip("Virtual library to switch to.") # enable/disable combo based on check. self.virtlib_combo.setEnabled(self.apply_virtlib_checkbox.isChecked()) self.apply_virtlib_checkbox.stateChanged.connect( lambda x: self.virtlib_combo.setEnabled(self.apply_virtlib_checkbox .isChecked())) self.apply_restriction_checkbox = QCheckBox( 'Apply VL additional search &restriction', self) self.apply_restriction_checkbox.setToolTip( "Apply the selected saved search as a Virtual library additional restriction when the View is activated." ) self.search_restriction_combo = SearchComboBox( self, entries=saved_searches().names(), empty="(Clear VL restriction search)") self.search_restriction_combo.setToolTip( "Saved search to apply as VL additional search restriction.") # enable/disable combo based on check. self.search_restriction_combo.setEnabled( self.apply_restriction_checkbox.isChecked()) self.apply_restriction_checkbox.stateChanged.connect( lambda x: self.search_restriction_combo.setEnabled( self.apply_restriction_checkbox.isChecked())) other_layout.addWidget(self.apply_search_checkbox, 0, 0, 1, 1) other_layout.addWidget(self.saved_search_combo, 0, 1, 1, 1) other_layout.addWidget(self.apply_virtlib_checkbox, 1, 0, 1, 1) other_layout.addWidget(self.virtlib_combo, 1, 1, 1, 1) other_layout.addWidget(self.apply_restriction_checkbox, 2, 0, 1, 1) other_layout.addWidget(self.search_restriction_combo, 2, 1, 1, 1) # other_layout.setRowStretch(4, 1) #layout.addSpacing(10) other_group_box = QGroupBox('General Options', self) layout.addWidget(other_group_box) other_group_box_layout = QGridLayout() other_group_box.setLayout(other_group_box_layout) self.jump_to_top_checkbox = QCheckBox( 'Jump to the top when applying this View', self) jump_to_top = self.library.get(KEY_JUMP_TO_TOP, False) self.jump_to_top_checkbox.setCheckState( Qt.Checked if jump_to_top else Qt.Unchecked) restart_label = QLabel( 'When restarting Calibre or switching to this library...') self.auto_apply_checkbox = QCheckBox('&Automatically apply view:', self) auto_apply = self.library.get(KEY_AUTO_APPLY_VIEW, False) self.auto_apply_checkbox.setCheckState( Qt.Checked if auto_apply else Qt.Unchecked) self.auto_view_combo = ViewComboBox(self, self.views, special=LAST_VIEW_ITEM) self.auto_view_combo.select_view( self.library.get(KEY_VIEW_TO_APPLY, LAST_VIEW_ITEM)) self.auto_view_combo.setMinimumSize(150, 20) info_apply_label = QLabel( 'Enabling this option may override any startup search restriction or ' 'title sort set in Preferences -> Behaviour/Tweaks.') info_apply_label.setWordWrap(True) other_group_box_layout.addWidget(self.jump_to_top_checkbox, 0, 0, 1, 2) other_group_box_layout.addWidget(restart_label, 1, 0, 1, 2) other_group_box_layout.addWidget(self.auto_apply_checkbox, 2, 0, 1, 1) other_group_box_layout.addWidget(self.auto_view_combo, 2, 1, 1, 1) other_group_box_layout.addWidget(info_apply_label, 3, 0, 1, 2) #other_group_box.setMaximumHeight(other_group_box.sizeHint().height()) keyboard_layout = QHBoxLayout() layout.addLayout(keyboard_layout) keyboard_shortcuts_button = QPushButton('Keyboard shortcuts...', self) keyboard_shortcuts_button.setToolTip( _('Edit the keyboard shortcuts associated with this plugin')) keyboard_shortcuts_button.clicked.connect(self.edit_shortcuts) view_prefs_button = QPushButton('&View library preferences...', self) view_prefs_button.setToolTip( _('View data stored in the library database for this plugin')) view_prefs_button.clicked.connect(self.view_prefs) keyboard_layout.addWidget(keyboard_shortcuts_button) keyboard_layout.addWidget(view_prefs_button) keyboard_layout.addStretch(1) # Force an initial display of view information if KEY_LAST_VIEW in list(self.library.keys()): last_view = self.library[KEY_LAST_VIEW] if last_view in self.views: self.select_view_combo.select_view(self.library[KEY_LAST_VIEW]) self.select_view_combo_index_changed(save_previous=False) self.select_view_combo.currentIndexChanged.connect( partial(self.select_view_combo_index_changed, save_previous=True)) def save_settings(self): # We only need to update the store for the current view, as switching views # will have updated the other stores self.persist_view_config() library_config = get_library_config(self.gui.current_db) library_config[KEY_VIEWS] = self.views library_config[ KEY_AUTO_APPLY_VIEW] = self.auto_apply_checkbox.checkState( ) == Qt.Checked library_config[KEY_VIEW_TO_APPLY] = unicode( self.auto_view_combo.currentText()) set_library_config(self.gui.current_db, library_config) def persist_view_config(self): if not self.view_name: return # Update all of the current user information in the store view_info = self.views[self.view_name] view_info[KEY_COLUMNS] = self.columns_list.get_data() view_info[KEY_SORT] = self.sort_list.get_data() if self.has_pin_view: view_info[ KEY_APPLY_PIN_COLUMNS] = self.apply_pin_columns_checkbox.checkState( ) == Qt.Checked view_info[KEY_PIN_COLUMNS] = self.pin_columns_list.get_data() view_info[ KEY_APPLY_RESTRICTION] = self.apply_restriction_checkbox.checkState( ) == Qt.Checked if view_info[KEY_APPLY_RESTRICTION]: view_info[KEY_RESTRICTION] = unicode( self.search_restriction_combo.currentText()).strip() else: view_info[KEY_RESTRICTION] = '' view_info[KEY_APPLY_SEARCH] = self.apply_search_checkbox.checkState( ) == Qt.Checked if view_info[KEY_APPLY_SEARCH]: view_info[KEY_SEARCH] = unicode( self.saved_search_combo.currentText()).strip() else: view_info[KEY_SEARCH] = '' view_info[KEY_APPLY_VIRTLIB] = self.apply_virtlib_checkbox.checkState( ) == Qt.Checked if view_info[KEY_APPLY_VIRTLIB]: view_info[KEY_VIRTLIB] = unicode( self.virtlib_combo.currentText()).strip() else: view_info[KEY_VIRTLIB] = '' view_info[KEY_JUMP_TO_TOP] = self.jump_to_top_checkbox.checkState( ) == Qt.Checked self.views[self.view_name] = view_info def select_view_combo_index_changed(self, save_previous=True): # Update the dialog contents with data for the selected view if save_previous: # Switching views, persist changes made to the other view self.persist_view_config() if self.select_view_combo.count() == 0: self.view_name = None else: self.view_name = unicode( self.select_view_combo.currentText()).strip() columns = [] sort_columns = [] all_columns = [] pin_columns = [] apply_columns = True apply_pin_columns = False apply_sort = True apply_restriction = False restriction_to_apply = '' apply_search = False search_to_apply = '' apply_virtlib = False virtlib_to_apply = '' jump_to_top = False if self.view_name != None: view_info = self.views[self.view_name] columns = copy.deepcopy(view_info[KEY_COLUMNS]) pin_columns = copy.deepcopy(view_info.get(KEY_PIN_COLUMNS, {})) sort_columns = copy.deepcopy(view_info[KEY_SORT]) all_columns = self.all_columns apply_pin_columns = view_info.get(KEY_APPLY_PIN_COLUMNS, False) apply_restriction = view_info[KEY_APPLY_RESTRICTION] restriction_to_apply = view_info[KEY_RESTRICTION] apply_search = view_info[KEY_APPLY_SEARCH] search_to_apply = view_info[KEY_SEARCH] apply_virtlib = view_info.get(KEY_APPLY_VIRTLIB, False) virtlib_to_apply = view_info.get(KEY_VIRTLIB, '') jump_to_top = view_info.get(KEY_JUMP_TO_TOP, False) self.columns_list.populate(columns, all_columns) self.sort_list.populate(sort_columns, all_columns) if self.has_pin_view: self.pin_columns_list.populate(pin_columns, all_columns) self.apply_pin_columns_checkbox.setCheckState( Qt.Checked if apply_pin_columns else Qt.Unchecked) self.apply_restriction_checkbox.setCheckState( Qt.Checked if apply_restriction else Qt.Unchecked) self.search_restriction_combo.select_value(restriction_to_apply) self.apply_search_checkbox.setCheckState( Qt.Checked if apply_search else Qt.Unchecked) self.saved_search_combo.select_value(search_to_apply) self.apply_virtlib_checkbox.setCheckState( Qt.Checked if apply_virtlib else Qt.Unchecked) self.virtlib_combo.select_value(virtlib_to_apply) self.jump_to_top_checkbox.setCheckState( Qt.Checked if jump_to_top else Qt.Unchecked) def add_view(self): # Display a prompt allowing user to specify a new view new_view_name, ok = QInputDialog.getText( self, 'Add new view', 'Enter a unique display name for this view:', text='Default') if not ok: # Operation cancelled return new_view_name = unicode(new_view_name).strip() # Verify it does not clash with any other views in the list for view_name in self.views.keys(): if view_name.lower() == new_view_name.lower(): return error_dialog(self, 'Add Failed', 'A view with the same name already exists', show=True) self.persist_view_config() view_info = get_empty_view() if self.view_name != None: # We will copy values from the currently selected view old_view_info = self.views[self.view_name] view_info[KEY_COLUMNS] = copy.deepcopy(old_view_info[KEY_COLUMNS]) view_info[KEY_APPLY_PIN_COLUMNS] = copy.deepcopy( old_view_info.get(KEY_APPLY_PIN_COLUMNS, False)) view_info[KEY_PIN_COLUMNS] = copy.deepcopy( old_view_info.get(KEY_PIN_COLUMNS, {})) view_info[KEY_SORT] = copy.deepcopy(old_view_info[KEY_SORT]) view_info[KEY_APPLY_RESTRICTION] = copy.deepcopy( old_view_info[KEY_APPLY_RESTRICTION]) view_info[KEY_RESTRICTION] = copy.deepcopy( old_view_info[KEY_RESTRICTION]) view_info[KEY_APPLY_SEARCH] = copy.deepcopy( old_view_info[KEY_APPLY_SEARCH]) view_info[KEY_SEARCH] = copy.deepcopy(old_view_info[KEY_SEARCH]) view_info[KEY_APPLY_VIRTLIB] = copy.deepcopy( old_view_info.get(KEY_APPLY_VIRTLIB, False)) view_info[KEY_VIRTLIB] = copy.deepcopy( old_view_info.get(KEY_VIRTLIB, '')) view_info[KEY_JUMP_TO_TOP] = copy.deepcopy( old_view_info[KEY_JUMP_TO_TOP]) else: # We will copy values from the current library view view_info[KEY_COLUMNS] = self.get_current_columns( visible_only=True) self.view_name = new_view_name self.views[new_view_name] = view_info # Now update the views combobox self.select_view_combo.populate_combo(self.views, new_view_name) self.select_view_combo_index_changed(save_previous=False) self.auto_view_combo.populate_combo( self.views, unicode(self.auto_view_combo.currentText())) def rename_view(self): if not self.view_name != None: return # Display a prompt allowing user to specify a rename view old_view_name = self.view_name new_view_name, ok = QInputDialog.getText( self, 'Rename view', 'Enter a new display name for this view:', text=old_view_name) if not ok: # Operation cancelled return new_view_name = unicode(new_view_name).strip() if new_view_name == old_view_name: return # Verify it does not clash with any other views in the list for view_name in self.views.keys(): if view_name == old_view_name: continue if view_name.lower() == new_view_name.lower(): return error_dialog(self, 'Add Failed', 'A view with the same name already exists', show=True) # Ensure any changes are persisted self.persist_view_config() view_info = self.views[old_view_name] del self.views[old_view_name] self.view_name = new_view_name self.views[new_view_name] = view_info # Now update the views combobox self.select_view_combo.populate_combo(self.views, new_view_name) self.select_view_combo_index_changed(save_previous=False) if unicode(self.auto_view_combo.currentText()) == old_view_name: self.auto_view_combo.populate_combo(self.views, new_view_name) else: self.auto_view_combo.populate_combo(self.views) def delete_view(self): if self.view_name == None: return if not question_dialog( self, _('Are you sure?'), '<p>' + 'Do you want to delete the view named \'%s\'' % self.view_name, show_copy_button=False): return del self.views[self.view_name] # Now update the views combobox self.select_view_combo.populate_combo(self.views) self.select_view_combo_index_changed(save_previous=False) self.auto_view_combo.populate_combo(self.views) def get_current_columns(self, defaults=False, visible_only=False): model = self.gui.library_view.model() colmap = list(model.column_map) state = self.columns_state(defaults) positions = state['column_positions'] colmap.sort(key=lambda x: positions[x]) hidden_cols = state['hidden_columns'] if visible_only: colmap = [ col for col in colmap if col not in hidden_cols or col == 'ondevice' ] # Convert our list of column names into a list of tuples with column widths colsizemap = [] for col in colmap: if col in hidden_cols: colsizemap.append((col, -1)) else: colsizemap.append((col, state['column_sizes'].get(col, -1))) return colsizemap def columns_state(self, defaults=False): if defaults: return self.gui.library_view.get_default_state() return self.gui.library_view.get_state() def edit_shortcuts(self): self.save_settings() # Force the menus to be rebuilt immediately, so we have all our actions registered self.plugin_action.rebuild_menus() d = KeyboardConfigDialog(self.plugin_action.gui, self.plugin_action.action_spec[0]) if d.exec_() == d.Accepted: self.plugin_action.gui.keyboard.finalize() def view_prefs(self): d = PrefsViewerDialog(self.plugin_action.gui, PREFS_NAMESPACE) d.exec_()
class AnnotationsAppearance(SizePersistedDialog): ''' Dialog for managing CSS rules, including Preview window ''' if isosx: FONT = QFont('Monaco', 12) elif iswindows: FONT = QFont('Lucida Console', 9) elif islinux: FONT = QFont('Monospace', 9) FONT.setStyleHint(QFont.TypeWriter) def __init__(self, parent, icon, prefs): self.parent = parent self.prefs = prefs self.icon = icon super(AnnotationsAppearance, self).__init__(parent, 'appearance_dialog') self.setWindowTitle(_('Modify appearance')) self.setWindowIcon(icon) self.l = QVBoxLayout(self) self.setLayout(self.l) # Add a label for description #self.description_label = QLabel(_("Descriptive text here")) #self.l.addWidget(self.description_label) # Add a group box, vertical layout for preview window self.preview_gb = QGroupBox(self) self.preview_gb.setTitle(_("Preview")) self.preview_vl = QVBoxLayout(self.preview_gb) self.l.addWidget(self.preview_gb) self.wv = QWebView() self.wv.setHtml('<p></p>') self.wv.setMinimumHeight(100) self.wv.setMaximumHeight(16777215) self.wv.setGeometry(0, 0, 200, 100) self.wv.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.preview_vl.addWidget(self.wv) # Create a group box, horizontal layout for the table self.css_table_gb = QGroupBox(self) self.css_table_gb.setTitle(_("Annotation elements")) self.elements_hl = QHBoxLayout(self.css_table_gb) self.l.addWidget(self.css_table_gb) # Add the group box to the main layout self.elements_table = AnnotationElementsTable(self, 'annotation_elements_tw') self.elements_hl.addWidget(self.elements_table) self.elements_table.initialize() # Options self.options_gb = QGroupBox(self) self.options_gb.setTitle(_("Options")) self.options_gl = QGridLayout(self.options_gb) self.l.addWidget(self.options_gb) current_row = 0 # <hr/> separator # addWidget(widget, row, col, rowspan, colspan) self.hr_checkbox = QCheckBox(_('Add horizontal rule between annotations')) self.hr_checkbox.stateChanged.connect(self.hr_checkbox_changed) self.hr_checkbox.setCheckState( JSONConfig('plugins/annotations').get('appearance_hr_checkbox', False)) self.options_gl.addWidget(self.hr_checkbox, current_row, 0, 1, 4) current_row += 1 # Timestamp self.timestamp_fmt_label = QLabel(_("Timestamp format:")) self.options_gl.addWidget(self.timestamp_fmt_label, current_row, 0) self.timestamp_fmt_le = QLineEdit( JSONConfig('plugins/annotations').get('appearance_timestamp_format', default_timestamp), parent=self) self.timestamp_fmt_le.textEdited.connect(self.timestamp_fmt_changed) self.timestamp_fmt_le.setFont(self.FONT) self.timestamp_fmt_le.setObjectName('timestamp_fmt_le') self.timestamp_fmt_le.setToolTip(_('Format string for timestamp')) self.timestamp_fmt_le.setMaximumWidth(16777215) self.timestamp_fmt_le.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum) self.options_gl.addWidget(self.timestamp_fmt_le, current_row, 1) self.timestamp_fmt_reset_tb = QToolButton(self) self.timestamp_fmt_reset_tb.setToolTip(_("Reset to default")) self.timestamp_fmt_reset_tb.setIcon(QIcon(I('trash.png'))) self.timestamp_fmt_reset_tb.clicked.connect(self.reset_timestamp_to_default) self.options_gl.addWidget(self.timestamp_fmt_reset_tb, current_row, 2) self.timestamp_fmt_help_tb = QToolButton(self) self.timestamp_fmt_help_tb.setToolTip(_("Format string reference")) self.timestamp_fmt_help_tb.setIcon(QIcon(I('help.png'))) self.timestamp_fmt_help_tb.clicked.connect(self.show_help) self.options_gl.addWidget(self.timestamp_fmt_help_tb, current_row, 3) # Button box bb = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel) bb.accepted.connect(self.accept) bb.rejected.connect(self.reject) self.l.addWidget(bb) # Spacer self.spacerItem = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) self.l.addItem(self.spacerItem) # Sizing sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.sizePolicy().hasHeightForWidth()) self.setSizePolicy(sizePolicy) self.resize_dialog() def hr_checkbox_changed(self, state): self.prefs.set('appearance_hr_checkbox', state) self.elements_table.preview_css() def reset_timestamp_to_default(self): from calibre_plugins.annotations.appearance import default_timestamp self.timestamp_fmt_le.setText(default_timestamp) self.timestamp_fmt_changed() def show_help(self): ''' Display strftime help file ''' hv = HelpView(self, self.icon, self.prefs, html=get_resources('help/timestamp_formats.html'), title=_("Timestamp formats")) hv.show() def sizeHint(self): return QtCore.QSize(600, 200) def timestamp_fmt_changed(self): self.prefs.set('appearance_timestamp_format', str(self.timestamp_fmt_le.text())) self.elements_table.preview_css()
class ConfigWidget(QWidget): def __init__(self, plugin_action): QWidget.__init__(self) self.plugin_action = plugin_action layout = QVBoxLayout(self) self.setLayout(layout) self.help_anchor = "configuration" title_layout = ImageTitleLayout(self, 'images/icon.png', 'Sony Utilities Options') layout.addLayout(title_layout) # c = plugin_prefs[STORE_NAME] library_config = get_library_config(self.plugin_action.gui.current_db) custom_column_group = QGroupBox(_('Custom Columns'), self) layout.addWidget(custom_column_group ) options_layout = QGridLayout() custom_column_group.setLayout(options_layout) avail_text_columns = self.get_text_custom_columns() avail_number_columns = self.get_number_custom_columns() avail_rating_columns = self.get_rating_custom_columns() avail_date_columns = self.get_date_custom_columns() # debug_print("avail_rating_columns=", avail_rating_columns) # debug_print("default columns=", self.plugin_action.gui.library_view.model().orig_headers) current_Location_column = library_config.get(KEY_CURRENT_LOCATION_CUSTOM_COLUMN, DEFAULT_LIBRARY_VALUES[KEY_CURRENT_LOCATION_CUSTOM_COLUMN]) precent_read_column = library_config.get(KEY_PERCENT_READ_CUSTOM_COLUMN, DEFAULT_LIBRARY_VALUES[KEY_PERCENT_READ_CUSTOM_COLUMN]) rating_column = library_config.get(KEY_RATING_CUSTOM_COLUMN, DEFAULT_LIBRARY_VALUES[KEY_RATING_CUSTOM_COLUMN]) last_read_column = library_config.get(KEY_LAST_READ_CUSTOM_COLUMN, DEFAULT_LIBRARY_VALUES[KEY_LAST_READ_CUSTOM_COLUMN]) store_on_connect = get_plugin_pref(COMMON_OPTIONS_STORE_NAME, KEY_STORE_ON_CONNECT) prompt_to_store = get_plugin_pref(COMMON_OPTIONS_STORE_NAME, KEY_PROMPT_TO_STORE) store_if_more_recent = get_plugin_pref(COMMON_OPTIONS_STORE_NAME, KEY_STORE_IF_MORE_RECENT) do_not_store_if_reopened = get_plugin_pref(COMMON_OPTIONS_STORE_NAME, KEY_DO_NOT_STORE_IF_REOPENED) # do_check_for_firmware_updates = get_plugin_pref(UPDATE_OPTIONS_STORE_NAME, KEY_DO_UPDATE_CHECK) # do_early_firmware_updates = get_plugin_pref(UPDATE_OPTIONS_STORE_NAME, KEY_DO_EARLY_FIRMWARE_CHECK) # self.update_check_last_time = get_plugin_pref(UPDATE_OPTIONS_STORE_NAME, KEY_LAST_FIRMWARE_CHECK_TIME) do_daily_backup = get_plugin_pref(BACKUP_OPTIONS_STORE_NAME, KEY_DO_DAILY_BACKUP) dest_directory = get_plugin_pref(BACKUP_OPTIONS_STORE_NAME, KEY_BACKUP_DEST_DIRECTORY) copies_to_keep = get_plugin_pref(BACKUP_OPTIONS_STORE_NAME, KEY_BACKUP_COPIES_TO_KEEP) # debug_print("current_Location_column=%s, precent_read_column=%s, rating_column=%s" % (current_Location_column, precent_read_column, rating_column)) current_Location_label = QLabel(_('Current Reading Location Column:'), self) current_Location_label.setToolTip(_("Select a custom column to store the current reading location. The column type must be 'text'. Leave this blank if you do not want to store or restore the current reading location.")) self.current_Location_combo = CustomColumnComboBox(self, avail_text_columns, current_Location_column) current_Location_label.setBuddy(self.current_Location_combo) options_layout.addWidget(current_Location_label, 0, 0, 1, 1) options_layout.addWidget(self.current_Location_combo, 0, 1, 1, 1) percent_read_label = QLabel(_('Percent Read Column:'), self) percent_read_label.setToolTip(_("Column used to store the current percent read. The column type must be a 'integer'. Leave this blank if you do not want to store or restore the percentage read.")) self.percent_read_combo = CustomColumnComboBox(self, avail_number_columns, precent_read_column) percent_read_label.setBuddy(self.percent_read_combo) options_layout.addWidget(percent_read_label, 2, 0, 1, 1) options_layout.addWidget(self.percent_read_combo, 2, 1, 1, 1) rating_label = QLabel(_('Rating Column:'), self) rating_label.setToolTip(_("Column used to store the rating. The column type must be a 'integer'. Leave this blank if you do not want to store or restore the rating.")) self.rating_combo = CustomColumnComboBox(self, avail_rating_columns, rating_column) rating_label.setBuddy(self.rating_combo) options_layout.addWidget(rating_label, 3, 0, 1, 1) options_layout.addWidget(self.rating_combo, 3, 1, 1, 1) last_read_label = QLabel(_('Last Read Column:'), self) last_read_label.setToolTip(_("Column used to store when the book was last read. The column type must be a 'Date'. Leave this blank if you do not want to store the last read timestamp.")) self.last_read_combo = CustomColumnComboBox(self, avail_date_columns, last_read_column) last_read_label.setBuddy(self.last_read_combo) options_layout.addWidget(last_read_label, 4, 0, 1, 1) options_layout.addWidget(self.last_read_combo, 4, 1, 1, 1) auto_store_group = QGroupBox(_('Store on connect'), self) layout.addWidget(auto_store_group ) options_layout = QGridLayout() auto_store_group.setLayout(options_layout) self.store_on_connect_checkbox = QCheckBox(_("Store current bookmarks on connect"), self) self.store_on_connect_checkbox.setToolTip(_("When this is checked, the library will be updated with the current bookmark for all books on the device.")) self.store_on_connect_checkbox.setCheckState(Qt.Checked if store_on_connect else Qt.Unchecked) self.store_on_connect_checkbox.clicked.connect(self.store_on_connect_checkbox_clicked) options_layout.addWidget(self.store_on_connect_checkbox, 0, 0, 1, 3) self.prompt_to_store_checkbox = QCheckBox(_("Prompt to store any changes"), self) self.prompt_to_store_checkbox.setToolTip(_("Enable this to be prompted to save the changed bookmarks after an automatic store is done.")) self.prompt_to_store_checkbox.setCheckState(Qt.Checked if prompt_to_store else Qt.Unchecked) self.prompt_to_store_checkbox.setEnabled(store_on_connect) options_layout.addWidget(self.prompt_to_store_checkbox, 1, 0, 1, 1) self.store_if_more_recent_checkbox = QCheckBox(_("Only if more recent"), self) self.store_if_more_recent_checkbox.setToolTip(_("Only store the reading position if the last read timestamp on the device is more recent than in the library.")) self.store_if_more_recent_checkbox.setCheckState(Qt.Checked if store_if_more_recent else Qt.Unchecked) self.store_if_more_recent_checkbox.setEnabled(store_on_connect) options_layout.addWidget(self.store_if_more_recent_checkbox, 1, 1, 1, 1) self.do_not_store_if_reopened_checkbox = QCheckBox(_("Not if finished in library"), self) self.do_not_store_if_reopened_checkbox.setToolTip(_("Do not store the reading position if the library has the book as finished. This is if the percent read is 100%.")) self.do_not_store_if_reopened_checkbox.setCheckState(Qt.Checked if do_not_store_if_reopened else Qt.Unchecked) self.do_not_store_if_reopened_checkbox.setEnabled(store_on_connect) options_layout.addWidget(self.do_not_store_if_reopened_checkbox, 1, 2, 1, 1) # update_options_group = QGroupBox(_('Firmware Update Options'), self) # layout.addWidget(update_options_group) # options_layout = QGridLayout() # update_options_group.setLayout(options_layout) # # self.do_update_check = QCheckBox(_('Check for Sony firmware updates daily?'), self) # self.do_update_check.setToolTip(_('If this is selected the plugin will check for Sony firmware updates when your Sony device is plugged in, once per 24-hour period.')) # self.do_update_check.setCheckState(Qt.Checked if do_check_for_firmware_updates else Qt.Unchecked) # options_layout.addWidget(self.do_update_check, 0, 0, 1, 1) # # self.do_early_firmware_check = QCheckBox(_('Use early firmware adopter affiliate?'), self) # self.do_early_firmware_check.setToolTip(_('WARNING: THIS OPTION RISKS DOWNLOADING THE WRONG FIRMWARE FOR YOUR DEVICE! YOUR DEVICE MAY NOT FUNCTION PROPERLY IF THIS HAPPENS! Choose this option to attempt to download Sony firmware updates before they are officially available for your device.')) # self.do_early_firmware_check.setCheckState(Qt.Checked if do_early_firmware_updates else Qt.Unchecked) # options_layout.addWidget(self.do_early_firmware_check, 0, 1, 1, 1) backup_options_group = QGroupBox(_('Device Database Backup'), self) layout.addWidget(backup_options_group) options_layout = QGridLayout() backup_options_group.setLayout(options_layout) self.do_daily_backp_checkbox = QCheckBox(_('Backup the device database daily'), self) self.do_daily_backp_checkbox.setToolTip(_('If this is selected the plugin will backup the device database the first time it is connected each day.')) self.do_daily_backp_checkbox.setCheckState(Qt.Checked if do_daily_backup else Qt.Unchecked) self.do_daily_backp_checkbox.clicked.connect(self.do_daily_backp_checkbox_clicked) options_layout.addWidget(self.do_daily_backp_checkbox, 0, 0, 1, 3) self.dest_directory_label = QLabel(_("Destination:"), self) self.dest_directory_label.setToolTip(_("Select the destination the annotations files are to be backed up in.")) self.dest_directory_edit = QLineEdit(self) self.dest_directory_edit.setMinimumSize(150, 0) self.dest_directory_edit.setText(dest_directory) self.dest_directory_label.setBuddy(self.dest_directory_edit) self.dest_pick_button = QPushButton(_("..."), self) self.dest_pick_button.setMaximumSize(24, 20) self.dest_pick_button.clicked.connect(self._get_dest_directory_name) options_layout.addWidget(self.dest_directory_label, 1, 0, 1, 1) options_layout.addWidget(self.dest_directory_edit, 1, 1, 1, 1) options_layout.addWidget(self.dest_pick_button, 1, 2, 1, 1) self.copies_to_keep_checkbox = QCheckBox(_('Copies to keep'), self) self.copies_to_keep_checkbox.setToolTip(_("Select this to limit the number of backup kept. If not set, the backup files must be manually deleted.")) self.copies_to_keep_spin = QSpinBox(self) self.copies_to_keep_spin.setMinimum(2) self.copies_to_keep_spin.setToolTip(_("The number of backup copies of the database to keep. The minimum is 2.")) options_layout.addWidget(self.copies_to_keep_checkbox, 1, 3, 1, 1) options_layout.addWidget(self.copies_to_keep_spin, 1, 4, 1, 1) self.copies_to_keep_checkbox.clicked.connect(self.copies_to_keep_checkbox_clicked) if copies_to_keep == -1: self.copies_to_keep_checkbox.setCheckState(not Qt.Checked) else: self.copies_to_keep_checkbox.setCheckState(Qt.Checked) self.copies_to_keep_spin.setProperty('value', copies_to_keep) self.do_daily_backp_checkbox_clicked(do_daily_backup) other_options_group = QGroupBox(_('Other Options'), self) layout.addWidget(other_options_group ) options_layout = QGridLayout() other_options_group.setLayout(options_layout) library_default_label = QLabel(_('&Library Button default:'), self) library_default_label.setToolTip(_('If plugin is placed as a toolbar button, choose a default action when clicked on')) self.library_default_combo = KeyComboBox(self, self.plugin_action.library_actions_map, unicode(get_plugin_pref(COMMON_OPTIONS_STORE_NAME, KEY_BUTTON_ACTION_LIBRARY))) library_default_label.setBuddy(self.library_default_combo) options_layout.addWidget(library_default_label, 0, 0, 1, 1) options_layout.addWidget(self.library_default_combo, 0, 1, 1, 2) device_default_label = QLabel(_('&Device Button default:'), self) device_default_label.setToolTip(_('If plugin is placed as a toolbar button, choose a default action when clicked on')) self.device_default_combo = KeyComboBox(self, self.plugin_action.device_actions_map, unicode(get_plugin_pref(COMMON_OPTIONS_STORE_NAME, KEY_BUTTON_ACTION_DEVICE))) device_default_label.setBuddy(self.device_default_combo) options_layout.addWidget(device_default_label, 1, 0, 1, 1) options_layout.addWidget(self.device_default_combo, 1, 1, 1, 2) keyboard_shortcuts_button = QPushButton(_('Keyboard shortcuts...'), self) keyboard_shortcuts_button.setToolTip(_('Edit the keyboard shortcuts associated with this plugin')) keyboard_shortcuts_button.clicked.connect(self.edit_shortcuts) layout.addWidget(keyboard_shortcuts_button) layout.addStretch(1) def store_on_connect_checkbox_clicked(self, checked): self.prompt_to_store_checkbox.setEnabled(checked) self.store_if_more_recent_checkbox.setEnabled(checked) self.do_not_store_if_reopened_checkbox.setEnabled(checked) def do_daily_backp_checkbox_clicked(self, checked): self.dest_directory_edit.setEnabled(checked) self.dest_pick_button.setEnabled(checked) self.dest_directory_label.setEnabled(checked) self.copies_to_keep_checkbox.setEnabled(checked) self.copies_to_keep_checkbox_clicked(checked and self.copies_to_keep_checkbox.checkState() == Qt.Checked) def copies_to_keep_checkbox_clicked(self, checked): self.copies_to_keep_spin.setEnabled(checked) # Called by Calibre before save_settings def validate(self): # import traceback # traceback.print_stack() debug_print('BEGIN Validate') valid = True # Only save if we were able to get data to avoid corrupting stored data # if self.do_daily_backp_checkbox.checkState() == Qt.Checked and not len(self.dest_directory_edit.text()): # error_dialog(self, 'No destination directory', # 'If the automatic device backup is set, there must be a destination directory.', # show=True, show_copy_button=False) # valid = False debug_print('END Validate, status = %s' % valid) return valid def save_settings(self): new_prefs = {} new_prefs[KEY_BUTTON_ACTION_DEVICE] = unicode(self.device_default_combo.currentText()) new_prefs[KEY_BUTTON_ACTION_LIBRARY] = unicode(self.library_default_combo.currentText()) new_prefs[KEY_STORE_ON_CONNECT] = self.store_on_connect_checkbox.checkState() == Qt.Checked new_prefs[KEY_PROMPT_TO_STORE] = self.prompt_to_store_checkbox.checkState() == Qt.Checked new_prefs[KEY_STORE_IF_MORE_RECENT] = self.store_if_more_recent_checkbox.checkState() == Qt.Checked new_prefs[KEY_DO_NOT_STORE_IF_REOPENED] = self.do_not_store_if_reopened_checkbox.checkState() == Qt.Checked plugin_prefs[COMMON_OPTIONS_STORE_NAME] = new_prefs new_update_prefs = {} # new_update_prefs[KEY_DO_UPDATE_CHECK] = self.do_update_check.checkState() == Qt.Checked # new_update_prefs[KEY_DO_EARLY_FIRMWARE_CHECK] = self.do_early_firmware_check.checkState() == Qt.Checked # new_update_prefs[KEY_LAST_FIRMWARE_CHECK_TIME] = self.update_check_last_time plugin_prefs[UPDATE_OPTIONS_STORE_NAME] = new_update_prefs backup_prefs = {} backup_prefs[KEY_DO_DAILY_BACKUP] = self.do_daily_backp_checkbox.checkState() == Qt.Checked backup_prefs[KEY_BACKUP_DEST_DIRECTORY] = unicode(self.dest_directory_edit.text()) backup_prefs[KEY_BACKUP_COPIES_TO_KEEP] = int(unicode(self.copies_to_keep_spin.value())) if self.copies_to_keep_checkbox.checkState() == Qt.Checked else -1 plugin_prefs[BACKUP_OPTIONS_STORE_NAME] = backup_prefs db = self.plugin_action.gui.current_db library_config = get_library_config(db) library_config[KEY_CURRENT_LOCATION_CUSTOM_COLUMN] = self.current_Location_combo.get_selected_column() library_config[KEY_PERCENT_READ_CUSTOM_COLUMN] = self.percent_read_combo.get_selected_column() library_config[KEY_RATING_CUSTOM_COLUMN] = self.rating_combo.get_selected_column() library_config[KEY_LAST_READ_CUSTOM_COLUMN] = self.last_read_combo.get_selected_column() set_library_config(db, library_config) def get_number_custom_columns(self): column_types = ['float','int'] return self.get_custom_columns(column_types) def get_rating_custom_columns(self): column_types = ['rating','int'] custom_columns = self.get_custom_columns(column_types) ratings_column_name = self.plugin_action.gui.library_view.model().orig_headers['rating'] custom_columns['rating'] = {'name': ratings_column_name} return custom_columns def get_text_custom_columns(self): column_types = ['text'] return self.get_custom_columns(column_types) def get_date_custom_columns(self): column_types = ['datetime'] return self.get_custom_columns(column_types) def get_custom_columns(self, column_types): custom_columns = self.plugin_action.gui.library_view.model().custom_columns available_columns = {} for key, column in custom_columns.iteritems(): typ = column['datatype'] if typ in column_types and not column['is_multiple']: available_columns[key] = column return available_columns def help_link_activated(self, url): self.plugin_action.show_help(anchor="configuration") def edit_shortcuts(self): d = KeyboardConfigDialog(self.plugin_action.gui, self.plugin_action.action_spec[0]) if d.exec_() == d.Accepted: self.plugin_action.gui.keyboard.finalize() def _get_dest_directory_name(self): path = choose_dir(self, 'backup annotations destination dialog','Choose destination directory') if path: self.dest_directory_edit.setText(path)
class TwoPlots(QWidget): ''' Widget to plot 2 curves x(t) & y(t), or Vx(t) & Vy(t)''' Ylabels = { "position": ("X [pixel]", "Y [pixel]"), "velocity": ("VX [pixel/s]", "VY [pixel/s]"), "position_mm": ("X [mm]", "Y [mm]"), "velocity_mm": ("VX [mm/s]", "VY [mm/s]") } CurveLabels = { "position": ("X(t) [{}]", "Y(t) [{}]"), "velocity": ("VX(t) {}", "VY(t) {}") } def __init__(self, mainWindow, quantity): # appel du constructeur de la classe de base : QWidget.__init__(self, mainWindow) self.mw = mainWindow # la fenêtre de l'application principale # Attributs (objets persistants) self.__quantity = quantity # "position" or "velocity" self.__data1 = None # data for the first plot self.__data2 = None # data for tthe second self.__figure = None # figure tracé self.__axes1 = None # système d'axes tracé 1 self.__axes2 = None # système d'axes tracé 2 self.__canvas = None # pour le tracé matplot.lib self.__toolbar = None # barre d'outils tracé self.__time = None # abcissa values for plot self.__xlim = None # xmin, xmay tracé self.__xlabel = None # étiquette axe X (n° image ou temps [s]) self.__ylim1 = None # ymin, ymax tracé x(t) self.__ylim2 = None # ymin, ymax tracé y(t) self.btn_imageSize = QRadioButton("ImageSize", self) self.btn_autoSize = QRadioButton("AutoSize", self) self.btn_smooth_Vx = QCheckBox("Lissage Vx", self) self.btn_smooth_Vy = QCheckBox("Lissage Vy", self) self.x_mav_nb_pts = QSpinBox(parent=self) # X velocity moving average self.y_mav_nb_pts = QSpinBox(parent=self) # Y velocity moving average self.__initUI() # Initialisation de l'interface utilisateur def __initUI(self): if self.__quantity == "position": for w in (self.btn_smooth_Vx, self.btn_smooth_Vy, self.x_mav_nb_pts, self.y_mav_nb_pts): w.setVisible(False) w.setEnabled(False) group = QButtonGroup(self) group.addButton(self.btn_imageSize) group.addButton(self.btn_autoSize) self.btn_imageSize.toggled.connect(self.__ImageSizePlotXYLim) self.btn_imageSize.setEnabled(False) texte = "Tracé avec les bornes min et max de l'image" self.btn_imageSize.setStatusTip(texte) self.btn_imageSize.setChecked(True) self.btn_autoSize.toggled.connect(self.__AutoSizePlotXYLim) self.btn_autoSize.setEnabled(False) texte = "Tracé avec les bornes min et max de la " texte += "trajectoire calculée" self.btn_autoSize.setStatusTip(texte) elif self.__quantity == "velocity": for w in (self.btn_imageSize, self.btn_autoSize): w.setVisible(False) w.setEnabled(False) self.btn_smooth_Vx.stateChanged.connect(self.__smooth_Vx_wanted) self.btn_smooth_Vy.stateChanged.connect(self.__smooth_Vy_wanted) self.x_mav_nb_pts.setEnabled(False) self.y_mav_nb_pts.setEnabled(False) self.x_mav_nb_pts.setRange(3, 100) self.y_mav_nb_pts.setRange(3, 100) self.x_mav_nb_pts.setSingleStep(2) self.y_mav_nb_pts.setSingleStep(2) self.x_mav_nb_pts.valueChanged.connect(self.__smooth_vX) self.y_mav_nb_pts.valueChanged.connect(self.__smooth_vY) vbox = QVBoxLayout() self.setLayout(vbox) # Ligne 1 : tracé de l'image self.setLayout(vbox) self.__figure = Figure() self.__axes1 = self.__figure.add_subplot(211) self.__axes2 = self.__figure.add_subplot(212) self.__figure.subplots_adjust(left=0.120, right=0.99, bottom=0.11, top=0.98) self.__canvas = FigureCanvas(self.__figure) self.__toolbar = NavigationToolbar(self.__canvas, self) #self.__toolbar.setMinimumWidth(450) vbox.addWidget(self.__canvas) hbox = QHBoxLayout() hbox.addWidget(self.__toolbar) hbox.addStretch() if self.__quantity == "position": hbox.addWidget(self.btn_imageSize) hbox.addWidget(self.btn_autoSize) elif self.__quantity == "velocity": vb = QVBoxLayout() hb = QHBoxLayout() hb.addWidget(self.btn_smooth_Vx) hb.addWidget(self.x_mav_nb_pts) vb.addLayout(hb) hb = QHBoxLayout() hb.addWidget(self.btn_smooth_Vy) hb.addWidget(self.y_mav_nb_pts) vb.addLayout(hb) hbox.addLayout(vb) vbox.addLayout(hbox) def reset(self): if self.__quantity == "velocity": for w in (self.btn_smooth_Vx, self.btn_smooth_Vy, self.x_mav_nb_pts, self.y_mav_nb_pts): w.setVisible(True) w.setEnabled(True) self.x_mav_nb_pts.setValue(self.x_mav_nb_pts.minimum()) self.y_mav_nb_pts.setValue(self.y_mav_nb_pts.minimum()) self.btn_smooth_Vx.setCheckState(Qt.Unchecked) self.btn_smooth_Vy.setCheckState(Qt.Unchecked) def __smooth_Vx_wanted(self, checked): if checked: self.x_mav_nb_pts.setEnabled(True) else: self.x_mav_nb_pts.setEnabled(False) self.Plot() def __smooth_Vy_wanted(self, checked): if checked: self.y_mav_nb_pts.setEnabled(True) else: self.y_mav_nb_pts.setEnabled(False) self.Plot() def __smooth_vX(self, nb_pts): if self.btn_smooth_Vx.isChecked(): self.Plot() else: pass def __smooth_vY(self, nb_pts): if self.btn_smooth_Vy.isChecked(): self.Plot() else: pass def __compute_velocity(self, U, plot_id): """Computes the velocity with the centered finite difference of order 1 : V[i] = (U[i+1] - U[i-1])/(T[i+1] - T[i-1]) for i in [1,N-1] V[0] = (U[1] - U[0])/(T[1] - T[0]) V[-1] = (U[-1] - U[-2])/(T[-1] - T[-2]) """ V = U.copy() T = self.__time V[0] = (U[1] - U[0]) / (T[1] - T[0]) V[-1] = (U[-1] - U[-2]) / (T[-1] - T[-2]) V[1:-1] = (U[2:] - U[:-2]) / (T[2:] - T[:-2]) if plot_id == "x": if self.btn_smooth_Vx.isChecked(): N = self.x_mav_nb_pts.value() V = self.__smooth_data(V, N) elif plot_id == "y": if self.btn_smooth_Vy.isChecked(): N = self.y_mav_nb_pts.value() V = self.__smooth_data(V, N) return V def __smooth_data(self, U, nb_pts): """Computes the nb_pts moving average on U.""" N = nb_pts V = U.copy() V.fill(np.nan) mav = deque(maxlen=N) # initialize the mav (moving average) for e in U[:N]: mav.append(e) # move! index, count = N // 2, 0 while count < V.shape[0] - N: V[index] = np.mean(mav) mav.append(U[N + count]) count += 1 index += 1 return V def Plot(self): target_pos = self.mw.target_pos if target_pos is None: return else: self.__data1, self.__data2, I = target_pos scale = self.mw.imageTab.pix_to_mm_coeff if self.__quantity == "position": self.btn_imageSize.setEnabled(True) self.btn_autoSize.setEnabled(True) elif self.__quantity == "velocity": pass # Effacement automatiqe si demandé à chaque nouveau tracé : if self.mw.flags["autoClearTraj"]: if self.__axes1 is not None: self.__axes1.clear() if self.__axes2 is not None: self.__axes2.clear() # Récupération du nom de l'alagorithme de traitement : algo = self.mw.imageTab.btn_algo.currentText() # Récupération de la valeur de FP (Frame per seconde) pour calcul # du pas de temps et des abscisses : deltaT = None if self.mw.imageTab.video_FPS is not None: deltaT = 1. / self.mw.imageTab.video_FPS self.__time = np.array(I) * deltaT self.__xlabel = "temps [s]" else: self.__time = np.array(I) self.__xlabel = "image #" if self.__quantity == "velocity": if deltaT is not None: self.__data1 = self.__compute_velocity(self.__data1, "x") self.__data2 = self.__compute_velocity(self.__data2, "y") if self.btn_smooth_Vx.isChecked(): N = self.x_mav_nb_pts.value() if self.btn_smooth_Vy.isChecked(): N = self.y_mav_nb_pts.value() self.__AutoSizePlotXYLim() else: self.__data1, self.__data2 = None, None self.mw.target_veloc = np.array([self.__data1, self.__data2]) else: self.__ImageSizePlotXYLim() if self.__data1 is None or self.__data2 is None: return curveLabelX, curveLabelY = TwoPlots.CurveLabels[self.__quantity] if self.__quantity == "position": Xlabel, Ylabel = curveLabelX.format(algo), curveLabelY.format(algo) else: Xlabel, Ylabel = curveLabelX.format(""), curveLabelY.format("") color = 'b' if self.mw.target_RGB is None else self.mw.target_RGB / 255 # tracé de courbe x(t) self.__axes1.plot(self.__time, self.__data1 * scale, color=color, marker='o', markersize=2, linewidth=.4, label=Xlabel) self.__axes1.grid(True) #self.__axes1.legend(fontsize=9, framealpha=0.7, # bbox_to_anchor=(-0.1, 1.1), loc='upper left') self.__axes1.legend(loc='best', fontsize=10) # tracé de courbe y(t) self.__axes2.plot(self.__time, self.__data2 * scale, color=color, marker='o', markersize=2, linewidth=.4, label=Ylabel) self.__axes2.grid(True) #self.__axes2.legend(fontsize=9, framealpha=0.7, # bbox_to_anchor=(1.1, 1.1), loc='upper right') self.__axes2.legend(loc='best', fontsize=10) self.__canvas.draw() def __AutoSizePlotXYLim(self): if self.mw.target_pos is None: return y1label, y2label = TwoPlots.Ylabels[self.__quantity] self.__xlim = np.array( [np.nanmin(self.__time), np.nanmax(self.__time)]) scale = self.mw.imageTab.pix_to_mm_coeff if not self.btn_smooth_Vx.isChecked(): self.__ylim1 = np.array( [np.nanmin(self.__data1), np.nanmax(self.__data1)]) * scale offset = (self.__ylim1[1] - self.__ylim1[0]) / 10 self.__ylim1 += np.array([-offset, offset]) else: #self.__ylim1 = self.__axes1.get_ylim() pass if not self.btn_smooth_Vy.isChecked(): self.__ylim2 = np.array( [np.nanmin(self.__data2), np.nanmax(self.__data2)]) * scale offset = (self.__ylim2[1] - self.__ylim2[0]) / 10 self.__ylim2 += np.array([-offset, offset]) else: #self.__ylim2 = self.__axes2.get_ylim() pass if self.mw.imageTab.valid_scale: y1label, y2label = TwoPlots.Ylabels[self.__quantity + "_mm"] self.__axes1.set_xlim(*self.__xlim) self.__axes2.set_xlim(*self.__xlim) self.__axes1.set_ylim(*self.__ylim1) self.__axes2.set_ylim(*self.__ylim2) self.__axes1.set_ylabel(y1label) self.__axes2.set_ylabel(y2label) self.__axes2.set_xlabel(self.__xlabel) self.__canvas.draw() def __ImageSizePlotXYLim(self): if self.mw.target_pos is None: return scale = self.mw.imageTab.pix_to_mm_coeff y1label, y2label = TwoPlots.Ylabels[self.__quantity] w, h = self.mw.imageTab.video_size self.__xlim = np.array( [np.nanmin(self.__time), np.nanmax(self.__time)]) self.__ylim1 = np.array([0, w - 1], dtype=float) * scale self.__ylim2 = np.array([0, h - 1], dtype=float) * scale if self.mw.imageTab.valid_scale: y1label, y2label = TwoPlots.Ylabels[self.__quantity + "_mm"] self.__axes1.set_xlim(*self.__xlim) self.__axes2.set_xlim(*self.__xlim) self.__axes1.set_ylim(*self.__ylim1) self.__axes2.set_ylim(*self.__ylim2) self.__axes1.set_ylabel(y1label) self.__axes2.set_ylabel(y2label) self.__axes2.set_xlabel(self.__xlabel) self.__canvas.draw() def ClearAxes(self): if self.__axes1 is not None: self.__axes1.clear() if self.__axes2 is not None: self.__axes2.clear() self.__canvas.draw()
class ConfigWidget(QWidget): plugin = None def __init__(self, plugin): QWidget.__init__(self) self.plugin = plugin self.layout = QVBoxLayout() self.setLayout(self.layout) self.engine_location_label = QLabel('ElasticSearch engine location:') self.layout.addWidget(self.engine_location_label) self.elasticsearch_url_textbox = QLineEdit(self) self.elasticsearch_url_textbox.setText(prefs['elasticsearch_url']) self.layout.addWidget(self.elasticsearch_url_textbox) self.engine_location_label.setBuddy(self.elasticsearch_url_textbox) self.layout.addSpacing(10) self.pdftotext_path_label = QLabel('Path to pdftotext tool:') self.layout.addWidget(self.pdftotext_path_label) self.pdftotext_path_textbox = QLineEdit(self) self.pdftotext_path_textbox.setText(prefs['pdftotext_path']) self.layout.addWidget(self.pdftotext_path_textbox) self.pdftotext_path_label.setBuddy(self.pdftotext_path_textbox) self.layout.addSpacing(10) self.concurrency_label = QLabel( 'Number of parallel processes for text extraction:') self.layout.addWidget(self.concurrency_label) self.concurrency_textbox = QLineEdit(self) self.concurrency_textbox.setText(str(prefs['concurrency'])) self.layout.addWidget(self.concurrency_textbox) self.concurrency_label.setBuddy(self.concurrency_textbox) self.layout.addSpacing(10) self.formats_label = QLabel('Index book formats:') self.layout.addWidget(self.formats_label) file_formats = prefs['file_formats'].split(',') self.formats_list = QListWidget(self) for fmt in SUPPORTED_FORMATS: item = QListWidgetItem(fmt) item.setFlags(item.flags() | Qt.ItemIsUserCheckable) item.setCheckState(Qt.Checked if fmt in file_formats else Qt.Unchecked) self.formats_list.addItem(item) self.layout.addWidget(self.formats_list) self.formats_label.setBuddy(self.formats_list) self.layout.addSpacing(10) self.autoindex_checkbox = QCheckBox( "Automatically index new books on search", self) self.autoindex_checkbox.setCheckState( Qt.Checked if prefs['autoindex'] else Qt.Unchecked) self.layout.addWidget(self.autoindex_checkbox) self.layout.addSpacing(10) self.privacy_label = QLabel('Privacy:') self.layout.addWidget(self.privacy_label) self.clear_search_history_button = QPushButton('Clear search &history', self) self.clear_search_history_button.clicked.connect(self.on_clear_history) self.layout.addWidget(self.clear_search_history_button) self.clear_search_index_buttin = QPushButton('Clear search &index', self) self.clear_search_index_buttin.clicked.connect(self.on_clear_index) self.layout.addWidget(self.clear_search_index_buttin) def save_settings(self): prefs['elasticsearch_url'] = self.elasticsearch_url_textbox.text() prefs['pdftotext_path'] = self.pdftotext_path_textbox.text() try: prefs['concurrency'] = int(self.concurrency_textbox.text()) except Exception: pass file_formats = [] for i in range(len(SUPPORTED_FORMATS)): if self.formats_list.item(i).checkState() == Qt.Checked: file_formats.append(self.formats_list.item(i).text()) prefs['file_formats'] = ','.join(file_formats) prefs['autoindex'] = True if self.autoindex_checkbox.checkState( ) == Qt.Checked else False def on_clear_history(self): from calibre.gui2 import info_dialog if 'search_lru' in prefs: del prefs['search_lru'] if self.plugin.search_dialog: self.plugin.search_dialog.clear_lru() info_dialog(self, TITLE, 'History cleared', show=True) def on_clear_index(self): from calibre.gui2 import question_dialog, error_dialog if question_dialog( self, TITLE, 'You are about to clear all fulltext search index. Rebuilding it might take a while. Are you sure?', default_yes=False): elastic_search_client = Elasticsearch([prefs['elasticsearch_url']], timeout=20.0) if not elastic_search_client.ping(): error_dialog( self, TITLE, 'Could not connect to ElasticSearch cluster. Please make sure that it\'s running.', show=True) return elastic_search_client.indices.delete(index='library', ignore=[400, 404]) prefs[self.plugin.gui.current_db.new_api.library_id] = { 'index_state': {} }
class AnnotationsAppearance(SizePersistedDialog): ''' Dialog for managing CSS rules, including Preview window ''' if isosx: FONT = QFont('Monaco', 12) elif iswindows: FONT = QFont('Lucida Console', 9) elif islinux: FONT = QFont('Monospace', 9) FONT.setStyleHint(QFont.TypeWriter) def __init__(self, parent, icon, prefs): self.opts = parent.opts self.parent = parent self.prefs = prefs self.icon = icon super(AnnotationsAppearance, self).__init__(parent, 'appearance_dialog') self.setWindowTitle('Annotations appearance') self.setWindowIcon(icon) self.l = QVBoxLayout(self) self.setLayout(self.l) # Add a label for description #self.description_label = QLabel("Descriptive text here") #self.l.addWidget(self.description_label) # Add a group box, vertical layout for preview window self.preview_gb = QGroupBox(self) self.preview_gb.setTitle("Preview") self.preview_vl = QVBoxLayout(self.preview_gb) self.l.addWidget(self.preview_gb) self.wv = QWebView() self.wv.setHtml('<p></p>') self.wv.setMinimumHeight(100) self.wv.setMaximumHeight(16777215) self.wv.setGeometry(0, 0, 200, 100) self.wv.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.preview_vl.addWidget(self.wv) # Create a group box, horizontal layout for the table self.css_table_gb = QGroupBox(self) self.css_table_gb.setTitle("Annotation elements") self.elements_hl = QHBoxLayout(self.css_table_gb) self.l.addWidget(self.css_table_gb) # Add the group box to the main layout self.elements_table = AnnotationElementsTable(self, 'annotation_elements_tw') self.elements_hl.addWidget(self.elements_table) self.elements_table.initialize() # Options self.options_gb = QGroupBox(self) self.options_gb.setTitle("Options") self.options_gl = QGridLayout(self.options_gb) self.l.addWidget(self.options_gb) current_row = 0 # <hr/> separator # addWidget(widget, row, col, rowspan, colspan) self.hr_checkbox = QCheckBox('Add horizontal rule between annotations') self.hr_checkbox.stateChanged.connect(self.hr_checkbox_changed) self.hr_checkbox.setCheckState( prefs.get('appearance_hr_checkbox', False)) self.options_gl.addWidget(self.hr_checkbox, current_row, 0, 1, 4) current_row += 1 # Timestamp self.timestamp_fmt_label = QLabel("Timestamp format:") self.options_gl.addWidget(self.timestamp_fmt_label, current_row, 0) self.timestamp_fmt_le = QLineEdit( prefs.get('appearance_timestamp_format', default_timestamp), parent=self) self.timestamp_fmt_le.textEdited.connect(self.timestamp_fmt_changed) self.timestamp_fmt_le.setFont(self.FONT) self.timestamp_fmt_le.setObjectName('timestamp_fmt_le') self.timestamp_fmt_le.setToolTip('Format string for timestamp') self.timestamp_fmt_le.setMaximumWidth(16777215) self.timestamp_fmt_le.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum) self.options_gl.addWidget(self.timestamp_fmt_le, current_row, 1) self.timestamp_fmt_reset_tb = QToolButton(self) self.timestamp_fmt_reset_tb.setToolTip("Reset to default") self.timestamp_fmt_reset_tb.setIcon(QIcon(I('trash.png'))) self.timestamp_fmt_reset_tb.clicked.connect(self.reset_timestamp_to_default) self.options_gl.addWidget(self.timestamp_fmt_reset_tb, current_row, 2) self.timestamp_fmt_help_tb = QToolButton(self) self.timestamp_fmt_help_tb.setToolTip("Format string reference") self.timestamp_fmt_help_tb.setIcon(QIcon(I('help.png'))) self.timestamp_fmt_help_tb.clicked.connect(self.show_help) self.options_gl.addWidget(self.timestamp_fmt_help_tb, current_row, 3) # Button box bb = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel) bb.accepted.connect(self.accept) bb.rejected.connect(self.reject) self.l.addWidget(bb) # Spacer #self.spacerItem = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) #self.l.addItem(self.spacerItem) # Sizing #sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Preferred) #sizePolicy.setHorizontalStretch(0) #sizePolicy.setVerticalStretch(0) #sizePolicy.setHeightForWidth(self.sizePolicy().hasHeightForWidth()) #self.setSizePolicy(sizePolicy) self.resize_dialog() def hr_checkbox_changed(self, state): self.prefs.set('appearance_hr_checkbox', state) self.prefs.commit() self.elements_table.preview_css() def reset_timestamp_to_default(self): from calibre_plugins.marvin_manager.appearance import default_timestamp self.timestamp_fmt_le.setText(default_timestamp) self.timestamp_fmt_changed() def show_help(self): ''' Display strftime help file ''' from calibre.gui2 import open_url path = os.path.join(self.parent.resources_path, 'help/timestamp_formats.html') open_url(QUrl.fromLocalFile(path)) def sizeHint(self): return QtCore.QSize(600, 200) def timestamp_fmt_changed(self): self.prefs.set('appearance_timestamp_format', str(self.timestamp_fmt_le.text())) self.prefs.commit() self.elements_table.preview_css()