def _intialize_file_settings(self, layout): '''Initialize file creation/sending settings''' separator_a = QFrame() separator_a.setFrameStyle(QFrame.HLine) separator_a.setFrameShadow(QFrame.Sunken) layout.addWidget(separator_a) files_to_create = QGroupBox() files_to_create.setTitle('Files to create/send') files_to_create.setLayout(QGridLayout(files_to_create)) self._settings['create_send_xray'] = QCheckBox('X-Ray') self._settings['create_send_xray'].setChecked(__prefs__['create_send_xray']) files_to_create.layout().addWidget(self._settings['create_send_xray'], 0, 0) self._settings['create_send_author_profile'] = QCheckBox('Author Profile') self._settings['create_send_author_profile'].setChecked(__prefs__['create_send_author_profile']) files_to_create.layout().addWidget(self._settings['create_send_author_profile'], 1, 0) self._settings['create_send_start_actions'] = QCheckBox('Start Actions') self._settings['create_send_start_actions'].setChecked(__prefs__['create_send_start_actions']) files_to_create.layout().addWidget(self._settings['create_send_start_actions'], 0, 1) self._settings['create_send_end_actions'] = QCheckBox('End Actions') self._settings['create_send_end_actions'].setChecked(__prefs__['create_send_end_actions']) files_to_create.layout().addWidget(self._settings['create_send_end_actions'], 1, 1) layout.addWidget(files_to_create)
def _initialize_file_type_settings(self, layout): '''Initialize file creation/sending type settings''' separator_b = QFrame() separator_b.setFrameStyle(QFrame.HLine) separator_b.setFrameShadow(QFrame.Sunken) layout.addWidget(separator_b) book_types_to_create = QGroupBox() book_types_to_create.setTitle('Book types to create files for:') book_types_to_create.setLayout(QHBoxLayout(book_types_to_create)) self._settings['mobi'] = QCheckBox('MOBI') self._settings['mobi'].setChecked('mobi' in __prefs__['formats']) book_types_to_create.layout().addWidget(self._settings['mobi']) self._settings['azw3'] = QCheckBox('AZW3') self._settings['azw3'].setChecked('azw3' in __prefs__['formats']) book_types_to_create.layout().addWidget(self._settings['azw3']) layout.addWidget(book_types_to_create) file_preference_layout = QGroupBox() file_preference_layout.setTitle('If device has both (mobi and azw3) formats, prefer:') file_preference_layout.setLayout(QHBoxLayout(file_preference_layout)) file_preference_group = QButtonGroup() self._settings['file_preference_mobi'] = QRadioButton('MOBI') self._settings['file_preference_mobi'].setChecked(__prefs__['file_preference'] == 'mobi') file_preference_group.addButton(self._settings['file_preference_mobi']) file_preference_layout.layout().addWidget(self._settings['file_preference_mobi']) self._settings['file_preference_azw3'] = QRadioButton('AZW3') self._settings['file_preference_azw3'].setChecked(__prefs__['file_preference'] == 'azw3') file_preference_group.addButton(self._settings['file_preference_azw3']) file_preference_layout.layout().addWidget(self._settings['file_preference_azw3']) layout.addWidget(file_preference_layout)
def add_separator(self): """ Add an horizontal line to layout. """ separator = QFrame() separator.setFrameShape(QFrame.HLine) separator.setFrameShadow(QFrame.Sunken) separator.setMinimumSize(self.width(), 2) # Add to layout self.main_layout.addWidget(separator, self.row, 0, 1, 5) # Increment vertically position for next widget self.row += 1
def _initialize_file_type_settings(self, layout): '''Initialize file creation/sending type settings''' separator_b = QFrame() separator_b.setFrameStyle(QFrame.HLine) separator_b.setFrameShadow(QFrame.Sunken) layout.addWidget(separator_b) book_types_to_create = QGroupBox() book_types_to_create.setTitle('Book types to create files for:') book_types_to_create.setLayout(QHBoxLayout(book_types_to_create)) self._settings['mobi'] = QCheckBox('MOBI') self._settings['mobi'].setChecked('mobi' in __prefs__['formats']) book_types_to_create.layout().addWidget(self._settings['mobi']) self._settings['azw3'] = QCheckBox('AZW3') self._settings['azw3'].setChecked('azw3' in __prefs__['formats']) book_types_to_create.layout().addWidget(self._settings['azw3']) layout.addWidget(book_types_to_create) file_preference_layout = QGroupBox() file_preference_layout.setTitle( 'If device has both (mobi and azw3) formats, prefer:') file_preference_layout.setLayout(QHBoxLayout(file_preference_layout)) file_preference_group = QButtonGroup() self._settings['file_preference_mobi'] = QRadioButton('MOBI') self._settings['file_preference_mobi'].setChecked( __prefs__['file_preference'] == 'mobi') file_preference_group.addButton(self._settings['file_preference_mobi']) file_preference_layout.layout().addWidget( self._settings['file_preference_mobi']) self._settings['file_preference_azw3'] = QRadioButton('AZW3') self._settings['file_preference_azw3'].setChecked( __prefs__['file_preference'] == 'azw3') file_preference_group.addButton(self._settings['file_preference_azw3']) file_preference_layout.layout().addWidget( self._settings['file_preference_azw3']) layout.addWidget(file_preference_layout)
def _intialize_file_settings(self, layout): '''Initialize file creation/sending settings''' separator_a = QFrame() separator_a.setFrameStyle(QFrame.HLine) separator_a.setFrameShadow(QFrame.Sunken) layout.addWidget(separator_a) files_to_create = QGroupBox() files_to_create.setTitle('Files to create/send') files_to_create.setLayout(QGridLayout(files_to_create)) self._settings['create_send_xray'] = QCheckBox('X-Ray') self._settings['create_send_xray'].setChecked( __prefs__['create_send_xray']) files_to_create.layout().addWidget(self._settings['create_send_xray'], 0, 0) self._settings['create_send_author_profile'] = QCheckBox( 'Author Profile') self._settings['create_send_author_profile'].setChecked( __prefs__['create_send_author_profile']) files_to_create.layout().addWidget( self._settings['create_send_author_profile'], 1, 0) self._settings['create_send_start_actions'] = QCheckBox( 'Start Actions') self._settings['create_send_start_actions'].setChecked( __prefs__['create_send_start_actions']) files_to_create.layout().addWidget( self._settings['create_send_start_actions'], 0, 1) self._settings['create_send_end_actions'] = QCheckBox('End Actions') self._settings['create_send_end_actions'].setChecked( __prefs__['create_send_end_actions']) files_to_create.layout().addWidget( self._settings['create_send_end_actions'], 1, 1) layout.addWidget(files_to_create)
class FindAnnotationsDialog(SizePersistedDialog, Logger): GENERIC_STYLE = 'Any style' GENERIC_READER = 'Any reader' def __init__(self, opts): self.matched_ids = set() self.opts = opts self.prefs = opts.prefs super(FindAnnotationsDialog, self).__init__(self.opts.gui, 'find_annotations_dialog') self.setWindowTitle('Find Annotations') self.setWindowIcon(self.opts.icon) self.l = QVBoxLayout(self) self.setLayout(self.l) self.search_criteria_gb = QGroupBox(self) self.search_criteria_gb.setTitle("Search criteria") self.scgl = QGridLayout(self.search_criteria_gb) self.l.addWidget(self.search_criteria_gb) # addWidget(widget, row, col, rowspan, colspan) row = 0 # ~~~~~~~~ Create the Readers comboBox ~~~~~~~~ self.reader_label = QLabel('Reader') self.reader_label.setAlignment(Qt.AlignRight|Qt.AlignVCenter) self.scgl.addWidget(self.reader_label, row, 0, 1, 1) self.find_annotations_reader_comboBox = QComboBox() self.find_annotations_reader_comboBox.setObjectName('find_annotations_reader_comboBox') self.find_annotations_reader_comboBox.setToolTip('Reader annotations to search for') self.find_annotations_reader_comboBox.addItem(self.GENERIC_READER) racs = ReaderApp.get_reader_app_classes() for ra in sorted(racs.keys()): self.find_annotations_reader_comboBox.addItem(ra) self.scgl.addWidget(self.find_annotations_reader_comboBox, row, 1, 1, 4) row += 1 # ~~~~~~~~ Create the Styles comboBox ~~~~~~~~ self.style_label = QLabel('Style') self.style_label.setAlignment(Qt.AlignRight|Qt.AlignVCenter) self.scgl.addWidget(self.style_label, row, 0, 1, 1) self.find_annotations_color_comboBox = QComboBox() self.find_annotations_color_comboBox.setObjectName('find_annotations_color_comboBox') self.find_annotations_color_comboBox.setToolTip('Annotation style to search for') self.find_annotations_color_comboBox.addItem(self.GENERIC_STYLE) all_colors = COLOR_MAP.keys() all_colors.remove('Default') for color in sorted(all_colors): self.find_annotations_color_comboBox.addItem(color) self.scgl.addWidget(self.find_annotations_color_comboBox, row, 1, 1, 4) row += 1 # ~~~~~~~~ Create the Text LineEdit control ~~~~~~~~ self.text_label = QLabel('Text') self.text_label.setAlignment(Qt.AlignRight|Qt.AlignVCenter) self.scgl.addWidget(self.text_label, row, 0, 1, 1) self.find_annotations_text_lineEdit = MyLineEdit() self.find_annotations_text_lineEdit.setObjectName('find_annotations_text_lineEdit') self.scgl.addWidget(self.find_annotations_text_lineEdit, row, 1, 1, 3) self.reset_text_tb = QToolButton() self.reset_text_tb.setObjectName('reset_text_tb') self.reset_text_tb.setToolTip('Clear search criteria') self.reset_text_tb.setIcon(QIcon(I('trash.png'))) self.reset_text_tb.clicked.connect(self.clear_text_field) self.scgl.addWidget(self.reset_text_tb, row, 4, 1, 1) row += 1 # ~~~~~~~~ Create the Note LineEdit control ~~~~~~~~ self.note_label = QLabel('Note') self.note_label.setAlignment(Qt.AlignRight|Qt.AlignVCenter) self.scgl.addWidget(self.note_label, row, 0, 1, 1) self.find_annotations_note_lineEdit = MyLineEdit() self.find_annotations_note_lineEdit.setObjectName('find_annotations_note_lineEdit') self.scgl.addWidget(self.find_annotations_note_lineEdit, row, 1, 1, 3) self.reset_note_tb = QToolButton() self.reset_note_tb.setObjectName('reset_note_tb') self.reset_note_tb.setToolTip('Clear search criteria') self.reset_note_tb.setIcon(QIcon(I('trash.png'))) self.reset_note_tb.clicked.connect(self.clear_note_field) self.scgl.addWidget(self.reset_note_tb, row, 4, 1, 1) row += 1 # ~~~~~~~~ Create the Date range controls ~~~~~~~~ self.date_range_label = QLabel('Date range') self.date_range_label.setAlignment(Qt.AlignRight|Qt.AlignVCenter) self.scgl.addWidget(self.date_range_label, row, 0, 1, 1) # Date 'From' self.find_annotations_date_from_dateEdit = MyDateEdit(self, datetime(1970,1,1)) self.find_annotations_date_from_dateEdit.setObjectName('find_annotations_date_from_dateEdit') #self.find_annotations_date_from_dateEdit.current_val = datetime(1970,1,1) self.find_annotations_date_from_dateEdit.clear_button.clicked.connect(self.find_annotations_date_from_dateEdit.reset_from_date) self.scgl.addWidget(self.find_annotations_date_from_dateEdit, row, 1, 1, 1) self.scgl.addWidget(self.find_annotations_date_from_dateEdit.clear_button, row, 2, 1, 1) # Date 'To' self.find_annotations_date_to_dateEdit = MyDateEdit(self, datetime.today()) self.find_annotations_date_to_dateEdit.setObjectName('find_annotations_date_to_dateEdit') #self.find_annotations_date_to_dateEdit.current_val = datetime.today() self.find_annotations_date_to_dateEdit.clear_button.clicked.connect(self.find_annotations_date_to_dateEdit.reset_to_date) self.scgl.addWidget(self.find_annotations_date_to_dateEdit, row, 3, 1, 1) self.scgl.addWidget(self.find_annotations_date_to_dateEdit.clear_button, row, 4, 1, 1) row += 1 # ~~~~~~~~ Create a horizontal line ~~~~~~~~ self.hl = QFrame(self) self.hl.setGeometry(QRect(0, 0, 1, 3)) self.hl.setFrameShape(QFrame.HLine) self.hl.setFrameShadow(QFrame.Raised) self.scgl.addWidget(self.hl, row, 0, 1, 5) row += 1 # ~~~~~~~~ Create the results label field ~~~~~~~~ self.result_label = QLabel('<p style="color:red">scanning…</p>') self.result_label.setAlignment(Qt.AlignCenter) self.result_label.setWordWrap(False) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Maximum) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.result_label.sizePolicy().hasHeightForWidth()) self.result_label.setSizePolicy(sizePolicy) self.result_label.setMinimumSize(QtCore.QSize(250, 0)) self.scgl.addWidget(self.result_label, row, 0, 1, 5) row += 1 # ~~~~~~~~ Create the ButtonBox ~~~~~~~~ self.dialogButtonBox = QDialogButtonBox(self) self.dialogButtonBox.setOrientation(Qt.Horizontal) if False: self.update_button = QPushButton('Update results') self.update_button.setDefault(True) self.update_button.setVisible(False) self.dialogButtonBox.addButton(self.update_button, QDialogButtonBox.ActionRole) self.cancel_button = self.dialogButtonBox.addButton(self.dialogButtonBox.Cancel) self.find_button = self.dialogButtonBox.addButton(self.dialogButtonBox.Ok) self.find_button.setText('Find Matching Books') self.l.addWidget(self.dialogButtonBox) self.dialogButtonBox.clicked.connect(self.find_annotations_dialog_clicked) # ~~~~~~~~ Add a spacer ~~~~~~~~ self.spacerItem = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) self.l.addItem(self.spacerItem) # ~~~~~~~~ Restore previously saved settings ~~~~~~~~ self.restore_settings() # ~~~~~~~~ Declare 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() # ~~~~~~~~ Connect all signals ~~~~~~~~ self.find_annotations_reader_comboBox.currentIndexChanged.connect(partial(self.update_results, 'reader')) self.find_annotations_color_comboBox.currentIndexChanged.connect(partial(self.update_results, 'color')) self.find_annotations_text_lineEdit.editingFinished.connect(partial(self.update_results, 'text')) self.find_annotations_note_lineEdit.editingFinished.connect(partial(self.update_results, 'note')) # self.connect(self.find_annotations_text_lineEdit, pyqtSignal("return_pressed"), self.return_pressed) self.find_annotations_text_lineEdit.return_pressed.connect(self.return_pressed) # self.connect(self.find_annotations_note_lineEdit, pyqtSignal("return_pressed"), self.return_pressed) self.find_annotations_note_lineEdit.return_pressed.connect(self.return_pressed) # Date range signals connected in inventory_available() # ~~~~~~~~ Allow dialog to render before doing inventory ~~~~~~~~ #field = self.prefs.get('cfg_annotations_destination_field', None) field = get_cc_mapping('annotations', 'field', None) self.annotated_books_scanner = InventoryAnnotatedBooks(self.opts.gui, field, get_date_range=True) self.annotated_books_scanner.signal.connect(self.inventory_available) QTimer.singleShot(1, self.start_inventory_scan) def clear_note_field(self): if str(self.find_annotations_note_lineEdit.text()) > '': self.find_annotations_note_lineEdit.setText('') self.update_results('clear_note_field') def clear_text_field(self): if str(self.find_annotations_text_lineEdit.text()) > '': self.find_annotations_text_lineEdit.setText('') self.update_results('clear_text_field') def find_annotations_dialog_clicked(self, button): if self.dialogButtonBox.buttonRole(button) == QDialogButtonBox.AcceptRole: self.save_settings() self.accept() elif self.dialogButtonBox.buttonRole(button) == QDialogButtonBox.RejectRole: self.close() def inventory_available(self): ''' Update the Date range widgets with the rounded oldest, newest dates Don't connect date signals until date range available ''' self._log_location() # Reset the date range based on available annotations oldest = QDateTime(datetime.fromtimestamp(self.annotated_books_scanner.oldest_annotation)) oldest_day = QDateTime(datetime.fromtimestamp(self.annotated_books_scanner.oldest_annotation).replace(hour=0, minute=0, second=0)) newest = QDateTime(datetime.fromtimestamp(self.annotated_books_scanner.newest_annotation)) newest_day = QDateTime(datetime.fromtimestamp(self.annotated_books_scanner.newest_annotation).replace(hour=23, minute=59, second=59)) # Set 'From' date limits to inventory values self.find_annotations_date_from_dateEdit.setMinimumDateTime(oldest_day) self.find_annotations_date_from_dateEdit.current_val = oldest self.find_annotations_date_from_dateEdit.setMaximumDateTime(newest_day) # Set 'To' date limits to inventory values self.find_annotations_date_to_dateEdit.setMinimumDateTime(oldest_day) self.find_annotations_date_to_dateEdit.current_val = newest_day self.find_annotations_date_to_dateEdit.setMaximumDateTime(newest_day) # Connect the signals for date range changes self.find_annotations_date_from_dateEdit.dateTimeChanged.connect(partial(self.update_results, 'from_date')) self.find_annotations_date_to_dateEdit.dateTimeChanged.connect(partial(self.update_results, 'to_date')) self.update_results('inventory_available') def restore_settings(self): self.blockSignals(True) ra = self.prefs.get('find_annotations_reader_comboBox', self.GENERIC_READER) ra_index = self.find_annotations_reader_comboBox.findText(ra) self.find_annotations_reader_comboBox.setCurrentIndex(ra_index) color = self.prefs.get('find_annotations_color_comboBox', self.GENERIC_STYLE) color_index = self.find_annotations_color_comboBox.findText(color) self.find_annotations_color_comboBox.setCurrentIndex(color_index) text = self.prefs.get('find_annotations_text_lineEdit', '') self.find_annotations_text_lineEdit.setText(text) note = self.prefs.get('find_annotations_note_lineEdit', '') self.find_annotations_note_lineEdit.setText(note) if False: from_date = self.prefs.get('find_annotations_date_from_dateEdit', datetime(1970,1,1)) self.find_annotations_date_from_dateEdit.current_val = from_date to_date = self.prefs.get('find_annotations_date_to_dateEdit', datetime.today()) self.find_annotations_date_to_dateEdit.current_val = to_date self.blockSignals(False) def return_pressed(self): self.update_results("return_pressed") def save_settings(self): ra = str(self.find_annotations_reader_comboBox.currentText()) self.prefs.set('find_annotations_reader_comboBox', ra) color = str(self.find_annotations_color_comboBox.currentText()) self.prefs.set('find_annotations_color_comboBox', color) text = str(self.find_annotations_text_lineEdit.text()) self.prefs.set('find_annotations_text_lineEdit', text) note = str(self.find_annotations_note_lineEdit.text()) self.prefs.set('find_annotations_note_lineEdit', note) if False: from_date = self.find_annotations_date_from_dateEdit.current_val self.prefs.set('find_annotations_date_from_dateEdit', from_date) to_date = self.find_annotations_date_to_dateEdit.current_val self.prefs.set('find_annotations_date_to_dateEdit', to_date) def start_inventory_scan(self): self._log_location() self.annotated_books_scanner.start() def update_results(self, trigger): #self._log_location(trigger) reader_to_match = str(self.find_annotations_reader_comboBox.currentText()) color_to_match = str(self.find_annotations_color_comboBox.currentText()) text_to_match = str(self.find_annotations_text_lineEdit.text()) note_to_match = str(self.find_annotations_note_lineEdit.text()) from_date = self.find_annotations_date_from_dateEdit.dateTime().toTime_t() to_date = self.find_annotations_date_to_dateEdit.dateTime().toTime_t() annotation_map = self.annotated_books_scanner.annotation_map #field = self.prefs.get("cfg_annotations_destination_field", None) field = get_cc_mapping('annotations', 'field', None) db = self.opts.gui.current_db matched_titles = [] self.matched_ids = set() for cid in annotation_map: mi = db.get_metadata(cid, index_is_id=True) soup = None if field == 'Comments': if mi.comments: soup = BeautifulSoup(mi.comments) else: if mi.get_user_metadata(field, False)['#value#'] is not None: soup = BeautifulSoup(mi.get_user_metadata(field, False)['#value#']) if soup: uas = soup.findAll('div', 'annotation') for ua in uas: # Are we already logged? if cid in self.matched_ids: continue # Check reader if reader_to_match != self.GENERIC_READER: this_reader = ua['reader'] if this_reader != reader_to_match: continue # Check color if color_to_match != self.GENERIC_STYLE: this_color = ua.find('table')['color'] if this_color != color_to_match: continue # Check date range, allow for mangled timestamp try: timestamp = float(ua.find('td', 'timestamp')['uts']) if timestamp < from_date or timestamp > to_date: continue except: continue highlight_text = '' try: pels = ua.findAll('p', 'highlight') for pel in pels: highlight_text += pel.string + '\n' except: pass if text_to_match > '': if not re.search(text_to_match, highlight_text, flags=re.IGNORECASE): continue note_text = '' try: nels = ua.findAll('p', 'note') for nel in nels: note_text += nel.string + '\n' except: pass if note_to_match > '': if not re.search(note_to_match, note_text, flags=re.IGNORECASE): continue # If we made it this far, add the id to matched_ids self.matched_ids.add(cid) matched_titles.append(mi.title) # Update the results box matched_titles.sort() if len(annotation_map): if len(matched_titles): first_match = ("<i>%s</i>" % matched_titles[0]) if len(matched_titles) == 1: results = first_match else: results = first_match + (" and %d more." % (len(matched_titles) - 1)) self.result_label.setText('<p style="color:blue">{0}</p>'.format(results)) else: self.result_label.setText('<p style="color:red">no matches</p>') else: self.result_label.setText('<p style="color:red">no annotated books in library</p>') self.resize_dialog()
class MessageWidget(QWidget): """ Class the widget of list item to display message in chat """ FORMAT_PATTERN = { r'\*\*(.+)\*\*': r'<b>\1</b>', '__(.+)__': r'<u>\1</u>', '##(.+)##': r'<i>\1</i>', } def __init__(self, user, text, time, parent=None): super().__init__(parent) self.ui() self.userLbl.setText(user) self.timeLbl.setText(time) self.set_text(self.apply_format(text)) # self.msgLbl.setHtml(text) def ui(self): """ Method build ui """ grid = QGridLayout(self) self.setLayout(grid) self.userLbl = QLabel(self) self.timeLbl = QLabel(self) self.timeLbl.setAlignment(Qt.AlignRight) self.line_1 = QFrame(self) self.line_1.setFrameShape(QFrame.HLine) self.line_1.setFrameShadow(QFrame.Sunken) self.line_2 = QFrame(self) self.line_2.setFrameShape(QFrame.HLine) self.line_2.setFrameShadow(QFrame.Sunken) self.msgLbl = QTextBrowser(self) # font = QFont() # font.setPointSize(12) # self.msgLbl.setFont(font) self.msgLbl.setFrameShape(QFrame.NoFrame) self.msgLbl.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.msgLbl.setSizeAdjustPolicy(QAbstractScrollArea.AdjustToContents) self.msgLbl.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) grid.addWidget(self.userLbl, 0, 0) grid.addWidget(self.timeLbl, 0, 1) grid.addWidget(self.line_1, 1, 0, 1, 2) grid.addWidget(self.msgLbl, 2, 0, 1, 2) grid.addWidget(self.line_2, 3, 0, 1, 2) def set_text(self, text): size = self.msgLbl.font().pointSize() * 2 line_count = len(text.split('<br>')) size = size * line_count + 6 self.msgLbl.setMaximumHeight(size) self.msgLbl.setMinimumHeight(size) self.msgLbl.setHtml(text) def apply_format(self, text): text = text.strip() text = text.replace('\n', '<br>') for pat, fmt in self.FORMAT_PATTERN.items(): text = re.sub(pat, fmt, text) return text
class FindAnnotationsDialog(SizePersistedDialog, Logger): GENERIC_STYLE = 'Any style' GENERIC_READER = 'Any reader' def __init__(self, opts): self.matched_ids = set() self.opts = opts self.prefs = opts.prefs super(FindAnnotationsDialog, self).__init__(self.opts.gui, 'find_annotations_dialog') self.setWindowTitle(_('Find Annotations')) self.setWindowIcon(self.opts.icon) self.l = QVBoxLayout(self) self.setLayout(self.l) self.search_criteria_gb = QGroupBox(self) self.search_criteria_gb.setTitle(_("Search criteria")) self.scgl = QGridLayout(self.search_criteria_gb) self.l.addWidget(self.search_criteria_gb) # addWidget(widget, row, col, rowspan, colspan) row = 0 # ~~~~~~~~ Create the Readers comboBox ~~~~~~~~ self.reader_label = QLabel(_('Reader')) self.reader_label.setAlignment(Qt.AlignRight|Qt.AlignVCenter) self.scgl.addWidget(self.reader_label, row, 0, 1, 1) self.find_annotations_reader_comboBox = QComboBox() self.find_annotations_reader_comboBox.setObjectName('find_annotations_reader_comboBox') self.find_annotations_reader_comboBox.setToolTip(_('Reader annotations to search for')) self.find_annotations_reader_comboBox.addItem(self.GENERIC_READER) racs = ReaderApp.get_reader_app_classes() for ra in sorted(racs.keys()): self.find_annotations_reader_comboBox.addItem(ra) self.scgl.addWidget(self.find_annotations_reader_comboBox, row, 1, 1, 4) row += 1 # ~~~~~~~~ Create the Styles comboBox ~~~~~~~~ self.style_label = QLabel(_('Style')) self.style_label.setAlignment(Qt.AlignRight|Qt.AlignVCenter) self.scgl.addWidget(self.style_label, row, 0, 1, 1) self.find_annotations_color_comboBox = QComboBox() self.find_annotations_color_comboBox.setObjectName('find_annotations_color_comboBox') self.find_annotations_color_comboBox.setToolTip(_('Annotation style to search for')) self.find_annotations_color_comboBox.addItem(self.GENERIC_STYLE) all_colors = COLOR_MAP.keys() all_colors.remove('Default') for color in sorted(all_colors): self.find_annotations_color_comboBox.addItem(color) self.scgl.addWidget(self.find_annotations_color_comboBox, row, 1, 1, 4) row += 1 # ~~~~~~~~ Create the Text LineEdit control ~~~~~~~~ self.text_label = QLabel(_('Text')) self.text_label.setAlignment(Qt.AlignRight|Qt.AlignVCenter) self.scgl.addWidget(self.text_label, row, 0, 1, 1) self.find_annotations_text_lineEdit = MyLineEdit() self.find_annotations_text_lineEdit.setObjectName('find_annotations_text_lineEdit') self.scgl.addWidget(self.find_annotations_text_lineEdit, row, 1, 1, 3) self.reset_text_tb = QToolButton() self.reset_text_tb.setObjectName('reset_text_tb') self.reset_text_tb.setToolTip(_('Clear search criteria')) self.reset_text_tb.setIcon(QIcon(I('trash.png'))) self.reset_text_tb.clicked.connect(self.clear_text_field) self.scgl.addWidget(self.reset_text_tb, row, 4, 1, 1) row += 1 # ~~~~~~~~ Create the Note LineEdit control ~~~~~~~~ self.note_label = QLabel(_('Note')) self.note_label.setAlignment(Qt.AlignRight|Qt.AlignVCenter) self.scgl.addWidget(self.note_label, row, 0, 1, 1) self.find_annotations_note_lineEdit = MyLineEdit() self.find_annotations_note_lineEdit.setObjectName('find_annotations_note_lineEdit') self.scgl.addWidget(self.find_annotations_note_lineEdit, row, 1, 1, 3) self.reset_note_tb = QToolButton() self.reset_note_tb.setObjectName('reset_note_tb') self.reset_note_tb.setToolTip(_('Clear search criteria')) self.reset_note_tb.setIcon(QIcon(I('trash.png'))) self.reset_note_tb.clicked.connect(self.clear_note_field) self.scgl.addWidget(self.reset_note_tb, row, 4, 1, 1) row += 1 # ~~~~~~~~ Create the Date range controls ~~~~~~~~ self.date_range_label = QLabel(_('Date range')) self.date_range_label.setAlignment(Qt.AlignRight|Qt.AlignVCenter) self.scgl.addWidget(self.date_range_label, row, 0, 1, 1) # Date 'From' self.find_annotations_date_from_dateEdit = MyDateEdit(self, datetime(1970,1,1)) self.find_annotations_date_from_dateEdit.setObjectName('find_annotations_date_from_dateEdit') #self.find_annotations_date_from_dateEdit.current_val = datetime(1970,1,1) self.find_annotations_date_from_dateEdit.clear_button.clicked.connect(self.find_annotations_date_from_dateEdit.reset_from_date) self.scgl.addWidget(self.find_annotations_date_from_dateEdit, row, 1, 1, 1) self.scgl.addWidget(self.find_annotations_date_from_dateEdit.clear_button, row, 2, 1, 1) # Date 'To' self.find_annotations_date_to_dateEdit = MyDateEdit(self, datetime.today()) self.find_annotations_date_to_dateEdit.setObjectName('find_annotations_date_to_dateEdit') #self.find_annotations_date_to_dateEdit.current_val = datetime.today() self.find_annotations_date_to_dateEdit.clear_button.clicked.connect(self.find_annotations_date_to_dateEdit.reset_to_date) self.scgl.addWidget(self.find_annotations_date_to_dateEdit, row, 3, 1, 1) self.scgl.addWidget(self.find_annotations_date_to_dateEdit.clear_button, row, 4, 1, 1) row += 1 # ~~~~~~~~ Create a horizontal line ~~~~~~~~ self.hl = QFrame(self) self.hl.setGeometry(QRect(0, 0, 1, 3)) self.hl.setFrameShape(QFrame.HLine) self.hl.setFrameShadow(QFrame.Raised) self.scgl.addWidget(self.hl, row, 0, 1, 5) row += 1 # ~~~~~~~~ Create the results label field ~~~~~~~~ self.result_label = QLabel('<p style="color:red">{0}</p>'.format(_('scanning…'))) self.result_label.setAlignment(Qt.AlignCenter) self.result_label.setWordWrap(False) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Maximum) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.result_label.sizePolicy().hasHeightForWidth()) self.result_label.setSizePolicy(sizePolicy) self.result_label.setMinimumSize(QtCore.QSize(250, 0)) self.scgl.addWidget(self.result_label, row, 0, 1, 5) row += 1 # ~~~~~~~~ Create the ButtonBox ~~~~~~~~ self.dialogButtonBox = QDialogButtonBox(self) self.dialogButtonBox.setOrientation(Qt.Horizontal) if False: self.update_button = QPushButton(_('Update results')) self.update_button.setDefault(True) self.update_button.setVisible(False) self.dialogButtonBox.addButton(self.update_button, QDialogButtonBox.ActionRole) self.cancel_button = self.dialogButtonBox.addButton(self.dialogButtonBox.Cancel) self.find_button = self.dialogButtonBox.addButton(self.dialogButtonBox.Ok) self.find_button.setText(_('Find Matching Books')) self.l.addWidget(self.dialogButtonBox) self.dialogButtonBox.clicked.connect(self.find_annotations_dialog_clicked) # ~~~~~~~~ Add a spacer ~~~~~~~~ self.spacerItem = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) self.l.addItem(self.spacerItem) # ~~~~~~~~ Restore previously saved settings ~~~~~~~~ self.restore_settings() # ~~~~~~~~ Declare 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() # ~~~~~~~~ Connect all signals ~~~~~~~~ self.find_annotations_reader_comboBox.currentIndexChanged.connect(partial(self.update_results, 'reader')) self.find_annotations_color_comboBox.currentIndexChanged.connect(partial(self.update_results, 'color')) self.find_annotations_text_lineEdit.editingFinished.connect(partial(self.update_results, 'text')) self.find_annotations_note_lineEdit.editingFinished.connect(partial(self.update_results, 'note')) # self.connect(self.find_annotations_text_lineEdit, pyqtSignal("return_pressed"), self.return_pressed) self.find_annotations_text_lineEdit.return_pressed.connect(self.return_pressed) # self.connect(self.find_annotations_note_lineEdit, pyqtSignal("return_pressed"), self.return_pressed) self.find_annotations_note_lineEdit.return_pressed.connect(self.return_pressed) # Date range signals connected in inventory_available() # ~~~~~~~~ Allow dialog to render before doing inventory ~~~~~~~~ #field = self.prefs.get('cfg_annotations_destination_field', None) field = get_cc_mapping('annotations', 'field', None) self.annotated_books_scanner = InventoryAnnotatedBooks(self.opts.gui, field, get_date_range=True) self.annotated_books_scanner.signal.connect(self.inventory_available) QTimer.singleShot(1, self.start_inventory_scan) def clear_note_field(self): if str(self.find_annotations_note_lineEdit.text()) > '': self.find_annotations_note_lineEdit.setText('') self.update_results('clear_note_field') def clear_text_field(self): if str(self.find_annotations_text_lineEdit.text()) > '': self.find_annotations_text_lineEdit.setText('') self.update_results('clear_text_field') def find_annotations_dialog_clicked(self, button): if self.dialogButtonBox.buttonRole(button) == QDialogButtonBox.AcceptRole: self.save_settings() self.accept() elif self.dialogButtonBox.buttonRole(button) == QDialogButtonBox.RejectRole: self.close() def inventory_available(self): ''' Update the Date range widgets with the rounded oldest, newest dates Don't connect date signals until date range available ''' self._log_location() # Reset the date range based on available annotations oldest = QDateTime(datetime.fromtimestamp(self.annotated_books_scanner.oldest_annotation)) oldest_day = QDateTime(datetime.fromtimestamp(self.annotated_books_scanner.oldest_annotation).replace(hour=0, minute=0, second=0)) newest = QDateTime(datetime.fromtimestamp(self.annotated_books_scanner.newest_annotation)) newest_day = QDateTime(datetime.fromtimestamp(self.annotated_books_scanner.newest_annotation).replace(hour=23, minute=59, second=59)) # Set 'From' date limits to inventory values self.find_annotations_date_from_dateEdit.setMinimumDateTime(oldest_day) self.find_annotations_date_from_dateEdit.current_val = oldest self.find_annotations_date_from_dateEdit.setMaximumDateTime(newest_day) # Set 'To' date limits to inventory values self.find_annotations_date_to_dateEdit.setMinimumDateTime(oldest_day) self.find_annotations_date_to_dateEdit.current_val = newest_day self.find_annotations_date_to_dateEdit.setMaximumDateTime(newest_day) # Connect the signals for date range changes self.find_annotations_date_from_dateEdit.dateTimeChanged.connect(partial(self.update_results, 'from_date')) self.find_annotations_date_to_dateEdit.dateTimeChanged.connect(partial(self.update_results, 'to_date')) self.update_results('inventory_available') def restore_settings(self): self.blockSignals(True) ra = self.prefs.get('find_annotations_reader_comboBox', self.GENERIC_READER) ra_index = self.find_annotations_reader_comboBox.findText(ra) self.find_annotations_reader_comboBox.setCurrentIndex(ra_index) color = self.prefs.get('find_annotations_color_comboBox', self.GENERIC_STYLE) color_index = self.find_annotations_color_comboBox.findText(color) self.find_annotations_color_comboBox.setCurrentIndex(color_index) text = self.prefs.get('find_annotations_text_lineEdit', '') self.find_annotations_text_lineEdit.setText(text) note = self.prefs.get('find_annotations_note_lineEdit', '') self.find_annotations_note_lineEdit.setText(note) if False: from_date = self.prefs.get('find_annotations_date_from_dateEdit', datetime(1970,1,1)) self.find_annotations_date_from_dateEdit.current_val = from_date to_date = self.prefs.get('find_annotations_date_to_dateEdit', datetime.today()) self.find_annotations_date_to_dateEdit.current_val = to_date self.blockSignals(False) def return_pressed(self): self.update_results("return_pressed") def save_settings(self): ra = str(self.find_annotations_reader_comboBox.currentText()) self.prefs.set('find_annotations_reader_comboBox', ra) color = str(self.find_annotations_color_comboBox.currentText()) self.prefs.set('find_annotations_color_comboBox', color) text = str(self.find_annotations_text_lineEdit.text()) self.prefs.set('find_annotations_text_lineEdit', text) note = str(self.find_annotations_note_lineEdit.text()) self.prefs.set('find_annotations_note_lineEdit', note) if False: from_date = self.find_annotations_date_from_dateEdit.current_val self.prefs.set('find_annotations_date_from_dateEdit', from_date) to_date = self.find_annotations_date_to_dateEdit.current_val self.prefs.set('find_annotations_date_to_dateEdit', to_date) def start_inventory_scan(self): self._log_location() self.annotated_books_scanner.start() def update_results(self, trigger): #self._log_location(trigger) reader_to_match = str(self.find_annotations_reader_comboBox.currentText()) color_to_match = str(self.find_annotations_color_comboBox.currentText()) text_to_match = str(self.find_annotations_text_lineEdit.text()) note_to_match = str(self.find_annotations_note_lineEdit.text()) from_date = self.find_annotations_date_from_dateEdit.dateTime().toTime_t() to_date = self.find_annotations_date_to_dateEdit.dateTime().toTime_t() annotation_map = self.annotated_books_scanner.annotation_map #field = self.prefs.get("cfg_annotations_destination_field", None) field = get_cc_mapping('annotations', 'field', None) db = self.opts.gui.current_db matched_titles = [] self.matched_ids = set() for cid in annotation_map: mi = db.get_metadata(cid, index_is_id=True) soup = None if field == 'Comments': if mi.comments: soup = BeautifulSoup(mi.comments) else: if mi.get_user_metadata(field, False)['#value#'] is not None: soup = BeautifulSoup(mi.get_user_metadata(field, False)['#value#']) if soup: uas = soup.findAll('div', 'annotation') for ua in uas: # Are we already logged? if cid in self.matched_ids: continue # Check reader if reader_to_match != self.GENERIC_READER: this_reader = ua['reader'] if this_reader != reader_to_match: continue # Check color if color_to_match != self.GENERIC_STYLE: this_color = ua.find('table')['color'] if this_color != color_to_match: continue # Check date range, allow for mangled timestamp try: timestamp = float(ua.find('td', 'timestamp')['uts']) if timestamp < from_date or timestamp > to_date: continue except: continue highlight_text = '' try: pels = ua.findAll('p', 'highlight') highlight_text = '\n'.join([p.string for p in pels]) except: pass if text_to_match > '': if not re.search(text_to_match, highlight_text, flags=re.IGNORECASE): continue note_text = '' try: nels = ua.findAll('p', 'note') note_text = '\n'.join([n.string for n in nels]) except: pass if note_to_match > '': if not re.search(note_to_match, note_text, flags=re.IGNORECASE): continue # If we made it this far, add the id to matched_ids self.matched_ids.add(cid) matched_titles.append(mi.title) # Update the results box matched_titles.sort() if len(annotation_map): if len(matched_titles): first_match = ("<i>%s</i>" % matched_titles[0]) if len(matched_titles) == 1: results = first_match else: results = first_match + (_(" and {0} more.").format(len(matched_titles) - 1)) self.result_label.setText('<p style="color:blue">{0}</p>'.format(results)) else: self.result_label.setText('<p style="color:red">{0}</p>'.format(_('no matches'))) else: self.result_label.setText('<p style="color:red">{0}</p>'.format(_('no annotated books in library'))) self.resize_dialog()
def __init__(self, parent, old, new): super(QDialog, self).__init__(parent.gui) self.db = parent.gui.current_db self.gui = parent.gui self._log_location() layout = QVBoxLayout() self.setLayout(layout) header = QLabel(_("Move annotations or change destination?")) header.setAlignment(Qt.AlignCenter) header_font = QFont() header_font.setPointSize(16) header.setFont(header_font) layout.addWidget(header) change_group = QGroupBox("", self) layout.addWidget(change_group) self.gl = QGridLayout() change_group.setLayout(self.gl) horizontal_line = QFrame(self) horizontal_line.setGeometry(QRect(0, 0, 1, 3)) horizontal_line.setFrameShape(QFrame.HLine) horizontal_line.setFrameShadow(QFrame.Raised) self.gl.addWidget(horizontal_line, 1, 0, 1, 2) self.move_button = QPushButton(_("Move")) self.gl.addWidget(self.move_button, 3, 0, 1, 1) self.move_label = QLabel( _('<html><head/><body><p>• Move existing annotations from <span style=" font-weight:600;">{old}</span> to <span style=" font-weight:600;">{new}</span>.<br/>• Existing annotations will be removed from <span style=" font-weight:600;">{old}</span>.<br/>• Newly imported annotations will be added to <span style=" font-weight:600;">{new}</span>.</p></body></html>' )) self.move_label.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) self.gl.addWidget(self.move_label, 3, 1) self.gl.addWidget(horizontal_line, 4, 1) self.change_button = QPushButton(_("Change")) self.gl.addWidget(self.change_button, 5, 0, 1, 1) self.change_label = QLabel( _('<html><head/><body><p>• Change annotations storage from <span style=" font-weight:600;">{old}</span> to <span style=" font-weight:600;">{new}</span>.<br/>• Existing annotations will remain in <span style=" font-weight:600;">{old}</span>.<br/>• Newly imported annotations will be added to <span style=" font-weight:600;">{new}</span>.</p></body></html>' )) self.change_label.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) self.gl.addWidget(self.change_label, 5, 1, 1, 1) self.bb = QDialogButtonBox(QDialogButtonBox.Cancel) layout.addWidget(self.bb) # Hook the button events self.bb.clicked.connect(partial(self.button_clicked, 'cancel')) self.move_button.clicked.connect(partial(self.button_clicked, 'move')) self.change_button.clicked.connect( partial(self.button_clicked, 'change')) # Customize the dialog text self.move_label.setText( str(self.move_label.text()).format(old=old, new=new)) self.change_label.setText( str(self.change_label.text()).format(old=old, new=new)) self.command = 'cancel' self.do_resize()
class ConfigWidget(QWidget, Logger): ''' Config dialog for Marvin Manager ''' WIZARD_PROFILES = { 'Annotations': { 'label': 'mm_annotations', 'datatype': 'comments', 'display': {}, 'is_multiple': False }, 'Collections': { 'label': 'mm_collections', 'datatype': 'text', 'display': {u'is_names': False}, 'is_multiple': True }, 'Last read': { 'label': 'mm_date_read', 'datatype': 'datetime', 'display': {}, 'is_multiple': False }, 'Locked': { 'label': 'mm_locked', 'datatype': 'bool', 'display': {}, 'is_multiple': False }, 'Progress': { 'label': 'mm_progress', 'datatype': 'float', 'display': {u'number_format': u'{0:.0f}%'}, 'is_multiple': False }, 'Read': { 'label': 'mm_read', 'datatype': 'bool', 'display': {}, 'is_multiple': False }, 'Reading list': { 'label': 'mm_reading_list', 'datatype': 'bool', 'display': {}, 'is_multiple': False }, 'Word count': { 'label': 'mm_word_count', 'datatype': 'int', 'display': {u'number_format': u'{0:n}'}, 'is_multiple': False } } def __init__(self, plugin_action): QWidget.__init__(self) self.parent = plugin_action self.gui = get_gui() self.icon = plugin_action.icon self.opts = plugin_action.opts self.prefs = plugin_prefs self.resources_path = plugin_action.resources_path self.verbose = plugin_action.verbose self.restart_required = False self._log_location() self.l = QGridLayout() self.setLayout(self.l) self.column1_layout = QVBoxLayout() self.l.addLayout(self.column1_layout, 0, 0) self.column2_layout = QVBoxLayout() self.l.addLayout(self.column2_layout, 0, 1) # ----------------------------- Column 1 ----------------------------- # ~~~~~~~~ Create the Custom fields options group box ~~~~~~~~ self.cfg_custom_fields_gb = QGroupBox(self) self.cfg_custom_fields_gb.setTitle('Custom column assignments') self.column1_layout.addWidget(self.cfg_custom_fields_gb) self.cfg_custom_fields_qgl = QGridLayout(self.cfg_custom_fields_gb) current_row = 0 # ++++++++ Labels + HLine ++++++++ self.marvin_source_label = QLabel("Marvin source") self.cfg_custom_fields_qgl.addWidget(self.marvin_source_label, current_row, 0) self.calibre_destination_label = QLabel("calibre destination") self.cfg_custom_fields_qgl.addWidget(self.calibre_destination_label, current_row, 1) current_row += 1 self.sd_hl = QFrame(self.cfg_custom_fields_gb) self.sd_hl.setFrameShape(QFrame.HLine) self.sd_hl.setFrameShadow(QFrame.Raised) self.cfg_custom_fields_qgl.addWidget(self.sd_hl, current_row, 0, 1, 3) current_row += 1 # ++++++++ Annotations ++++++++ self.cfg_annotations_label = QLabel('Annotations') self.cfg_annotations_label.setAlignment(Qt.AlignLeft) self.cfg_custom_fields_qgl.addWidget(self.cfg_annotations_label, current_row, 0) self.annotations_field_comboBox = QComboBox(self.cfg_custom_fields_gb) self.annotations_field_comboBox.setObjectName('annotations_field_comboBox') self.annotations_field_comboBox.setToolTip('Select a custom column to store Marvin annotations') self.cfg_custom_fields_qgl.addWidget(self.annotations_field_comboBox, current_row, 1) self.cfg_highlights_wizard = QToolButton() self.cfg_highlights_wizard.setIcon(QIcon(I('wizard.png'))) self.cfg_highlights_wizard.setToolTip("Create a custom column to store Marvin annotations") self.cfg_highlights_wizard.clicked.connect(partial(self.launch_cc_wizard, 'Annotations')) self.cfg_custom_fields_qgl.addWidget(self.cfg_highlights_wizard, current_row, 2) current_row += 1 # ++++++++ Collections ++++++++ self.cfg_collections_label = QLabel('Collections') self.cfg_collections_label.setAlignment(Qt.AlignLeft) self.cfg_custom_fields_qgl.addWidget(self.cfg_collections_label, current_row, 0) self.collection_field_comboBox = QComboBox(self.cfg_custom_fields_gb) self.collection_field_comboBox.setObjectName('collection_field_comboBox') self.collection_field_comboBox.setToolTip('Select a custom column to store Marvin collection assignments') self.cfg_custom_fields_qgl.addWidget(self.collection_field_comboBox, current_row, 1) self.cfg_collections_wizard = QToolButton() self.cfg_collections_wizard.setIcon(QIcon(I('wizard.png'))) self.cfg_collections_wizard.setToolTip("Create a custom column for Marvin collection assignments") self.cfg_collections_wizard.clicked.connect(partial(self.launch_cc_wizard, 'Collections')) self.cfg_custom_fields_qgl.addWidget(self.cfg_collections_wizard, current_row, 2) current_row += 1 # ++++++++ Last read ++++++++ self.cfg_date_read_label = QLabel("Last read") self.cfg_date_read_label.setAlignment(Qt.AlignLeft) self.cfg_custom_fields_qgl.addWidget(self.cfg_date_read_label, current_row, 0) self.date_read_field_comboBox = QComboBox(self.cfg_custom_fields_gb) self.date_read_field_comboBox.setObjectName('date_read_field_comboBox') self.date_read_field_comboBox.setToolTip('Select a custom column to store Last read date') self.cfg_custom_fields_qgl.addWidget(self.date_read_field_comboBox, current_row, 1) self.cfg_collections_wizard = QToolButton() self.cfg_collections_wizard.setIcon(QIcon(I('wizard.png'))) self.cfg_collections_wizard.setToolTip("Create a custom column to store Last read date") self.cfg_collections_wizard.clicked.connect(partial(self.launch_cc_wizard, 'Last read')) self.cfg_custom_fields_qgl.addWidget(self.cfg_collections_wizard, current_row, 2) current_row += 1 # ++++++++ Locked ++++++++ self.cfg_locked_label = QLabel("Locked") self.cfg_locked_label.setAlignment(Qt.AlignLeft) self.cfg_custom_fields_qgl.addWidget(self.cfg_locked_label, current_row, 0) self.locked_field_comboBox = QComboBox(self.cfg_custom_fields_gb) self.locked_field_comboBox.setObjectName('locked_field_comboBox') self.locked_field_comboBox.setToolTip('Select a custom column to store Locked status') self.cfg_custom_fields_qgl.addWidget(self.locked_field_comboBox, current_row, 1) self.cfg_locked_wizard = QToolButton() self.cfg_locked_wizard.setIcon(QIcon(I('wizard.png'))) self.cfg_locked_wizard.setToolTip("Create a custom column to store Locked status") self.cfg_locked_wizard.clicked.connect(partial(self.launch_cc_wizard, 'Locked')) self.cfg_custom_fields_qgl.addWidget(self.cfg_locked_wizard, current_row, 2) current_row += 1 # ++++++++ Progress ++++++++ self.cfg_progress_label = QLabel('Progress') self.cfg_progress_label.setAlignment(Qt.AlignLeft) self.cfg_custom_fields_qgl.addWidget(self.cfg_progress_label, current_row, 0) self.progress_field_comboBox = QComboBox(self.cfg_custom_fields_gb) self.progress_field_comboBox.setObjectName('progress_field_comboBox') self.progress_field_comboBox.setToolTip('Select a custom column to store Marvin reading progress') self.cfg_custom_fields_qgl.addWidget(self.progress_field_comboBox, current_row, 1) self.cfg_progress_wizard = QToolButton() self.cfg_progress_wizard.setIcon(QIcon(I('wizard.png'))) self.cfg_progress_wizard.setToolTip("Create a custom column to store Marvin reading progress") self.cfg_progress_wizard.clicked.connect(partial(self.launch_cc_wizard, 'Progress')) self.cfg_custom_fields_qgl.addWidget(self.cfg_progress_wizard, current_row, 2) current_row += 1 # ++++++++ Read flag ++++++++ self.cfg_read_label = QLabel('Read') self.cfg_read_label.setAlignment(Qt.AlignLeft) self.cfg_custom_fields_qgl.addWidget(self.cfg_read_label, current_row, 0) self.read_field_comboBox = QComboBox(self.cfg_custom_fields_gb) self.read_field_comboBox.setObjectName('read_field_comboBox') self.read_field_comboBox.setToolTip('Select a custom column to store Marvin Read status') self.cfg_custom_fields_qgl.addWidget(self.read_field_comboBox, current_row, 1) self.cfg_read_wizard = QToolButton() self.cfg_read_wizard.setIcon(QIcon(I('wizard.png'))) self.cfg_read_wizard.setToolTip("Create a custom column to store Marvin Read status") self.cfg_read_wizard.clicked.connect(partial(self.launch_cc_wizard, 'Read')) self.cfg_custom_fields_qgl.addWidget(self.cfg_read_wizard, current_row, 2) current_row += 1 # ++++++++ Reading list flag ++++++++ self.cfg_reading_list_label = QLabel('Reading list') self.cfg_reading_list_label.setAlignment(Qt.AlignLeft) self.cfg_custom_fields_qgl.addWidget(self.cfg_reading_list_label, current_row, 0) self.reading_list_field_comboBox = QComboBox(self.cfg_custom_fields_gb) self.reading_list_field_comboBox.setObjectName('reading_list_field_comboBox') self.reading_list_field_comboBox.setToolTip('Select a custom column to store Marvin Reading list status') self.cfg_custom_fields_qgl.addWidget(self.reading_list_field_comboBox, current_row, 1) self.cfg_reading_list_wizard = QToolButton() self.cfg_reading_list_wizard.setIcon(QIcon(I('wizard.png'))) self.cfg_reading_list_wizard.setToolTip("Create a custom column to store Marvin Reading list status") self.cfg_reading_list_wizard.clicked.connect(partial(self.launch_cc_wizard, 'Reading list')) self.cfg_custom_fields_qgl.addWidget(self.cfg_reading_list_wizard, current_row, 2) current_row += 1 # ++++++++ Word count ++++++++ self.cfg_word_count_label = QLabel('Word count') self.cfg_word_count_label.setAlignment(Qt.AlignLeft) self.cfg_custom_fields_qgl.addWidget(self.cfg_word_count_label, current_row, 0) self.word_count_field_comboBox = QComboBox(self.cfg_custom_fields_gb) self.word_count_field_comboBox.setObjectName('word_count_field_comboBox') self.word_count_field_comboBox.setToolTip('Select a custom column to store Marvin word counts') self.cfg_custom_fields_qgl.addWidget(self.word_count_field_comboBox, current_row, 1) self.cfg_word_count_wizard = QToolButton() self.cfg_word_count_wizard.setIcon(QIcon(I('wizard.png'))) self.cfg_word_count_wizard.setToolTip("Create a custom column to store Marvin word counts") self.cfg_word_count_wizard.clicked.connect(partial(self.launch_cc_wizard, 'Word count')) self.cfg_custom_fields_qgl.addWidget(self.cfg_word_count_wizard, current_row, 2) current_row += 1 self.spacerItem1 = QSpacerItem(20, 20, QSizePolicy.Minimum, QSizePolicy.Expanding) self.column1_layout.addItem(self.spacerItem1) # ----------------------------- Column 2 ----------------------------- # ~~~~~~~~ Create the CSS group box ~~~~~~~~ self.cfg_css_options_gb = QGroupBox(self) self.cfg_css_options_gb.setTitle('CSS') self.column2_layout.addWidget(self.cfg_css_options_gb) self.cfg_css_options_qgl = QGridLayout(self.cfg_css_options_gb) current_row = 0 # ++++++++ Annotations appearance ++++++++ self.annotations_icon = QIcon(os.path.join(self.resources_path, 'icons', 'annotations_hiliter.png')) self.cfg_annotations_appearance_toolbutton = QToolButton() self.cfg_annotations_appearance_toolbutton.setIcon(self.annotations_icon) self.cfg_annotations_appearance_toolbutton.clicked.connect(self.configure_appearance) self.cfg_css_options_qgl.addWidget(self.cfg_annotations_appearance_toolbutton, current_row, 0) self.cfg_annotations_label = ClickableQLabel("Book notes, Bookmark notes and Annotations") self.cfg_annotations_label.clicked.connect(self.configure_appearance) self.cfg_css_options_qgl.addWidget(self.cfg_annotations_label, current_row, 1) current_row += 1 # ++++++++ Injected CSS ++++++++ self.css_editor_icon = QIcon(I('format-text-heading.png')) self.cfg_css_editor_toolbutton = QToolButton() self.cfg_css_editor_toolbutton.setIcon(self.css_editor_icon) self.cfg_css_editor_toolbutton.clicked.connect(self.edit_css) self.cfg_css_options_qgl.addWidget(self.cfg_css_editor_toolbutton, current_row, 0) self.cfg_css_editor_label = ClickableQLabel("Articles, Vocabulary") self.cfg_css_editor_label.clicked.connect(self.edit_css) self.cfg_css_options_qgl.addWidget(self.cfg_css_editor_label, current_row, 1) """ # ~~~~~~~~ Create the Dropbox syncing group box ~~~~~~~~ self.cfg_dropbox_syncing_gb = QGroupBox(self) self.cfg_dropbox_syncing_gb.setTitle('Dropbox') self.column2_layout.addWidget(self.cfg_dropbox_syncing_gb) self.cfg_dropbox_syncing_qgl = QGridLayout(self.cfg_dropbox_syncing_gb) current_row = 0 # ++++++++ Syncing enabled checkbox ++++++++ self.dropbox_syncing_checkbox = QCheckBox('Enable Dropbox updates') self.dropbox_syncing_checkbox.setObjectName('dropbox_syncing') self.dropbox_syncing_checkbox.setToolTip('Refresh custom column content from Marvin metadata') self.cfg_dropbox_syncing_qgl.addWidget(self.dropbox_syncing_checkbox, current_row, 0, 1, 3) current_row += 1 # ++++++++ Dropbox folder picker ++++++++ self.dropbox_folder_icon = QIcon(os.path.join(self.resources_path, 'icons', 'dropbox.png')) self.cfg_dropbox_folder_toolbutton = QToolButton() self.cfg_dropbox_folder_toolbutton.setIcon(self.dropbox_folder_icon) self.cfg_dropbox_folder_toolbutton.setToolTip("Specify Dropbox folder location on your computer") self.cfg_dropbox_folder_toolbutton.clicked.connect(self.select_dropbox_folder) self.cfg_dropbox_syncing_qgl.addWidget(self.cfg_dropbox_folder_toolbutton, current_row, 1) # ++++++++ Dropbox location lineedit ++++++++ self.dropbox_location_lineedit = QLineEdit() self.dropbox_location_lineedit.setPlaceholderText("Dropbox folder location") self.cfg_dropbox_syncing_qgl.addWidget(self.dropbox_location_lineedit, current_row, 2) """ # ~~~~~~~~ Create the General options group box ~~~~~~~~ self.cfg_runtime_options_gb = QGroupBox(self) self.cfg_runtime_options_gb.setTitle('General options') self.column2_layout.addWidget(self.cfg_runtime_options_gb) self.cfg_runtime_options_qvl = QVBoxLayout(self.cfg_runtime_options_gb) # ++++++++ Temporary markers: Duplicates ++++++++ self.duplicate_markers_checkbox = QCheckBox('Apply temporary markers to duplicate books') self.duplicate_markers_checkbox.setObjectName('apply_markers_to_duplicates') self.duplicate_markers_checkbox.setToolTip('Books with identical content will be flagged in the Library window') self.cfg_runtime_options_qvl.addWidget(self.duplicate_markers_checkbox) # ++++++++ Temporary markers: Updated ++++++++ self.updated_markers_checkbox = QCheckBox('Apply temporary markers to books with updated content') self.updated_markers_checkbox.setObjectName('apply_markers_to_updated') self.updated_markers_checkbox.setToolTip('Books with updated content will be flagged in the Library window') self.cfg_runtime_options_qvl.addWidget(self.updated_markers_checkbox) # ++++++++ Auto refresh checkbox ++++++++ self.auto_refresh_checkbox = QCheckBox('Automatically refresh custom column content') self.auto_refresh_checkbox.setObjectName('auto_refresh_at_startup') self.auto_refresh_checkbox.setToolTip('Update calibre custom column when Marvin XD is opened') self.cfg_runtime_options_qvl.addWidget(self.auto_refresh_checkbox) # ++++++++ Progress as percentage checkbox ++++++++ self.reading_progress_checkbox = QCheckBox('Show reading progress as percentage') self.reading_progress_checkbox.setObjectName('show_progress_as_percentage') self.reading_progress_checkbox.setToolTip('Display percentage in Progress column') self.cfg_runtime_options_qvl.addWidget(self.reading_progress_checkbox) # ~~~~~~~~ Create the Debug options group box ~~~~~~~~ self.cfg_debug_options_gb = QGroupBox(self) self.cfg_debug_options_gb.setTitle('Debug options') self.column2_layout.addWidget(self.cfg_debug_options_gb) self.cfg_debug_options_qvl = QVBoxLayout(self.cfg_debug_options_gb) # ++++++++ Debug logging checkboxes ++++++++ self.debug_plugin_checkbox = QCheckBox('Enable debug logging for Marvin XD') self.debug_plugin_checkbox.setObjectName('debug_plugin_checkbox') self.debug_plugin_checkbox.setToolTip('Print plugin diagnostic messages to console') self.cfg_debug_options_qvl.addWidget(self.debug_plugin_checkbox) self.debug_libimobiledevice_checkbox = QCheckBox('Enable debug logging for libiMobileDevice') self.debug_libimobiledevice_checkbox.setObjectName('debug_libimobiledevice_checkbox') self.debug_libimobiledevice_checkbox.setToolTip('Print libiMobileDevice diagnostic messages to console') self.cfg_debug_options_qvl.addWidget(self.debug_libimobiledevice_checkbox) self.spacerItem2 = QSpacerItem(20, 20, QSizePolicy.Minimum, QSizePolicy.Expanding) self.column2_layout.addItem(self.spacerItem2) # ~~~~~~~~ End of construction zone ~~~~~~~~ self.resize(self.sizeHint()) # ~~~~~~~~ Populate/restore config options ~~~~~~~~ # Annotations comboBox self.populate_annotations() self.populate_collections() self.populate_date_read() self.populate_locked() self.populate_progress() self.populate_read() self.populate_reading_list() self.populate_word_count() """ # Restore Dropbox settings, hook changes dropbox_syncing = self.prefs.get('dropbox_syncing', False) self.dropbox_syncing_checkbox.setChecked(dropbox_syncing) self.set_dropbox_syncing(dropbox_syncing) self.dropbox_syncing_checkbox.clicked.connect(partial(self.set_dropbox_syncing)) self.dropbox_location_lineedit.setText(self.prefs.get('dropbox_folder', '')) """ # Restore general settings self.duplicate_markers_checkbox.setChecked(self.prefs.get('apply_markers_to_duplicates', True)) self.updated_markers_checkbox.setChecked(self.prefs.get('apply_markers_to_updated', True)) self.auto_refresh_checkbox.setChecked(self.prefs.get('auto_refresh_at_startup', False)) self.reading_progress_checkbox.setChecked(self.prefs.get('show_progress_as_percentage', False)) # Restore debug settings, hook changes self.debug_plugin_checkbox.setChecked(self.prefs.get('debug_plugin', False)) self.debug_plugin_checkbox.stateChanged.connect(self.set_restart_required) self.debug_libimobiledevice_checkbox.setChecked(self.prefs.get('debug_libimobiledevice', False)) self.debug_libimobiledevice_checkbox.stateChanged.connect(self.set_restart_required) # Hook changes to Annotations comboBox # self.annotations_field_comboBox.currentIndexChanged.connect( # partial(self.save_combobox_setting, 'annotations_field_comboBox')) # self.connect(self.annotations_field_comboBox, # SIGNAL('currentIndexChanged(const QString &)'), # self.annotations_destination_changed) self.annotations_field_comboBox.currentIndexChanged.connect(self.annotations_destination_changed) # Launch the annotated_books_scanner field = get_cc_mapping('annotations', 'field', None) self.annotated_books_scanner = InventoryAnnotatedBooks(self.gui, field) self.annotated_books_scanner.signal.connect(self.inventory_complete) QTimer.singleShot(1, self.start_inventory) def annotations_destination_changed(self, qs_new_destination_name): ''' If the destination field changes, move all existing annotations from old to new ''' self._log_location(str(qs_new_destination_name)) self._log("self.eligible_annotations_fields: %s" % self.eligible_annotations_fields) old_destination_field = get_cc_mapping('annotations', 'field', None) old_destination_name = get_cc_mapping('annotations', 'combobox', None) self._log("old_destination_field: %s" % old_destination_field) self._log("old_destination_name: %s" % old_destination_name) # new_destination_name = unicode(qs_new_destination_name) # Signnls available have changed. Now receivin an indec rather than a name. Can get the name # from the combobox new_destination_name = unicode(self.annotations_field_comboBox.currentText()) self._log("new_destination_name: %s" % repr(new_destination_name)) if old_destination_name == new_destination_name: self._log_location("old_destination_name = new_destination_name, no changes") return if new_destination_name == '': self._log_location("annotations storage disabled") set_cc_mapping('annotations', field=None, combobox=new_destination_name) return new_destination_field = self.eligible_annotations_fields[new_destination_name] if existing_annotations(self.parent, old_destination_field): command = self.launch_new_destination_dialog(old_destination_name, new_destination_name) if command == 'move': set_cc_mapping('annotations', field=new_destination_field, combobox=new_destination_name) if self.annotated_books_scanner.isRunning(): self.annotated_books_scanner.wait() move_annotations(self, self.annotated_books_scanner.annotation_map, old_destination_field, new_destination_field) elif command == 'change': # Keep the updated destination field, but don't move annotations pass elif command == 'cancel': # Restore previous destination self.annotations_field_comboBox.blockSignals(True) old_index = self.annotations_field_comboBox.findText(old_destination_name) self.annotations_field_comboBox.setCurrentIndex(old_index) self.annotations_field_comboBox.blockSignals(False) else: # No existing annotations, just update prefs self._log("no existing annotations, updating destination to '{0}'".format(new_destination_name)) set_cc_mapping('annotations', field=new_destination_field, combobox=new_destination_name) def configure_appearance(self): ''' ''' self._log_location() appearance_settings = { 'appearance_css': default_elements, 'appearance_hr_checkbox': False, 'appearance_timestamp_format': default_timestamp } # Save, hash the original settings original_settings = {} osh = hashlib.md5() for setting in appearance_settings: original_settings[setting] = plugin_prefs.get(setting, appearance_settings[setting]) osh.update(repr(plugin_prefs.get(setting, appearance_settings[setting]))) # Display the Annotations appearance dialog aa = AnnotationsAppearance(self, self.annotations_icon, plugin_prefs) cancelled = False if aa.exec_(): # appearance_hr_checkbox and appearance_timestamp_format changed live to prefs during previews plugin_prefs.set('appearance_css', aa.elements_table.get_data()) # Generate a new hash nsh = hashlib.md5() for setting in appearance_settings: nsh.update(repr(plugin_prefs.get(setting, appearance_settings[setting]))) else: for setting in appearance_settings: plugin_prefs.set(setting, original_settings[setting]) nsh = osh # If there were changes, and there are existing annotations, # and there is an active Annotations field, offer to re-render field = get_cc_mapping('annotations', 'field', None) if osh.digest() != nsh.digest() and existing_annotations(self.parent, field): title = 'Update annotations?' msg = '<p>Update existing annotations to new appearance settings?</p>' d = MessageBox(MessageBox.QUESTION, title, msg, show_copy_button=False) self._log_location("QUESTION: %s" % msg) if d.exec_(): self._log_location("Updating existing annotations to modified appearance") # Wait for indexing to complete while not self.annotated_books_scanner.isFinished(): Application.processEvents() move_annotations(self, self.annotated_books_scanner.annotation_map, field, field, window_title="Updating appearance") def edit_css(self): ''' ''' self._log_location() from calibre_plugins.marvin_manager.book_status import dialog_resources_path klass = os.path.join(dialog_resources_path, 'css_editor.py') if os.path.exists(klass): sys.path.insert(0, dialog_resources_path) this_dc = importlib.import_module('css_editor') sys.path.remove(dialog_resources_path) dlg = this_dc.CSSEditorDialog(self, 'css_editor') dlg.initialize(self) dlg.exec_() def get_eligible_custom_fields(self, eligible_types=[], is_multiple=None): ''' Discover qualifying custom fields for eligible_types[] ''' #self._log_location(eligible_types) eligible_custom_fields = {} for cf in self.gui.current_db.custom_field_keys(): cft = self.gui.current_db.metadata_for_field(cf)['datatype'] cfn = self.gui.current_db.metadata_for_field(cf)['name'] cfim = self.gui.current_db.metadata_for_field(cf)['is_multiple'] #self._log("cf: %s cft: %s cfn: %s cfim: %s" % (cf, cft, cfn, cfim)) if cft in eligible_types: if is_multiple is not None: if bool(cfim) == is_multiple: eligible_custom_fields[cfn] = cf else: eligible_custom_fields[cfn] = cf return eligible_custom_fields def inventory_complete(self, msg): self._log_location(msg) def launch_cc_wizard(self, column_type): ''' ''' def _update_combo_box(comboBox, destination, previous): ''' ''' cb = getattr(self, comboBox) cb.blockSignals(True) all_items = [str(cb.itemText(i)) for i in range(cb.count())] if previous and previous in all_items: all_items.remove(previous) all_items.append(destination) cb.clear() cb.addItems(sorted(all_items, key=lambda s: s.lower())) # Select the new destination in the comboBox idx = cb.findText(destination) if idx > -1: cb.setCurrentIndex(idx) cb.blockSignals(False) from calibre_plugins.marvin_manager.book_status import dialog_resources_path klass = os.path.join(dialog_resources_path, 'cc_wizard.py') if os.path.exists(klass): #self._log("importing CC Wizard dialog from '%s'" % klass) sys.path.insert(0, dialog_resources_path) this_dc = importlib.import_module('cc_wizard') sys.path.remove(dialog_resources_path) dlg = this_dc.CustomColumnWizard(self, column_type, self.WIZARD_PROFILES[column_type], verbose=True) dlg.exec_() if dlg.modified_column: self._log("modified_column: %s" % dlg.modified_column) self.restart_required = True destination = dlg.modified_column['destination'] label = dlg.modified_column['label'] previous = dlg.modified_column['previous'] source = dlg.modified_column['source'] if source == "Annotations": _update_combo_box("annotations_field_comboBox", destination, previous) # Add/update the new destination so save_settings() can find it self.eligible_annotations_fields[destination] = label # Save manually in case user cancels set_cc_mapping('annotations', combobox=destination, field=label) elif source == 'Collections': _update_combo_box("collection_field_comboBox", destination, previous) # Add/update the new destination so save_settings() can find it self.eligible_collection_fields[destination] = label # Save manually in case user cancels set_cc_mapping('collections', combobox=destination, field=label) elif source == 'Last read': _update_combo_box("date_read_field_comboBox", destination, previous) # Add/update the new destination so save_settings() can find it self.eligible_date_read_fields[destination] = label # Save manually in case user cancels set_cc_mapping('date_read', combobox=destination, field=label) elif source == 'Locked': _update_combo_box("locked_field_comboBox", destination, previous) # Add/update the new destination so save_settings() can find it self.eligible_locked_fields[destination] = label # Save manually in case user cancels set_cc_mapping('locked', combobox=destination, field=label) elif source == "Progress": _update_combo_box("progress_field_comboBox", destination, previous) # Add/update the new destination so save_settings() can find it self.eligible_progress_fields[destination] = label # Save manually in case user cancels set_cc_mapping('progress', combobox=destination, field=label) elif source == "Read": _update_combo_box("read_field_comboBox", destination, previous) # Add/update the new destination so save_settings() can find it self.eligible_read_fields[destination] = label # Save manually in case user cancels set_cc_mapping('read', combobox=destination, field=label) elif source == "Reading list": _update_combo_box("reading_list_field_comboBox", destination, previous) # Add/update the new destination so save_settings() can find it self.eligible_reading_list_fields[destination] = label # Save manually in case user cancels set_cc_mapping('reading_list', combobox=destination, field=label) elif source == "Word count": _update_combo_box("word_count_field_comboBox", destination, previous) # Add/update the new destination so save_settings() can find it self.eligible_word_count_fields[destination] = label # Save manually in case user cancels set_cc_mapping('word_count', combobox=destination, field=label) else: self._log("ERROR: Can't import from '%s'" % klass) def launch_new_destination_dialog(self, old, new): ''' Return 'move', 'change' or 'cancel' ''' from calibre_plugins.marvin_manager.book_status import dialog_resources_path self._log_location() klass = os.path.join(dialog_resources_path, 'new_destination.py') if os.path.exists(klass): self._log("importing new destination dialog from '%s'" % klass) sys.path.insert(0, dialog_resources_path) this_dc = importlib.import_module('new_destination') sys.path.remove(dialog_resources_path) dlg = this_dc.NewDestinationDialog(self, old, new) dlg.exec_() return dlg.command def populate_annotations(self): datatype = self.WIZARD_PROFILES['Annotations']['datatype'] self.eligible_annotations_fields = self.get_eligible_custom_fields([datatype]) self.annotations_field_comboBox.addItems(['']) ecf = sorted(self.eligible_annotations_fields.keys(), key=lambda s: s.lower()) self.annotations_field_comboBox.addItems(ecf) # Retrieve stored value existing = get_cc_mapping('annotations', 'combobox') if existing: ci = self.annotations_field_comboBox.findText(existing) self.annotations_field_comboBox.setCurrentIndex(ci) def populate_collections(self): datatype = self.WIZARD_PROFILES['Collections']['datatype'] self.eligible_collection_fields = self.get_eligible_custom_fields([datatype], is_multiple=True) self.collection_field_comboBox.addItems(['']) ecf = sorted(self.eligible_collection_fields.keys(), key=lambda s: s.lower()) self.collection_field_comboBox.addItems(ecf) # Retrieve stored value existing = get_cc_mapping('collections', 'combobox') if existing: ci = self.collection_field_comboBox.findText(existing) self.collection_field_comboBox.setCurrentIndex(ci) def populate_date_read(self): #self.eligible_date_read_fields = self.get_eligible_custom_fields(['datetime']) datatype = self.WIZARD_PROFILES['Last read']['datatype'] self.eligible_date_read_fields = self.get_eligible_custom_fields([datatype]) self.date_read_field_comboBox.addItems(['']) ecf = sorted(self.eligible_date_read_fields.keys(), key=lambda s: s.lower()) self.date_read_field_comboBox.addItems(ecf) # Retrieve stored value existing = get_cc_mapping('date_read', 'combobox') if existing: ci = self.date_read_field_comboBox.findText(existing) self.date_read_field_comboBox.setCurrentIndex(ci) def populate_locked(self): datatype = self.WIZARD_PROFILES['Locked']['datatype'] self.eligible_locked_fields = self.get_eligible_custom_fields([datatype]) self.locked_field_comboBox.addItems(['']) ecf = sorted(self.eligible_locked_fields.keys(), key=lambda s: s.lower()) self.locked_field_comboBox.addItems(ecf) # Retrieve stored value existing = get_cc_mapping('locked', 'combobox') if existing: ci = self.locked_field_comboBox.findText(existing) self.locked_field_comboBox.setCurrentIndex(ci) def populate_progress(self): #self.eligible_progress_fields = self.get_eligible_custom_fields(['float']) datatype = self.WIZARD_PROFILES['Progress']['datatype'] self.eligible_progress_fields = self.get_eligible_custom_fields([datatype]) self.progress_field_comboBox.addItems(['']) ecf = sorted(self.eligible_progress_fields.keys(), key=lambda s: s.lower()) self.progress_field_comboBox.addItems(ecf) # Retrieve stored value existing = get_cc_mapping('progress', 'combobox') if existing: ci = self.progress_field_comboBox.findText(existing) self.progress_field_comboBox.setCurrentIndex(ci) def populate_read(self): datatype = self.WIZARD_PROFILES['Read']['datatype'] self.eligible_read_fields = self.get_eligible_custom_fields([datatype]) self.read_field_comboBox.addItems(['']) ecf = sorted(self.eligible_read_fields.keys(), key=lambda s: s.lower()) self.read_field_comboBox.addItems(ecf) # Retrieve stored value existing = get_cc_mapping('read', 'combobox') if existing: ci = self.read_field_comboBox.findText(existing) self.read_field_comboBox.setCurrentIndex(ci) def populate_reading_list(self): datatype = self.WIZARD_PROFILES['Reading list']['datatype'] self.eligible_reading_list_fields = self.get_eligible_custom_fields([datatype]) self.reading_list_field_comboBox.addItems(['']) ecf = sorted(self.eligible_reading_list_fields.keys(), key=lambda s: s.lower()) self.reading_list_field_comboBox.addItems(ecf) # Retrieve stored value existing = get_cc_mapping('reading_list', 'combobox') if existing: ci = self.reading_list_field_comboBox.findText(existing) self.reading_list_field_comboBox.setCurrentIndex(ci) def populate_word_count(self): #self.eligible_word_count_fields = self.get_eligible_custom_fields(['int']) datatype = self.WIZARD_PROFILES['Word count']['datatype'] self.eligible_word_count_fields = self.get_eligible_custom_fields([datatype]) self.word_count_field_comboBox.addItems(['']) ecf = sorted(self.eligible_word_count_fields.keys(), key=lambda s: s.lower()) self.word_count_field_comboBox.addItems(ecf) # Retrieve stored value existing = get_cc_mapping('word_count', 'combobox') if existing: ci = self.word_count_field_comboBox.findText(existing) self.word_count_field_comboBox.setCurrentIndex(ci) """ def select_dropbox_folder(self): ''' ''' self._log_location() dropbox_location = QFileDialog.getExistingDirectory( self, "Dropbox folder", os.path.expanduser("~"), QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks) self.dropbox_location_lineedit.setText(unicode(dropbox_location)) def set_dropbox_syncing(self, state): ''' Called when checkbox changes state, or when restoring state Set enabled state of Dropbox folder picker to match ''' self.cfg_dropbox_folder_toolbutton.setEnabled(state) self.dropbox_location_lineedit.setEnabled(state) """ def set_restart_required(self, state): ''' Set restart_required flag to show show dialog when closing dialog ''' self.restart_required = True """ def save_combobox_setting(self, cb, index): ''' Apply changes immediately ''' cf = str(getattr(self, cb).currentText()) self._log_location("%s => %s" % (cb, repr(cf))) if cb == 'annotations_field_comboBox': field = None if cf: field = self.eligible_annotations_fields[cf] set_cc_mapping('annotations', combobox=cf, field=field) """ def save_settings(self): self._log_location() # Annotations field cf = unicode(self.annotations_field_comboBox.currentText()) field = None if cf: field = self.eligible_annotations_fields[cf] set_cc_mapping('annotations', combobox=cf, field=field) # Collections field cf = unicode(self.collection_field_comboBox.currentText()) field = None if cf: field = self.eligible_collection_fields[cf] set_cc_mapping('collections', combobox=cf, field=field) # Date read field cf = unicode(self.date_read_field_comboBox.currentText()) field = None if cf: field = self.eligible_date_read_fields[cf] set_cc_mapping('date_read', combobox=cf, field=field) # Locked field cf = unicode(self.locked_field_comboBox.currentText()) field = None if cf: field = self.eligible_locked_fields[cf] set_cc_mapping('locked', combobox=cf, field=field) # Progress field cf = unicode(self.progress_field_comboBox.currentText()) field = None if cf: field = self.eligible_progress_fields[cf] set_cc_mapping('progress', combobox=cf, field=field) # Read field cf = unicode(self.read_field_comboBox.currentText()) field = None if cf: field = self.eligible_read_fields[cf] set_cc_mapping('read', combobox=cf, field=field) # Reading list field cf = unicode(self.reading_list_field_comboBox.currentText()) field = None if cf: field = self.eligible_reading_list_fields[cf] set_cc_mapping('reading_list', combobox=cf, field=field) # Word count field cf = unicode(self.word_count_field_comboBox.currentText()) field = None if cf: field = self.eligible_word_count_fields[cf] set_cc_mapping('word_count', combobox=cf, field=field) ''' # Save Dropbox settings self.prefs.set('dropbox_syncing', self.dropbox_syncing_checkbox.isChecked()) self.prefs.set('dropbox_folder', unicode(self.dropbox_location_lineedit.text())) ''' # Save general settings self.prefs.set('apply_markers_to_duplicates', self.duplicate_markers_checkbox.isChecked()) self.prefs.set('apply_markers_to_updated', self.updated_markers_checkbox.isChecked()) self.prefs.set('auto_refresh_at_startup', self.auto_refresh_checkbox.isChecked()) self.prefs.set('show_progress_as_percentage', self.reading_progress_checkbox.isChecked()) # Save debug settings self.prefs.set('debug_plugin', self.debug_plugin_checkbox.isChecked()) self.prefs.set('debug_libimobiledevice', self.debug_libimobiledevice_checkbox.isChecked()) # If restart needed, inform user if self.restart_required: do_restart = show_restart_warning('Restart calibre for the changes to be applied.', parent=self.gui) if do_restart: self.gui.quit(restart=True) def start_inventory(self): self._log_location() self.annotated_books_scanner.start()
class ComicalibreDialog(QDialog): """ The main dialog for users to enter options before starting. """ def __init__(self, gui, icon, do_user_config): """ Initialize the gui dialog. """ QDialog.__init__(self, gui) self.gui = gui self.icon = icon self.do_user_config = do_user_config self.db = gui.current_db self.create_gui() def start_process(self, process_type): """ Starts the processing worker and progress bar. """ prefs["comic_vine_api_key"] = self.api_msg.text() prefs["tags_to_add"] = self.tags_msg.text() self.worker = ComicalibreWork(self.gui) self.result_text.setText("Processing...") self.title_start.setEnabled(False) self.series_start.setEnabled(False) self.ids_start.setEnabled(False) keep_tags = self.keep_tags_cb.isChecked() errors = self.worker.process(self.progress_bar, process_type, keep_tags) self.title_start.setEnabled(True) self.series_start.setEnabled(True) self.ids_start.setEnabled(True) results = "Finished!" + os.linesep for error in errors: results = results + error + os.linesep self.result_text.setText(results) def title_process(self): self.start_process(0) def series_process(self): self.start_process(1) def ids_process(self): self.start_process(2) def create_gui(self): """ Layout arrangement for the dialog and its input widgets. """ self.layout = QVBoxLayout() self.layout.setSpacing(20) self.setLayout(self.layout) # Create an editable box for user to input Comic Vine API Key. self.api_layout = QHBoxLayout() self.api_msg = QLineEdit(self) self.api_msg.setFixedWidth(350) self.api_msg.setText(prefs["comic_vine_api_key"]) self.api_label = QLabel("Comic Vine API Key") self.api_label.setBuddy(self.api_msg) self.api_layout.addWidget(self.api_label) self.api_layout.addWidget(self.api_msg) # Create an editable box for user to input Tags to add to all books. self.tags_layout = QHBoxLayout() self.tags_msg = QLineEdit(self) self.tags_msg.setText(prefs["tags_to_add"]) self.tags_label = QLabel("Tags To Add") self.tags_label.setBuddy(self.tags_msg) self.tags_layout.addWidget(self.tags_label) self.tags_layout.addWidget(self.tags_msg) # Add the fields to the main layout. self.layout.addLayout(self.api_layout) self.layout.addLayout(self.tags_layout) # Option to keep current tags. self.keep_tags_cb = QCheckBox("Keep existing tags while adding new.", self) self.layout.addWidget(self.keep_tags_cb) # Separate the buttons from the input. self.btn_sep = QFrame() self.btn_sep.setFrameShape(QFrame.HLine) self.btn_sep.setFrameShadow(QFrame.Sunken) self.layout.addWidget(self.btn_sep) # Create a start button to kick off the processing with title. self.title_start = QPushButton("Using Title - Hover For Details", self) self.title_start.setToolTip( "This expects the title of a book to have a " "specific formatted string containing volume " "ID and issue#. e.g." + os.linesep + "Desired " "Title --- v1234 n0124" + os.linesep + "Where " "--- is required and the number after v " "matches Comic Vine's volume ID. The number " "after n is the issue number.") self.title_start.clicked.connect(self.title_process) self.layout.addWidget(self.title_start) # Create a start button to kick off the processing with series. self.series_start = QPushButton("Using Series - Hover For Details", self) self.series_start.setToolTip( "This expects the series name to match the " "Comic Vine volume ID and the series number " "equals issue number.") self.series_start.clicked.connect(self.series_process) self.layout.addWidget(self.series_start) # Create a start button to kick off the processing with CV IDs. self.ids_start = QPushButton("Using IDs - Hover For Details", self) self.ids_start.setToolTip( "This expects two custom columns with lookup " "comicvineissueid and comicvinevolumeid. " "These must match Comic Vine's IDs for the " "issue and the volume.") self.ids_start.clicked.connect(self.ids_process) self.layout.addWidget(self.ids_start) # Separate the progress and results. self.result_sep = QFrame() self.result_sep.setFrameShape(QFrame.HLine) self.result_sep.setFrameShadow(QFrame.Sunken) self.layout.addWidget(self.result_sep) # Create progress bar. self.progress_bar = QProgressBar() self.progress_bar.setRange(0, 100) self.progress_bar.setMinimumWidth(485) self.layout.addWidget(self.progress_bar) # Create results text area. self.result_box = QGroupBox() self.result_box.setTitle("Results") self.result_text = QLabel("Run Comicalibre to see results.") self.result_scroll = QScrollArea() self.result_scroll.setWidget(self.result_text) self.result_scroll.setWidgetResizable(True) self.result_layout = QVBoxLayout() self.result_layout.addWidget(self.result_scroll) self.result_box.setLayout(self.result_layout) self.layout.addWidget(self.result_box) self.setWindowTitle("Comicalibre") self.setWindowIcon(self.icon) self.resize(self.sizeHint())
def _initUi(self): app = Application.instance() icon = QIcon(app.findResource('Subtitles.png')) iconLabel = QLabel() iconLabel.setPixmap(icon.pixmap(128)) iconLabel.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) appNameLabel = QLabel(self) appNameLabel.setText(app.applicationName()) appNameLabel.setAlignment(Qt.AlignHCenter | Qt.AlignTop) appNameLabel.setStyleSheet('font-weight: bold; font-size: 18pt') appNameLabel.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Maximum) version_labels = self._createVersionLabels() licenseLabel = self._createSelectableLabel( 'Copyright © 2018–2019 Philip Belemezov<br>' 'Licensed under the <a href="about:blank">' 'GNU General Public License, version 3</a>.') licenseLabel.setStyleSheet('color: gray; font-size: 8pt') licenseLabel.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) licenseLabel.setTextFormat(Qt.RichText) licenseLabel.setTextInteractionFlags(Qt.TextBrowserInteraction) licenseLabel.linkActivated.connect(self.showLicense) homepageLabel = self._createLinkLabel( text=self.tr("Homepage"), href="https://github.io/philipbel/subtitles") homepageLabel.setAlignment(Qt.AlignCenter) ackLabel = self._createLinkLabel(text=self.tr("Acknowledgements")) ackLabel.setAlignment(Qt.AlignCenter) ackLabel.linkActivated.connect(self.showAcknowledgements) linksLayout = QVBoxLayout() linksLayout.addWidget(homepageLabel) linksLayout.addSpacing(5) linksLayout.addWidget(ackLabel) versionInfoLayout = QFormLayout() versionInfoLayout.setFormAlignment(Qt.AlignHCenter) versionInfoLayout.setHorizontalSpacing(4) for name, value in version_labels: name.setText(name.text() + ':') versionInfoLayout.addRow(name, value) mainLayout = QVBoxLayout(self) mainLayout.setSpacing(0) mainLayout.addWidget(iconLabel) mainLayout.addWidget(appNameLabel) mainLayout.addSpacing(5) mainLayout.addLayout(versionInfoLayout) mainLayout.addSpacing(20) mainLayout.addLayout(linksLayout) mainLayout.addSpacing(20) mainLayout.addWidget(licenseLabel) if sys.platform != 'darwin': buttonBox = QDialogButtonBox(QDialogButtonBox.Close) buttonBox.setCenterButtons(True) buttonBox.button(QDialogButtonBox.Close).clicked.connect( self.reject) line = QFrame() line.setFrameShape(QFrame.HLine) line.setFrameShadow(QFrame.Sunken) mainLayout.addSpacing(8) mainLayout.addWidget(line) mainLayout.addSpacing(8) mainLayout.addWidget(buttonBox)
class ConfigWidget(QWidget, Logger): # Manually managed controls when saving/restoring EXCLUDED_CONTROLS = [ 'cfg_annotations_destination_comboBox' ] #LOCATION_TEMPLATE = "{cls}:{func}({arg1}) {arg2}" WIZARD_PROFILES = { 'Annotations': { 'label': 'mm_annotations', 'datatype': 'comments', 'display': {}, 'is_multiple': False } } def __init__(self, plugin_action): self.gui = plugin_action.gui self.opts = plugin_action.opts QWidget.__init__(self) self.l = QVBoxLayout() self.setLayout(self.l) # ~~~~~~~~ Create the runtime options group box ~~~~~~~~ self.cfg_runtime_options_gb = QGroupBox(self) self.cfg_runtime_options_gb.setTitle(_('Runtime options')) self.l.addWidget(self.cfg_runtime_options_gb) self.cfg_runtime_options_qvl = QVBoxLayout(self.cfg_runtime_options_gb) # ~~~~~~~~ Disable caching checkbox ~~~~~~~~ self.cfg_disable_caching_checkbox = QCheckBox(_('Disable caching')) self.cfg_disable_caching_checkbox.setObjectName('cfg_disable_caching_checkbox') self.cfg_disable_caching_checkbox.setToolTip(_('Force reload of reader database')) self.cfg_disable_caching_checkbox.setChecked(False) self.cfg_runtime_options_qvl.addWidget(self.cfg_disable_caching_checkbox) # ~~~~~~~~ plugin logging checkbox ~~~~~~~~ self.cfg_plugin_debug_log_checkbox = QCheckBox(_('Enable debug logging for Annotations plugin')) self.cfg_plugin_debug_log_checkbox.setObjectName('cfg_plugin_debug_log_checkbox') self.cfg_plugin_debug_log_checkbox.setToolTip(_('Print plugin diagnostic messages to console')) self.cfg_plugin_debug_log_checkbox.setChecked(False) self.cfg_runtime_options_qvl.addWidget(self.cfg_plugin_debug_log_checkbox) # ~~~~~~~~ libiMobileDevice logging checkbox ~~~~~~~~ self.cfg_libimobiledevice_debug_log_checkbox = QCheckBox(_('Enable debug logging for libiMobileDevice')) self.cfg_libimobiledevice_debug_log_checkbox.setObjectName('cfg_libimobiledevice_debug_log_checkbox') self.cfg_libimobiledevice_debug_log_checkbox.setToolTip(_('Print libiMobileDevice debug messages to console')) self.cfg_libimobiledevice_debug_log_checkbox.setChecked(False) self.cfg_libimobiledevice_debug_log_checkbox.setEnabled(LIBIMOBILEDEVICE_AVAILABLE) self.cfg_runtime_options_qvl.addWidget(self.cfg_libimobiledevice_debug_log_checkbox) # ~~~~~~~~ Create the Annotations options group box ~~~~~~~~ self.cfg_annotation_options_gb = QGroupBox(self) self.cfg_annotation_options_gb.setTitle(_('Annotation options')) self.l.addWidget(self.cfg_annotation_options_gb) self.cfg_annotation_options_qgl = QGridLayout(self.cfg_annotation_options_gb) current_row = 0 # Add the label/combobox for annotations destination self.cfg_annotations_destination_label = QLabel(_('<b>Add fetched annotations to<b>')) self.cfg_annotations_destination_label.setAlignment(Qt.AlignLeft) self.cfg_annotation_options_qgl.addWidget(self.cfg_annotations_destination_label, current_row, 0) current_row += 1 self.cfg_annotations_destination_comboBox = QComboBox(self.cfg_annotation_options_gb) self.cfg_annotations_destination_comboBox.setObjectName('cfg_annotations_destination_comboBox') self.cfg_annotations_destination_comboBox.setToolTip(_('Custom field to store annotations')) self.cfg_annotation_options_qgl.addWidget(self.cfg_annotations_destination_comboBox, current_row, 0) # Populate annotations_field combobox db = self.gui.current_db all_custom_fields = db.custom_field_keys() self.custom_fields = {} for custom_field in all_custom_fields: field_md = db.metadata_for_field(custom_field) if field_md['datatype'] in ['comments']: self.custom_fields[field_md['name']] = {'field': custom_field, 'datatype': field_md['datatype']} all_fields = self.custom_fields.keys() + ['Comments'] for cf in sorted(all_fields): self.cfg_annotations_destination_comboBox.addItem(cf) # Add CC Wizard self.cfg_annotations_wizard = QToolButton() self.cfg_annotations_wizard.setIcon(QIcon(I('wizard.png'))) self.cfg_annotations_wizard.setToolTip(_("Create a custom column to store annotations")) self.cfg_annotations_wizard.clicked.connect(partial(self.launch_cc_wizard, 'Annotations')) self.cfg_annotation_options_qgl.addWidget(self.cfg_annotations_wizard, current_row, 2) current_row += 1 # ~~~~~~~~ Add a horizontal line ~~~~~~~~ self.cfg_appearance_hl = QFrame(self) self.cfg_appearance_hl.setGeometry(QRect(0, 0, 1, 3)) self.cfg_appearance_hl.setFrameShape(QFrame.HLine) self.cfg_appearance_hl.setFrameShadow(QFrame.Raised) self.cfg_annotation_options_qgl.addWidget(self.cfg_appearance_hl, current_row, 0) current_row += 1 # ~~~~~~~~ Add the Modify… button ~~~~~~~~ self.cfg_annotations_appearance_pushbutton = QPushButton(_("Modify appearance…")) self.cfg_annotations_appearance_pushbutton.clicked.connect(self.configure_appearance) self.cfg_annotation_options_qgl.addWidget(self.cfg_annotations_appearance_pushbutton, current_row, 0) current_row += 1 self.spacerItem = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) self.cfg_annotation_options_qgl.addItem(self.spacerItem, current_row, 0, 1, 1) # ~~~~~~~~ Compilations group box ~~~~~~~~ self.cfg_compilation_options_gb = QGroupBox(self) self.cfg_compilation_options_gb.setTitle(_('Compilations')) self.l.addWidget(self.cfg_compilation_options_gb) self.cfg_compilation_options_qgl = QGridLayout(self.cfg_compilation_options_gb) current_row = 0 # News clippings self.cfg_news_clippings_checkbox = QCheckBox(_('Collect News clippings')) self.cfg_news_clippings_checkbox.setObjectName('cfg_news_clippings_checkbox') self.cfg_compilation_options_qgl.addWidget(self.cfg_news_clippings_checkbox, current_row, 0) self.cfg_news_clippings_lineEdit = QLineEdit() self.cfg_news_clippings_lineEdit.setObjectName('cfg_news_clippings_lineEdit') self.cfg_news_clippings_lineEdit.setToolTip(_('Title for collected news clippings')) self.cfg_compilation_options_qgl.addWidget(self.cfg_news_clippings_lineEdit, current_row, 1) # ~~~~~~~~ End of construction zone ~~~~~~~~ self.resize(self.sizeHint()) # Restore state of controls, populate annotations combobox self.controls = inventory_controls(self, dump_controls=False) restore_state(self) self.populate_annotations() # Hook changes to annotations_destination_combobox # self.connect(self.cfg_annotations_destination_comboBox, # pyqtSignal('currentIndexChanged(const QString &)'), # self.annotations_destination_changed) self.cfg_annotations_destination_comboBox.currentIndexChanged.connect(self.annotations_destination_changed) # Hook changes to diagnostic checkboxes self.cfg_disable_caching_checkbox.stateChanged.connect(self.restart_required) self.cfg_libimobiledevice_debug_log_checkbox.stateChanged.connect(self.restart_required) self.cfg_plugin_debug_log_checkbox.stateChanged.connect(self.restart_required) # Hook changes to News clippings, initialize self.cfg_news_clippings_checkbox.stateChanged.connect(self.news_clippings_toggled) self.news_clippings_toggled(self.cfg_news_clippings_checkbox.checkState()) self.cfg_news_clippings_lineEdit.editingFinished.connect(self.news_clippings_destination_changed) # Launch the annotated_books_scanner field = get_cc_mapping('annotations', 'field', 'Comments') self.annotated_books_scanner = InventoryAnnotatedBooks(self.gui, field) self.annotated_books_scanner.signal.connect(self.inventory_complete) # self.connect(self.annotated_books_scanner, self.annotated_books_scanner.signal, # self.inventory_complete) QTimer.singleShot(1, self.start_inventory) def annotations_destination_changed(self, qs_new_destination_name): ''' If the destination field changes, move all existing annotations from old to new ''' self._log_location(repr(qs_new_destination_name)) self._log("self.custom_fields: %s" % self.custom_fields) old_destination_field = get_cc_mapping('annotations', 'field', None) if old_destination_field and not (old_destination_field in self.gui.current_db.custom_field_keys() or old_destination_field == 'Comments'): return old_destination_name = get_cc_mapping('annotations', 'combobox', None) self._log("old_destination_field: %s" % old_destination_field) self._log("old_destination_name: %s" % old_destination_name) # Catch initial change from None to Comments - first run only if old_destination_field is None: return # new_destination_name = unicode(qs_new_destination_name) new_destination_name = unicode(self.cfg_annotations_destination_comboBox.currentText()) self._log("new_destination_name: %s" % new_destination_name) if old_destination_name == new_destination_name: self._log_location("old_destination_name = new_destination_name, no changes") return new_destination_field = None if new_destination_name == 'Comments': new_destination_field = 'Comments' else: new_destination_field = self.custom_fields[new_destination_name]['field'] if existing_annotations(self.opts.parent, old_destination_field): command = self.launch_new_destination_dialog(old_destination_name, new_destination_name) if command == 'move': set_cc_mapping('annotations', field=new_destination_field, combobox=new_destination_name) if self.annotated_books_scanner.isRunning(): self.annotated_books_scanner.wait() move_annotations(self, self.annotated_books_scanner.annotation_map, old_destination_field, new_destination_field) elif command == 'change': # Keep the updated destination field, but don't move annotations pass elif command == 'cancel': # Restore previous destination self.cfg_annotations_destination_comboBox.blockSignals(True) old_index = self.cfg_annotations_destination_comboBox.findText(old_destination_name) self.cfg_annotations_destination_comboBox.setCurrentIndex(old_index) self.cfg_annotations_destination_comboBox.blockSignals(False) """ # Warn user that change will move existing annotations to new field title = 'Move annotations?' msg = ("<p>Existing annotations will be moved from <b>%s</b> to <b>%s</b>.</p>" % (old_destination_name, new_destination_name) + "<p>New annotations will be added to <b>%s</b>.</p>" % new_destination_name + "<p>Proceed?</p>") d = MessageBox(MessageBox.QUESTION, title, msg, show_copy_button=False) self._log_location("QUESTION: %s" % msg) if d.exec_(): set_cc_mapping('annotations', field=new_destination_field, combobox=new_destination_name) if self.annotated_books_scanner.isRunning(): self.annotated_books_scanner.wait() move_annotations(self, self.annotated_books_scanner.annotation_map, old_destination_field, new_destination_field) else: self.cfg_annotations_destination_comboBox.blockSignals(True) old_index = self.cfg_annotations_destination_comboBox.findText(old_destination_name) self.cfg_annotations_destination_comboBox.setCurrentIndex(old_index) self.cfg_annotations_destination_comboBox.blockSignals(False) """ else: # No existing annotations, just update prefs set_cc_mapping('annotations', field=new_destination_field, combobox=new_destination_name) def configure_appearance(self): ''' ''' from calibre_plugins.annotations.appearance import default_elements from calibre_plugins.annotations.appearance import default_timestamp appearance_settings = { 'appearance_css': default_elements, 'appearance_hr_checkbox': False, 'appearance_timestamp_format': default_timestamp } # Save, hash the original settings original_settings = {} osh = hashlib.md5() for setting in appearance_settings: original_settings[setting] = plugin_prefs.get(setting, appearance_settings[setting]) osh.update(repr(plugin_prefs.get(setting, appearance_settings[setting]))) # Display the appearance dialog aa = AnnotationsAppearance(self, get_icon('images/annotations.png'), plugin_prefs) cancelled = False if aa.exec_(): # appearance_hr_checkbox and appearance_timestamp_format changed live to prefs during previews plugin_prefs.set('appearance_css', aa.elements_table.get_data()) # Generate a new hash nsh = hashlib.md5() for setting in appearance_settings: nsh.update(repr(plugin_prefs.get(setting, appearance_settings[setting]))) else: for setting in appearance_settings: plugin_prefs.set(setting, original_settings[setting]) nsh = osh # If there were changes, and there are existing annotations, offer to re-render field = get_cc_mapping('annotations', 'field', None) if osh.digest() != nsh.digest() and existing_annotations(self.opts.parent,field): title = _('Update annotations?') msg = _('<p>Update existing annotations to new appearance settings?</p>') d = MessageBox(MessageBox.QUESTION, title, msg, show_copy_button=False) self._log_location("QUESTION: %s" % msg) if d.exec_(): self._log_location("Updating existing annotations to modified appearance") if self.annotated_books_scanner.isRunning(): self.annotated_books_scanner.wait() move_annotations(self, self.annotated_books_scanner.annotation_map, field, field, window_title=_("Updating appearance")) def inventory_complete(self, msg): self._log_location(msg) def launch_cc_wizard(self, column_type): ''' ''' def _update_combo_box(comboBox, destination, previous): ''' ''' self._log_location() cb = getattr(self, comboBox) cb.blockSignals(True) all_items = [str(cb.itemText(i)) for i in range(cb.count())] if previous and previous in all_items: all_items.remove(previous) all_items.append(destination) cb.clear() cb.addItems(sorted(all_items, key=lambda s: s.lower())) # Select the new destination in the comboBox idx = cb.findText(destination) if idx > -1: cb.setCurrentIndex(idx) # Process the changed destination self.annotations_destination_changed(destination) cb.blockSignals(False) klass = os.path.join(dialog_resources_path, 'cc_wizard.py') if os.path.exists(klass): #self._log("importing CC Wizard dialog from '%s'" % klass) sys.path.insert(0, dialog_resources_path) this_dc = importlib.import_module('cc_wizard') sys.path.remove(dialog_resources_path) dlg = this_dc.CustomColumnWizard(self, column_type, self.WIZARD_PROFILES[column_type], verbose=True) dlg.exec_() if dlg.modified_column: self._log("modified_column: %s" % dlg.modified_column) destination = dlg.modified_column['destination'] label = dlg.modified_column['label'] previous = dlg.modified_column['previous'] source = dlg.modified_column['source'] self._log("destination: %s" % destination) self._log("label: %s" % label) self._log("previous: %s" % previous) self._log("source: %s" % source) if source == "Annotations": # Add/update the new destination so save_settings() can find it if destination in self.custom_fields: self.custom_fields[destination]['field'] = label else: self.custom_fields[destination] = {'field': label} _update_combo_box('cfg_annotations_destination_comboBox', destination, previous) # Save field manually in case user cancels #self.prefs.set('cfg_annotations_destination_comboBox', destination) #self.prefs.set('cfg_annotations_destination_field', label) set_cc_mapping('annotations', field=label, combobox=destination) # Inform user to restart self.restart_required('custom_column') else: self._log("ERROR: Can't import from '%s'" % klass) def launch_new_destination_dialog(self, old, new): ''' Return 'move', 'change' or 'cancel' ''' self._log_location() klass = os.path.join(dialog_resources_path, 'new_destination.py') if os.path.exists(klass): self._log("importing new destination dialog from '%s'" % klass) sys.path.insert(0, dialog_resources_path) this_dc = importlib.import_module('new_destination') sys.path.remove(dialog_resources_path) dlg = this_dc.NewDestinationDialog(self, old, new) dlg.exec_() return dlg.command def news_clippings_destination_changed(self): qs_new_destination_name = self.cfg_news_clippings_lineEdit.text() if not re.match(r'^\S+[A-Za-z0-9 ]+$', qs_new_destination_name): # Complain about News clippings title title = _('Invalid title for News clippings') msg = _("Supply a valid title for News clippings, for example 'My News Clippings'.") d = MessageBox(MessageBox.WARNING, title, msg, show_copy_button=False) self._log_location("WARNING: %s" % msg) d.exec_() def news_clippings_toggled(self, state): if state == Qt.Checked: self.cfg_news_clippings_lineEdit.setEnabled(True) else: self.cfg_news_clippings_lineEdit.setEnabled(False) def populate_annotations(self): ''' Restore annotations combobox ''' self._log_location() target = 'Comments' existing = get_cc_mapping('annotations', 'combobox') if existing: target = existing ci = self.cfg_annotations_destination_comboBox.findText(target) self.cfg_annotations_destination_comboBox.setCurrentIndex(ci) def restart_required(self, state): title = _('Restart required') msg = _('To apply changes, restart calibre.') d = MessageBox(MessageBox.WARNING, title, msg, show_copy_button=False) self._log_location("WARNING: %s" % (msg)) d.exec_() def save_settings(self): save_state(self) # Save the annotation destination field ann_dest = unicode(self.cfg_annotations_destination_comboBox.currentText()) self._log_location("INFO: ann_dest=%s" % (ann_dest)) self._log_location("INFO: self.custom_fields=%s" % (self.custom_fields)) if ann_dest == 'Comments': set_cc_mapping('annotations', field='Comments', combobox='Comments') elif ann_dest: set_cc_mapping('annotations', field=self.custom_fields[ann_dest]['field'], combobox=ann_dest) def start_inventory(self): self.annotated_books_scanner.start()