class DaysOfMonth(Base): HELP = _('''\ Download this periodical every month, on the specified days. The download will happen as soon after the specified time as possible on the specified days of each month. For example, if you choose the 1st and the 15th after 9:00 AM, the periodical will be downloaded on the 1st and 15th of every month, as soon after 9:00 AM as possible. ''') def __init__(self, parent=None): Base.__init__(self, parent) self.l1 = QLabel(_('&Days of the month:')) self.days = QLineEdit(self) self.days.setToolTip( _('Comma separated list of days of the month.' ' For example: 1, 15')) self.l1.setBuddy(self.days) self.l2 = QLabel(_('Download &after:')) self.time = QTimeEdit(self) self.time.setDisplayFormat('hh:mm AP') self.l2.setBuddy(self.time) self.l.addWidget(self.l1, 0, 0, 1, 1) self.l.addWidget(self.days, 0, 1, 1, 1) self.l.addWidget(self.l2, 1, 0, 1, 1) self.l.addWidget(self.time, 1, 1, 1, 1) def initialize(self, typ=None, val=None): if val is None: val = ((1, ), 6, 0) days_of_month, hour, minute = val self.days.setText(', '.join(map(str, map(int, days_of_month)))) self.time.setTime(QTime(hour, minute)) @property def schedule(self): parts = [ x.strip() for x in unicode(self.days.text()).split(',') if x.strip() ] try: days_of_month = tuple(map(int, parts)) except: days_of_month = (1, ) if not days_of_month: days_of_month = (1, ) t = self.time.time() hour, minute = t.hour(), t.minute() return 'days_of_month', (days_of_month, int(hour), int(minute))
class FileSelector(QWidget): def __init__(self, parent, filter_, file_dialog_service): QWidget.__init__(self, parent) self._file_dialog_service = file_dialog_service self._filter = filter_ self._line_edit = QLineEdit(self) self._open_dialog = ShrunkPushButton('...', self) self._layout = QHBoxLayout(self) self._layout.setContentsMargins(0, 0, 0, 0) self._layout.addWidget(self._line_edit) self._layout.addWidget(self._open_dialog) self._line_edit.textChanged.connect(self.textChanged) self._open_dialog.clicked.connect(self._open_file_dialog) textChanged = pyqtSignal(str) @auto_property(str) def text(self): return self._line_edit.text() @text.setter def text(self, value): self._line_edit.setText(value) def _open_file_dialog(self): file_name = self._file_dialog_service.get_open_filename( self, self._filter) if file_name: self.text = file_name
class ConfigWidget(QWidget): def __init__(self): QWidget.__init__(self) self.l = QVBoxLayout() self.setLayout(self.l) self.pandoc_label = QLabel('Pandoc Executable path:') self.l1 = QHBoxLayout() self.l.addLayout(self.l1) self.l1.addWidget(self.pandoc_label) self.pandoc_path = QLineEdit(self) self.pandoc_path.setText(prefs['pandoc_exec']) self.l1.addWidget(self.pandoc_path) self.pandoc_label.setBuddy(self.pandoc_path) self.l2 = QHBoxLayout() self.l.addLayout(self.l2) self.java_label = QLabel('Java Executable path:') self.l2.addWidget(self.java_label) self.java_path = QLineEdit(self) self.java_path.setText(prefs['java_exec']) self.l2.addWidget(self.java_path) self.java_label.setBuddy(self.java_path) self.resize(self.sizeHint()) def save_settings(self): prefs['pandoc_exec'] = unicode(self.pandoc_path.text()) prefs['java_exec'] = unicode(self.java_path.text())
class App(QWidget): def __init__(self): super().__init__() self.title = 'PyQt5 textbox' self.left = 10 self.top = 10 self.width = 320 self.height = 200 self.initUI() def initUI(self): self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, self.width, self.height) # create textbox self.textbox = QLineEdit(self) self.textbox.move(20, 20) self.textbox.resize(280, 40) # Create a button in the window self.button = QPushButton('show text', self) self.button.move(20, 80) # connect button to function on_click self.button.clicked.connect(self.on_click) self.show() @pyqtSlot() def on_click(self): textboxValue = self.textbox.text() QMessageBox.question(self, "Message", 'You typed:' + textboxValue, QMessageBox.Ok, QMessageBox.Ok) """打印完毕之后清空文本框""" self.textbox.setText('')
class ConfigWidget(QWidget): def __init__(self): QWidget.__init__(self) self.layout = QVBoxLayout() self.setLayout(self.layout) self.AuthLayout = QHBoxLayout() self.layout.addLayout(self.AuthLayout) self.AuthLabel = QLabel('Auth token:') self.AuthLayout.addWidget(self.AuthLabel) self.AuthMsg = QLineEdit(self) self.AuthMsg.setText(prefs['cookie_auth_token']) self.AuthLayout.addWidget(self.AuthMsg) self.AuthLabel.setBuddy(self.AuthMsg) self.DownloadLayout = QHBoxLayout() self.layout.addLayout(self.DownloadLayout) self.DownloadLabel = QLabel('Download location:') self.DownloadLayout.addWidget(self.DownloadLabel) self.DownloadMsg = QLineEdit(self) self.DownloadMsg.setText(prefs['download_loc']) self.DownloadLayout.addWidget(self.DownloadMsg) self.DownloadLabel.setBuddy(self.DownloadMsg) def save_settings(self): prefs['cookie_auth_token'] = unicode(self.AuthMsg.text()) prefs['download_loc'] = unicode(self.DownloadMsg.text())
class ConfigWidget(QWidget): 'Configuration widget' def __init__(self): QWidget.__init__(self) self.layout = QGridLayout() self.layout.setSpacing(10) self.setLayout(self.layout) self.key_label = QLabel('&api key:') self.key_msg = QLineEdit(self) self.key_msg.setText(PREFS['api_key']) self.layout.addWidget(self.key_label, 1, 0) self.layout.addWidget(self.key_msg, 1, 1) self.key_label.setBuddy(self.key_msg) self.threads_label = QLabel('&worker_threads:') self.threads_msg = QLineEdit(self) self.threads_msg.setText(unicode(PREFS['worker_threads'])) self.layout.addWidget(self.threads_label, 2, 0) self.layout.addWidget(self.threads_msg, 2, 1) self.threads_label.setBuddy(self.threads_msg) def save_settings(self): 'Apply new settings value' PREFS['api_key'] = unicode(self.key_msg.text()) PREFS['worker_threads'] = int(self.threads_msg.text()) pycomicvine.api_key = PREFS['api_key']
class ConfigWidget(QWidget): def __init__(self): QWidget.__init__(self) self.layout = QVBoxLayout() self.setLayout(self.layout) self.l = QHBoxLayout() self.layout.addLayout(self.l) self.l = QHBoxLayout() self.layout.addLayout(self.l) self.label = QLabel('epub tag') self.l.addWidget(self.label) self.tags = QLineEdit(self) self.tags.setText(prefs['tags']) self.l.addWidget(self.tags) self.label.setBuddy(self.tags) self.l = QHBoxLayout() self.layout.addLayout(self.l) self.label = QLabel('search result limit:') self.l.addWidget(self.label) self.search_result_count = QLineEdit(self) self.search_result_count.setValidator( QIntValidator(self.search_result_count)) self.search_result_count.setText(prefs['search_result_count']) self.l.addWidget(self.search_result_count) self.label.setBuddy(self.search_result_count) def save_settings(self): prefs['tags'] = self.tags.text() prefs['search_result_count'] = self.search_result_count.text()
def create_widgets(self, opt): val = self.plugin.prefs[opt.name] if opt.type == 'number': c = QSpinBox if isinstance(opt.default, int) else QDoubleSpinBox widget = c(self) widget.setValue(val) elif opt.type == 'string': widget = QLineEdit(self) widget.setText(val if val else '') elif opt.type == 'bool': widget = QCheckBox(opt.label, self) widget.setChecked(bool(val)) elif opt.type == 'choices': widget = QComboBox(self) items = list(opt.choices.iteritems()) items.sort(key=lambda (k, v): sort_key(v)) for key, label in items: widget.addItem(label, (key)) idx = widget.findData((val)) widget.setCurrentIndex(idx) widget.opt = opt widget.setToolTip(textwrap.fill(opt.desc)) self.widgets.append(widget) r = self.l.rowCount() if opt.type == 'bool': self.l.addWidget(widget, r, 0, 1, self.l.columnCount()) else: l = QLabel(opt.label) l.setToolTip(widget.toolTip()) self.memory.append(l) l.setBuddy(widget) self.l.addWidget(l, r, 0, 1, 1) self.l.addWidget(widget, r, 1, 1, 1)
class ConfigWidget(QWidget): def __init__(self): QWidget.__init__(self) self.l = QVBoxLayout() self.setLayout(self.l) self.about_button = QPushButton('Help', self) self.about_button.clicked.connect(self.about) self.l.addWidget(self.about_button) self.notebookLabel = QLabel('evernote Notebook') self.l.addWidget(self.notebookLabel) self.notebookMsg = QLineEdit(self) self.notebookMsg.setText(prefs['notebook']) self.l.addWidget(self.notebookMsg) self.notebookLabel.setBuddy(self.notebookMsg) self.tagsCsvLabel = QLabel('evernote Tags CSV (ie calibre,mykindle)') self.l.addWidget(self.tagsCsvLabel) self.tagsCsvMsg = QLineEdit(self) self.tagsCsvMsg.setText(prefs['tagsCsv']) self.l.addWidget(self.tagsCsvMsg) self.tagsCsvLabel.setBuddy(self.tagsCsvMsg) def save_settings(self): prefs['notebook'] = unicode(self.notebookMsg.text()) prefs['tagsCsv'] = unicode(self.tagsCsvMsg.text()) def about(self): text = get_resources('about.txt') QMessageBox.about(self, 'About calibrebeam', text.decode('utf-8'))
def setup_ui(self): self.l = l = QGridLayout() self.setLayout(l) self.la = la = QLabel(_( 'Arrange the files in this book into sub-folders based on their types.' ' If you leave a folder blank, the files will be placed in the root.')) la.setWordWrap(True) l.addWidget(la, 0, 0, 1, -1) folders = tprefs['folders_for_types'] for i, (typ, text) in enumerate(self.TYPE_MAP): la = QLabel('&' + text) setattr(self, '%s_label' % typ, la) le = QLineEdit(self) setattr(self, '%s_folder' % typ, le) val = folders.get(typ, '') if val and not val.endswith('/'): val += '/' le.setText(val) la.setBuddy(le) l.addWidget(la, i + 1, 0) l.addWidget(le, i + 1, 1) self.la2 = la = QLabel(_( 'Note that this will only arrange files inside the book,' ' it will not affect how they are displayed in the Files Browser')) la.setWordWrap(True) l.addWidget(la, i + 2, 0, 1, -1) l.addWidget(self.bb, i + 3, 0, 1, -1)
def create_widgets(self, opt): val = self.plugin.prefs[opt.name] if opt.type == 'number': c = QSpinBox if isinstance(opt.default, numbers.Integral) else QDoubleSpinBox widget = c(self) widget.setValue(val) elif opt.type == 'string': widget = QLineEdit(self) widget.setText(val if val else '') elif opt.type == 'bool': widget = QCheckBox(opt.label, self) widget.setChecked(bool(val)) elif opt.type == 'choices': widget = QComboBox(self) items = list(iteritems(opt.choices)) items.sort(key=lambda k_v: sort_key(k_v[1])) for key, label in items: widget.addItem(label, (key)) idx = widget.findData((val)) widget.setCurrentIndex(idx) widget.opt = opt widget.setToolTip(textwrap.fill(opt.desc)) self.widgets.append(widget) r = self.l.rowCount() if opt.type == 'bool': self.l.addWidget(widget, r, 0, 1, self.l.columnCount()) else: l = QLabel(opt.label) l.setToolTip(widget.toolTip()) self.memory.append(l) l.setBuddy(widget) self.l.addWidget(l, r, 0, 1, 1) self.l.addWidget(widget, r, 1, 1, 1)
class classicWindow(QMainWindow): def __init__(self): super().__init__() self.currentPath = sys.path[0] # 程序运行的路径 self.setWindowIcon(QIcon(r'G:\myRepostory\graduateProject\logo\xjtu.jpg')) self.main_widget = QWidget(self) para1 = QLabel('α') para1.setFixedSize(20,20) self.para1Txt = QLineEdit() self.para1Txt.setText('0') self.para1Txt.setFixedSize(50,20) para2 = QLabel('β') para2.setFixedSize(20, 20) self.para2Txt = QLineEdit() self.para2Txt.setText('0') self.para2Txt.setFixedSize(50, 20) # para1Txt.setFixedWidth(100) self.paraSettingLayout = QGridLayout(self.main_widget) self.paraSettingLayout.addWidget(para1,0,0) self.paraSettingLayout.addWidget(self.para1Txt,0,1) self.paraSettingLayout.addWidget(para1, 1, 0) self.paraSettingLayout.addWidget(self.para1Txt, 1, 1) self.setLayout(self.paraSettingLayout) # self.setGeometry(300, 300, 300, 300) self.main_widget.setFocus() self.setCentralWidget(self.main_widget)
class MainWindow(QWidget): def __init__(self): super().__init__() self.setWindowTitle('...') self.url_le = QLineEdit('http://qt-project.org/') self.go_pb = QPushButton('Go') self.go_pb.clicked.connect(self._on_load_url) url_layout = QHBoxLayout() url_layout.addWidget(self.url_le) url_layout.addWidget(self.go_pb) self.view = QWebEngineView() self.view.urlChanged.connect(self._on_url_changed) self.view.titleChanged.connect(self.setWindowTitle) main_layout = QVBoxLayout() main_layout.addLayout(url_layout) main_layout.addWidget(self.view) self.setLayout(main_layout) def _on_load_url(self): self.view.load(QUrl(self.url_le.text())) def _on_url_changed(self, url: QUrl): self.url_le.setText(url.toString())
class CollectionsGroupBox(DeviceOptionsGroupBox): def __init__(self, parent, device): super(CollectionsGroupBox, self).__init__(parent, device) self.setTitle(_("Collections")) self.options_layout = QGridLayout() self.options_layout.setObjectName("options_layout") self.setLayout(self.options_layout) self.setCheckable(True) self.setChecked(device.get_pref('manage_collections')) self.setToolTip( wrap_msg( _('Create new bookshelves on the Kobo if they do not exist. This is only for firmware V2.0.0 or later.' ))) self.collections_columns_label = QLabel(_('Collections Columns')) self.collections_columns_edit = QLineEdit(self) self.collections_columns_edit.setToolTip( _('The Kobo from firmware V2.0.0 supports bookshelves.' ' These are created on the Kobo. ' + 'Specify a tags type column for automatic management.')) self.collections_columns_edit.setText( device.get_pref('collections_columns')) self.create_collections_checkbox = create_checkbox( _("Create Collections"), _('Create new bookshelves on the Kobo if they do not exist. This is only for firmware V2.0.0 or later.' ), device.get_pref('create_collections')) self.delete_empty_collections_checkbox = create_checkbox( _('Delete Empty Bookshelves'), _('Delete any empty bookshelves from the Kobo when syncing is finished. This is only for firmware V2.0.0 or later.' ), device.get_pref('delete_empty_collections')) self.options_layout.addWidget(self.collections_columns_label, 1, 0, 1, 1) self.options_layout.addWidget(self.collections_columns_edit, 1, 1, 1, 1) self.options_layout.addWidget(self.create_collections_checkbox, 2, 0, 1, 2) self.options_layout.addWidget(self.delete_empty_collections_checkbox, 3, 0, 1, 2) self.options_layout.setRowStretch(4, 1) @property def manage_collections(self): return self.isChecked() @property def collections_columns(self): return self.collections_columns_edit.text().strip() @property def create_collections(self): return self.create_collections_checkbox.isChecked() @property def delete_empty_collections(self): return self.delete_empty_collections_checkbox.isChecked()
class App(QWidget): def __init__(self): ''' 初始化操作 ''' # 调用父类QWidget的方法 super().__init__() # 对话框标题文字 self.title = '人人都是Pythonista' # 对话框初始显示位置(从屏幕左边开始往右数) self.left = 200 # 对话框的初始高度(从屏幕上面开始往下数) self.top = 250 # 对话框宽 self.width = 500 # 对话框高 self.height = 500 # 初始化时执行页面ui初始化方法 self.initUI() def initUI(self): ''' 页面ui初始化 ''' # 设置窗口标题 self.setWindowTitle(self.title) # 设置窗口初始位置 self.setGeometry(self.left, self.top, self.width, self.height) # 设置窗口小图标(icon) self.setWindowIcon(QtGui.QIcon('icon.jpg')) # 创建文本框 self.textbox = QLineEdit(self) # 文本框在界面中的初始位置(左上角的位置) self.textbox.move(20, 30) # 文本框的尺寸(长,宽) self.textbox.resize(400, 300) # 创建一个按钮 self.button = QPushButton('在这里点击', self) # 按钮的初始位置(左上角的位置) self.button.move(20, 340) # 将按钮点击事件与下面的on_click方法关联起来 self.button.clicked.connect(self.on_click) # 弹出对话框 self.show() @pyqtSlot() def on_click(self): ''' 点击事件 ''' # 获取文本框中输入的值 textboxValue = self.textbox.text() # 弹出对话框 QMessageBox.question(self, "Message", '你输入了这些内容:' + textboxValue, QMessageBox.Ok, QMessageBox.Ok) # 点击以后清空文本框 self.textbox.setText('')
class MovedDialog(QDialog): # {{{ def __init__(self, stats, location, parent=None): QDialog.__init__(self, parent) self.setWindowTitle(_('No library found')) self._l = l = QGridLayout(self) self.setLayout(l) self.stats, self.location = stats, location loc = self.oldloc = location.replace('/', os.sep) self.header = QLabel( _('No existing calibre library was found at %s. ' 'If the library was moved, select its new location below. ' 'Otherwise calibre will forget this library.') % loc) self.header.setWordWrap(True) ncols = 2 l.addWidget(self.header, 0, 0, 1, ncols) self.cl = QLabel('<b>' + _('New location of this library:')) l.addWidget(self.cl, l.rowCount(), 0, 1, ncols) self.loc = QLineEdit(loc, self) l.addWidget(self.loc, l.rowCount(), 0, 1, 1) self.cd = QToolButton(self) self.cd.setIcon(QIcon(I('document_open.png'))) self.cd.clicked.connect(self.choose_dir) l.addWidget(self.cd, l.rowCount() - 1, 1, 1, 1) self.bb = QDialogButtonBox(QDialogButtonBox.StandardButton.Abort) b = self.bb.addButton(_('Library moved'), QDialogButtonBox.ButtonRole.AcceptRole) b.setIcon(QIcon(I('ok.png'))) b = self.bb.addButton(_('Forget library'), QDialogButtonBox.ButtonRole.RejectRole) b.setIcon(QIcon(I('edit-clear.png'))) b.clicked.connect(self.forget_library) self.bb.accepted.connect(self.accept) self.bb.rejected.connect(self.reject) l.addWidget(self.bb, 3, 0, 1, ncols) self.resize(self.sizeHint() + QSize(120, 0)) def choose_dir(self): d = choose_dir(self, 'library moved choose new loc', _('New library location'), default_dir=self.oldloc) if d is not None: self.loc.setText(d) def forget_library(self): self.stats.remove(self.location) def accept(self): newloc = unicode_type(self.loc.text()) if not db_class().exists_at(newloc): error_dialog(self, _('No library found'), _('No existing calibre library found at %s') % newloc, show=True) return self.stats.rename(self.location, newloc) self.newloc = newloc QDialog.accept(self)
class ConfigWidget(QWidget): def __init__(self): QWidget.__init__(self) self.l = QVBoxLayout() self.setLayout(self.l) self.label = QLabel("Voice") self.l.addWidget(self.label) self.voiceOptions = QComboBox() self.l.addWidget(self.voiceOptions) self.rateLabel = QLabel("Rate (-10 to 10)") self.l.addWidget(self.rateLabel) self.rateEdit = QLineEdit() self.rateEdit.setValidator(QIntValidator(-10, 10)) self.rateEdit.setText(str(prefs['rate'])) self.l.addWidget(self.rateEdit) self.volumeLabel = QLabel("Volume (0 to 100)") self.volumeEdit = QLineEdit() self.volumeEdit.setValidator(QIntValidator(0, 100)) self.volumeEdit.setText(str(prefs['volume'])) self.l.addWidget(self.volumeLabel) self.l.addWidget(self.volumeEdit) import win32com.client self.spVoice = win32com.client.Dispatch("SAPI.SpVoice") voices = self.spVoice.GetVoices("", "") for i in range(voices.Count): self.voiceOptions.addItem(voices.Item(i).GetDescription()) if voices.Item(i).GetDescription() == prefs['voice']: self.voiceOptions.setCurrentIndex(i) self.pauseHotKey = HotkeyWidget(prefs, "pause", "Enable Pause/Play hotkey") self.l.addWidget(self.pauseHotKey) self.stopHotKey = HotkeyWidget(prefs, "stop", "Enable Stop hotkey") self.l.addWidget(self.stopHotKey) self.selectHotKey = HotkeyWidget(prefs, "select", "Enable Select Mode hotkey") self.l.addWidget(self.selectHotKey) def save_settings(self): from calibre_plugins.tts_ebook_viewer.hotkeys import keycodes prefs['voice'] = unicode(self.voiceOptions.currentText()) prefs['rate'] = int(self.rateEdit.text()) prefs['volume'] = int(self.volumeEdit.text()) self.pauseHotKey.save_settings(prefs) self.stopHotKey.save_settings(prefs) self.selectHotKey.save_settings(prefs)
class MovedDialog(QDialog): # {{{ def __init__(self, stats, location, parent=None): QDialog.__init__(self, parent) self.setWindowTitle(_("No library found")) self._l = l = QGridLayout(self) self.setLayout(l) self.stats, self.location = stats, location loc = self.oldloc = location.replace("/", os.sep) self.header = QLabel( _( "No existing calibre library was found at %s. " "If the library was moved, select its new location below. " "Otherwise calibre will forget this library." ) % loc ) self.header.setWordWrap(True) ncols = 2 l.addWidget(self.header, 0, 0, 1, ncols) self.cl = QLabel("<br><b>" + _("New location of this library:")) l.addWidget(self.cl, 1, 0, 1, ncols) self.loc = QLineEdit(loc, self) l.addWidget(self.loc, 2, 0, 1, 1) self.cd = QToolButton(self) self.cd.setIcon(QIcon(I("document_open.png"))) self.cd.clicked.connect(self.choose_dir) l.addWidget(self.cd, 2, 1, 1, 1) self.bb = QDialogButtonBox(QDialogButtonBox.Abort) b = self.bb.addButton(_("Library moved"), self.bb.AcceptRole) b.setIcon(QIcon(I("ok.png"))) b = self.bb.addButton(_("Forget library"), self.bb.RejectRole) b.setIcon(QIcon(I("edit-clear.png"))) b.clicked.connect(self.forget_library) self.bb.accepted.connect(self.accept) self.bb.rejected.connect(self.reject) l.addWidget(self.bb, 3, 0, 1, ncols) self.resize(self.sizeHint() + QSize(100, 50)) def choose_dir(self): d = choose_dir(self, "library moved choose new loc", _("New library location"), default_dir=self.oldloc) if d is not None: self.loc.setText(d) def forget_library(self): self.stats.remove(self.location) def accept(self): newloc = unicode(self.loc.text()) if not db_class().exists_at(newloc): error_dialog(self, _("No library found"), _("No existing calibre library found at %s") % newloc, show=True) return self.stats.rename(self.location, newloc) self.newloc = newloc QDialog.accept(self)
class ConfigWidget(QWidget): def __init__(self): QWidget.__init__(self) self.layout = QGridLayout() self.layout.setSpacing(10) self.ip_label = QLabel("Remarkable IP:") self.layout.addWidget(self.ip_label, 1, 0) self.ip_label_msg = QLineEdit(self) self.ip_label_msg.setText(prefs["ip"]) self.layout.addWidget(self.ip_label_msg, 1, 1) self.ip_label.setBuddy(self.ip_label_msg) self.books_path_label = QLabel("Books Path:") self.layout.addWidget(self.books_path_label, 2, 0) self.books_path_label_msg = QLineEdit(self) self.books_path_label_msg.setText(prefs["books_path"]) self.layout.addWidget(self.books_path_label_msg, 2, 1) self.books_path_label.setBuddy(self.books_path_label_msg) self.metadata_path_label = QLabel("Config Path:") self.layout.addWidget(self.metadata_path_label, 3, 0) self.metadata_path_label_msg = QLineEdit(self) self.metadata_path_label_msg.setText(prefs["metadata_path"]) self.layout.addWidget(self.metadata_path_label_msg, 3, 1) self.metadata_path_label.setBuddy(self.metadata_path_label_msg) self.password_label = QLabel("Password ( If not using Key ):") self.layout.addWidget(self.password_label, 4, 0) self.password_label_msg = QLineEdit(self) self.password_label_msg.setText(prefs["password"]) self.layout.addWidget(self.password_label_msg, 4, 1) self.password_label.setBuddy(self.password_label_msg) self.storage_label = QLabel("Storage ( like '/dev/mmcblk1p7' ):") self.layout.addWidget(self.storage_label, 5, 0) self.storage_label_msg = QLineEdit(self) self.storage_label_msg.setText(prefs["storage"]) self.layout.addWidget(self.storage_label_msg, 5, 1) self.storage_label.setBuddy(self.storage_label_msg) self.setLayout(self.layout) self.setGeometry(150, 150, 150, 150) self.setWindowTitle("Remarkable Config") def save_settings(self): prefs["ip"] = self.ip_label_msg.text() # append an extra '/' in case it was forgotten prefs["books_path"] = self.books_path_label_msg.text() + "/" prefs["metadata_path"] = self.metadata_path_label_msg.text() + "/" prefs["password"] = self.password_label_msg.text() prefs["storage"] = self.storage_label_msg.text()
class editpopup(QWidget): user_id = "" session = Session() def __init__(self): super().__init__() self.title = "Edit Pop Up" self.width = 500 self.height = 500 self.top = 100 self.left = 100 self.initUI() def initUI(self): self.username = QLineEdit(self) self.username.move(200, 200) self.password = QLineEdit(self) self.password.move(200, 240) self.password.setEchoMode(QLineEdit.Password) self.role = QComboBox(self) self.role.addItem("Student") self.role.addItem("Librarian") self.role.addItem("Admin") self.role.move(200, 280) self.savechanges = QPushButton("Save changes", self) self.savechanges.move(250, 350) self.savechanges.clicked.connect(self.updateValues) self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, self.width, self.height) def setValues(self, user_id, user_name, user_password, user_role): self.user_id = user_id self.username.setText(user_name) self.password.setText(user_password) self.role.setCurrentText(user_role) def updateValues(self): result = self.session.query(User).filter( User.id == self.user_id).first() result.username = self.username.text() result.password = self.password.text() result.role = self.role.currentText() try: self.session.add(result) self.session.commit() self.close() except: print("Error occured while saving")
class PlayerInfoField(QWidget): #Widget for inputting player info. names = ['Alex', 'Clifford', 'Tyrone', 'Ava', 'Ralph', 'Emily', 'Falcon', 'Giselle', 'Jaeger', 'Sally', 'Quentin', 'Lara'] def __init__(self, parent, index): super(PlayerInfoField, self).__init__(parent) self.index = index self.layout = QHBoxLayout() self.auto_button = Button(self, 'Auto') self.auto_button.setFixedWidth(60) self.auto_button.clicked.connect(self.generate_name) self.layout.addWidget(self.auto_button) self.name_field = QLineEdit() self.name_field.setPalette(QPalette(Qt.white)) self.name_field.setPlaceholderText('Name') self.name_field.setClearButtonEnabled(True) self.name_field.setFixedWidth(250) self.layout.addWidget(self.name_field) self.AItoggle = QCheckBox() self.AItoggle.setText('Computer') self.AItoggle.setFixedWidth(100) self.layout.addWidget(self.AItoggle) self.AItoggle.stateChanged.connect(self.AIToggled) self.AIdifficulty = QComboBox() self.AIdifficulty.setPalette(QPalette(Qt.white)) self.AIdifficulty.setFixedWidth(100) self.AIdifficulty.addItems(['Braindead', 'Easy', 'Normal', 'Hard', 'HAL-9000']) self.AIdifficulty.setCurrentIndex(2) self.AIdifficulty.setDisabled(True) self.layout.addWidget(self.AIdifficulty) self.spacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.layout.addItem(self.spacer) self.layout.setContentsMargins(0, 0, 0, 0) self.setLayout(self.layout) def generate_name(self): self.name_field.setText(PlayerInfoField.names[self.index]) def AIToggled(self): if self.AItoggle.checkState() == Qt.Checked: self.AIdifficulty.setEnabled(True) else: self.AIdifficulty.setDisabled(True)
class AdvancedGroupBox(DeviceOptionsGroupBox): def __init__(self, parent, device): super(AdvancedGroupBox, self).__init__(parent, device, _("Advanced options")) # self.setTitle(_("Advanced Options")) self.options_layout = QGridLayout() self.options_layout.setObjectName("options_layout") self.setLayout(self.options_layout) self.support_newer_firmware_checkbox = create_checkbox( _("Attempt to support newer firmware"), _('Kobo routinely updates the firmware and the ' 'database version. With this option calibre will attempt ' 'to perform full read-write functionality - Here be Dragons!! ' 'Enable only if you are comfortable with restoring your kobo ' 'to factory defaults and testing software. ' 'This driver supports firmware V2.x.x and DBVersion up to ') + unicode(device.supported_dbversion), device.get_pref('support_newer_firmware')) self.debugging_title_checkbox = create_checkbox( _("Title to test when debugging"), _('Part of title of a book that can be used when doing some tests for debugging. ' 'The test is to see if the string is contained in the title of a book. ' 'The better the match, the less extraneous output.'), device.get_pref('debugging_title')) self.debugging_title_label = QLabel(_('Title to test when debugging:')) self.debugging_title_edit = QLineEdit(self) self.debugging_title_edit.setToolTip( _('Part of title of a book that can be used when doing some tests for debugging. ' 'The test is to see if the string is contained in the title of a book. ' 'The better the match, the less extraneous output.')) self.debugging_title_edit.setText(device.get_pref('debugging_title')) self.debugging_title_label.setBuddy(self.debugging_title_edit) self.options_layout.addWidget(self.support_newer_firmware_checkbox, 0, 0, 1, 2) self.options_layout.addWidget(self.debugging_title_label, 1, 0, 1, 1) self.options_layout.addWidget(self.debugging_title_edit, 1, 1, 1, 1) self.options_layout.setRowStretch(2, 2) @property def support_newer_firmware(self): return self.support_newer_firmware_checkbox.isChecked() @property def debugging_title(self): return self.debugging_title_edit.text().strip()
class AdvancedGroupBox(DeviceOptionsGroupBox): def __init__(self, parent, device): super(AdvancedGroupBox, self).__init__(parent, device, _("Advanced Options")) # self.setTitle(_("Advanced Options")) self.options_layout = QGridLayout() self.options_layout.setObjectName("options_layout") self.setLayout(self.options_layout) self.support_newer_firmware_checkbox = create_checkbox( _("Attempt to support newer firmware"), _('Kobo routinely updates the firmware and the ' 'database version. With this option Calibre will attempt ' 'to perform full read-write functionality - Here be Dragons!! ' 'Enable only if you are comfortable with restoring your kobo ' 'to factory defaults and testing software. ' 'This driver supports firmware V2.x.x and DBVersion up to ') + unicode( device.supported_dbversion), device.get_pref('support_newer_firmware') ) self.debugging_title_checkbox = create_checkbox( _("Title to test when debugging"), _('Part of title of a book that can be used when doing some tests for debugging. ' 'The test is to see if the string is contained in the title of a book. ' 'The better the match, the less extraneous output.'), device.get_pref('debugging_title') ) self.debugging_title_label = QLabel(_('Title to test when debugging:')) self.debugging_title_edit = QLineEdit(self) self.debugging_title_edit.setToolTip(_('Part of title of a book that can be used when doing some tests for debugging. ' 'The test is to see if the string is contained in the title of a book. ' 'The better the match, the less extraneous output.')) self.debugging_title_edit.setText(device.get_pref('debugging_title')) self.debugging_title_label.setBuddy(self.debugging_title_edit) self.options_layout.addWidget(self.support_newer_firmware_checkbox, 0, 0, 1, 2) self.options_layout.addWidget(self.debugging_title_label, 1, 0, 1, 1) self.options_layout.addWidget(self.debugging_title_edit, 1, 1, 1, 1) self.options_layout.setRowStretch(2, 2) @property def support_newer_firmware(self): return self.support_newer_firmware_checkbox.isChecked() @property def debugging_title(self): return self.debugging_title_edit.text().strip()
class ConfigWidget(QWidget): def __init__(self): QWidget.__init__(self) self.l = QHBoxLayout() self.setLayout(self.l) self.label = QLabel('Hello world &message:') self.l.addWidget(self.label) self.msg = QLineEdit(self) self.msg.setText(prefs['hello_world_msg']) self.l.addWidget(self.msg) self.label.setBuddy(self.msg) def save_settings(self): prefs['hello_world_msg'] = self.msg.text()
class ConfigWidget(QWidget): def __init__(self): QWidget.__init__(self) self.l = QHBoxLayout() self.setLayout(self.l) self.label = QLabel('Enter the keyword') self.l.addWidget(self.label) self.msg = QLineEdit(self) self.msg.setText("") self.l.addWidget(self.msg) self.label.setBuddy(self.msg) def save_settings(self): prefs['hello_world_msg'] = unicode(self.msg.text())
def select_channel(self, text): # 先初始化数据 self.linedit_list.clear() self.channel.clear() # 排序包体参数,防止参数写入乱排序 self.channel['name'] = '' self.channel['sdk'] = text self.channel['channelId'] = '' self.channel['gameName'] = '' self.channel['package'] = '' self.channel['gameVersionCode'] = '' self.channel['gameVersionName'] = '' self.channel['debug'] = "false" # 获取渠道参数定义 if not Utils.get_channel_config(self.channel): return # 再添加当前选择的渠道参数模板,刷新界面(先清空之前渠道参数表单,再添加) for i in range(self.form_layout2.rowCount()): # 因为formlayout清除一行,会自动上移,所以只需remove第一行 self.form_layout2.removeRow(0) channel_name = QLabel(self.channel['name'] + '\t\t\tVersion:' + self.channel['sdkVersionName'] + '\t\tUpdate:' + self.channel['sdkUpdateTime']) channel_name.setAlignment(Qt.AlignRight) self.form_layout2.addRow(channel_name) if self.default_channel is not None and text == self.default_channel[ 'sdk']: self.channel_id_value.setText(self.default_channel['channelId']) self.game_name_value.setText(self.default_channel['gameName']) self.game_package_value.setText(self.default_channel['package']) self.game_vcode_value.setText( self.default_channel['gameVersionCode']) self.game_vname_value.setText( self.default_channel['gameVersionName']) for param in self.default_channel['sdkParams']: line_edit = QLineEdit() line_edit.setText(param['value']) self.form_layout2.addRow(param['showName'] + ':', line_edit) self.linedit_list.append(line_edit) else: for param in self.channel['sdkParams']: line_edit = QLineEdit() line_edit.setPlaceholderText("渠道参数必填") self.form_layout2.addRow(param['showName'] + ':', line_edit) self.linedit_list.append(line_edit)
class GenerateLabelsDialog(QDialog): #Generate labels dialog class def __init__(self, p=None): QDialog.__init__(self, p) self.setWindowTitle("Generate page layout...") self.layout = QFormLayout() self.numberOfLabelsSpinBox = QSpinBox() self.numberOfLabelsSpinBox.setRange(1, 200) self.numberOfLabelsSpinBox.setValue( generate_settings['defaultNumberOfLabels']) self.layout.addRow("Number of labels", self.numberOfLabelsSpinBox) self.firstLabelArticleNumberSB = QSpinBox() self.firstLabelArticleNumberSB.setRange(0, 9999) self.firstLabelArticleNumberSB.setValue( generate_settings['firstLabelArticleNumber']) self.layout.addRow("First label article number", self.firstLabelArticleNumberSB) self.weekNrYearLE = QLineEdit() self.weekNrYearLE.setText(generate_settings['weekNrYear']) self.layout.addRow("Week nr/ year", self.weekNrYearLE) self.lotNameLE = QLineEdit() self.lotNameLE.setText(generate_settings['lotName']) self.layout.addRow("Lot", self.lotNameLE) self.generatePB = QPushButton("Generate") self.generatePB.clicked.connect(self.onGenerate) self.layout.addRow("", self.generatePB) self.cancelPB = QPushButton("Cancel") self.cancelPB.clicked.connect(self.close) self.layout.addRow("", self.cancelPB) self.setLayout(self.layout) def onGenerate(self): generate_settings[ 'defaultNumberOfLabels'] = self.numberOfLabelsSpinBox.value() generate_settings[ 'firstLabelArticleNumber'] = self.firstLabelArticleNumberSB.value( ) generate_settings['weekNrYear'] = self.weekNrYearLE.text() generate_settings['lotName'] = self.lotNameLE.text() self.accept()
class ChooseName(Dialog): # {{{ ''' Chooses the filename for a newly imported file, with error checking ''' def __init__(self, candidate, parent=None): self.candidate = candidate self.filename = None Dialog.__init__(self, _('Choose file name'), 'choose-file-name', parent=parent) def setup_ui(self): self.l = l = QFormLayout(self) self.setLayout(l) self.err_label = QLabel('') self.name_edit = QLineEdit(self) self.name_edit.textChanged.connect(self.verify) self.name_edit.setText(self.candidate) pos = self.candidate.rfind('.') if pos > -1: self.name_edit.setSelection(0, pos) l.addRow(_('File &name:'), self.name_edit) l.addRow(self.err_label) l.addRow(self.bb) def show_error(self, msg): self.err_label.setText('<p style="color:red">' + msg) return False def verify(self): return name_is_ok(unicode_type(self.name_edit.text()), self.show_error) def accept(self): if not self.verify(): return error_dialog( self, _('No name specified'), _('You must specify a file name for the new file, with an extension.' ), show=True) n = unicode_type(self.name_edit.text()).replace('\\', '/') name, ext = n.rpartition('.')[0::2] self.filename = name + '.' + ext.lower() super(ChooseName, self).accept()
class ChooseName(Dialog): # {{{ """ Chooses the filename for a newly imported file, with error checking """ def __init__(self, candidate, parent=None): self.candidate = candidate self.filename = None Dialog.__init__(self, _("Choose file name"), "choose-file-name", parent=parent) def setup_ui(self): self.l = l = QFormLayout(self) self.setLayout(l) self.err_label = QLabel("") self.name_edit = QLineEdit(self) self.name_edit.textChanged.connect(self.verify) self.name_edit.setText(self.candidate) pos = self.candidate.rfind(".") if pos > -1: self.name_edit.setSelection(0, pos) l.addRow(_("File &name:"), self.name_edit) l.addRow(self.err_label) l.addRow(self.bb) def show_error(self, msg): self.err_label.setText('<p style="color:red">' + msg) return False def verify(self): return name_is_ok(unicode(self.name_edit.text()), self.show_error) def accept(self): if not self.verify(): return error_dialog( self, _("No name specified"), _("You must specify a file name for the new file, with an extension."), show=True, ) n = unicode(self.name_edit.text()).replace("\\", "/") name, ext = n.rpartition(".")[0::2] self.filename = name + "." + ext.lower() super(ChooseName, self).accept()
class ConfigWidget(QWidget): """ GUI for configuration of default user input values. """ def __init__(self): """ Initialize the GUI elements for the config dialog. """ QWidget.__init__(self) 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) def save_settings(self): """ Save user input to the default configuration. """ prefs["comic_vine_api_key"] = self.api_msg.text() prefs["tags_to_add"] = self.tags_msg.text()
class App(QWidget): def __init__(self): super().__init__() self.title = '随机数' self.left = 300 self.top = 300 self.width = 320 self.height = 200 self.initUI() def initUI(self): self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, self.width, self.height) # create textbox self.textbox = QLineEdit(self) self.textbox.move(20, 20) self.textbox.resize(280, 40) # Create a button in the window self.button = QPushButton('确定', self) self.button.move(20, 80) # connect button to function on_click self.button.clicked.connect(self.on_click) self.show() @pyqtSlot() def on_click(self): textboxValue = self.textbox.text() number = textboxValue.split(' ') while True: number = list(set(number)) if len(number) <= 4: number.append(random.randrange(1,10)) elif len(list(set(number))) == 5: break QMessageBox.question(self, "Message", 'You typed:' + str(number), QMessageBox.Ok, QMessageBox.Ok) self.textbox.setText(str(number).replace('\'','').replace('[','').replace(']','').replace(' ',''))
class CountSelectorWidget(QWidget): def __init__(self, parent, minimum, maximum, initial=0): QWidget.__init__(self, parent) self._slider = QScrollBar(Qt.Horizontal, self) self._slider.setMinimum(minimum) self._slider.setMaximum(maximum) self._slider.setSingleStep(1) # self._slider.setTickInterval(1) self._linedit = QLineEdit(self) self._linedit.setValidator(QIntValidator(minimum, maximum, self)) self._linedit.setMinimumWidth(20) self._layout = QHBoxLayout(self) self._layout.setContentsMargins(0, 0, 0, 0) self._layout.addWidget(self._slider, 4) self._layout.addWidget(self._linedit, 1) self._slider.valueChanged.connect(self._update_value) self._linedit.textChanged.connect(self._update_value) self._update_value(initial) valueChanged = pyqtSignal(int) def _update_value(self, value): value = int(value) if value else 0 self.value = value self.valueChanged.emit(value) @property def value(self): return self._slider.value() @value.setter def value(self, value): if self._slider.value() != value: self._slider.setValue(value) self._linedit.setText(str(value))
class editpopup(QWidget): book_id="" session=Session() def __init__(self): super().__init__() self.title="Edit Pop Up" self.width = 500 self.height = 500 self.top = 100 self.left = 100 self.initUI() def initUI(self): self.bookname=QLineEdit(self) self.bookname.move(200,200) self.author=QLineEdit(self) self.author.move(200,240) self.savechanges=QPushButton("Save changes",self) self.savechanges.move(250,300) self.savechanges.clicked.connect(self.updateValues) self.setWindowTitle(self.title) self.setGeometry(self.left,self.top,self.width,self.height) def setValues(self,book_id,book_name,book_author): self.book_id=book_id self.bookname.setText(book_name) self.author.setText(book_author) def updateValues(self): result=self.session.query(Book).filter(Book.id==self.book_id).first() result.name=self.bookname.text() result.author=self.author.text() try: self.session.add(result) self.session.commit() self.close() except: print("Error occured while saving")
class App(QWidget): def __init__(self): super().__init__() self.title = 'PyQt5 textbox' self.left = 20 self.top = 20 self.width = 640 self.height = 400 self.initUI() def initUI(self): self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, self.width, self.height) # create textbox self.textbox = QLineEdit(self) self.textbox.move(40, 40) self.textbox.resize(560, 80) # Create a button in the window self.button = QPushButton('get triple', self) self.button.move(500, 160) # connect button to function on_click self.button.clicked.connect(self.on_click) self.show() @pyqtSlot() def on_click(self): textboxValue = self.textbox.text() pre_relation = svm_pre(textboxValue, utils_path='../baseline_svm/utils') # QMessageBox.question(self, "Message", 'Relation: ' + pre_relation[0], # QMessageBox.Ok, QMessageBox.Ok) QMessageBox.about(self, 'Triple', pre_relation[0]) """打印完毕之后清空文本框""" self.textbox.setText('')
class App(QWidget): def __init__(self): super().__init__() self.title = 'PyQt5 textbox' self.left = 10 self.top = 10 self.width = 320 self.height = 200 self.initUI() def initUI(self): self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, self.width, self.height) # create textbox self.textbox = QLineEdit(self) self.textbox.move(20, 20) self.textbox.resize(280, 40) # Create a button in the window self.button = QPushButton('show text', self) self.button.move(20, 80) # connect button to function on_click self.button.clicked.connect(self.on_click) self.show() @pyqtSlot() def on_click(self): with open('/Users/sheldonwong/workspace/github/algo/tool/1.txt', 'r') as f: lines = f.readlines() lines_str = ''.join(lines) self.textbox.setText(lines_str) #textboxValue = self.textbox.text() #QMessageBox.question(self, "Message", 'You typed:' + textboxValue, # QMessageBox.Ok, QMessageBox.Ok) """打印完毕之后清空文本框"""
class PluginWidget(QWidget): TITLE = _('HTML Virtual Shelf options') HELP = _('Options specific to Virtual Shelf plugin') sync_enabled = False formats = set(['html']) def __init__(self, parent=None): QWidget.__init__(self, parent) self.layout = QHBoxLayout(self) label = QLabel(_("Output directory")) self.pathEdit = QLineEdit() self.pathEdit.setText("/home/carles/virtual-shelf") self.layout.addWidget(label) self.layout.addWidget(self.pathEdit) def initialize(self, catalog_name): print "def Virtual Shelf initialize" def options(self): return { 'output_directory': self.pathEdit.text() }
def _create_widget(self, value): if isinstance(value, str): widget = QLineEdit() widget.setText(value) widget.setPlaceholderText('String Val') elif isinstance(value, list): return [self._create_widget(v) for v in value] else: if isinstance(value, int): widget = QSpinBox() else: widget = QDoubleSpinBox() decimal = str(value)[::-1].find('.') widget.setDecimals(decimal) widget.setSingleStep(pow(10, -decimal)) widget.setMinimum(-9999999) widget.setMaximum(9999999) widget.setValue(value) widget.setFixedWidth(100) widget.setAlignment(Qt.AlignRight) return widget
class ConfigWidget(QWidget): def __init__(self): QWidget.__init__(self) self.l = QVBoxLayout() self.setLayout(self.l) self.label = QLabel('''\ Specify path for annotation database. If left blank, defaults to %s ''' % prefs.defaults['annotator_db_path']) self.l.addWidget(self.label) self.annotator_db_path = QLineEdit(self) self.annotator_db_path.setText(prefs['annotator_db_path']) self.l.addWidget(self.annotator_db_path) self.label.setBuddy(self.annotator_db_path) def save_settings(self): # TODO add more intelligence here given_path = unicode(self.annotator_db_path.text()).strip() if os.path.exists(given_path): prefs['annotator_db_path'] = given_path else: prefs['annotator_db_path'] = prefs.defaults['annotator_db_path']
class ConfigWidget(QWidget): def __init__(self): QWidget.__init__(self) self.l = QVBoxLayout() self.setLayout(self.l) self.labelText = QLabel('Set the paths to the different directories.\nDo not use "/" at the end of the path.\nDefaults are:\nPath to Calibre Library directory: $HOME/Calibre Library\nPath to Calibre Cofig directory: $HOME/.config/calibre\nPath to recoll bin directory: /usr/bin') self.l.addWidget(self.labelText) self.labelLibrary = QLabel('Path to Calibre Library directory:') self.l.addWidget(self.labelLibrary) self.pathToLibrarySetting = QLineEdit(self) self.pathToLibrarySetting.setText(prefs['pathToLibrary']) self.l.addWidget(self.pathToLibrarySetting) self.labelLibrary.setBuddy(self.pathToLibrarySetting) self.labelCofig = QLabel('Path to Calibre Cofig directory:') self.l.addWidget(self.labelCofig) self.pathToCofigSetting = QLineEdit(self) self.pathToCofigSetting.setText(prefs['pathToCofig']) self.l.addWidget(self.pathToCofigSetting) self.labelCofig.setBuddy(self.pathToCofigSetting) self.labelRecoll = QLabel('Path to recoll bin directory:') self.l.addWidget(self.labelRecoll) self.pathToRecollSetting = QLineEdit(self) self.pathToRecollSetting.setText(prefs['pathToRecoll']) self.l.addWidget(self.pathToRecollSetting) self.labelRecoll.setBuddy(self.pathToRecollSetting) def save_settings(self): prefs['pathToLibrary'] = unicode(self.pathToLibrarySetting.text()) prefs['pathToCofig'] = unicode(self.pathToCofigSetting.text()) prefs['pathToRecoll'] = unicode(self.pathToRecollSetting.text())
class ConfigWidget(QWidget): # GUI definition def __init__(self): QWidget.__init__(self) self.l = QVBoxLayout() self.setLayout(self.l) self.ll = QHBoxLayout() self.l.addLayout(self.ll) self.label_exe = QLabel(_('&Prince executable:')) self.ll.addWidget(self.label_exe) self.exe = QLineEdit(self) self.exe.setText(prefs['prince_exe']) self.exe.setToolTip(_('<qt>Executable for the Prince program (command-line interface)</qt>')) self.ll.addWidget(self.exe) self.label_exe.setBuddy(self.exe) self.browse = QPushButton(_('&Browse') + '...', self) self.browse.setToolTip(_('<qt>Search the Prince executable in your computer</qt>')) self.browse.clicked.connect(self.select_exe) self.ll.addWidget(self.browse) self.lll = QHBoxLayout() self.l.addLayout(self.lll) self.label_fmts = QLabel(_('Preferred &formats:')) self.lll.addWidget(self.label_fmts) self.fmts = QLineEdit(self) self.fmts.setText(','.join(prefs['formats'])) self.fmts.setToolTip(_('<qt>Comma-separated list of preferred formats to use as source, the first that matches will be used</qt>')) self.lll.addWidget(self.fmts) self.label_fmts.setBuddy(self.fmts) self.add_book = QCheckBox(_('&Add PDF to the book record')) self.add_book.setToolTip(_('<qt>Add the converted PDF to the selected book record</qt>')) self.add_book.setChecked(prefs['add_book']) self.l.addWidget(self.add_book) self.show_css = QCheckBox(_('&Show CSS in the Convert dialog')) self.show_css.setToolTip(_('<qt>Show by default the stylesheets in the Convert dialog</qt>')) self.show_css.setChecked(prefs['show_CSS']) self.l.addWidget(self.show_css) self.css_layout = QVBoxLayout() self.llll = QHBoxLayout() self.css_layout.addLayout(self.llll) self.css_list = QComboBox() self.css_list.setToolTip(_('<qt>List of custom stylesheets defined. Select one to edit</qt>')) self.css_list.setSizeAdjustPolicy(QComboBox.AdjustToContents) self.CSS_list = prefs['custom_CSS_list'].copy() self.default_CSS = prefs['default_CSS'] if 'custom_CSS' in prefs: self.CSS_list[_('old')] = prefs['custom_CSS'] self.default_CSS = _('old') if self.default_CSS not in self.CSS_list: self.default_CSS = sorted(self.CSS_list, key=lambda x: x.lower())[0] for key in sorted(self.CSS_list, key=lambda x: x.lower()): self.css_list.addItem(key, key) self.css_list.setCurrentIndex(self.css_list.findText(self.default_CSS)) self.css_list.currentIndexChanged.connect(self.set_css) self.llll.addWidget(self.css_list) self.css_rename = QPushButton(_('Re&name')) self.css_rename.setToolTip(_('<qt>Rename the current stylesheet to the name on the right</qt>')) self.css_rename.clicked.connect(self.rename_css) self.css_rename.setEnabled(False) self.llll.addWidget(self.css_rename) self.css_name = QLineEdit(self) self.css_name.setToolTip(_('<qt>Name for the new or renamed stylesheet</qt>')) self.css_name.setText(self.css_list.currentText()) self.css_name.textChanged.connect(self.check_names) self.llll.addWidget(self.css_name) self.css_add = QPushButton(_('A&dd')) self.css_add.setToolTip(_('<qt>Add a new empty stylesheet with the name on the left</qt>')) self.css_add.clicked.connect(self.add_css) self.css_add.setEnabled(False) self.llll.addWidget(self.css_add) self.css_remove = QPushButton(_('Re&move')) self.css_remove.setToolTip(_('<qt>Remove the current stylesheet</qt>')) self.css_remove.clicked.connect(self.remove_css) self.llll.addWidget(self.css_remove) self.css = TextEditWithTooltip() self.css.setLineWrapMode(TextEditWithTooltip.NoWrap) self.css.load_text(self.CSS_list[unicode(self.css_list.currentText())],'css') self.css.setToolTip(_('<qt>Custom stylesheet that will be applied, if selected, to all Prince PDF conversions</qt>')) self.css_layout.addWidget(self.css) self.css_templates = QLabel(_('Book metadata can be used in the stylesheet. Anything between %(s1)s and %(s2)s will be processed as a calibre template. For instance, %(s3)s in the stylesheet will be replaced with the book title in the conversion.') % \ {'s1':'<span style="font-family:monospace ; font-weight:bold">@{@</span>', \ 's2':'<span style="font-family:monospace ; font-weight:bold">@}@</span>', \ 's3':'<span style="font-family:monospace ; font-weight:bold">@{@{title}@}@</span>'}) self.css_templates.setWordWrap(True) self.css_layout.addWidget(self.css_templates) self.css_box = QGroupBox(_('&Custom CSS:')) self.css_box.setLayout(self.css_layout) self.l.addWidget(self.css_box) self.lllll = QHBoxLayout() self.lllll.setAlignment(Qt.AlignLeft) self.l.addLayout(self.lllll) self.defaults = QPushButton(_('&Restore defaults')) self.defaults.setToolTip(_('<qt>Restore the default settings</qt>')) self.defaults.clicked.connect(self.restore_defaults) self.lllll.addWidget(self.defaults, alignment=Qt.AlignLeft) self.warning = QLabel(_('<b>Warning</b>: Deletes modified stylesheets')) self.lllll.addWidget(self.warning) self.adjustSize() def select_exe(self): ''' Create a dialog to select the Prince executable ''' dialog = QFileDialog() dialog.setFileMode(QFileDialog.ExistingFile) filename = dialog.getOpenFileName(self, _('Select Prince executable'), '', '') if filename: try: self.exe.setText(filename) except(TypeError): self.exe.setText(filename[0]) def restore_defaults(self): ''' Restore the default settings ''' self.exe.setText(prefs.defaults['prince_exe']) self.fmts.setText(','.join(prefs.defaults['formats']).lower()) self.show_css.setChecked(prefs.defaults['show_CSS']) self.add_book.setChecked(prefs.defaults['add_book']) self.css_list.currentIndexChanged.disconnect() self.css_list.clear() self.CSS_list = prefs.defaults['custom_CSS_list'].copy() self.default_CSS = prefs.defaults['default_CSS'] for key in sorted(self.CSS_list, key=lambda x: x.lower()): self.css_list.addItem(key, key) self.css_list.setCurrentIndex(self.css_list.findText(self.default_CSS)) self.css_name.setText(self.default_CSS) self.css.load_text(self.CSS_list[unicode(self.css_list.currentText())],'css') self.css_list.currentIndexChanged.connect(self.set_css) def save_settings(self): ''' Save the current settings ''' prefs['prince_exe'] = unicode(self.exe.text()) prefs['formats'] = unicode(self.fmts.text().lower()).split(',') prefs['show_CSS'] = self.show_css.isChecked() prefs['add_book'] = self.add_book.isChecked() self.set_css() prefs['default_CSS'] = self.default_CSS prefs['custom_CSS_list'] = self.CSS_list.copy() if 'custom_CSS' in prefs: del prefs['custom_CSS'] def set_css(self): ''' Fill the CSS text box with the selected stylesheet ''' self.CSS_list[self.default_CSS] = unicode(self.css.toPlainText()) self.default_CSS = unicode(self.css_list.currentText()) self.css.load_text(self.CSS_list[self.default_CSS],'css') self.css_name.setText(self.css_list.currentText()) def add_css(self): ''' Add a new stylesheet ''' from calibre.gui2 import error_dialog name = unicode(self.css_name.text()) if name in self.CSS_list: error_dialog(self, _('Cannot add stylesheet'), _('A stylesheet with the name "%s" is already defined, use a different name.') % name, show=True) else: self.CSS_list[name] = '' self.css_list.addItem(name, name) self.css_list.setCurrentIndex(self.css_list.findText(name)) self.css_add.setEnabled(False) self.css_rename.setEnabled(False) def remove_css(self): ''' Remove an existing stylesheet ''' from calibre.gui2 import error_dialog if (self.css_list.count() > 1): self.css_list.currentIndexChanged.disconnect() self.css_list.removeItem(self.css_list.currentIndex()) del self.CSS_list[self.default_CSS] self.default_CSS = unicode(self.css_list.currentText()) self.css.load_text(self.CSS_list[self.default_CSS],'css') self.css_list.currentIndexChanged.connect(self.set_css) self.css_name.setText(self.css_list.currentText()) else: error_dialog(self, _('Cannot delete the last stylesheet'), _('The last stylesheet cannot be removed. You can rename it and/or remove its contents.'), show=True) def rename_css(self): ''' Rename a stylesheet ''' from calibre.gui2 import error_dialog name = unicode(self.css_name.text()) if name in self.CSS_list: error_dialog(self, _('Cannot rename stylesheet'), _('A stylesheet with the name "%s" is already defined, use a different name.') % name, show=True) else: self.CSS_list[name] = self.CSS_list.pop(self.default_CSS) self.css_list.setItemText(self.css_list.currentIndex(),name) self.default_CSS = name def check_names(self, text): name = unicode(text) if name in self.CSS_list: self.css_add.setEnabled(False) self.css_rename.setEnabled(False) else: self.css_add.setEnabled(True) self.css_rename.setEnabled(True)
class BasicTab(QWidget): def __init__(self, parent_dialog, plugin_action): self.parent_dialog = parent_dialog self.plugin_action = plugin_action QWidget.__init__(self) self.l = QVBoxLayout() self.setLayout(self.l) label = QLabel(_('These settings control the basic features of the plugin.')) label.setWordWrap(True) self.l.addWidget(label) self.l.addSpacing(5) no_toc_warning = _('''If both 'Insert Table of Contents entry' and 'Copy Table of Contents entries' are unchecked, there will be no Table of Contents in merged books.''') self.titlenavpoints = QCheckBox(_('Insert Table of Contents entry for each title?'),self) self.titlenavpoints.setToolTip(_('''If set, a new TOC entry will be made for each title and it's existing TOC nested underneath it.''')+'\n'+no_toc_warning) self.titlenavpoints.setChecked(prefs['titlenavpoints']) self.l.addWidget(self.titlenavpoints) self.originalnavpoints = QCheckBox(_('Copy Table of Contents entries from each title?'),self) self.originalnavpoints.setToolTip(_('''If set, the original TOC entries will be included the new epub.''')+'\n'+no_toc_warning) self.originalnavpoints.setChecked(prefs['originalnavpoints']) self.l.addWidget(self.originalnavpoints) def f(): if not self.originalnavpoints.isChecked() and not self.titlenavpoints.isChecked(): confirm("<br>"+no_toc_warning, # force HTML to get auto wrap. 'epubmerge_no_toc_warning_again', parent=self, show_cancel_button=False) self.originalnavpoints.stateChanged.connect(f) self.titlenavpoints.stateChanged.connect(f) self.flattentoc = QCheckBox(_('Flatten Table of Contents?'),self) self.flattentoc.setToolTip(_('Remove nesting and make TOC all on one level.')) self.flattentoc.setChecked(prefs['flattentoc']) self.l.addWidget(self.flattentoc) self.includecomments = QCheckBox(_("Include Books' Comments?"),self) self.includecomments.setToolTip(_('''Include all the merged books' comments in the new book's comments. Default is a list of included titles only.''')) self.includecomments.setChecked(prefs['includecomments']) self.l.addWidget(self.includecomments) self.keepmeta = QCheckBox(_('Keep UnMerge Metadata?'),self) self.keepmeta.setToolTip(_('''If set, a copy of the original metadata for each merged book will be included, allowing for UnMerge. This includes your calibre custom columns. Leave off if you plan to distribute the epub to others.''')) self.keepmeta.setChecked(prefs['keepmeta']) self.l.addWidget(self.keepmeta) # self.showunmerge = QCheckBox(_('Show UnMerge Option?'),self) # self.showunmerge.setToolTip(_('''If set, the UnMerge Epub option will be shown on the EpubMerge menu. # Only Epubs merged with 'Keep UnMerge Metadata' can be UnMerged.''')) # self.showunmerge.setChecked(prefs['showunmerge']) # self.l.addWidget(self.showunmerge) horz = QHBoxLayout() horz.addWidget(QLabel(_("Add tags to merged books:"))) self.mergetags = QLineEdit(self) self.mergetags.setText(prefs['mergetags']) self.mergetags.setToolTip(_('Tags you enter here will be added to all new merged books')) horz.addWidget(self.mergetags) self.l.addLayout(horz) horz = QHBoxLayout() horz.addWidget(QLabel(_("Merged Book Word:"))) self.mergeword = QLineEdit(self) self.mergeword.setText(prefs['mergeword']) self.mergeword.setToolTip(_('''Word use to describe merged books in default title and summary. For people who don't like the word Anthology.''')) ## Defaults back to Anthology if cleared. horz.addWidget(self.mergeword) self.l.addLayout(horz) self.l.addSpacing(15) label = QLabel(_("These controls aren't plugin settings as such, but convenience buttons for setting Keyboard shortcuts and getting all the EpubMerge confirmation dialogs back again.")) label.setWordWrap(True) self.l.addWidget(label) self.l.addSpacing(5) keyboard_shortcuts_button = QPushButton(_('Keyboard shortcuts...'), self) keyboard_shortcuts_button.setToolTip(_('Edit the keyboard shortcuts associated with this plugin')) keyboard_shortcuts_button.clicked.connect(parent_dialog.edit_shortcuts) self.l.addWidget(keyboard_shortcuts_button) reset_confirmation_button = QPushButton(_('Reset disabled &confirmation dialogs'), self) reset_confirmation_button.setToolTip(_('Reset all show me again dialogs for the EpubMerge plugin')) reset_confirmation_button.clicked.connect(self.reset_dialogs) self.l.addWidget(reset_confirmation_button) view_prefs_button = QPushButton(_('View library preferences...'), self) view_prefs_button.setToolTip(_('View data stored in the library database for this plugin')) view_prefs_button.clicked.connect(self.view_prefs) self.l.addWidget(view_prefs_button) self.l.insertStretch(-1) def view_prefs(self): d = PrefsViewerDialog(self.plugin_action.gui, PREFS_NAMESPACE) d.exec_() def reset_dialogs(self): for key in dynamic.keys(): if key.startswith('epubmerge_') and key.endswith('_again') \ and dynamic[key] is False: dynamic[key] = True info_dialog(self, _('Done'), _('Confirmation dialogs have all been reset'), show=True, show_copy_button=False)
class CheckLibraryDialog(QDialog): def __init__(self, parent, db): QDialog.__init__(self, parent) self.db = db self.setWindowTitle(_('Check Library -- Problems Found')) self.setWindowIcon(QIcon(I('debug.png'))) self._tl = QHBoxLayout() self.setLayout(self._tl) self.splitter = QSplitter(self) self.left = QWidget(self) self.splitter.addWidget(self.left) self.helpw = QTextEdit(self) self.splitter.addWidget(self.helpw) self._tl.addWidget(self.splitter) self._layout = QVBoxLayout() self.left.setLayout(self._layout) self.helpw.setReadOnly(True) self.helpw.setText(_('''\ <h1>Help</h1> <p>calibre stores the list of your books and their metadata in a database. The actual book files and covers are stored as normal files in the calibre library folder. The database contains a list of the files and covers belonging to each book entry. This tool checks that the actual files in the library folder on your computer match the information in the database.</p> <p>The result of each type of check is shown to the left. The various checks are: </p> <ul> <li><b>Invalid titles</b>: These are files and folders appearing in the library where books titles should, but that do not have the correct form to be a book title.</li> <li><b>Extra titles</b>: These are extra files in your calibre library that appear to be correctly-formed titles, but have no corresponding entries in the database</li> <li><b>Invalid authors</b>: These are files appearing in the library where only author folders should be.</li> <li><b>Extra authors</b>: These are folders in the calibre library that appear to be authors but that do not have entries in the database</li> <li><b>Missing book formats</b>: These are book formats that are in the database but have no corresponding format file in the book's folder. <li><b>Extra book formats</b>: These are book format files found in the book's folder but not in the database. <li><b>Unknown files in books</b>: These are extra files in the folder of each book that do not correspond to a known format or cover file.</li> <li><b>Missing cover files</b>: These represent books that are marked in the database as having covers but the actual cover files are missing.</li> <li><b>Cover files not in database</b>: These are books that have cover files but are marked as not having covers in the database.</li> <li><b>Folder raising exception</b>: These represent folders in the calibre library that could not be processed/understood by this tool.</li> </ul> <p>There are two kinds of automatic fixes possible: <i>Delete marked</i> and <i>Fix marked</i>.</p> <p><i>Delete marked</i> is used to remove extra files/folders/covers that have no entries in the database. Check the box next to the item you want to delete. Use with caution.</p> <p><i>Fix marked</i> is applicable only to covers and missing formats (the three lines marked 'fixable'). In the case of missing cover files, checking the fixable box and pushing this button will tell calibre that there is no cover for all of the books listed. Use this option if you are not going to restore the covers from a backup. In the case of extra cover files, checking the fixable box and pushing this button will tell calibre that the cover files it found are correct for all the books listed. Use this when you are not going to delete the file(s). In the case of missing formats, checking the fixable box and pushing this button will tell calibre that the formats are really gone. Use this if you are not going to restore the formats from a backup.</p> ''')) self.log = QTreeWidget(self) self.log.itemChanged.connect(self.item_changed) self.log.itemExpanded.connect(self.item_expanded_or_collapsed) self.log.itemCollapsed.connect(self.item_expanded_or_collapsed) self._layout.addWidget(self.log) self.check_button = QPushButton(_('&Run the check again')) self.check_button.setDefault(False) self.check_button.clicked.connect(self.run_the_check) self.copy_button = QPushButton(_('Copy &to clipboard')) self.copy_button.setDefault(False) self.copy_button.clicked.connect(self.copy_to_clipboard) self.ok_button = QPushButton(_('&Done')) self.ok_button.setDefault(True) self.ok_button.clicked.connect(self.accept) self.mark_delete_button = QPushButton(_('Mark &all for delete')) self.mark_delete_button.setToolTip(_('Mark all deletable subitems')) self.mark_delete_button.setDefault(False) self.mark_delete_button.clicked.connect(self.mark_for_delete) self.delete_button = QPushButton(_('Delete &marked')) self.delete_button.setToolTip(_('Delete marked files (checked subitems)')) self.delete_button.setDefault(False) self.delete_button.clicked.connect(self.delete_marked) self.mark_fix_button = QPushButton(_('Mar&k all for fix')) self.mark_fix_button.setToolTip(_('Mark all fixable items')) self.mark_fix_button.setDefault(False) self.mark_fix_button.clicked.connect(self.mark_for_fix) self.fix_button = QPushButton(_('&Fix marked')) self.fix_button.setDefault(False) self.fix_button.setEnabled(False) self.fix_button.setToolTip(_('Fix marked sections (checked fixable items)')) self.fix_button.clicked.connect(self.fix_items) self.bbox = QGridLayout() self.bbox.addWidget(self.check_button, 0, 0) self.bbox.addWidget(self.copy_button, 0, 1) self.bbox.addWidget(self.ok_button, 0, 2) self.bbox.addWidget(self.mark_delete_button, 1, 0) self.bbox.addWidget(self.delete_button, 1, 1) self.bbox.addWidget(self.mark_fix_button, 2, 0) self.bbox.addWidget(self.fix_button, 2, 1) h = QHBoxLayout() ln = QLabel(_('Names to ignore:')) h.addWidget(ln) self.name_ignores = QLineEdit() self.name_ignores.setText(db.prefs.get('check_library_ignore_names', '')) self.name_ignores.setToolTip( _('Enter comma-separated standard file name wildcards, such as synctoy*.dat')) ln.setBuddy(self.name_ignores) h.addWidget(self.name_ignores) le = QLabel(_('Extensions to ignore')) h.addWidget(le) self.ext_ignores = QLineEdit() self.ext_ignores.setText(db.prefs.get('check_library_ignore_extensions', '')) self.ext_ignores.setToolTip( _('Enter comma-separated extensions without a leading dot. Used only in book folders')) le.setBuddy(self.ext_ignores) h.addWidget(self.ext_ignores) self._layout.addLayout(h) self._layout.addLayout(self.bbox) self.resize(950, 500) def do_exec(self): self.run_the_check() probs = 0 for c in self.problem_count: probs += self.problem_count[c] if probs == 0: return False self.exec_() return True def accept(self): self.db.new_api.set_pref('check_library_ignore_extensions', unicode(self.ext_ignores.text())) self.db.new_api.set_pref('check_library_ignore_names', unicode(self.name_ignores.text())) QDialog.accept(self) def box_to_list(self, txt): return [f.strip() for f in txt.split(',') if f.strip()] def run_the_check(self): checker = CheckLibrary(self.db.library_path, self.db) checker.scan_library(self.box_to_list(unicode(self.name_ignores.text())), self.box_to_list(unicode(self.ext_ignores.text()))) plaintext = [] def builder(tree, checker, check): attr, h, checkable, fixable = check list = getattr(checker, attr, None) if list is None: self.problem_count[attr] = 0 return else: self.problem_count[attr] = len(list) tl = Item() tl.setText(0, h) if fixable and list: tl.setText(1, _('(fixable)')) tl.setFlags(Qt.ItemIsEnabled | Qt.ItemIsUserCheckable) tl.setCheckState(1, False) else: tl.setFlags(Qt.ItemIsEnabled) self.top_level_items[attr] = tl for problem in list: it = Item() if checkable: it.setFlags(Qt.ItemIsEnabled | Qt.ItemIsUserCheckable) it.setCheckState(1, False) else: it.setFlags(Qt.ItemIsEnabled) it.setText(0, problem[0]) it.setData(0, Qt.UserRole, problem[2]) it.setText(1, problem[1]) tl.addChild(it) self.all_items.append(it) plaintext.append(','.join([h, problem[0], problem[1]])) tree.addTopLevelItem(tl) t = self.log t.clear() t.setColumnCount(2) t.setHeaderLabels([_('Name'), _('Path from library')]) self.all_items = [] self.top_level_items = {} self.problem_count = {} for check in CHECKS: builder(t, checker, check) t.resizeColumnToContents(0) t.resizeColumnToContents(1) self.delete_button.setEnabled(False) self.fix_button.setEnabled(False) self.text_results = '\n'.join(plaintext) def item_expanded_or_collapsed(self, item): self.log.resizeColumnToContents(0) self.log.resizeColumnToContents(1) def item_changed(self, item, column): self.fix_button.setEnabled(False) for it in self.top_level_items.values(): if it.checkState(1): self.fix_button.setEnabled(True) self.delete_button.setEnabled(False) for it in self.all_items: if it.checkState(1): self.delete_button.setEnabled(True) return def mark_for_fix(self): for it in self.top_level_items.values(): if it.flags() & Qt.ItemIsUserCheckable: it.setCheckState(1, Qt.Checked) def mark_for_delete(self): for it in self.all_items: if it.flags() & Qt.ItemIsUserCheckable: it.setCheckState(1, Qt.Checked) def delete_marked(self): if not confirm('<p>'+_('The marked files and folders will be ' '<b>permanently deleted</b>. Are you sure?') +'</p>', 'check_library_editor_delete', self): return # Sort the paths in reverse length order so that we can be sure that # if an item is in another item, the sub-item will be deleted first. items = sorted(self.all_items, key=lambda x: len(x.text(1)), reverse=True) for it in items: if it.checkState(1): try: p = os.path.join(self.db.library_path ,unicode(it.text(1))) if os.path.isdir(p): delete_tree(p) else: delete_file(p) except: prints('failed to delete', os.path.join(self.db.library_path, unicode(it.text(1)))) self.run_the_check() def fix_missing_formats(self): tl = self.top_level_items['missing_formats'] child_count = tl.childCount() for i in range(0, child_count): item = tl.child(i) id = int(item.data(0, Qt.UserRole)) all = self.db.formats(id, index_is_id=True, verify_formats=False) all = set([f.strip() for f in all.split(',')]) if all else set() valid = self.db.formats(id, index_is_id=True, verify_formats=True) valid = set([f.strip() for f in valid.split(',')]) if valid else set() for fmt in all-valid: self.db.remove_format(id, fmt, index_is_id=True, db_only=True) def fix_missing_covers(self): tl = self.top_level_items['missing_covers'] child_count = tl.childCount() for i in range(0, child_count): item = tl.child(i) id = int(item.data(0, Qt.UserRole)) self.db.set_has_cover(id, False) def fix_extra_covers(self): tl = self.top_level_items['extra_covers'] child_count = tl.childCount() for i in range(0, child_count): item = tl.child(i) id = int(item.data(0, Qt.UserRole)) self.db.set_has_cover(id, True) def fix_items(self): for check in CHECKS: attr = check[0] fixable = check[3] tl = self.top_level_items[attr] if fixable and tl.checkState(1): func = getattr(self, 'fix_' + attr, None) if func is not None and callable(func): func() self.run_the_check() def copy_to_clipboard(self): QApplication.clipboard().setText(self.text_results)
class ConfigWidget(QWidget): def __init__(self, plugin_action): QWidget.__init__(self) self.plugin_action = plugin_action layout = QVBoxLayout(self) self.setLayout(layout) self.help_anchor = "configuration" title_layout = ImageTitleLayout(self, 'images/icon.png', 'Sony Utilities Options') layout.addLayout(title_layout) # c = plugin_prefs[STORE_NAME] library_config = get_library_config(self.plugin_action.gui.current_db) custom_column_group = QGroupBox(_('Custom Columns'), self) layout.addWidget(custom_column_group ) options_layout = QGridLayout() custom_column_group.setLayout(options_layout) avail_text_columns = self.get_text_custom_columns() avail_number_columns = self.get_number_custom_columns() avail_rating_columns = self.get_rating_custom_columns() avail_date_columns = self.get_date_custom_columns() # debug_print("avail_rating_columns=", avail_rating_columns) # debug_print("default columns=", self.plugin_action.gui.library_view.model().orig_headers) current_Location_column = library_config.get(KEY_CURRENT_LOCATION_CUSTOM_COLUMN, DEFAULT_LIBRARY_VALUES[KEY_CURRENT_LOCATION_CUSTOM_COLUMN]) precent_read_column = library_config.get(KEY_PERCENT_READ_CUSTOM_COLUMN, DEFAULT_LIBRARY_VALUES[KEY_PERCENT_READ_CUSTOM_COLUMN]) rating_column = library_config.get(KEY_RATING_CUSTOM_COLUMN, DEFAULT_LIBRARY_VALUES[KEY_RATING_CUSTOM_COLUMN]) last_read_column = library_config.get(KEY_LAST_READ_CUSTOM_COLUMN, DEFAULT_LIBRARY_VALUES[KEY_LAST_READ_CUSTOM_COLUMN]) store_on_connect = get_plugin_pref(COMMON_OPTIONS_STORE_NAME, KEY_STORE_ON_CONNECT) prompt_to_store = get_plugin_pref(COMMON_OPTIONS_STORE_NAME, KEY_PROMPT_TO_STORE) store_if_more_recent = get_plugin_pref(COMMON_OPTIONS_STORE_NAME, KEY_STORE_IF_MORE_RECENT) do_not_store_if_reopened = get_plugin_pref(COMMON_OPTIONS_STORE_NAME, KEY_DO_NOT_STORE_IF_REOPENED) # do_check_for_firmware_updates = get_plugin_pref(UPDATE_OPTIONS_STORE_NAME, KEY_DO_UPDATE_CHECK) # do_early_firmware_updates = get_plugin_pref(UPDATE_OPTIONS_STORE_NAME, KEY_DO_EARLY_FIRMWARE_CHECK) # self.update_check_last_time = get_plugin_pref(UPDATE_OPTIONS_STORE_NAME, KEY_LAST_FIRMWARE_CHECK_TIME) do_daily_backup = get_plugin_pref(BACKUP_OPTIONS_STORE_NAME, KEY_DO_DAILY_BACKUP) dest_directory = get_plugin_pref(BACKUP_OPTIONS_STORE_NAME, KEY_BACKUP_DEST_DIRECTORY) copies_to_keep = get_plugin_pref(BACKUP_OPTIONS_STORE_NAME, KEY_BACKUP_COPIES_TO_KEEP) # debug_print("current_Location_column=%s, precent_read_column=%s, rating_column=%s" % (current_Location_column, precent_read_column, rating_column)) current_Location_label = QLabel(_('Current Reading Location Column:'), self) current_Location_label.setToolTip(_("Select a custom column to store the current reading location. The column type must be 'text'. Leave this blank if you do not want to store or restore the current reading location.")) self.current_Location_combo = CustomColumnComboBox(self, avail_text_columns, current_Location_column) current_Location_label.setBuddy(self.current_Location_combo) options_layout.addWidget(current_Location_label, 0, 0, 1, 1) options_layout.addWidget(self.current_Location_combo, 0, 1, 1, 1) percent_read_label = QLabel(_('Percent Read Column:'), self) percent_read_label.setToolTip(_("Column used to store the current percent read. The column type must be a 'integer'. Leave this blank if you do not want to store or restore the percentage read.")) self.percent_read_combo = CustomColumnComboBox(self, avail_number_columns, precent_read_column) percent_read_label.setBuddy(self.percent_read_combo) options_layout.addWidget(percent_read_label, 2, 0, 1, 1) options_layout.addWidget(self.percent_read_combo, 2, 1, 1, 1) rating_label = QLabel(_('Rating Column:'), self) rating_label.setToolTip(_("Column used to store the rating. The column type must be a 'integer'. Leave this blank if you do not want to store or restore the rating.")) self.rating_combo = CustomColumnComboBox(self, avail_rating_columns, rating_column) rating_label.setBuddy(self.rating_combo) options_layout.addWidget(rating_label, 3, 0, 1, 1) options_layout.addWidget(self.rating_combo, 3, 1, 1, 1) last_read_label = QLabel(_('Last Read Column:'), self) last_read_label.setToolTip(_("Column used to store when the book was last read. The column type must be a 'Date'. Leave this blank if you do not want to store the last read timestamp.")) self.last_read_combo = CustomColumnComboBox(self, avail_date_columns, last_read_column) last_read_label.setBuddy(self.last_read_combo) options_layout.addWidget(last_read_label, 4, 0, 1, 1) options_layout.addWidget(self.last_read_combo, 4, 1, 1, 1) auto_store_group = QGroupBox(_('Store on connect'), self) layout.addWidget(auto_store_group ) options_layout = QGridLayout() auto_store_group.setLayout(options_layout) self.store_on_connect_checkbox = QCheckBox(_("Store current bookmarks on connect"), self) self.store_on_connect_checkbox.setToolTip(_("When this is checked, the library will be updated with the current bookmark for all books on the device.")) self.store_on_connect_checkbox.setCheckState(Qt.Checked if store_on_connect else Qt.Unchecked) self.store_on_connect_checkbox.clicked.connect(self.store_on_connect_checkbox_clicked) options_layout.addWidget(self.store_on_connect_checkbox, 0, 0, 1, 3) self.prompt_to_store_checkbox = QCheckBox(_("Prompt to store any changes"), self) self.prompt_to_store_checkbox.setToolTip(_("Enable this to be prompted to save the changed bookmarks after an automatic store is done.")) self.prompt_to_store_checkbox.setCheckState(Qt.Checked if prompt_to_store else Qt.Unchecked) self.prompt_to_store_checkbox.setEnabled(store_on_connect) options_layout.addWidget(self.prompt_to_store_checkbox, 1, 0, 1, 1) self.store_if_more_recent_checkbox = QCheckBox(_("Only if more recent"), self) self.store_if_more_recent_checkbox.setToolTip(_("Only store the reading position if the last read timestamp on the device is more recent than in the library.")) self.store_if_more_recent_checkbox.setCheckState(Qt.Checked if store_if_more_recent else Qt.Unchecked) self.store_if_more_recent_checkbox.setEnabled(store_on_connect) options_layout.addWidget(self.store_if_more_recent_checkbox, 1, 1, 1, 1) self.do_not_store_if_reopened_checkbox = QCheckBox(_("Not if finished in library"), self) self.do_not_store_if_reopened_checkbox.setToolTip(_("Do not store the reading position if the library has the book as finished. This is if the percent read is 100%.")) self.do_not_store_if_reopened_checkbox.setCheckState(Qt.Checked if do_not_store_if_reopened else Qt.Unchecked) self.do_not_store_if_reopened_checkbox.setEnabled(store_on_connect) options_layout.addWidget(self.do_not_store_if_reopened_checkbox, 1, 2, 1, 1) # update_options_group = QGroupBox(_('Firmware Update Options'), self) # layout.addWidget(update_options_group) # options_layout = QGridLayout() # update_options_group.setLayout(options_layout) # # self.do_update_check = QCheckBox(_('Check for Sony firmware updates daily?'), self) # self.do_update_check.setToolTip(_('If this is selected the plugin will check for Sony firmware updates when your Sony device is plugged in, once per 24-hour period.')) # self.do_update_check.setCheckState(Qt.Checked if do_check_for_firmware_updates else Qt.Unchecked) # options_layout.addWidget(self.do_update_check, 0, 0, 1, 1) # # self.do_early_firmware_check = QCheckBox(_('Use early firmware adopter affiliate?'), self) # self.do_early_firmware_check.setToolTip(_('WARNING: THIS OPTION RISKS DOWNLOADING THE WRONG FIRMWARE FOR YOUR DEVICE! YOUR DEVICE MAY NOT FUNCTION PROPERLY IF THIS HAPPENS! Choose this option to attempt to download Sony firmware updates before they are officially available for your device.')) # self.do_early_firmware_check.setCheckState(Qt.Checked if do_early_firmware_updates else Qt.Unchecked) # options_layout.addWidget(self.do_early_firmware_check, 0, 1, 1, 1) backup_options_group = QGroupBox(_('Device Database Backup'), self) layout.addWidget(backup_options_group) options_layout = QGridLayout() backup_options_group.setLayout(options_layout) self.do_daily_backp_checkbox = QCheckBox(_('Backup the device database daily'), self) self.do_daily_backp_checkbox.setToolTip(_('If this is selected the plugin will backup the device database the first time it is connected each day.')) self.do_daily_backp_checkbox.setCheckState(Qt.Checked if do_daily_backup else Qt.Unchecked) self.do_daily_backp_checkbox.clicked.connect(self.do_daily_backp_checkbox_clicked) options_layout.addWidget(self.do_daily_backp_checkbox, 0, 0, 1, 3) self.dest_directory_label = QLabel(_("Destination:"), self) self.dest_directory_label.setToolTip(_("Select the destination the annotations files are to be backed up in.")) self.dest_directory_edit = QLineEdit(self) self.dest_directory_edit.setMinimumSize(150, 0) self.dest_directory_edit.setText(dest_directory) self.dest_directory_label.setBuddy(self.dest_directory_edit) self.dest_pick_button = QPushButton(_("..."), self) self.dest_pick_button.setMaximumSize(24, 20) self.dest_pick_button.clicked.connect(self._get_dest_directory_name) options_layout.addWidget(self.dest_directory_label, 1, 0, 1, 1) options_layout.addWidget(self.dest_directory_edit, 1, 1, 1, 1) options_layout.addWidget(self.dest_pick_button, 1, 2, 1, 1) self.copies_to_keep_checkbox = QCheckBox(_('Copies to keep'), self) self.copies_to_keep_checkbox.setToolTip(_("Select this to limit the number of backup kept. If not set, the backup files must be manually deleted.")) self.copies_to_keep_spin = QSpinBox(self) self.copies_to_keep_spin.setMinimum(2) self.copies_to_keep_spin.setToolTip(_("The number of backup copies of the database to keep. The minimum is 2.")) options_layout.addWidget(self.copies_to_keep_checkbox, 1, 3, 1, 1) options_layout.addWidget(self.copies_to_keep_spin, 1, 4, 1, 1) self.copies_to_keep_checkbox.clicked.connect(self.copies_to_keep_checkbox_clicked) if copies_to_keep == -1: self.copies_to_keep_checkbox.setCheckState(not Qt.Checked) else: self.copies_to_keep_checkbox.setCheckState(Qt.Checked) self.copies_to_keep_spin.setProperty('value', copies_to_keep) self.do_daily_backp_checkbox_clicked(do_daily_backup) other_options_group = QGroupBox(_('Other Options'), self) layout.addWidget(other_options_group ) options_layout = QGridLayout() other_options_group.setLayout(options_layout) library_default_label = QLabel(_('&Library Button default:'), self) library_default_label.setToolTip(_('If plugin is placed as a toolbar button, choose a default action when clicked on')) self.library_default_combo = KeyComboBox(self, self.plugin_action.library_actions_map, unicode(get_plugin_pref(COMMON_OPTIONS_STORE_NAME, KEY_BUTTON_ACTION_LIBRARY))) library_default_label.setBuddy(self.library_default_combo) options_layout.addWidget(library_default_label, 0, 0, 1, 1) options_layout.addWidget(self.library_default_combo, 0, 1, 1, 2) device_default_label = QLabel(_('&Device Button default:'), self) device_default_label.setToolTip(_('If plugin is placed as a toolbar button, choose a default action when clicked on')) self.device_default_combo = KeyComboBox(self, self.plugin_action.device_actions_map, unicode(get_plugin_pref(COMMON_OPTIONS_STORE_NAME, KEY_BUTTON_ACTION_DEVICE))) device_default_label.setBuddy(self.device_default_combo) options_layout.addWidget(device_default_label, 1, 0, 1, 1) options_layout.addWidget(self.device_default_combo, 1, 1, 1, 2) keyboard_shortcuts_button = QPushButton(_('Keyboard shortcuts...'), self) keyboard_shortcuts_button.setToolTip(_('Edit the keyboard shortcuts associated with this plugin')) keyboard_shortcuts_button.clicked.connect(self.edit_shortcuts) layout.addWidget(keyboard_shortcuts_button) layout.addStretch(1) def store_on_connect_checkbox_clicked(self, checked): self.prompt_to_store_checkbox.setEnabled(checked) self.store_if_more_recent_checkbox.setEnabled(checked) self.do_not_store_if_reopened_checkbox.setEnabled(checked) def do_daily_backp_checkbox_clicked(self, checked): self.dest_directory_edit.setEnabled(checked) self.dest_pick_button.setEnabled(checked) self.dest_directory_label.setEnabled(checked) self.copies_to_keep_checkbox.setEnabled(checked) self.copies_to_keep_checkbox_clicked(checked and self.copies_to_keep_checkbox.checkState() == Qt.Checked) def copies_to_keep_checkbox_clicked(self, checked): self.copies_to_keep_spin.setEnabled(checked) # Called by Calibre before save_settings def validate(self): # import traceback # traceback.print_stack() debug_print('BEGIN Validate') valid = True # Only save if we were able to get data to avoid corrupting stored data # if self.do_daily_backp_checkbox.checkState() == Qt.Checked and not len(self.dest_directory_edit.text()): # error_dialog(self, 'No destination directory', # 'If the automatic device backup is set, there must be a destination directory.', # show=True, show_copy_button=False) # valid = False debug_print('END Validate, status = %s' % valid) return valid def save_settings(self): new_prefs = {} new_prefs[KEY_BUTTON_ACTION_DEVICE] = unicode(self.device_default_combo.currentText()) new_prefs[KEY_BUTTON_ACTION_LIBRARY] = unicode(self.library_default_combo.currentText()) new_prefs[KEY_STORE_ON_CONNECT] = self.store_on_connect_checkbox.checkState() == Qt.Checked new_prefs[KEY_PROMPT_TO_STORE] = self.prompt_to_store_checkbox.checkState() == Qt.Checked new_prefs[KEY_STORE_IF_MORE_RECENT] = self.store_if_more_recent_checkbox.checkState() == Qt.Checked new_prefs[KEY_DO_NOT_STORE_IF_REOPENED] = self.do_not_store_if_reopened_checkbox.checkState() == Qt.Checked plugin_prefs[COMMON_OPTIONS_STORE_NAME] = new_prefs new_update_prefs = {} # new_update_prefs[KEY_DO_UPDATE_CHECK] = self.do_update_check.checkState() == Qt.Checked # new_update_prefs[KEY_DO_EARLY_FIRMWARE_CHECK] = self.do_early_firmware_check.checkState() == Qt.Checked # new_update_prefs[KEY_LAST_FIRMWARE_CHECK_TIME] = self.update_check_last_time plugin_prefs[UPDATE_OPTIONS_STORE_NAME] = new_update_prefs backup_prefs = {} backup_prefs[KEY_DO_DAILY_BACKUP] = self.do_daily_backp_checkbox.checkState() == Qt.Checked backup_prefs[KEY_BACKUP_DEST_DIRECTORY] = unicode(self.dest_directory_edit.text()) backup_prefs[KEY_BACKUP_COPIES_TO_KEEP] = int(unicode(self.copies_to_keep_spin.value())) if self.copies_to_keep_checkbox.checkState() == Qt.Checked else -1 plugin_prefs[BACKUP_OPTIONS_STORE_NAME] = backup_prefs db = self.plugin_action.gui.current_db library_config = get_library_config(db) library_config[KEY_CURRENT_LOCATION_CUSTOM_COLUMN] = self.current_Location_combo.get_selected_column() library_config[KEY_PERCENT_READ_CUSTOM_COLUMN] = self.percent_read_combo.get_selected_column() library_config[KEY_RATING_CUSTOM_COLUMN] = self.rating_combo.get_selected_column() library_config[KEY_LAST_READ_CUSTOM_COLUMN] = self.last_read_combo.get_selected_column() set_library_config(db, library_config) def get_number_custom_columns(self): column_types = ['float','int'] return self.get_custom_columns(column_types) def get_rating_custom_columns(self): column_types = ['rating','int'] custom_columns = self.get_custom_columns(column_types) ratings_column_name = self.plugin_action.gui.library_view.model().orig_headers['rating'] custom_columns['rating'] = {'name': ratings_column_name} return custom_columns def get_text_custom_columns(self): column_types = ['text'] return self.get_custom_columns(column_types) def get_date_custom_columns(self): column_types = ['datetime'] return self.get_custom_columns(column_types) def get_custom_columns(self, column_types): custom_columns = self.plugin_action.gui.library_view.model().custom_columns available_columns = {} for key, column in custom_columns.iteritems(): typ = column['datatype'] if typ in column_types and not column['is_multiple']: available_columns[key] = column return available_columns def help_link_activated(self, url): self.plugin_action.show_help(anchor="configuration") def edit_shortcuts(self): d = KeyboardConfigDialog(self.plugin_action.gui, self.plugin_action.action_spec[0]) if d.exec_() == d.Accepted: self.plugin_action.gui.keyboard.finalize() def _get_dest_directory_name(self): path = choose_dir(self, 'backup annotations destination dialog','Choose destination directory') if path: self.dest_directory_edit.setText(path)
class ExtraCustomization(DeviceConfigTab): # {{{ def __init__(self, extra_customization_message, extra_customization_choices, device_settings): super(ExtraCustomization, self).__init__() debug_print("ExtraCustomization.__init__ - extra_customization_message=", extra_customization_message) debug_print("ExtraCustomization.__init__ - extra_customization_choices=", extra_customization_choices) debug_print("ExtraCustomization.__init__ - device_settings.extra_customization=", device_settings.extra_customization) debug_print("ExtraCustomization.__init__ - device_settings=", device_settings) self.extra_customization_message = extra_customization_message self.l = QVBoxLayout(self) self.setLayout(self.l) options_group = QGroupBox(_("Extra driver customization options"), self) self.l.addWidget(options_group) self.extra_layout = QGridLayout() self.extra_layout.setObjectName("extra_layout") options_group.setLayout(self.extra_layout) if extra_customization_message: extra_customization_choices = extra_customization_choices or {} def parse_msg(m): msg, _, tt = m.partition(':::') if m else ('', '', '') return msg.strip(), textwrap.fill(tt.strip(), 100) if isinstance(extra_customization_message, list): self.opt_extra_customization = [] if len(extra_customization_message) > 6: row_func = lambda x, y: ((x/2) * 2) + y col_func = lambda x: x%2 else: row_func = lambda x, y: x*2 + y col_func = lambda x: 0 for i, m in enumerate(extra_customization_message): label_text, tt = parse_msg(m) if not label_text: self.opt_extra_customization.append(None) continue if isinstance(device_settings.extra_customization[i], bool): self.opt_extra_customization.append(QCheckBox(label_text)) self.opt_extra_customization[-1].setToolTip(tt) self.opt_extra_customization[i].setChecked(bool(device_settings.extra_customization[i])) elif i in extra_customization_choices: cb = QComboBox(self) self.opt_extra_customization.append(cb) l = QLabel(label_text) l.setToolTip(tt), cb.setToolTip(tt), l.setBuddy(cb), cb.setToolTip(tt) for li in sorted(extra_customization_choices[i]): self.opt_extra_customization[i].addItem(li) cb.setCurrentIndex(max(0, cb.findText(device_settings.extra_customization[i]))) else: self.opt_extra_customization.append(QLineEdit(self)) l = QLabel(label_text) l.setToolTip(tt) self.opt_extra_customization[i].setToolTip(tt) l.setBuddy(self.opt_extra_customization[i]) l.setWordWrap(True) self.opt_extra_customization[i].setText(device_settings.extra_customization[i]) self.opt_extra_customization[i].setCursorPosition(0) self.extra_layout.addWidget(l, row_func(i + 2, 0), col_func(i)) self.extra_layout.addWidget(self.opt_extra_customization[i], row_func(i + 2, 1), col_func(i)) spacerItem1 = QSpacerItem(10, 10, QSizePolicy.Minimum, QSizePolicy.Expanding) self.extra_layout.addItem(spacerItem1, row_func(i + 2 + 2, 1), 0, 1, 2) self.extra_layout.setRowStretch(row_func(i + 2 + 2, 1), 2) else: self.opt_extra_customization = QLineEdit() label_text, tt = parse_msg(extra_customization_message) l = QLabel(label_text) l.setToolTip(tt) l.setBuddy(self.opt_extra_customization) l.setWordWrap(True) if device_settings.extra_customization: self.opt_extra_customization.setText(device_settings.extra_customization) self.opt_extra_customization.setCursorPosition(0) self.opt_extra_customization.setCursorPosition(0) self.extra_layout.addWidget(l, 0, 0) self.extra_layout.addWidget(self.opt_extra_customization, 1, 0) def extra_customization(self): ec = [] if self.extra_customization_message: if isinstance(self.extra_customization_message, list): for i in range(0, len(self.extra_customization_message)): if self.opt_extra_customization[i] is None: ec.append(None) continue if hasattr(self.opt_extra_customization[i], 'isChecked'): ec.append(self.opt_extra_customization[i].isChecked()) elif hasattr(self.opt_extra_customization[i], 'currentText'): ec.append(unicode_type(self.opt_extra_customization[i].currentText()).strip()) else: ec.append(unicode_type(self.opt_extra_customization[i].text()).strip()) else: ec = unicode_type(self.opt_extra_customization.text()).strip() if not ec: ec = None return ec @property def has_extra_customizations(self): debug_print("ExtraCustomization::has_extra_customizations - self.extra_customization_message", self.extra_customization_message) return self.extra_customization_message and len(self.extra_customization_message) > 0
class ConditionEditor(QWidget): # {{{ ACTION_MAP = { 'bool' : ( (_('is true'), 'is true',), (_('is false'), 'is false'), (_('is undefined'), 'is undefined') ), 'ondevice' : ( (_('is true'), 'is set',), (_('is false'), 'is not set'), ), 'identifiers' : ( (_('has id'), 'has id'), (_('does not have id'), 'does not have id'), ), 'int' : ( (_('is equal to'), 'eq'), (_('is less than'), 'lt'), (_('is greater than'), 'gt') ), 'datetime' : ( (_('is equal to'), 'eq'), (_('is less than'), 'lt'), (_('is greater than'), 'gt'), (_('is set'), 'is set'), (_('is not set'), 'is not set'), (_('is more days ago than'), 'older count days'), (_('is fewer days ago than'), 'count_days'), (_('is more days from now than'), 'newer future days'), (_('is fewer days from now than'), 'older future days') ), 'multiple' : ( (_('has'), 'has'), (_('does not have'), 'does not have'), (_('has pattern'), 'has pattern'), (_('does not have pattern'), 'does not have pattern'), (_('is set'), 'is set'), (_('is not set'), 'is not set'), ), 'single' : ( (_('is'), 'is'), (_('is not'), 'is not'), (_('matches pattern'), 'matches pattern'), (_('does not match pattern'), 'does not match pattern'), (_('is set'), 'is set'), (_('is not set'), 'is not set'), ), } for x in ('float', 'rating'): ACTION_MAP[x] = ACTION_MAP['int'] def __init__(self, fm, parent=None): QWidget.__init__(self, parent) self.fm = fm self.action_map = self.ACTION_MAP self.l = l = QGridLayout(self) self.setLayout(l) texts = _('If the ___ column ___ values') try: one, two, three = texts.split('___') except: one, two, three = 'If the ', ' column ', ' value ' self.l1 = l1 = QLabel(one) l.addWidget(l1, 0, 0) self.column_box = QComboBox(self) l.addWidget(self.column_box, 0, 1) self.l2 = l2 = QLabel(two) l.addWidget(l2, 0, 2) self.action_box = QComboBox(self) l.addWidget(self.action_box, 0, 3) self.l3 = l3 = QLabel(three) l.addWidget(l3, 0, 4) self.value_box = QLineEdit(self) l.addWidget(self.value_box, 0, 5) self.column_box.addItem('', '') for key in sorted( conditionable_columns(fm), key=lambda(key): sort_key(fm[key]['name'])): self.column_box.addItem(fm[key]['name'], key) self.column_box.setCurrentIndex(0) self.column_box.currentIndexChanged.connect(self.init_action_box) self.action_box.currentIndexChanged.connect(self.init_value_box) for b in (self.column_box, self.action_box): b.setSizeAdjustPolicy(b.AdjustToMinimumContentsLengthWithIcon) b.setMinimumContentsLength(20) @dynamic_property def current_col(self): def fget(self): idx = self.column_box.currentIndex() return unicode(self.column_box.itemData(idx) or '') def fset(self, val): for idx in range(self.column_box.count()): c = unicode(self.column_box.itemData(idx) or '') if c == val: self.column_box.setCurrentIndex(idx) return raise ValueError('Column %r not found'%val) return property(fget=fget, fset=fset) @dynamic_property def current_action(self): def fget(self): idx = self.action_box.currentIndex() return unicode(self.action_box.itemData(idx) or '') def fset(self, val): for idx in range(self.action_box.count()): c = unicode(self.action_box.itemData(idx) or '') if c == val: self.action_box.setCurrentIndex(idx) return raise ValueError('Action %r not valid for current column'%val) return property(fget=fget, fset=fset) @property def current_val(self): ans = unicode(self.value_box.text()).strip() if self.current_col == 'languages': rmap = {lower(v):k for k, v in lang_map().iteritems()} ans = rmap.get(lower(ans), ans) return ans @dynamic_property def condition(self): def fget(self): c, a, v = (self.current_col, self.current_action, self.current_val) if not c or not a: return None return (c, a, v) def fset(self, condition): c, a, v = condition if not v: v = '' v = v.strip() self.current_col = c self.current_action = a self.value_box.setText(v) return property(fget=fget, fset=fset) def init_action_box(self): self.action_box.blockSignals(True) self.action_box.clear() self.action_box.addItem('', '') col = self.current_col if col: m = self.fm[col] dt = m['datatype'] if dt in self.action_map: actions = self.action_map[dt] else: if col == 'ondevice': k = 'ondevice' elif col == 'identifiers': k = 'identifiers' else: k = 'multiple' if m['is_multiple'] else 'single' actions = self.action_map[k] for text, key in actions: self.action_box.addItem(text, key) self.action_box.setCurrentIndex(0) self.action_box.blockSignals(False) self.init_value_box() def init_value_box(self): self.value_box.setEnabled(True) self.value_box.setText('') self.value_box.setInputMask('') self.value_box.setValidator(None) col = self.current_col if not col: return action = self.current_action if not action: return m = self.fm[col] dt = m['datatype'] tt = '' if col == 'identifiers': tt = _('Enter either an identifier type or an ' 'identifier type and value of the form identifier:value') elif col == 'languages': tt = _('Enter a 3 letter ISO language code, like fra for French' ' or deu for German or eng for English. You can also use' ' the full language name, in which case calibre will try to' ' automatically convert it to the language code.') elif dt in ('int', 'float', 'rating'): tt = _('Enter a number') v = QIntValidator if dt == 'int' else QDoubleValidator self.value_box.setValidator(v(self.value_box)) elif dt == 'datetime': if action == 'count_days': self.value_box.setValidator(QIntValidator(self.value_box)) tt = _('Enter the maximum days old the item can be. Zero is today. ' 'Dates in the future always match') elif action == 'older count days': self.value_box.setValidator(QIntValidator(self.value_box)) tt = _('Enter the minimum days old the item can be. Zero is today. ' 'Dates in the future never match') elif action == 'older future days': self.value_box.setValidator(QIntValidator(self.value_box)) tt = _('Enter the maximum days in the future the item can be. ' 'Zero is today. Dates in the past always match') elif action == 'newer future days': self.value_box.setValidator(QIntValidator(self.value_box)) tt = _('Enter the minimum days in the future the item can be. ' 'Zero is today. Dates in the past never match') else: self.value_box.setInputMask('9999-99-99') tt = _('Enter a date in the format YYYY-MM-DD') else: tt = _('Enter a string.') if 'pattern' in action: tt = _('Enter a regular expression') elif m.get('is_multiple', False): tt += '\n' + _('You can match multiple values by separating' ' them with %s')%m['is_multiple']['ui_to_list'] self.value_box.setToolTip(tt) if action in ('is set', 'is not set', 'is true', 'is false', 'is undefined'): self.value_box.setEnabled(False)
class ThemeCreateDialog(Dialog): def __init__(self, parent, report): self.report = report Dialog.__init__(self, _('Create an icon theme'), 'create-icon-theme', parent) def setup_ui(self): self.splitter = QSplitter(self) self.l = l = QVBoxLayout(self) l.addWidget(self.splitter) l.addWidget(self.bb) self.w = w = QGroupBox(_('Theme Metadata'), self) self.splitter.addWidget(w) l = w.l = QFormLayout(w) l.setFieldGrowthPolicy(l.ExpandingFieldsGrow) self.missing_icons_group = mg = QGroupBox(self) self.mising_icons = mi = QListWidget(mg) mi.setSelectionMode(mi.NoSelection) mg.l = QVBoxLayout(mg) mg.l.addWidget(mi) self.splitter.addWidget(mg) self.title = QLineEdit(self) l.addRow(_('&Title:'), self.title) self.author = QLineEdit(self) l.addRow(_('&Author:'), self.author) self.version = v = QSpinBox(self) v.setMinimum(1), v.setMaximum(1000000) l.addRow(_('&Version:'), v) self.license = lc = QLineEdit(self) l.addRow(_('&License:'), lc) self.url = QLineEdit(self) l.addRow(_('&URL:'), self.url) lc.setText(_( 'The license for the icons in this theme. Common choices are' ' Creative Commons or Public Domain.')) self.description = QTextEdit(self) l.addRow(self.description) self.refresh_button = rb = self.bb.addButton(_('&Refresh'), self.bb.ActionRole) rb.setIcon(QIcon(I('view-refresh.png'))) rb.clicked.connect(self.refresh) self.apply_report() def sizeHint(self): return QSize(900, 670) @property def metadata(self): self.report.theme.title = self.title.text().strip() # Needed for report.name to work return { 'title': self.title.text().strip(), 'author': self.author.text().strip(), 'version': self.version.value(), 'description': self.description.toPlainText().strip(), 'number': len(self.report.name_map) - len(self.report.extra), 'date': utcnow().date().isoformat(), 'name': self.report.name, 'license': self.license.text().strip() or 'Unknown', 'url': self.url.text().strip() or None, } def save_metadata(self): with open(os.path.join(self.report.path, THEME_METADATA), 'wb') as f: json.dump(self.metadata, f, indent=2) def refresh(self): self.save_metadata() self.report = read_theme_from_folder(self.report.path) self.apply_report() def apply_report(self): theme = self.report.theme self.title.setText((theme.title or '').strip()) self.author.setText((theme.author or '').strip()) self.version.setValue(theme.version or 1) self.description.setText((theme.description or '').strip()) self.license.setText((theme.license or 'Unknown').strip()) self.url.setText((theme.url or '').strip()) if self.report.missing: title = _('%d icons missing in this theme') % len(self.report.missing) else: title = _('No missing icons') self.missing_icons_group.setTitle(title) mi = self.mising_icons mi.clear() for name in sorted(self.report.missing): QListWidgetItem(QIcon(I(name, allow_user_override=False)), name, mi) def accept(self): mi = self.metadata if not mi.get('title'): return error_dialog(self, _('No title specified'), _( 'You must specify a title for this icon theme'), show=True) if not mi.get('author'): return error_dialog(self, _('No author specified'), _( 'You must specify an author for this icon theme'), show=True) return Dialog.accept(self)
class BookConfigWidget(QDialog): '''Creates book specific preferences dialog''' # title case given words except for articles in the middle # i.e the lord ruler would become The Lord Ruler but john the great would become John the Great ARTICLES = ['The', 'For', 'De', 'And', 'Or', 'Of', 'La'] TITLE_CASE = lambda self, words: ' '.join([word.lower() if word in self.ARTICLES and index != 0 else word for index, word in enumerate(words.title().split())]) def __init__(self, parent, book_settings): QDialog.__init__(self, parent) self.resize(500, 500) self.setWindowTitle('title - author') self._index = 0 self._book_settings = book_settings v_layout = QVBoxLayout(self) # add ASIN and Goodreads url text boxes and update buttons asin_browser_button, goodreads_browser_button = self._initialize_general(v_layout) # add scrollable area for aliases v_layout.addWidget(QLabel('Aliases:')) self._scroll_area = QScrollArea() v_layout.addWidget(self._scroll_area) # add status box self._status = QLabel('') v_layout.addWidget(self._status) previous_button = next_button = None if len(self._book_settings) > 1: previous_button = QPushButton('Previous') previous_button.setEnabled(False) previous_button.setFixedWidth(100) next_button = QPushButton('Next') next_button.setFixedWidth(100) previous_button.clicked.connect(lambda: self.previous_clicked(previous_button, next_button, asin_browser_button, goodreads_browser_button)) next_button.clicked.connect(lambda: self.next_clicked(previous_button, next_button, asin_browser_button, goodreads_browser_button)) self._initialize_navigation_buttons(v_layout, previous_button, next_button) self.setLayout(v_layout) self.show_book_prefs(asin_browser_button, goodreads_browser_button) self.show() def _initialize_general(self, v_layout): '''Initialize asin/goodreads sections''' # Add the ASIN label, line edit, and button to dialog self._asin_edit = QLineEdit('') asin_layout = QHBoxLayout(None) asin_label = QLabel('ASIN:') asin_label.setFixedWidth(100) asin_browser_button = QPushButton('Open..') asin_browser_button.clicked.connect(self.browse_amazon_url) asin_browser_button.setToolTip('Open Amazon page for the specified ASIN') self._asin_edit.textEdited.connect(lambda: self.edit_asin(self._asin_edit.text(), asin_browser_button)) asin_layout.addWidget(asin_label) asin_layout.addWidget(self._asin_edit) asin_layout.addWidget(asin_browser_button) v_layout.addLayout(asin_layout) # Add the Goodreads URL label, line edit, and button to dialog self._goodreads_url_edit = QLineEdit('') self._goodreads_url_edit.textEdited.connect(lambda: self.edit_goodreads_url(self._goodreads_url_edit.text(), goodreads_browser_button)) goodreads_layout = QHBoxLayout(None) goodreads_url_label = QLabel('Goodreads URL:') goodreads_url_label.setFixedWidth(100) goodreads_browser_button = QPushButton('Open..') goodreads_browser_button.clicked.connect(self.browse_goodreads_url) goodreads_browser_button.setToolTip('Open Goodreads page at the specified URL') goodreads_layout.addWidget(goodreads_url_label) goodreads_layout.addWidget(self._goodreads_url_edit) goodreads_layout.addWidget(goodreads_browser_button) v_layout.addLayout(goodreads_layout) # Add the sample xray label, line edit, and button to dialog self._sample_xray_edit = QLineEdit('') self._sample_xray_edit.textEdited.connect(lambda: self.edit_sample_xray(self._sample_xray_edit.text())) sample_xray_layout = QHBoxLayout(None) sample_xray_label = QLabel('X-Ray or JSON:') sample_xray_label.setFixedWidth(100) sample_xray_button = QPushButton('Browse...') sample_xray_button.clicked.connect(self.browse_sample_xray) sample_xray_button.setToolTip('Browse for a sample x-ray file or JSON to be used') sample_xray_layout.addWidget(sample_xray_label) sample_xray_layout.addWidget(self._sample_xray_edit) sample_xray_layout.addWidget(sample_xray_button) v_layout.addLayout(sample_xray_layout) # Add the update buttons to dialog search_buttons_layout = QHBoxLayout(None) search_asin_button = QPushButton('Search for ASIN') search_asin_button.setFixedWidth(225) search_asin_button.clicked.connect(lambda: self.search_for_asin_clicked(asin_browser_button)) search_buttons_layout.addWidget(search_asin_button) search_goodreads_url_button = QPushButton('Search for Goodreads URL') search_goodreads_url_button.setFixedWidth(225) search_goodreads_url_button.clicked.connect(lambda: self.search_for_goodreads_url(goodreads_browser_button)) search_buttons_layout.addWidget(search_goodreads_url_button) v_layout.addLayout(search_buttons_layout) update_buttons_layout = QHBoxLayout(None) self._update_from_url_button = QPushButton('Update Aliases from URL') self._update_from_url_button.setFixedWidth(225) self._update_from_url_button.clicked.connect(self.update_aliases_from_url) update_buttons_layout.addWidget(self._update_from_url_button) self._update_from_file_button = QPushButton('Update Aliases from Input File') self._update_from_file_button.setFixedWidth(225) self._update_from_file_button.clicked.connect(self.update_aliases_from_file) update_buttons_layout.addWidget(self._update_from_file_button) v_layout.addLayout(update_buttons_layout) return asin_browser_button, goodreads_browser_button def _initialize_navigation_buttons(self, v_layout, previous_button, next_button): '''Add previous, ok, cancel, and next buttons''' buttons_layout = QHBoxLayout(None) buttons_layout.setAlignment(Qt.AlignRight) if len(self._book_settings) > 1: buttons_layout.addWidget(previous_button) ok_button = QPushButton('OK') ok_button.setFixedWidth(100) ok_button.clicked.connect(self.ok_clicked) buttons_layout.addWidget(ok_button) cancel_button = QPushButton('Cancel') cancel_button.setFixedWidth(100) cancel_button.clicked.connect(self.cancel_clicked) buttons_layout.addWidget(cancel_button) if len(self._book_settings) > 1: buttons_layout.addWidget(next_button) v_layout.addLayout(buttons_layout) @property def book(self): return self._book_settings[self._index] def set_status_and_repaint(self, message): '''Sets the status text and redraws the status text box''' self._status.setText(message) self._status.repaint() def edit_asin(self, val, asin_browser_button): '''Set asin edit to specified value; update asin browser button accordingly''' self.book.asin = val if val == '': asin_browser_button.setEnabled(False) else: asin_browser_button.setEnabled(True) def edit_goodreads_url(self, val, goodreads_browser_button): '''Sets book's goodreads_url to val and warns if the url is invalid; update goodreads browser button accordingly''' self.book.goodreads_url = val if val == '': goodreads_browser_button.setEnabled(False) if self._status.text() == 'Warning: Invalid Goodreads URL. URL must have goodreads as the domain.': self._status.setText('') else: goodreads_browser_button.setEnabled(True) if 'goodreads.com' not in val: self._status.setText('Warning: Invalid Goodreads URL. URL must have goodreads as the domain.') def edit_sample_xray(self, val): '''Sets book's sample x-ray to val and warns if the file path is invalid''' self.book.sample_xray = val if os.path.isfile(val): if not val.lower().endwith('.json') and not val.lower().endswith('.asc'): return self.update_aliases_from_file() def search_for_asin_clicked(self, asin_browser_button): '''Searches for current book's ASIN on amazon''' asin = None self.set_status_and_repaint('Searching for ASIN...') if self.book.title != 'Unknown' and self.book.author != 'Unknown': asin = self.book.search_for_asin_on_amazon(self.book.title_and_author) if asin: self._status.setText('ASIN found.') asin_browser_button.setEnabled(True) self.book.asin = asin self._asin_edit.setText(asin) else: self._status.setText('ASIN not found.') asin_browser_button.setEnabled(False) self._asin_edit.setText('') def browse_amazon_url(self): '''Opens Amazon page for current book's ASIN using user's local store''' # Try to use the nearest Amazon store to the user. # If this fails we'll default to .com, the user will have to manually # edit the preferences file to fix it (it is a simple text file). if not prefs['tld']: import json from collections import defaultdict from urllib2 import urlopen, URLError try: country = json.loads(urlopen('http://ipinfo.io/json').read())['country'] except (URLError, KeyError): country = 'unknown' country_tld = defaultdict(lambda: 'com', {'AU': 'com.au', 'BR': 'com.br', 'CA': 'ca', 'CN': 'cn', 'FR': 'fr', 'DE': 'de', 'IN': 'in', 'IT': 'it', 'JP': 'co.jp', 'MX': 'com.mx', 'NL': 'nl', 'ES': 'es', 'GB': 'co.uk', 'US': 'com'}) prefs['tld'] = country_tld[country] webbrowser.open('https://www.amazon.{0}/gp/product/{1}/'.format(prefs['tld'], self._asin_edit.text())) def browse_goodreads_url(self): '''Opens url for current book's goodreads url''' webbrowser.open(self._goodreads_url_edit.text()) def browse_sample_xray(self): """Browse for a sample xray file to use during x-ray creation""" file_dialog = QFileDialog(self) sample_file = file_dialog.getOpenFileName(caption='Choose sample x-ray to use:', filter='X-Ray or JSON (*.asc *.json)')[0] self.book.sample_xray = sample_file self._sample_xray_edit.setText(sample_file) if sample_file: self.update_aliases_from_file() def search_for_goodreads_url(self, goodreads_browser_button): '''Searches for goodreads url using asin first then title and author if asin doesn't exist''' url = None self.set_status_and_repaint('Searching for Goodreads url...') if self.book.asin: url = self.book.search_for_goodreads_url(self.book.asin) if not url and self.book.title != 'Unknown' and self.book.author != 'Unknown': url = self.book.search_for_goodreads_url(self.book.title_and_author) if url: self._status.setText('Goodreads url found.') self._update_from_url_button.setEnabled(True) goodreads_browser_button.setEnabled(True) self.book.goodreads_url = url self._goodreads_url_edit.setText(url) else: self._status.setText('Goodreads url not found.') self._update_from_url_button.setEnabled(False) goodreads_browser_button.setEnabled(False) self._goodreads_url_edit.setText('') def update_aliases_from_url(self): '''Update aliases using goodreads''' if 'goodreads.com' not in self._goodreads_url_edit.text(): self._status.setText('Error: Invalid Goodreads URL. URL must have goodreads as the domain.') return self.update_aliases_from_goodreads() def update_aliases_from_file(self): '''Update aliases on the preferences dailog using the information in the specified file''' self.set_status_and_repaint('Updating aliases...') if os.path.exists(self.book.sample_xray): self.book.update_aliases(self.book.sample_xray, source_type=os.path.splitext(self.book.sample_xray)[1][1:]) self.update_aliases_on_gui() self._status.setText('Aliases updated.') else: self._status.setText('Error: Input file not found.') def update_aliases_from_goodreads(self): '''Updates aliases on the preferences dialog using the information on the current goodreads url''' try: self.set_status_and_repaint('Updating aliases...') self.book.update_aliases(self._goodreads_url_edit.text()) self.update_aliases_on_gui() self._status.setText('Aliases updated.') except PageDoesNotExist: self._status.setText('Invalid Goodreads url.') def edit_aliases(self, term, val): '''Sets book's aliases to tuple (term, val)''' self.book.set_aliases(term, val) def previous_clicked(self, previous_button, next_button, asin_browser_button, goodreads_browser_button): '''Goes to previous book''' self._status.setText('') self._index -= 1 next_button.setEnabled(True) if self._index == 0: previous_button.setEnabled(False) self.show_book_prefs(asin_browser_button, goodreads_browser_button) def ok_clicked(self): '''Saves book's settings using current settings''' for book in self._book_settings: book.save() self.close() def cancel_clicked(self): '''Closes dialog without saving settings''' self.close() def next_clicked(self, previous_button, next_button, asin_browser_button, goodreads_browser_button): '''Goes to next book''' self._status.setText('') self._index += 1 previous_button.setEnabled(True) if self._index == len(self._book_settings) - 1: next_button.setEnabled(False) self.show_book_prefs(asin_browser_button, goodreads_browser_button) def show_book_prefs(self, asin_browser_button, goodreads_browser_button): '''Shows current book's preferences''' self.setWindowTitle(self.book.title_and_author) self._asin_edit.setText(self.book.asin) if self._asin_edit.text() == '': asin_browser_button.setEnabled(False) else: asin_browser_button.setEnabled(True) self._goodreads_url_edit.setText(self.book.goodreads_url) if self._goodreads_url_edit.text() == '': goodreads_browser_button.setEnabled(False) else: goodreads_browser_button.setEnabled(True) self._sample_xray_edit.setText(self.book.sample_xray) self.update_aliases_on_gui() def update_aliases_on_gui(self): '''Updates aliases on the dialog using the info in the book's aliases dict''' aliases_widget = QWidget() aliases_layout = QGridLayout(aliases_widget) aliases_layout.setAlignment(Qt.AlignTop) # add aliases for current book for index, (character, aliases) in enumerate(sorted(self.book.aliases.items())): label = QLabel(character + ':') label.setFixedWidth(150) aliases_layout.addWidget(label, index, 0) line_edit = QLineEdit(', '.join([self.TITLE_CASE(alias) for alias in aliases])) line_edit.setFixedWidth(350) line_edit.textEdited.connect(functools.partial(self.edit_aliases, character)) aliases_layout.addWidget(line_edit, index, 1) self._scroll_area.setWidget(aliases_widget)
class CalibreBookBrainzPluginDialog(QDialog): def __init__(self, gui, icon, do_user_config): QDialog.__init__(self, gui) self.gui = gui self.do_user_config = do_user_config self.db = gui.current_db self.l = QVBoxLayout() self.setLayout(self.l) self.header = QLabel(prefs['searchinbookbrainz']) self.l.addWidget(self.header) self.img = QLabel() pixmap = QPixmap("images/BBt.svg") self.img.setPixmap(pixmap) self.l.addWidget(self.img) # QCol = QColor() # QCol.setRed(220) # QCol.setGreen(255) # QCol.setBlue(240) self.setWindowTitle('Calibre Book Brainz Integration') self.setWindowIcon(icon) self.search_space = QLineEdit() self.selected_button = QPushButton('Use title from selected book', self) self.selected_button.clicked.connect(self.exporttitlefromselected) self.l.addWidget(self.selected_button) self.search_space = QLineEdit() self.l.addWidget(self.search_space) self.listWidget = QListWidget() self.l.addWidget(self.listWidget) self.searchExecutionButton = QPushButton('Search', self) self.searchExecutionButton.clicked.connect(self.search) self.l.addWidget(self.searchExecutionButton) self.aboutButton = QPushButton('About', self) self.aboutButton.clicked.connect(self.about) self.l.addWidget(self.aboutButton) self.resize(400, 600) self.search_space.setFocus() def exporttitlefromselected(self): rows = self.gui.current_view().selectionModel().selectedRows() if len(rows) == 0: self.search_space.setText("") else: mi = self.gui.library_view.model().db.get_metadata(rows[0].row()) self.search_space.setText(mi.title) def search(self): text = self.search_space.text() print(text) self.listWidget.clear() self.listWidget.setFocus() try: url = "https://bookbrainz.org/ws/search/?q=\"" + text + "\"&mode=\"search\"" hits = request_get(url)['hits'] except: return numQueries = len(hits) act = 0 for i in range(numQueries): enttype = hits[i]['_source']['_type'] if not enttype in ['Publication', 'Work', 'Edition']: continue print(hits[i]) item = QListWidgetItem("%i. %s BBID : %i" % ((act + 1), hits[i]['_source']['default_alias']['name'], 1)) Qcol = QColor() if i % 2 == 0: Qcol.setRed(240) Qcol.setGreen(255) Qcol.setBlue(255) else: Qcol.setRed(220) Qcol.setGreen(255) Qcol.setBlue(240) item.setBackground(QBrush(Qcol)) self.listWidget.addItem(item) act += 1 self.listWidget.setFocus() self.searchExecutionButton.setFocus() def about(self): text = get_resources('about.txt') QMessageBox.about(self, 'About the Calibre Book Brainz Plugin', text.decode('utf-8')) def config(self): self.do_user_config(parent=self) # Apply the changes self.label.setText(prefs['hello_world_msg'])
class ConfigWidget(QWidget): def __init__(self, plugin_action): QWidget.__init__(self) self.plugin_action = plugin_action layout = QVBoxLayout(self) self.setLayout(layout) # --- Directory Options --- directory_group_box = QGroupBox(_('Default Unpack Directory:'), self) layout.addWidget(directory_group_box) directory_group_box_layout = QVBoxLayout() directory_group_box.setLayout(directory_group_box_layout) # Directory path Textbox # Load the textbox with the current preference setting self.directory_txtBox = QLineEdit(plugin_prefs['Unpack_Folder'], self) self.directory_txtBox.setToolTip(_('<p>Default directory to extract files to')) directory_group_box_layout.addWidget(self.directory_txtBox) self.directory_txtBox.setReadOnly(True) # Folder select button directory_button = QPushButton(_('Select/Change Unpack Directory'), self) directory_button.setToolTip(_('<p>Select/Change directory to extract files to.')) # Connect button to the getDirectory function directory_button.clicked.connect(self.getDirectory) directory_group_box_layout.addWidget(directory_button) self.default_folder_check = QCheckBox(_('Always use the Default Unpack Directory'), self) self.default_folder_check.setToolTip(_('<p>When unchecked... you will be prompted to select a destination '+ 'directory for the extracted content each time you use Mobiunpack.')) directory_group_box_layout.addWidget(self.default_folder_check) # Load the checkbox with the current preference setting self.default_folder_check.setChecked(plugin_prefs['Always_Use_Unpack_Folder']) misc_group_box = QGroupBox(_('Default settings:'), self) layout.addWidget(misc_group_box) misc_group_box_layout = QVBoxLayout() misc_group_box.setLayout(misc_group_box_layout) self.use_hd_images = QCheckBox(_('Always use HD images if present'), self) self.use_hd_images.setToolTip(_('<p>When checked... any HD images present in the kindlebook '+ 'will be used for creating the ePub.')) misc_group_box_layout.addWidget(self.use_hd_images) # Load the checkbox with the current preference setting self.use_hd_images.setChecked(plugin_prefs['Use_HD_Images']) combo_label = QLabel('Select epub version output:', self) misc_group_box_layout.addWidget(combo_label) self.epub_version_combobox = QComboBox() self.epub_version_combobox.setToolTip(_('<p>Select the type of OPF file to create.')) misc_group_box_layout.addWidget(self.epub_version_combobox) self.epub_version_combobox.addItems(['Auto-detect', 'ePub2', 'ePub3']) if plugin_prefs['Epub_Version'] == 'A': self.epub_version_combobox.setCurrentIndex(0) else: self.epub_version_combobox.setCurrentIndex(int(plugin_prefs['Epub_Version'])-1) def save_settings(self): # Save current dialog sttings back to JSON config file plugin_prefs['Unpack_Folder'] = unicode(self.directory_txtBox.displayText()) plugin_prefs['Always_Use_Unpack_Folder'] = self.default_folder_check.isChecked() plugin_prefs['Use_HD_Images'] = self.use_hd_images.isChecked() if unicode(self.epub_version_combobox.currentText()) == 'Auto-detect': plugin_prefs['Epub_Version'] = 'A' else: plugin_prefs['Epub_Version'] = unicode(self.epub_version_combobox.currentText())[4:] def getDirectory(self): c = choose_dir(self, _(PLUGIN_NAME + 'dir_chooser'), _('Select Default Directory To Unpack Kindle Book/Mobi To')) if c: self.directory_txtBox.setReadOnly(False) self.directory_txtBox.setText(c) self.directory_txtBox.setReadOnly(True) def validate(self): # This is just to catch the situation where somone might # manually enter a non-existent path in the Default path textbox. # Shouldn't be possible at this point. if not os.path.exists(self.directory_txtBox.text()): errmsg = '<p>The path specified for the Default Unpack folder does not exist.</p>' \ '<p>Your latest preference changes will <b>NOT</b> be saved!</p>' + \ '<p>You should configure again and make sure your settings are correct.' error_dialog(None, _(PLUGIN_NAME + ' v' + PLUGIN_VERSION), _(errmsg), show=True) return False return True
class CollectionsGroupBox(DeviceOptionsGroupBox): def __init__(self, parent, device): super(CollectionsGroupBox, self).__init__(parent, device) self.setTitle(_("Collections")) self.options_layout = QGridLayout() self.options_layout.setObjectName("options_layout") self.setLayout(self.options_layout) self.setCheckable(True) self.setChecked(device.get_pref('manage_collections')) self.setToolTip(wrap_msg(_('Create new bookshelves on the Kobo if they do not exist. This is only for firmware V2.0.0 or later.'))) self.collections_columns_label = QLabel(_('Collections Columns:')) self.collections_columns_edit = QLineEdit(self) self.collections_columns_edit.setToolTip(_('The Kobo from firmware V2.0.0 supports bookshelves.' ' These are created on the Kobo. ' + 'Specify a tags type column for automatic management.')) self.collections_columns_edit.setText(device.get_pref('collections_columns')) self.create_collections_checkbox = create_checkbox( _("Create Collections"), _('Create new bookshelves on the Kobo if they do not exist. This is only for firmware V2.0.0 or later.'), device.get_pref('create_collections') ) self.delete_empty_collections_checkbox = create_checkbox( _('Delete Empty Bookshelves'), _('Delete any empty bookshelves from the Kobo when syncing is finished. This is only for firmware V2.0.0 or later.'), device.get_pref('delete_empty_collections') ) self.ignore_collections_names_label = QLabel(_('Ignore Collections:')) self.ignore_collections_names_edit = QLineEdit(self) self.ignore_collections_names_edit.setToolTip(_('List the names of collections to be ignored by ' + 'the collection management. The collections listed ' + 'will not be changed. Names are separated by commas.')) self.ignore_collections_names_edit.setText(device.get_pref('ignore_collections_names')) self.options_layout.addWidget(self.collections_columns_label, 1, 0, 1, 1) self.options_layout.addWidget(self.collections_columns_edit, 1, 1, 1, 1) self.options_layout.addWidget(self.create_collections_checkbox, 2, 0, 1, 2) self.options_layout.addWidget(self.delete_empty_collections_checkbox, 3, 0, 1, 2) self.options_layout.addWidget(self.ignore_collections_names_label, 4, 0, 1, 1) self.options_layout.addWidget(self.ignore_collections_names_edit, 4, 1, 1, 1) self.options_layout.setRowStretch(4, 1) @property def manage_collections(self): return self.isChecked() @property def collections_columns(self): return self.collections_columns_edit.text().strip() @property def create_collections(self): return self.create_collections_checkbox.isChecked() @property def delete_empty_collections(self): return self.delete_empty_collections_checkbox.isChecked() @property def ignore_collections_names(self): return self.ignore_collections_names_edit.text().strip()
class ExtendedGroupBox(DeviceOptionsGroupBox): def __init__(self, parent, device): super(ExtendedGroupBox, self).__init__(parent, device, _("Extended driver")) self.options_layout = QGridLayout() self.options_layout.setObjectName("options_layout") self.setLayout(self.options_layout) self.extra_features_checkbox = create_checkbox( _('Enable Extended Kobo Features'), _('Choose whether to enable extra customizations'), device.get_pref('extra_features')) self.upload_encumbered_checkbox = create_checkbox( _('Upload DRM-encumbered ePub files'), _('Select this to upload ePub files encumbered by DRM. If this is ' 'not selected, it is a fatal error to upload an encumbered file'), device.get_pref('upload_encumbered')) self.skip_failed_checkbox = create_checkbox( _('Silently Ignore Failed Conversions'), _('Select this to not upload any book that fails conversion to ' 'kepub. If this is not selected, the upload process will be ' 'stopped at the first book that fails. If this is selected, ' 'failed books will be silently removed from the upload queue.'), device.get_pref('skip_failed')) self.hyphenate_checkbox = create_checkbox( _('Hyphenate Files'), _('Select this to add a CSS file which enables hyphenation. The ' 'language used will be the language defined for the book in ' 'calibre. Please see the README file for directions on updating ' 'hyphenation dictionaries.'), device.get_pref('hyphenate')) self.replace_lang_checkbox = create_checkbox( _('Replace Content Language Code'), _('Select this to replace the defined language in each content ' 'file inside the ePub.'), device.get_pref('replace_lang')) self.smarten_punctuation_checkbox = create_checkbox( _('Smarten Punctuation'), _('Select this to smarten punctuation in the ePub'), device.get_pref('smarten_punctuation')) self.clean_markup_checkbox = create_checkbox( _('Clean up ePub Markup'), _('Select this to clean up the internal ePub markup.'), device.get_pref('clean_markup')) self.file_copy_dir_checkbox = create_checkbox( _('Copy generated KePub files to a directory'), _('Enter an absolute directory path to copy all generated KePub ' 'files into for debugging purposes.'), device.get_pref('file_copy_dir')) self.file_copy_dir_label = QLabel(_( 'Copy generated KePub files to a directory')) self.file_copy_dir_edit = QLineEdit(self) self.file_copy_dir_edit.setToolTip( _('Enter an absolute directory path to copy all generated KePub ' 'files into for debugging purposes.')) self.file_copy_dir_edit.setText(device.get_pref('file_copy_dir')) self.file_copy_dir_label.setBuddy(self.file_copy_dir_edit) self.full_page_numbers_checkbox = create_checkbox( _('Use full book page numbers'), _('Select this to show page numbers for the whole book, instead of ' 'each chapter. This will also affect regular ePub page number ' 'display!'), device.get_pref('full_page_numbers')) self.disable_hyphenation_checkbox = create_checkbox( _('Disable hyphenation'), _('Select this to disable hyphenation for books.'), device.get_pref('disable_hyphenation')) self.options_layout.addWidget(self.extra_features_checkbox, 0, 0, 1, 1) self.options_layout.addWidget(self.upload_encumbered_checkbox, 0, 1, 1, 1) self.options_layout.addWidget(self.skip_failed_checkbox, 1, 0, 1, 1) self.options_layout.addWidget(self.hyphenate_checkbox, 1, 1, 1, 1) self.options_layout.addWidget(self.replace_lang_checkbox, 2, 0, 1, 1) self.options_layout.addWidget(self.smarten_punctuation_checkbox, 2, 1, 1, 1) self.options_layout.addWidget(self.clean_markup_checkbox, 3, 0, 1, 1) self.options_layout.addWidget(self.file_copy_dir_label, 4, 0, 1, 1) self.options_layout.addWidget(self.file_copy_dir_edit, 4, 1, 1, 1) self.options_layout.addWidget(self.full_page_numbers_checkbox, 5, 0, 1, 1) self.options_layout.addWidget(self.disable_hyphenation_checkbox, 5, 1, 1, 1) self.options_layout.setRowStretch(6, 2) @property def extra_features(self): return self.extra_features_checkbox.isChecked() @property def upload_encumbered(self): return self.upload_encumbered_checkbox.isChecked() @property def skip_failed(self): return self.skip_failed_checkbox.isChecked() @property def hyphenate(self): return self.hyphenate_checkbox.isChecked() @property def replace_lang(self): return self.replace_lang_checkbox.isChecked() @property def smarten_punctuation(self): return self.smarten_punctuation_checkbox.isChecked() @property def clean_markup(self): return self.clean_markup_checkbox.isChecked() @property def full_page_numbers(self): return self.full_page_numbers_checkbox.isChecked() @property def disable_hyphenation(self): return self.disable_hyphenation_checkbox.isChecked() @property def file_copy_dir(self): return self.file_copy_dir_edit.text().strip()
class CreateVirtualLibrary(QDialog): # {{{ def __init__(self, gui, existing_names, editing=None): QDialog.__init__(self, gui) self.gui = gui self.existing_names = existing_names if editing: self.setWindowTitle(_('Edit virtual library')) else: self.setWindowTitle(_('Create virtual library')) self.setWindowIcon(QIcon(I('lt.png'))) gl = QGridLayout() self.setLayout(gl) self.la1 = la1 = QLabel(_('Virtual library &name:')) gl.addWidget(la1, 0, 0) self.vl_name = QComboBox() self.vl_name.setEditable(True) self.vl_name.lineEdit().setMaxLength(MAX_VIRTUAL_LIBRARY_NAME_LENGTH) la1.setBuddy(self.vl_name) gl.addWidget(self.vl_name, 0, 1) self.editing = editing self.saved_searches_label = QLabel('') self.saved_searches_label.setTextInteractionFlags(Qt.TextSelectableByMouse) gl.addWidget(self.saved_searches_label, 2, 0, 1, 2) self.la2 = la2 = QLabel(_('&Search expression:')) gl.addWidget(la2, 1, 0) self.vl_text = QLineEdit() self.vl_text.textChanged.connect(self.search_text_changed) la2.setBuddy(self.vl_text) gl.addWidget(self.vl_text, 1, 1) self.vl_text.setText(_build_full_search_string(self.gui)) self.sl = sl = QLabel('<p>'+_('Create a virtual library based on: ')+ ('<a href="author.{0}">{0}</a>, ' '<a href="tag.{1}">{1}</a>, ' '<a href="publisher.{2}">{2}</a>, ' '<a href="series.{3}">{3}</a>, ' '<a href="search.{4}">{4}</a>.').format(_('Authors'), _('Tags'), _('Publishers'), _('Series'), _('Saved Searches'))) sl.setWordWrap(True) sl.setTextInteractionFlags(Qt.LinksAccessibleByMouse) sl.linkActivated.connect(self.link_activated) gl.addWidget(sl, 3, 0, 1, 2) gl.setRowStretch(3,10) self.hl = hl = QLabel(_(''' <h2>Virtual Libraries</h2> <p>Using <i>virtual libraries</i> you can restrict calibre to only show you books that match a search. When a virtual library is in effect, calibre behaves as though the library contains only the matched books. The Tag Browser display only the tags/authors/series/etc. that belong to the matched books and any searches you do will only search within the books in the virtual library. This is a good way to partition your large library into smaller and easier to work with subsets.</p> <p>For example you can use a Virtual Library to only show you books with the Tag <i>"Unread"</i> or only books by <i>"My Favorite Author"</i> or only books in a particular series.</p> <p>More information and examples are available in the <a href="%s">User Manual</a>.</p> ''') % localize_user_manual_link('http://manual.calibre-ebook.com/virtual_libraries.html')) hl.setWordWrap(True) hl.setOpenExternalLinks(True) hl.setFrameStyle(hl.StyledPanel) gl.addWidget(hl, 0, 3, 4, 1) bb = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) bb.accepted.connect(self.accept) bb.rejected.connect(self.reject) gl.addWidget(bb, 4, 0, 1, 0) if editing: db = self.gui.current_db virt_libs = db.prefs.get('virtual_libraries', {}) for dex,vl in enumerate(sorted(virt_libs.keys(), key=sort_key)): self.vl_name.addItem(vl, virt_libs.get(vl, '')) if vl == editing: self.vl_name.setCurrentIndex(dex) self.original_index = dex self.original_search = virt_libs.get(editing, '') self.vl_text.setText(self.original_search) self.new_name = editing self.vl_name.currentIndexChanged[int].connect(self.name_index_changed) self.vl_name.lineEdit().textEdited.connect(self.name_text_edited) self.resize(self.sizeHint()+QSize(150, 25)) def search_text_changed(self, txt): db = self.gui.current_db searches = [_('Saved searches recognized in the expression:')] txt = unicode(txt) while txt: p = txt.partition('search:') if p[1]: # found 'search:' possible_search = p[2] if possible_search: # something follows the 'search:' if possible_search[0] == '"': # strip any quotes possible_search = possible_search[1:].partition('"') else: # find end of the search name. Is EOL, space, rparen sp = possible_search.find(' ') pp = possible_search.find(')') if pp < 0 or (sp > 0 and sp <= pp): # space in string before rparen, or neither found possible_search = possible_search.partition(' ') else: # rparen in string before space possible_search = possible_search.partition(')') txt = possible_search[2] # grab remainder of the string search_name = possible_search[0] if search_name.startswith('='): search_name = search_name[1:] if search_name in db.saved_search_names(): searches.append(search_name + '=' + db.saved_search_lookup(search_name)) else: txt = '' else: txt = '' if len(searches) > 1: self.saved_searches_label.setText('\n'.join(searches)) else: self.saved_searches_label.setText('') def name_text_edited(self, new_name): self.new_name = unicode(new_name) def name_index_changed(self, dex): if self.editing and (self.vl_text.text() != self.original_search or self.new_name != self.editing): if not question_dialog(self.gui, _('Search text changed'), _('The virtual library name or the search text has changed. ' 'Do you want to discard these changes?'), default_yes=False): self.vl_name.blockSignals(True) self.vl_name.setCurrentIndex(self.original_index) self.vl_name.lineEdit().setText(self.new_name) self.vl_name.blockSignals(False) return self.new_name = self.editing = self.vl_name.currentText() self.original_index = dex self.original_search = unicode(self.vl_name.itemData(dex) or '') self.vl_text.setText(self.original_search) def link_activated(self, url): db = self.gui.current_db f, txt = unicode(url).partition('.')[0::2] if f == 'search': names = db.saved_search_names() else: names = getattr(db, 'all_%s_names'%f)() d = SelectNames(names, txt, parent=self) if d.exec_() == d.Accepted: prefix = f+'s' if f in {'tag', 'author'} else f if f == 'search': search = ['(%s)'%(db.saved_search_lookup(x)) for x in d.names] else: search = ['%s:"=%s"'%(prefix, x.replace('"', '\\"')) for x in d.names] if search: if not self.editing: self.vl_name.lineEdit().setText(d.names.next()) self.vl_name.lineEdit().setCursorPosition(0) self.vl_text.setText(d.match_type.join(search)) self.vl_text.setCursorPosition(0) def accept(self): n = unicode(self.vl_name.currentText()).strip() if not n: error_dialog(self.gui, _('No name'), _('You must provide a name for the new virtual library'), show=True) return if n.startswith('*'): error_dialog(self.gui, _('Invalid name'), _('A virtual library name cannot begin with "*"'), show=True) return if n in self.existing_names and n != self.editing: if not question_dialog(self.gui, _('Name already in use'), _('That name is already in use. Do you want to replace it ' 'with the new search?'), default_yes=False): return v = unicode(self.vl_text.text()).strip() if not v: error_dialog(self.gui, _('No search string'), _('You must provide a search to define the new virtual library'), show=True) return try: db = self.gui.library_view.model().db recs = db.data.search_getting_ids('', v, use_virtual_library=False, sort_results=False) except ParseException as e: error_dialog(self.gui, _('Invalid search'), _('The search in the search box is not valid'), det_msg=e.msg, show=True) return if not recs and not question_dialog( self.gui, _('Search found no books'), _('The search found no books, so the virtual library ' 'will be empty. Do you really want to use that search?'), default_yes=False): return self.library_name = n self.library_search = v QDialog.accept(self)
class ConfigWidget(QWidget, Ui_ConfigWidget): def __init__(self, settings, all_formats, supports_subdirs, supports_non_english_characters, must_read_metadata, supports_use_author_sort, extra_customization_message, device, extra_customization_choices=None): QWidget.__init__(self) Ui_ConfigWidget.__init__(self) self.setupUi(self) self.settings = settings all_formats = set(all_formats) self.calibre_known_formats = device.FORMATS try: self.device_name = device.get_gui_name() except TypeError: self.device_name = getattr(device, 'gui_name', None) or _('Device') if device.USER_CAN_ADD_NEW_FORMATS: all_formats = set(all_formats) | set(BOOK_EXTENSIONS) format_map = settings.format_map disabled_formats = list(set(all_formats).difference(format_map)) for format in format_map + list(sorted(disabled_formats)): item = QListWidgetItem(format, self.columns) item.setData(Qt.UserRole, (format)) item.setFlags(Qt.ItemIsEnabled|Qt.ItemIsUserCheckable|Qt.ItemIsSelectable) item.setCheckState(Qt.Checked if format in format_map else Qt.Unchecked) self.column_up.clicked.connect(self.up_column) self.column_down.clicked.connect(self.down_column) if device.HIDE_FORMATS_CONFIG_BOX: self.groupBox.hide() if supports_subdirs: self.opt_use_subdirs.setChecked(self.settings.use_subdirs) else: self.opt_use_subdirs.hide() if supports_non_english_characters: self.opt_asciiize.setChecked(self.settings.asciiize) else: self.opt_asciiize.hide() if not must_read_metadata: self.opt_read_metadata.setChecked(self.settings.read_metadata) else: self.opt_read_metadata.hide() if supports_use_author_sort: self.opt_use_author_sort.setChecked(self.settings.use_author_sort) else: self.opt_use_author_sort.hide() if extra_customization_message: extra_customization_choices = extra_customization_choices or {} def parse_msg(m): msg, _, tt = m.partition(':::') if m else ('', '', '') return msg.strip(), textwrap.fill(tt.strip(), 100) if isinstance(extra_customization_message, list): self.opt_extra_customization = [] if len(extra_customization_message) > 6: row_func = lambda x, y: ((x/2) * 2) + y col_func = lambda x: x%2 else: row_func = lambda x, y: x*2 + y col_func = lambda x: 0 for i, m in enumerate(extra_customization_message): label_text, tt = parse_msg(m) if not label_text: self.opt_extra_customization.append(None) continue if isinstance(settings.extra_customization[i], bool): self.opt_extra_customization.append(QCheckBox(label_text)) self.opt_extra_customization[-1].setToolTip(tt) self.opt_extra_customization[i].setChecked(bool(settings.extra_customization[i])) elif i in extra_customization_choices: cb = QComboBox(self) self.opt_extra_customization.append(cb) l = QLabel(label_text) l.setToolTip(tt), cb.setToolTip(tt), l.setBuddy(cb), cb.setToolTip(tt) for li in sorted(extra_customization_choices[i]): self.opt_extra_customization[i].addItem(li) cb.setCurrentIndex(max(0, cb.findText(settings.extra_customization[i]))) else: self.opt_extra_customization.append(QLineEdit(self)) l = QLabel(label_text) l.setToolTip(tt) self.opt_extra_customization[i].setToolTip(tt) l.setBuddy(self.opt_extra_customization[i]) l.setWordWrap(True) self.opt_extra_customization[i].setText(settings.extra_customization[i]) self.opt_extra_customization[i].setCursorPosition(0) self.extra_layout.addWidget(l, row_func(i, 0), col_func(i)) self.extra_layout.addWidget(self.opt_extra_customization[i], row_func(i, 1), col_func(i)) else: self.opt_extra_customization = QLineEdit() label_text, tt = parse_msg(extra_customization_message) l = QLabel(label_text) l.setToolTip(tt) l.setBuddy(self.opt_extra_customization) l.setWordWrap(True) if settings.extra_customization: self.opt_extra_customization.setText(settings.extra_customization) self.opt_extra_customization.setCursorPosition(0) self.opt_extra_customization.setCursorPosition(0) self.extra_layout.addWidget(l, 0, 0) self.extra_layout.addWidget(self.opt_extra_customization, 1, 0) self.opt_save_template.setText(settings.save_template) def up_column(self): idx = self.columns.currentRow() if idx > 0: self.columns.insertItem(idx-1, self.columns.takeItem(idx)) self.columns.setCurrentRow(idx-1) def down_column(self): idx = self.columns.currentRow() if idx < self.columns.count()-1: self.columns.insertItem(idx+1, self.columns.takeItem(idx)) self.columns.setCurrentRow(idx+1) def format_map(self): formats = [unicode(self.columns.item(i).data(Qt.UserRole) or '') for i in range(self.columns.count()) if self.columns.item(i).checkState()==Qt.Checked] return formats def use_subdirs(self): return self.opt_use_subdirs.isChecked() def read_metadata(self): return self.opt_read_metadata.isChecked() def use_author_sort(self): return self.opt_use_author_sort.isChecked() def asciiize(self): return self.opt_asciiize.isChecked() def validate(self): formats = set(self.format_map()) extra = formats - set(self.calibre_known_formats) if extra: fmts = sorted([x.upper() for x in extra]) if not question_dialog(self, _('Unknown formats'), _('You have enabled the <b>{0}</b> formats for' ' your {1}. The {1} may not support them.' ' If you send these formats to your {1} they ' 'may not work. Are you sure?').format( (', '.join(fmts)), self.device_name)): return False tmpl = unicode(self.opt_save_template.text()) try: validation_formatter.validate(tmpl) return True except Exception as err: error_dialog(self, _('Invalid template'), '<p>'+_('The template %s is invalid:')%tmpl + '<br>'+unicode(err), show=True) return False
class ItemEdit(QWidget): def __init__(self, parent, prefs=None): QWidget.__init__(self, parent) self.prefs = prefs or gprefs self.setLayout(QVBoxLayout()) self.la = la = QLabel('<b>'+_( 'Select a destination for the Table of Contents entry')) self.layout().addWidget(la) self.splitter = sp = QSplitter(self) self.layout().addWidget(sp) self.layout().setStretch(1, 10) sp.setOpaqueResize(False) sp.setChildrenCollapsible(False) self.dest_list = dl = QListWidget(self) dl.setMinimumWidth(250) dl.currentItemChanged.connect(self.current_changed) sp.addWidget(dl) w = self.w = QWidget(self) l = w.l = QGridLayout() w.setLayout(l) self.view = WebView(self) self.view.elem_clicked.connect(self.elem_clicked) l.addWidget(self.view, 0, 0, 1, 3) sp.addWidget(w) self.search_text = s = QLineEdit(self) s.setPlaceholderText(_('Search for text...')) l.addWidget(s, 1, 0) self.ns_button = b = QPushButton(QIcon(I('arrow-down.png')), _('Find &next'), self) b.clicked.connect(self.find_next) l.addWidget(b, 1, 1) self.ps_button = b = QPushButton(QIcon(I('arrow-up.png')), _('Find &previous'), self) l.addWidget(b, 1, 2) b.clicked.connect(self.find_previous) self.f = f = QFrame() f.setFrameShape(f.StyledPanel) f.setMinimumWidth(250) l = f.l = QVBoxLayout() f.setLayout(l) sp.addWidget(f) f.la = la = QLabel('<p>'+_( 'Here you can choose a destination for the Table of Contents\' entry' ' to point to. First choose a file from the book in the left-most panel. The' ' file will open in the central panel.<p>' 'Then choose a location inside the file. To do so, simply click on' ' the place in the central panel that you want to use as the' ' destination. As you move the mouse around the central panel, a' ' thick green line appears, indicating the precise location' ' that will be selected when you click.')) la.setStyleSheet('QLabel { margin-bottom: 20px }') la.setWordWrap(True) l.addWidget(la) f.la2 = la = QLabel('<b>'+_('&Name of the ToC entry:')) l.addWidget(la) self.name = QLineEdit(self) la.setBuddy(self.name) l.addWidget(self.name) self.base_msg = '<b>'+_('Currently selected destination:')+'</b>' self.dest_label = la = QLabel(self.base_msg) la.setWordWrap(True) la.setStyleSheet('QLabel { margin-top: 20px }') l.addWidget(la) l.addStretch() state = self.prefs.get('toc_edit_splitter_state', None) if state is not None: sp.restoreState(state) def keyPressEvent(self, ev): if ev.key() in (Qt.Key_Return, Qt.Key_Enter) and self.search_text.hasFocus(): # Prevent pressing enter in the search box from triggering the dialog's accept() method ev.accept() return return super(ItemEdit, self).keyPressEvent(ev) def find(self, forwards=True): text = unicode(self.search_text.text()).strip() flags = QWebPage.FindFlags(0) if forwards else QWebPage.FindBackward d = self.dest_list if d.count() == 1: flags |= QWebPage.FindWrapsAroundDocument if not self.view.findText(text, flags) and text: if d.count() == 1: return error_dialog(self, _('No match found'), _('No match found for: %s')%text, show=True) delta = 1 if forwards else -1 current = unicode(d.currentItem().data(Qt.DisplayRole) or '') next_index = (d.currentRow() + delta)%d.count() next = unicode(d.item(next_index).data(Qt.DisplayRole) or '') msg = '<p>'+_('No matches for %(text)s found in the current file [%(current)s].' ' Do you want to search in the %(which)s file [%(next)s]?') msg = msg%dict(text=text, current=current, next=next, which=_('next') if forwards else _('previous')) if question_dialog(self, _('No match found'), msg): self.pending_search = self.find_next if forwards else self.find_previous d.setCurrentRow(next_index) def find_next(self): return self.find() def find_previous(self): return self.find(forwards=False) def load(self, container): self.container = container spine_names = [container.abspath_to_name(p) for p in container.spine_items] spine_names = [n for n in spine_names if container.has_name(n)] self.dest_list.addItems(spine_names) def current_changed(self, item): name = self.current_name = unicode(item.data(Qt.DisplayRole) or '') self.current_frag = None path = self.container.name_to_abspath(name) # Ensure encoding map is populated root = self.container.parsed(name) nasty = root.xpath('//*[local-name()="head"]/*[local-name()="p"]') if nasty: body = root.xpath('//*[local-name()="body"]') if not body: return error_dialog(self, _('Bad markup'), _('This book has severely broken markup, its ToC cannot be edited.'), show=True) for x in reversed(nasty): body[0].insert(0, x) self.container.commit_item(name, keep_parsed=True) encoding = self.container.encoding_map.get(name, None) or 'utf-8' load_html(path, self.view, codec=encoding, mime_type=self.container.mime_map[name]) self.view.load_js() self.dest_label.setText(self.base_msg + '<br>' + _('File:') + ' ' + name + '<br>' + _('Top of the file')) if hasattr(self, 'pending_search'): f = self.pending_search del self.pending_search f() def __call__(self, item, where): self.current_item, self.current_where = item, where self.current_name = None self.current_frag = None self.name.setText(_('(Untitled)')) dest_index, frag = 0, None if item is not None: if where is None: self.name.setText(item.data(0, Qt.DisplayRole) or '') self.name.setCursorPosition(0) toc = item.data(0, Qt.UserRole) if toc.dest: for i in xrange(self.dest_list.count()): litem = self.dest_list.item(i) if unicode(litem.data(Qt.DisplayRole) or '') == toc.dest: dest_index = i frag = toc.frag break self.dest_list.blockSignals(True) self.dest_list.setCurrentRow(dest_index) self.dest_list.blockSignals(False) item = self.dest_list.item(dest_index) self.current_changed(item) if frag: self.current_frag = frag QTimer.singleShot(1, self.show_frag) def show_frag(self): self.view.show_frag(self.current_frag) QTimer.singleShot(1, self.check_frag) def check_frag(self): pos = self.view.scroll_frac if pos == 0: self.current_frag = None self.update_dest_label() def get_loctext(self, frac): frac = int(round(frac * 100)) if frac == 0: loctext = _('Top of the file') else: loctext = _('Approximately %d%% from the top')%frac return loctext def elem_clicked(self, tag, frac, elem_id, loc, totals): self.current_frag = elem_id or (loc, totals) base = _('Location: A <%s> tag inside the file')%tag loctext = base + ' [%s]'%self.get_loctext(frac) self.dest_label.setText(self.base_msg + '<br>' + _('File:') + ' ' + self.current_name + '<br>' + loctext) def update_dest_label(self): val = self.view.scroll_frac self.dest_label.setText(self.base_msg + '<br>' + _('File:') + ' ' + self.current_name + '<br>' + self.get_loctext(val)) @property def result(self): return (self.current_item, self.current_where, self.current_name, self.current_frag, unicode(self.name.text()))
class InputLine(QWidget): HEIGHT = 30 LABEL_WIDTH = 170 INPUT_TEXT_WIDTH = 200 BUTTON_WIDTH = 100 BUTTON_BLOCK_X = 380 ELEMENT_GAP = 5 ERROR_WIDTH = 250 ERROR_X = 595 def __init__(self, parent): super().__init__(parent) self.initUI() def initUI(self): component_stylesheet = """ .QPushButton { font-weight: bold; font-size: 13px; background-color:#E0E0E0; } .QPushButton:pressed { background-color:#CCCCCC; } .QLabel { padding-top: 7px; font-weight: bold; font-size: 14px; } .QLineEdit { font-weight: bold; font-size: 14px; } .QLabel#error { color: red; font-size: 12px; font-style: italic; padding-top: 0px; padding-left: 6px; } """ self.label = QLabel("", self) self.label.setStyleSheet(component_stylesheet) self.label.setAlignment(Qt.AlignRight) self.label.resize(InputLine.LABEL_WIDTH, InputLine.HEIGHT) self.label.move(0, 0) self.text = QLineEdit(self) self.text.setStyleSheet(component_stylesheet) self.text.resize(InputLine.INPUT_TEXT_WIDTH, InputLine.HEIGHT) self.text.move(InputLine.LABEL_WIDTH + InputLine.ELEMENT_GAP, 0) self.ok = QPushButton("OK", self) self.ok.setStyleSheet(component_stylesheet) self.ok.resize(InputLine.BUTTON_WIDTH, InputLine.HEIGHT) self.ok.move(InputLine.BUTTON_BLOCK_X, 0) self.cancel = QPushButton("Cancel", self) self.cancel.setStyleSheet(component_stylesheet) self.cancel.resize(InputLine.BUTTON_WIDTH, InputLine.HEIGHT) self.cancel.move(InputLine.BUTTON_BLOCK_X + InputLine.BUTTON_WIDTH + InputLine.ELEMENT_GAP, 0) self.error = QLabel("", self) self.error.setObjectName("error") self.error.setStyleSheet(component_stylesheet) self.error.resize(InputLine.ERROR_WIDTH, InputLine.HEIGHT) self.error.move(InputLine.ERROR_X, 0) """ listeners which control what happens on ok an cancel button presses """ def addOkListener(self, onOkEvent): self.ok.clicked.connect(onOkEvent) def addCancelListener(self, onCancelEvent): self.cancel.clicked.connect(onCancelEvent) """ utility methods of the widget """ def getText(self): return self.text.text() def setText(self, text): self.text.setText(text) def setLabelText(self, text): self.label.setText(text) def setErrorText(self, text): self.error.setText(text)
class PluginUpdaterDialog(SizePersistedDialog): initial_extra_size = QSize(350, 100) forum_label_text = _('Plugin homepage') def __init__(self, gui, initial_filter=FILTER_UPDATE_AVAILABLE): SizePersistedDialog.__init__(self, gui, 'Plugin Updater plugin:plugin updater dialog') self.gui = gui self.forum_link = None self.zip_url = None self.model = None self.do_restart = False self._initialize_controls() self._create_context_menu() try: display_plugins = read_available_plugins(raise_error=True) except Exception: display_plugins = [] import traceback error_dialog(self.gui, _('Update Check Failed'), _('Unable to reach the plugin index page.'), det_msg=traceback.format_exc(), show=True) if display_plugins: self.model = DisplayPluginModel(display_plugins) self.proxy_model = DisplayPluginSortFilterModel(self) self.proxy_model.setSourceModel(self.model) self.plugin_view.setModel(self.proxy_model) self.plugin_view.resizeColumnsToContents() self.plugin_view.selectionModel().currentRowChanged.connect(self._plugin_current_changed) self.plugin_view.doubleClicked.connect(self.install_button.click) self.filter_combo.setCurrentIndex(initial_filter) self._select_and_focus_view() else: self.filter_combo.setEnabled(False) # Cause our dialog size to be restored from prefs or created on first usage self.resize_dialog() def _initialize_controls(self): self.setWindowTitle(_('User plugins')) self.setWindowIcon(QIcon(I('plugins/plugin_updater.png'))) layout = QVBoxLayout(self) self.setLayout(layout) title_layout = ImageTitleLayout(self, 'plugins/plugin_updater.png', _('User plugins')) layout.addLayout(title_layout) header_layout = QHBoxLayout() layout.addLayout(header_layout) self.filter_combo = PluginFilterComboBox(self) self.filter_combo.setMinimumContentsLength(20) self.filter_combo.currentIndexChanged[int].connect(self._filter_combo_changed) la = QLabel(_('Filter list of &plugins')+':', self) la.setBuddy(self.filter_combo) header_layout.addWidget(la) header_layout.addWidget(self.filter_combo) header_layout.addStretch(10) # filter plugins by name la = QLabel(_('Filter by &name')+':', self) header_layout.addWidget(la) self.filter_by_name_lineedit = QLineEdit(self) la.setBuddy(self.filter_by_name_lineedit) self.filter_by_name_lineedit.setText("") self.filter_by_name_lineedit.textChanged.connect(self._filter_name_lineedit_changed) header_layout.addWidget(self.filter_by_name_lineedit) self.plugin_view = QTableView(self) self.plugin_view.horizontalHeader().setStretchLastSection(True) self.plugin_view.setSelectionBehavior(QAbstractItemView.SelectRows) self.plugin_view.setSelectionMode(QAbstractItemView.SingleSelection) self.plugin_view.setAlternatingRowColors(True) self.plugin_view.setSortingEnabled(True) self.plugin_view.setIconSize(QSize(28, 28)) layout.addWidget(self.plugin_view) details_layout = QHBoxLayout() layout.addLayout(details_layout) forum_label = self.forum_label = QLabel('') forum_label.setTextInteractionFlags(Qt.LinksAccessibleByMouse | Qt.LinksAccessibleByKeyboard) forum_label.linkActivated.connect(self._forum_label_activated) details_layout.addWidget(QLabel(_('Description')+':', self), 0, Qt.AlignLeft) details_layout.addWidget(forum_label, 1, Qt.AlignRight) self.description = QLabel(self) self.description.setFrameStyle(QFrame.Panel | QFrame.Sunken) self.description.setAlignment(Qt.AlignTop | Qt.AlignLeft) self.description.setMinimumHeight(40) self.description.setWordWrap(True) layout.addWidget(self.description) self.button_box = QDialogButtonBox(QDialogButtonBox.Close) self.button_box.rejected.connect(self.reject) self.finished.connect(self._finished) self.install_button = self.button_box.addButton(_('&Install'), QDialogButtonBox.AcceptRole) self.install_button.setToolTip(_('Install the selected plugin')) self.install_button.clicked.connect(self._install_clicked) self.install_button.setEnabled(False) self.configure_button = self.button_box.addButton(' '+_('&Customize plugin ')+' ', QDialogButtonBox.ResetRole) self.configure_button.setToolTip(_('Customize the options for this plugin')) self.configure_button.clicked.connect(self._configure_clicked) self.configure_button.setEnabled(False) layout.addWidget(self.button_box) def update_forum_label(self): txt = '' if self.forum_link: txt = '<a href="%s">%s</a>' % (self.forum_link, self.forum_label_text) self.forum_label.setText(txt) def _create_context_menu(self): self.plugin_view.setContextMenuPolicy(Qt.ActionsContextMenu) self.install_action = QAction(QIcon(I('plugins/plugin_upgrade_ok.png')), _('&Install'), self) self.install_action.setToolTip(_('Install the selected plugin')) self.install_action.triggered.connect(self._install_clicked) self.install_action.setEnabled(False) self.plugin_view.addAction(self.install_action) self.history_action = QAction(QIcon(I('chapters.png')), _('Version &history'), self) self.history_action.setToolTip(_('Show history of changes to this plugin')) self.history_action.triggered.connect(self._history_clicked) self.history_action.setEnabled(False) self.plugin_view.addAction(self.history_action) self.forum_action = QAction(QIcon(I('plugins/mobileread.png')), _('Plugin &forum thread'), self) self.forum_action.triggered.connect(self._forum_label_activated) self.forum_action.setEnabled(False) self.plugin_view.addAction(self.forum_action) sep1 = QAction(self) sep1.setSeparator(True) self.plugin_view.addAction(sep1) self.toggle_enabled_action = QAction(_('Enable/&disable plugin'), self) self.toggle_enabled_action.setToolTip(_('Enable or disable this plugin')) self.toggle_enabled_action.triggered.connect(self._toggle_enabled_clicked) self.toggle_enabled_action.setEnabled(False) self.plugin_view.addAction(self.toggle_enabled_action) self.uninstall_action = QAction(_('&Remove plugin'), self) self.uninstall_action.setToolTip(_('Uninstall the selected plugin')) self.uninstall_action.triggered.connect(self._uninstall_clicked) self.uninstall_action.setEnabled(False) self.plugin_view.addAction(self.uninstall_action) sep2 = QAction(self) sep2.setSeparator(True) self.plugin_view.addAction(sep2) self.donate_enabled_action = QAction(QIcon(I('donate.png')), _('Donate to developer'), self) self.donate_enabled_action.setToolTip(_('Donate to the developer of this plugin')) self.donate_enabled_action.triggered.connect(self._donate_clicked) self.donate_enabled_action.setEnabled(False) self.plugin_view.addAction(self.donate_enabled_action) sep3 = QAction(self) sep3.setSeparator(True) self.plugin_view.addAction(sep3) self.configure_action = QAction(QIcon(I('config.png')), _('&Customize plugin'), self) self.configure_action.setToolTip(_('Customize the options for this plugin')) self.configure_action.triggered.connect(self._configure_clicked) self.configure_action.setEnabled(False) self.plugin_view.addAction(self.configure_action) def _finished(self, *args): if self.model: update_plugins = list(filter(filter_upgradeable_plugins, self.model.display_plugins)) self.gui.recalc_update_label(len(update_plugins)) def _plugin_current_changed(self, current, previous): if current.isValid(): actual_idx = self.proxy_model.mapToSource(current) display_plugin = self.model.display_plugins[actual_idx.row()] self.description.setText(display_plugin.description) self.forum_link = display_plugin.forum_link self.zip_url = display_plugin.zip_url self.forum_action.setEnabled(bool(self.forum_link)) self.install_button.setEnabled(display_plugin.is_valid_to_install()) self.install_action.setEnabled(self.install_button.isEnabled()) self.uninstall_action.setEnabled(display_plugin.is_installed()) self.history_action.setEnabled(display_plugin.has_changelog) self.configure_button.setEnabled(display_plugin.is_installed()) self.configure_action.setEnabled(self.configure_button.isEnabled()) self.toggle_enabled_action.setEnabled(display_plugin.is_installed()) self.donate_enabled_action.setEnabled(bool(display_plugin.donation_link)) else: self.description.setText('') self.forum_link = None self.zip_url = None self.forum_action.setEnabled(False) self.install_button.setEnabled(False) self.install_action.setEnabled(False) self.uninstall_action.setEnabled(False) self.history_action.setEnabled(False) self.configure_button.setEnabled(False) self.configure_action.setEnabled(False) self.toggle_enabled_action.setEnabled(False) self.donate_enabled_action.setEnabled(False) self.update_forum_label() def _donate_clicked(self): plugin = self._selected_display_plugin() if plugin and plugin.donation_link: open_url(QUrl(plugin.donation_link)) def _select_and_focus_view(self, change_selection=True): if change_selection and self.plugin_view.model().rowCount() > 0: self.plugin_view.selectRow(0) else: idx = self.plugin_view.selectionModel().currentIndex() self._plugin_current_changed(idx, 0) self.plugin_view.setFocus() def _filter_combo_changed(self, idx): self.filter_by_name_lineedit.setText("") # clear the name filter text when a different group was selected self.proxy_model.set_filter_criteria(idx) if idx == FILTER_NOT_INSTALLED: self.plugin_view.sortByColumn(5, Qt.DescendingOrder) else: self.plugin_view.sortByColumn(0, Qt.AscendingOrder) self._select_and_focus_view() def _filter_name_lineedit_changed(self, text): self.proxy_model.set_filter_text(text) # set the filter text for filterAcceptsRow def _forum_label_activated(self): if self.forum_link: open_url(QUrl(self.forum_link)) def _selected_display_plugin(self): idx = self.plugin_view.selectionModel().currentIndex() actual_idx = self.proxy_model.mapToSource(idx) return self.model.display_plugins[actual_idx.row()] def _uninstall_plugin(self, name_to_remove): if DEBUG: prints('Removing plugin: ', name_to_remove) remove_plugin(name_to_remove) # Make sure that any other plugins that required this plugin # to be uninstalled first have the requirement removed for display_plugin in self.model.display_plugins: # Make sure we update the status and display of the # plugin we just uninstalled if name_to_remove in display_plugin.uninstall_plugins: if DEBUG: prints('Removing uninstall dependency for: ', display_plugin.name) display_plugin.uninstall_plugins.remove(name_to_remove) if display_plugin.qname == name_to_remove: if DEBUG: prints('Resetting plugin to uninstalled status: ', display_plugin.name) display_plugin.installed_version = None display_plugin.plugin = None display_plugin.uninstall_plugins = [] if self.proxy_model.filter_criteria not in [FILTER_INSTALLED, FILTER_UPDATE_AVAILABLE]: self.model.refresh_plugin(display_plugin) def _uninstall_clicked(self): display_plugin = self._selected_display_plugin() if not question_dialog(self, _('Are you sure?'), '<p>'+ _('Are you sure you want to uninstall the <b>%s</b> plugin?')%display_plugin.name, show_copy_button=False): return self._uninstall_plugin(display_plugin.qname) if self.proxy_model.filter_criteria in [FILTER_INSTALLED, FILTER_UPDATE_AVAILABLE]: self.model.beginResetModel(), self.model.endResetModel() self._select_and_focus_view() else: self._select_and_focus_view(change_selection=False) def _install_clicked(self): display_plugin = self._selected_display_plugin() if not question_dialog(self, _('Install %s')%display_plugin.name, '<p>' + _('Installing plugins is a <b>security risk</b>. ' 'Plugins can contain a virus/malware. ' 'Only install it if you got it from a trusted source.' ' Are you sure you want to proceed?'), show_copy_button=False): return if display_plugin.uninstall_plugins: uninstall_names = list(display_plugin.uninstall_plugins) if DEBUG: prints('Uninstalling plugin: ', ', '.join(uninstall_names)) for name_to_remove in uninstall_names: self._uninstall_plugin(name_to_remove) plugin_zip_url = display_plugin.zip_url if DEBUG: prints('Downloading plugin ZIP attachment: ', plugin_zip_url) self.gui.status_bar.showMessage(_('Downloading plugin ZIP attachment: %s') % plugin_zip_url) zip_path = self._download_zip(plugin_zip_url) if DEBUG: prints('Installing plugin: ', zip_path) self.gui.status_bar.showMessage(_('Installing plugin: %s') % zip_path) do_restart = False try: from calibre.customize.ui import config installed_plugins = frozenset(config['plugins']) try: plugin = add_plugin(zip_path) except NameConflict as e: return error_dialog(self.gui, _('Already exists'), unicode_type(e), show=True) # Check for any toolbars to add to. widget = ConfigWidget(self.gui) widget.gui = self.gui widget.check_for_add_to_toolbars(plugin, previously_installed=plugin.name in installed_plugins) self.gui.status_bar.showMessage(_('Plugin installed: %s') % display_plugin.name) d = info_dialog(self.gui, _('Success'), _('Plugin <b>{0}</b> successfully installed under <b>' ' {1} plugins</b>. You may have to restart calibre ' 'for the plugin to take effect.').format(plugin.name, plugin.type), show_copy_button=False) b = d.bb.addButton(_('&Restart calibre now'), d.bb.AcceptRole) b.setIcon(QIcon(I('lt.png'))) d.do_restart = False def rf(): d.do_restart = True b.clicked.connect(rf) d.set_details('') d.exec_() b.clicked.disconnect() do_restart = d.do_restart display_plugin.plugin = plugin # We cannot read the 'actual' version information as the plugin will not be loaded yet display_plugin.installed_version = display_plugin.available_version except: if DEBUG: prints('ERROR occurred while installing plugin: %s'%display_plugin.name) traceback.print_exc() error_dialog(self.gui, _('Install plugin failed'), _('A problem occurred while installing this plugin.' ' This plugin will now be uninstalled.' ' Please post the error message in details below into' ' the forum thread for this plugin and restart calibre.'), det_msg=traceback.format_exc(), show=True) if DEBUG: prints('Due to error now uninstalling plugin: %s'%display_plugin.name) remove_plugin(display_plugin.name) display_plugin.plugin = None display_plugin.uninstall_plugins = [] if self.proxy_model.filter_criteria in [FILTER_NOT_INSTALLED, FILTER_UPDATE_AVAILABLE]: self.model.beginResetModel(), self.model.endResetModel() self._select_and_focus_view() else: self.model.refresh_plugin(display_plugin) self._select_and_focus_view(change_selection=False) if do_restart: self.do_restart = True self.accept() def _history_clicked(self): display_plugin = self._selected_display_plugin() text = self._read_version_history_html(display_plugin.forum_link) if text: dlg = VersionHistoryDialog(self, display_plugin.name, text) dlg.exec_() else: return error_dialog(self, _('Version history missing'), _('Unable to find the version history for %s')%display_plugin.name, show=True) def _configure_clicked(self): display_plugin = self._selected_display_plugin() plugin = display_plugin.plugin if not plugin.is_customizable(): return info_dialog(self, _('Plugin not customizable'), _('Plugin: %s does not need customization')%plugin.name, show=True) from calibre.customize import InterfaceActionBase if isinstance(plugin, InterfaceActionBase) and not getattr(plugin, 'actual_iaction_plugin_loaded', False): return error_dialog(self, _('Must restart'), _('You must restart calibre before you can' ' configure the <b>%s</b> plugin')%plugin.name, show=True) plugin.do_user_config(self.parent()) def _toggle_enabled_clicked(self): display_plugin = self._selected_display_plugin() plugin = display_plugin.plugin if not plugin.can_be_disabled: return error_dialog(self,_('Plugin cannot be disabled'), _('The plugin: %s cannot be disabled')%plugin.name, show=True) if is_disabled(plugin): enable_plugin(plugin) else: disable_plugin(plugin) self.model.refresh_plugin(display_plugin) def _read_version_history_html(self, forum_link): br = browser() br.set_handle_gzip(True) try: raw = br.open_novisit(forum_link).read() if not raw: return None except: traceback.print_exc() return None raw = raw.decode('utf-8', errors='replace') root = html.fromstring(raw) spoiler_nodes = root.xpath('//div[@class="smallfont" and strong="Spoiler"]') for spoiler_node in spoiler_nodes: try: if spoiler_node.getprevious() is None: # This is a spoiler node that has been indented using [INDENT] # Need to go up to parent div, then previous node to get header heading_node = spoiler_node.getparent().getprevious() else: # This is a spoiler node after a BR tag from the heading heading_node = spoiler_node.getprevious().getprevious() if heading_node is None: continue if heading_node.text_content().lower().find('version history') != -1: div_node = spoiler_node.xpath('div')[0] text = html.tostring(div_node, method='html', encoding='unicode') return re.sub(r'<div\s.*?>', '<div>', text) except: if DEBUG: prints('======= MobileRead Parse Error =======') traceback.print_exc() prints(html.tostring(spoiler_node)) return None def _download_zip(self, plugin_zip_url): from calibre.ptempfile import PersistentTemporaryFile raw = get_https_resource_securely(plugin_zip_url, headers={'User-Agent':'%s %s' % (__appname__, __version__)}) with PersistentTemporaryFile('.zip') as pt: pt.write(raw) return pt.name
class AddEmptyBookDialog(QDialog): def __init__(self, parent, db, author, series=None, title=None): QDialog.__init__(self, parent) self.db = db self.setWindowTitle(_('How many empty books?')) self._layout = QGridLayout(self) self.setLayout(self._layout) self.qty_label = QLabel(_('How many empty books should be added?')) self._layout.addWidget(self.qty_label, 0, 0, 1, 2) self.qty_spinbox = QSpinBox(self) self.qty_spinbox.setRange(1, 10000) self.qty_spinbox.setValue(1) self._layout.addWidget(self.qty_spinbox, 1, 0, 1, 2) self.author_label = QLabel(_('Set the author of the new books to:')) self._layout.addWidget(self.author_label, 2, 0, 1, 2) self.authors_combo = EditWithComplete(self) self.authors_combo.setSizeAdjustPolicy( self.authors_combo.AdjustToMinimumContentsLengthWithIcon) self.authors_combo.setEditable(True) self._layout.addWidget(self.authors_combo, 3, 0, 1, 1) self.initialize_authors(db, author) self.clear_button = QToolButton(self) self.clear_button.setIcon(QIcon(I('trash.png'))) self.clear_button.setToolTip(_('Reset author to Unknown')) self.clear_button.clicked.connect(self.reset_author) self._layout.addWidget(self.clear_button, 3, 1, 1, 1) self.series_label = QLabel(_('Set the series of the new books to:')) self._layout.addWidget(self.series_label, 4, 0, 1, 2) self.series_combo = EditWithComplete(self) self.series_combo.setSizeAdjustPolicy( self.authors_combo.AdjustToMinimumContentsLengthWithIcon) self.series_combo.setEditable(True) self._layout.addWidget(self.series_combo, 5, 0, 1, 1) self.initialize_series(db, series) self.sclear_button = QToolButton(self) self.sclear_button.setIcon(QIcon(I('trash.png'))) self.sclear_button.setToolTip(_('Reset series')) self.sclear_button.clicked.connect(self.reset_series) self._layout.addWidget(self.sclear_button, 5, 1, 1, 1) self.title_label = QLabel(_('Set the title of the new books to:')) self._layout.addWidget(self.title_label, 6, 0, 1, 2) self.title_edit = QLineEdit(self) self.title_edit.setText(title or '') self._layout.addWidget(self.title_edit, 7, 0, 1, 1) self.tclear_button = QToolButton(self) self.tclear_button.setIcon(QIcon(I('trash.png'))) self.tclear_button.setToolTip(_('Reset title')) self.tclear_button.clicked.connect(self.title_edit.clear) self._layout.addWidget(self.tclear_button, 7, 1, 1, 1) self.format_label = QLabel(_('Also create an empty ebook in format:')) self._layout.addWidget(self.format_label, 8, 0, 1, 2) c = self.format_value = QComboBox(self) from calibre.ebooks.oeb.polish.create import valid_empty_formats possible_formats = [''] + sorted(x.upper() for x in valid_empty_formats) c.addItems(possible_formats) c.setToolTip(_('Also create an empty book format file that you can subsequently edit')) if gprefs.get('create_empty_epub_file', False): # Migration of the check box gprefs.set('create_empty_format_file', 'epub') del gprefs['create_empty_epub_file'] use_format = gprefs.get('create_empty_format_file', '').upper() try: c.setCurrentIndex(possible_formats.index(use_format)) except Exception: pass self._layout.addWidget(c, 9, 0, 1, 1) button_box = self.bb = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) button_box.accepted.connect(self.accept) button_box.rejected.connect(self.reject) self._layout.addWidget(button_box, 10, 0, 1, -1) self.resize(self.sizeHint()) def accept(self): gprefs['create_empty_format_file'] = self.format_value.currentText().lower() return QDialog.accept(self) def reset_author(self, *args): self.authors_combo.setEditText(_('Unknown')) def reset_series(self): self.series_combo.setEditText('') def initialize_authors(self, db, author): au = author if not au: au = _('Unknown') self.authors_combo.show_initial_value(au.replace('|', ',')) self.authors_combo.set_separator('&') self.authors_combo.set_space_before_sep(True) self.authors_combo.set_add_separator(tweaks['authors_completer_append_separator']) self.authors_combo.update_items_cache(db.all_author_names()) def initialize_series(self, db, series): self.series_combo.show_initial_value(series or '') self.series_combo.update_items_cache(db.all_series_names()) self.series_combo.set_separator(None) @property def qty_to_add(self): return self.qty_spinbox.value() @property def selected_authors(self): return string_to_authors(unicode(self.authors_combo.text())) @property def selected_series(self): return unicode(self.series_combo.text()) @property def selected_title(self): return self.title_edit.text().strip()