class ConfigWidget(QWidget): def __init__(self): QWidget.__init__(self) self.vl = QVBoxLayout() self.setLayout(self.vl) self.xray_button = QRadioButton('X-Ray', self) self.xray_button.setChecked(prefs['x-ray']) self.vl.addWidget(self.xray_button) self.donate_button = QPushButton('Donate', self) self.donate_button.clicked.connect(self.donate) self.vl.addWidget(self.donate_button) self.github_button = QPushButton('Source code', self) self.github_button.clicked.connect(self.github) self.vl.addWidget(self.github_button) def donate(self): webbrowser.open('https://liberapay.com/xxyzz/donate') def github(self): webbrowser.open('https://github.com/xxyzz/WordDumb') def save_settings(self): prefs['x-ray'] = self.xray_button.isChecked()
class SelectNames(QDialog): # {{{ def __init__(self, names, txt, parent=None): QDialog.__init__(self, parent) self.l = l = QVBoxLayout(self) self.setLayout(l) self.la = la = QLabel(_('Create a Virtual library based on %s') % txt) l.addWidget(la) self._names = QListWidget(self) self._names.addItems(sorted(names, key=sort_key)) self._names.setSelectionMode( QAbstractItemView.SelectionMode.MultiSelection) l.addWidget(self._names) self._or = QRadioButton(_('Match any of the selected %s') % txt) self._and = QRadioButton(_('Match all of the selected %s') % txt) self._or.setChecked(True) l.addWidget(self._or) l.addWidget(self._and) self.bb = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel) self.bb.accepted.connect(self.accept) self.bb.rejected.connect(self.reject) l.addWidget(self.bb) self.resize(self.sizeHint()) @property def names(self): for item in self._names.selectedItems(): yield unicode_type(item.data(Qt.ItemDataRole.DisplayRole) or '') @property def match_type(self): return ' and ' if self._and.isChecked() else ' or '
class SelectNames(QDialog): # {{{ def __init__(self, names, txt, parent=None): QDialog.__init__(self, parent) self.l = l = QVBoxLayout(self) self.setLayout(l) self.la = la = QLabel(_('Create a Virtual Library based on %s') % txt) l.addWidget(la) self._names = QListWidget(self) self._names.addItems(sorted(names, key=sort_key)) self._names.setSelectionMode(self._names.ExtendedSelection) l.addWidget(self._names) self._or = QRadioButton(_('Match any of the selected %s names')%txt) self._and = QRadioButton(_('Match all of the selected %s names')%txt) self._or.setChecked(True) l.addWidget(self._or) l.addWidget(self._and) self.bb = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.bb.accepted.connect(self.accept) self.bb.rejected.connect(self.reject) l.addWidget(self.bb) self.resize(self.sizeHint()) @property def names(self): for item in self._names.selectedItems(): yield unicode(item.data(Qt.DisplayRole) or '') @property def match_type(self): return ' and ' if self._and.isChecked() else ' or '
class SyncWidget(QWidget): def __init__(self, gui, do_user_config, selected_book_ids, is_sync_selected): QWidget.__init__(self, gui) api.build_request('/limits') self.logger = Logger( path.join(gui.current_db.library_path, 'bookfusion_sync.log')) self.logger.info( 'Open sync dialog: selected_book_ids={}; is_sync_selected={}'. format(selected_book_ids, is_sync_selected)) if len(selected_book_ids) == 0: is_sync_selected = False self.worker_thread = None self.do_user_config = do_user_config self.db = gui.current_db.new_api self.selected_book_ids = selected_book_ids self.l = QVBoxLayout() self.l.setContentsMargins(0, 0, 0, 0) self.setLayout(self.l) self.radio_layout = QVBoxLayout() self.l.addLayout(self.radio_layout) self.sync_all_radio = QRadioButton('Sync all books') self.sync_all_radio.setChecked(not is_sync_selected) self.radio_layout.addWidget(self.sync_all_radio) sync_selected_radio_label = 'Sync selected books' if len(selected_book_ids) > 0: sync_selected_radio_label = 'Sync {} selected {}'.format( len(selected_book_ids), 'book' if len(selected_book_ids) == 1 else 'books') self.sync_selected_radio = QRadioButton(sync_selected_radio_label) self.sync_selected_radio.toggled.connect(self.toggle_sync_selected) self.sync_selected_radio.setChecked(is_sync_selected) self.sync_selected_radio.setEnabled(len(selected_book_ids) > 0) self.radio_layout.addWidget(self.sync_selected_radio) self.reupload_possible = len(selected_book_ids) > 0 and len( selected_book_ids) <= 100 if self.reupload_possible: for book_id in selected_book_ids: identifiers = self.db.get_proxy_metadata(book_id).identifiers if not identifiers.get('bookfusion'): self.reupload_possible = False self.reupload_checkbox = QCheckBox('Re-upload book files', self) self.reupload_checkbox.setVisible(is_sync_selected and self.reupload_possible) self.radio_layout.addWidget(self.reupload_checkbox) self.btn_layout = QHBoxLayout() self.l.addLayout(self.btn_layout) self.config_btn = QPushButton('Configure') self.config_btn.clicked.connect(self.config) self.btn_layout.addWidget(self.config_btn) self.btn_layout.addStretch() self.start_btn = QPushButton('Start') self.start_btn.clicked.connect(self.start) self.btn_layout.addWidget(self.start_btn) self.cancel_btn = QPushButton('Cancel') self.cancel_btn.clicked.connect(self.cancel) self.cancel_btn.hide() self.btn_layout.addWidget(self.cancel_btn) self.info = QHBoxLayout() self.info.setContentsMargins(0, 0, 0, 0) self.l.addLayout(self.info) self.msg = QLabel() self.info.addWidget(self.msg) self.info.addStretch() self.log_btn = QLabel('<a href="#">Log</a>') self.log_btn.linkActivated.connect(self.toggle_log) self.log_btn.hide() self.info.addWidget(self.log_btn) self.log = QTableWidget(0, 2) self.log.setHorizontalHeaderLabels(['Book', 'Message']) self.log.horizontalHeader().setStretchLastSection(True) self.log.hide() self.l.addWidget(self.log) self.apply_config() def __del__(self): if self.worker_thread: self.worker_thread.quit() self.worker_thread.terminate() def config(self): self.do_user_config(parent=self) self.apply_config() def apply_config(self): configured = bool(prefs['api_key']) self.start_btn.setEnabled(configured) def toggle_sync_selected(self, is_sync_selected): if hasattr(self, 'reupload_checkbox'): self.reupload_checkbox.setVisible(is_sync_selected and self.reupload_possible) def start(self): if self.sync_selected_radio.isChecked( ) and self.reupload_checkbox.isChecked(): reply = QMessageBox.question( self, 'BookFusion Sync', 'Re-uploading book files can potentially result in previous highlights or bookmarks no longer working.\n\nPreviously uploaded files will be overwritten. Are you sure you want to re-upload?', QMessageBox.No | QMessageBox.Yes, QMessageBox.Yes) if reply != QMessageBox.Yes: return self.worker = None self.valid_book_ids = None self.book_log_map = {} self.book_progress_map = {} if self.sync_selected_radio.isChecked(): book_ids = list(self.selected_book_ids) else: book_ids = list(self.db.all_book_ids()) self.logger.info('Start sync: sync_selected={}; book_ids={}'.format( self.sync_selected_radio.isChecked(), book_ids)) self.in_progress = True self.total = len(book_ids) self.update_progress(None) self.start_btn.hide() self.cancel_btn.show() self.config_btn.setEnabled(False) self.sync_all_radio.setEnabled(False) self.sync_selected_radio.setEnabled(False) self.worker_thread = QThread(self) self.worker = CheckWorker(self.db, self.logger, book_ids) self.worker.finished.connect(self.finish_check) self.worker.finished.connect(self.worker_thread.quit) self.worker.progress.connect(self.update_progress) self.worker.limitsAvailable.connect(self.apply_limits) self.worker.resultsAvailable.connect(self.apply_results) self.worker.aborted.connect(self.abort) self.worker.moveToThread(self.worker_thread) self.worker_thread.started.connect(self.worker.start) self.worker_thread.start() def apply_limits(self, limits): self.logger.info('Limits: {}'.format(limits)) self.limits = limits def apply_results(self, books_count, valid_ids): self.logger.info('Check results: books_count={}; valid_ids={}'.format( books_count, valid_ids)) self.valid_book_ids = valid_ids self.books_count = books_count def finish_check(self): if self.valid_book_ids: is_filesize_exceeded = len(self.valid_book_ids) < self.books_count is_total_books_exceeded = self.limits[ 'total_books'] and self.books_count > self.limits['total_books'] if is_filesize_exceeded or is_total_books_exceeded: if self.limits['message']: msg_box = QMessageBox(self) msg_box.setWindowTitle('BookFusion Sync') msg_box.addButton(QMessageBox.No) msg_box.addButton(QMessageBox.Yes) msg_box.setText(self.limits['message']) msg_box.setDefaultButton(QMessageBox.Yes) reply = msg_box.exec_() if reply == QMessageBox.Yes: self.start_sync() else: self.in_progress = False self.msg.setText('Canceled.') self.finish_sync() else: self.start_sync() else: self.start_sync() else: if self.in_progress: self.in_progress = False self.msg.setText('No supported books selected.') self.finish_sync() def start_sync(self): self.log_btn.show() self.log.setRowCount(0) self.log.show() self.worker_thread = QThread(self) book_ids = self.valid_book_ids if self.limits['total_books']: book_ids = book_ids[:self.limits['total_books']] self.total = len(book_ids) self.worker = UploadManager( self.db, self.logger, book_ids, self.sync_selected_radio.isChecked() and self.reupload_checkbox.isChecked()) self.worker.finished.connect(self.finish_sync) self.worker.finished.connect(self.worker_thread.quit) self.worker.progress.connect(self.update_progress) self.worker.uploadProgress.connect(self.update_upload_progress) self.worker.started.connect(self.log_start) self.worker.skipped.connect(self.log_skip) self.worker.failed.connect(self.log_fail) self.worker.uploaded.connect(self.log_upload) self.worker.updated.connect(self.log_update) self.worker.aborted.connect(self.abort) self.worker.moveToThread(self.worker_thread) self.worker_thread.started.connect(self.worker.start) self.worker_thread.start() def finish_sync(self): if self.in_progress: self.msg.setText('Done.') self.cancel_btn.hide() self.cancel_btn.setEnabled(True) self.start_btn.show() self.config_btn.setEnabled(True) self.sync_all_radio.setEnabled(True) self.sync_selected_radio.setEnabled(len(self.selected_book_ids) > 0) def abort(self, error): self.in_progress = False self.msg.setText(error) def cancel(self): self.in_progress = False self.msg.setText('Canceled.') self.cancel_btn.setEnabled(False) self.worker.cancel() def update_progress(self, progress): if self.in_progress: if isinstance(self.worker, UploadManager): msg = 'Synchronizing...' else: msg = 'Preparing...' if progress: msg += ' {} of {}'.format(progress + 1, self.total) self.msg.setText(msg) def update_upload_progress(self, book_id, sent, total): if not book_id in self.book_progress_map: return progress = self.book_progress_map[book_id] if sent < total: progress.setValue(sent) progress.setMaximum(total) else: progress.setMaximum(0) def log_start(self, book_id): self.update_log(book_id, None) def log_fail(self, book_id, msg): self.update_log(book_id, msg) def log_skip(self, book_id): self.update_log(book_id, 'skipped') def log_upload(self, book_id): self.update_log(book_id, 'uploaded') def log_update(self, book_id): self.update_log(book_id, 'updated') def toggle_log(self, _): self.log.setVisible(not self.log.isVisible()) def update_log(self, book_id, msg): if book_id in self.book_log_map: index = self.book_log_map[book_id] else: index = self.log.rowCount() self.book_log_map[book_id] = index self.log.insertRow(index) title = self.db.get_proxy_metadata(book_id).title title_item = QTableWidgetItem(title) title_item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemNeverHasChildren) self.log.setItem(index, 0, title_item) progress = QProgressBar() progress.setMaximum(0) self.log.setCellWidget(index, 1, progress) self.book_progress_map[book_id] = progress if not msg is None: del (self.book_progress_map[book_id]) self.log.setCellWidget(index, 1, None) msg_item = QTableWidgetItem(msg) msg_item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemNeverHasChildren) self.log.setItem(index, 1, msg_item) def maybe_cancel(self): if self.worker_thread and self.worker_thread.isRunning(): reply = QMessageBox.question( self, 'BookFusion Sync', 'Are you sure you want to cancel the currently running process?', QMessageBox.No | QMessageBox.Yes, QMessageBox.Yes) if reply == QMessageBox.Yes: self.cancel() else: return False return True
class LLT_MC(QMainWindow): def __init__(self, dicTitle, dicList): super(LLT_MC, self).__init__() self.w = QWidget() self.setCentralWidget(self.w) self.title = dicTitle self.wordList = dicList self.setWindowTitle("Multiple Choice") self.setGeometry(0, 0, 300, 400) self.currentWordS = random.sample(self.wordList, 3) self.currentQWord = self.currentWordS[0][0] self.currentAWord = self.currentWordS[0][1] self.topLabel = QLabel("Multiple Choice: " + str(dicTitle)) self.wordLabel = QLabel() self.wordLabel.setFont(QFont('Times New Roman', 16)) self.wordLabel.setAlignment(Qt.AlignCenter) self.resultLabel = QLabel() self.resultLabel.setAlignment(Qt.AlignCenter) self.aRad = QRadioButton() self.bRad = QRadioButton() self.cRad = QRadioButton() self.group = QButtonGroup() self.group.addButton(self.aRad) self.group.addButton(self.bRad) self.group.addButton(self.cRad) self.checkBut = QPushButton("Check") self.checkBut.setMinimumWidth(100) self.checkBut.setMinimumHeight(70) self.checkBut.clicked.connect(self.check) self.nextBut = QPushButton("Next Word") self.nextBut.setMinimumWidth(100) self.nextBut.setMinimumHeight(70) self.nextBut.clicked.connect(self.next) self.exitBut = QPushButton("Exit") self.exitBut.setMinimumWidth(130) self.exitBut.setMinimumHeight(70) self.exitBut.clicked.connect(self.exit) self.hbox = QHBoxLayout() self.hbox.addWidget(self.checkBut) self.hbox.addWidget(self.nextBut) self.vbox = QVBoxLayout() self.vbox.addWidget(self.topLabel) self.vbox.addWidget(self.wordLabel) self.vbox.addWidget(self.aRad) self.vbox.addWidget(self.bRad) self.vbox.addWidget(self.cRad) self.vbox.addWidget(self.resultLabel) self.vbox.addLayout(self.hbox) self.vbox.addWidget(self.exitBut) self.w.setLayout(self.vbox) self.setWord() #define methods def setWord(self): newText = [] for item in self.currentWordS: newText.append(item[1]) random.shuffle(newText) self.group.setExclusive(False) self.aRad.setText(newText[0]) self.bRad.setText(newText[1]) self.cRad.setText(newText[2]) self.wordLabel.setText(self.currentQWord) self.group.setExclusive(True) def check(self): if self.aRad.isChecked() and self.aRad.text() == self.currentAWord: self.resultLabel.setText("CORRECT") elif self.aRad.isChecked() and self.aRad.text() != self.currentAWord: self.resultLabel.setText("Try again") if self.bRad.isChecked() and self.bRad.text() == self.currentAWord: self.resultLabel.setText("CORRECT") elif self.bRad.isChecked() and self.bRad.text() != self.currentAWord: self.resultLabel.setText("Try again") if self.cRad.isChecked() and self.cRad.text() == self.currentAWord: self.resultLabel.setText("CORRECT") elif self.cRad.isChecked() and self.cRad.text() != self.currentAWord: self.resultLabel.setText("Try again") def next(self): self.currentWordS = random.sample(self.wordList, 3) self.currentQWord = self.currentWordS[0][0] self.currentAWord = self.currentWordS[0][1] self.wordLabel.setText(self.currentQWord) self.resultLabel.setText('') self.group.setExclusive(False) self.aRad.setChecked(False) self.bRad.setChecked(False) self.cRad.setChecked(False) self.group.setExclusive(True) newText = [] for item in self.currentWordS: newText.append(item[1]) random.shuffle(newText) self.aRad.setText(newText[0]) self.bRad.setText(newText[1]) self.cRad.setText(newText[2]) def exit(self): confirm = QMessageBox.question(self.w, 'Quit', 'Are you sure you want to exit?', QMessageBox.Yes | QMessageBox.No) if confirm == QMessageBox.Yes: self.close() else: pass
class Editor(QFrame): # {{{ editing_done = pyqtSignal(object) def __init__(self, parent=None): QFrame.__init__(self, parent) self.setFocusPolicy(Qt.FocusPolicy.StrongFocus) self.setAutoFillBackground(True) self.capture = 0 self.setFrameShape(self.StyledPanel) self.setFrameShadow(self.Raised) self._layout = l = QGridLayout(self) self.setLayout(l) self.header = QLabel('') l.addWidget(self.header, 0, 0, 1, 2) self.use_default = QRadioButton('') self.use_custom = QRadioButton(_('&Custom')) l.addWidget(self.use_default, 1, 0, 1, 3) l.addWidget(self.use_custom, 2, 0, 1, 3) self.use_custom.toggled.connect(self.custom_toggled) off = 2 for which in (1, 2): text = _('&Shortcut:') if which == 1 else _('&Alternate shortcut:') la = QLabel(text) la.setStyleSheet('QLabel { margin-left: 1.5em }') l.addWidget(la, off + which, 0, 1, 3) setattr(self, 'label%d' % which, la) button = QPushButton(_('None'), self) button.clicked.connect(partial(self.capture_clicked, which=which)) button.installEventFilter(self) setattr(self, 'button%d' % which, button) clear = QToolButton(self) clear.setIcon(QIcon(I('clear_left.png'))) clear.clicked.connect(partial(self.clear_clicked, which=which)) setattr(self, 'clear%d' % which, clear) l.addWidget(button, off + which, 1, 1, 1) l.addWidget(clear, off + which, 2, 1, 1) la.setBuddy(button) self.done_button = doneb = QPushButton(_('Done'), self) l.addWidget(doneb, 0, 2, 1, 1) doneb.clicked.connect(lambda: self.editing_done.emit(self)) l.setColumnStretch(0, 100) self.custom_toggled(False) def initialize(self, shortcut, all_shortcuts): self.header.setText('<b>%s: %s</b>' % (_('Customize'), shortcut['name'])) self.all_shortcuts = all_shortcuts self.shortcut = shortcut self.default_keys = [ QKeySequence(k, QKeySequence.SequenceFormat.PortableText) for k in shortcut['default_keys'] ] self.current_keys = list(shortcut['keys']) default = ', '.join([ unicode_type(k.toString(k.NativeText)) for k in self.default_keys ]) if not default: default = _('None') current = ', '.join([ unicode_type(k.toString(k.NativeText)) for k in self.current_keys ]) if not current: current = _('None') self.use_default.setText( _('&Default: %(deflt)s [Currently not conflicting: %(curr)s]') % dict(deflt=default, curr=current)) if shortcut['set_to_default']: self.use_default.setChecked(True) else: self.use_custom.setChecked(True) for key, which in zip(self.current_keys, [1, 2]): button = getattr(self, 'button%d' % which) button.setText(key.toString(key.NativeText)) def custom_toggled(self, checked): for w in ('1', '2'): for o in ('label', 'button', 'clear'): getattr(self, o + w).setEnabled(checked) def capture_clicked(self, which=1): self.capture = which button = getattr(self, 'button%d' % which) button.setText(_('Press a key...')) button.setFocus(Qt.FocusReason.OtherFocusReason) button.setStyleSheet('QPushButton { font-weight: bold}') def clear_clicked(self, which=0): button = getattr(self, 'button%d' % which) button.setText(_('None')) def eventFilter(self, obj, event): if self.capture and obj in (self.button1, self.button2): t = event.type() if t == QEvent.Type.ShortcutOverride: event.accept() return True if t == QEvent.Type.KeyPress: self.key_press_event(event, 1 if obj is self.button1 else 2) return True return QFrame.eventFilter(self, obj, event) def key_press_event(self, ev, which=0): if self.capture == 0: return QWidget.keyPressEvent(self, ev) sequence = keysequence_from_event(ev) if sequence is None: return QWidget.keyPressEvent(self, ev) ev.accept() button = getattr(self, 'button%d' % which) button.setStyleSheet('QPushButton { font-weight: normal}') button.setText( sequence.toString(QKeySequence.SequenceFormat.NativeText)) self.capture = 0 dup_desc = self.dup_check(sequence) if dup_desc is not None: error_dialog(self, _('Already assigned'), unicode_type( sequence.toString( QKeySequence.SequenceFormat.NativeText)) + ' ' + _('already assigned to') + ' ' + dup_desc, show=True) self.clear_clicked(which=which) def dup_check(self, sequence): for sc in self.all_shortcuts: if sc is self.shortcut: continue for k in sc['keys']: if k == sequence: return sc['name'] @property def custom_keys(self): if self.use_default.isChecked(): return None ans = [] for which in (1, 2): button = getattr(self, 'button%d' % which) t = unicode_type(button.text()) if t == _('None'): continue ks = QKeySequence(t, QKeySequence.SequenceFormat.NativeText) if not ks.isEmpty(): ans.append(ks) return tuple(ans)
class Editor(QFrame): # {{{ editing_done = pyqtSignal(object) def __init__(self, parent=None): QFrame.__init__(self, parent) self.setFocusPolicy(Qt.StrongFocus) self.setAutoFillBackground(True) self.capture = 0 self.setFrameShape(self.StyledPanel) self.setFrameShadow(self.Raised) self._layout = l = QGridLayout(self) self.setLayout(l) self.header = QLabel('') l.addWidget(self.header, 0, 0, 1, 2) self.use_default = QRadioButton('') self.use_custom = QRadioButton(_('Custom')) l.addWidget(self.use_default, 1, 0, 1, 3) l.addWidget(self.use_custom, 2, 0, 1, 3) self.use_custom.toggled.connect(self.custom_toggled) off = 2 for which in (1, 2): text = _('&Shortcut:') if which == 1 else _('&Alternate shortcut:') la = QLabel(text) la.setStyleSheet('QLabel { margin-left: 1.5em }') l.addWidget(la, off+which, 0, 1, 3) setattr(self, 'label%d'%which, la) button = QPushButton(_('None'), self) button.clicked.connect(partial(self.capture_clicked, which=which)) button.keyPressEvent = partial(self.key_press_event, which=which) setattr(self, 'button%d'%which, button) clear = QToolButton(self) clear.setIcon(QIcon(I('clear_left.png'))) clear.clicked.connect(partial(self.clear_clicked, which=which)) setattr(self, 'clear%d'%which, clear) l.addWidget(button, off+which, 1, 1, 1) l.addWidget(clear, off+which, 2, 1, 1) la.setBuddy(button) self.done_button = doneb = QPushButton(_('Done'), self) l.addWidget(doneb, 0, 2, 1, 1) doneb.clicked.connect(lambda : self.editing_done.emit(self)) l.setColumnStretch(0, 100) self.custom_toggled(False) def initialize(self, shortcut, all_shortcuts): self.header.setText('<b>%s: %s</b>'%(_('Customize'), shortcut['name'])) self.all_shortcuts = all_shortcuts self.shortcut = shortcut self.default_keys = [QKeySequence(k, QKeySequence.PortableText) for k in shortcut['default_keys']] self.current_keys = list(shortcut['keys']) default = ', '.join([unicode(k.toString(k.NativeText)) for k in self.default_keys]) if not default: default = _('None') current = ', '.join([unicode(k.toString(k.NativeText)) for k in self.current_keys]) if not current: current = _('None') self.use_default.setText(_('Default: %(deflt)s [Currently not conflicting: %(curr)s]')% dict(deflt=default, curr=current)) if shortcut['set_to_default']: self.use_default.setChecked(True) else: self.use_custom.setChecked(True) for key, which in zip(self.current_keys, [1,2]): button = getattr(self, 'button%d'%which) button.setText(key.toString(key.NativeText)) def custom_toggled(self, checked): for w in ('1', '2'): for o in ('label', 'button', 'clear'): getattr(self, o+w).setEnabled(checked) def capture_clicked(self, which=1): self.capture = which button = getattr(self, 'button%d'%which) button.setText(_('Press a key...')) button.setFocus(Qt.OtherFocusReason) button.setStyleSheet('QPushButton { font-weight: bold}') def clear_clicked(self, which=0): button = getattr(self, 'button%d'%which) button.setText(_('None')) def key_press_event(self, ev, which=0): if self.capture == 0: return QWidget.keyPressEvent(self, ev) sequence = keysequence_from_event(ev) if sequence is None: return QWidget.keyPressEvent(self, ev) ev.accept() button = getattr(self, 'button%d'%which) button.setStyleSheet('QPushButton { font-weight: normal}') button.setText(sequence.toString(QKeySequence.NativeText)) self.capture = 0 dup_desc = self.dup_check(sequence) if dup_desc is not None: error_dialog(self, _('Already assigned'), unicode(sequence.toString(QKeySequence.NativeText)) + ' ' + _('already assigned to') + ' ' + dup_desc, show=True) self.clear_clicked(which=which) def dup_check(self, sequence): for sc in self.all_shortcuts: if sc is self.shortcut: continue for k in sc['keys']: if k == sequence: return sc['name'] @property def custom_keys(self): if self.use_default.isChecked(): return None ans = [] for which in (1, 2): button = getattr(self, 'button%d'%which) t = unicode(button.text()) if t == _('None'): continue ks = QKeySequence(t, QKeySequence.NativeText) if not ks.isEmpty(): ans.append(ks) return tuple(ans)
class ConversionDialog(Dialog): def __init__(self, parent, force_entire_book=False): self.prefs = self.prefsPrep() self.parent = parent self.force_entire_book = force_entire_book self.criteria = None Dialog.__init__(self, _('Chinese Conversion'), 'chinese_conversion_dialog', parent) def setup_ui(self): # Create layout for entire dialog layout = QVBoxLayout(self) self.setLayout(layout) #Create a scroll area for the top part of the dialog self.scrollArea = QScrollArea(self) self.scrollArea.setWidgetResizable(True) # Create widget for all the contents of the dialog except the OK and Cancel buttons self.scrollContentWidget = QWidget(self.scrollArea) self.scrollArea.setWidget(self.scrollContentWidget) widgetLayout = QVBoxLayout(self.scrollContentWidget) # Add scrollArea to dialog layout.addWidget(self.scrollArea) self.other_group_box = QGroupBox(_('Other Changes')) widgetLayout.addWidget(self.other_group_box) other_group_box_layout = QVBoxLayout() self.other_group_box.setLayout(other_group_box_layout) text_dir_layout = QHBoxLayout() other_group_box_layout.addLayout(text_dir_layout) direction_label = QLabel(_('Text Direction:')) text_dir_layout.addWidget(direction_label) self.text_dir_combo = QComboBox() text_dir_layout.addWidget(self.text_dir_combo) self.text_dir_combo.addItems([_('No Conversion'), _('Horizontal'), _('Vertical')]) self.text_dir_combo.setToolTip(_('Select the desired text orientation')) self.text_dir_combo.currentIndexChanged.connect(self.update_gui) self.optimization_group_box = QGroupBox(_('Reader Device Optimization')) other_group_box_layout.addWidget(self.optimization_group_box) optimization_group_box_layout = QVBoxLayout() self.optimization_group_box.setLayout(optimization_group_box_layout) punc_group=QButtonGroup(self) self.text_dir_punc_none_button = QRadioButton("""No presentation optimization""") optimization_group_box_layout.addWidget(self.text_dir_punc_none_button) self.text_dir_punc_button = QRadioButton("""Optimize presentation for Readium reader""") self.text_dir_punc_button.setToolTip(_('Use vert/horiz punctuation presentation forms for Chrome Readium Epub3 reader')) optimization_group_box_layout.addWidget(self.text_dir_punc_button) self.text_dir_punc_kindle_button = QRadioButton("""Optimize presentation for Kindle reader""") self.text_dir_punc_kindle_button.setToolTip(_('Use vert/horiz puncuation presentation forms for Kindle reader')) optimization_group_box_layout.addWidget(self.text_dir_punc_kindle_button) self.text_dir_punc_none_button.toggled.connect(self.update_gui) self.text_dir_punc_button.toggled.connect(self.update_gui) self.text_dir_punc_kindle_button.toggled.connect(self.update_gui) source_group=QButtonGroup(self) self.file_source_button = QRadioButton(_('Selected File Only')) self.book_source_button = QRadioButton(_('Entire eBook')) source_group.addButton(self.file_source_button) source_group.addButton(self.book_source_button) self.source_group_box = QGroupBox(_('Source')) if not self.force_entire_book: widgetLayout.addWidget(self.source_group_box) source_group_box_layout = QVBoxLayout() self.source_group_box.setLayout(source_group_box_layout) source_group_box_layout.addWidget(self.file_source_button) source_group_box_layout.addWidget(self.book_source_button) layout.addSpacing(10) self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.button_box.accepted.connect(self._ok_clicked) self.button_box.rejected.connect(self.reject) layout.addWidget(self.button_box) if not self.force_entire_book: self.file_source_button.setChecked(self.prefs['use_html_file']) self.book_source_button.setChecked(self.prefs['use_entire_book']) else: self.file_source_button.setChecked(False) self.book_source_button.setChecked(True) self.text_dir_combo.setCurrentIndex(self.prefs['orientation']) self.text_dir_punc_none_button.setChecked(self.prefs['no_optimization']) self.text_dir_punc_button.setChecked(self.prefs['readium_optimization']) self.text_dir_punc_kindle_button.setChecked(self.prefs['kindle_optimization']) self.update_gui() def update_gui(self): if self.text_dir_combo.currentIndex() == 0: self.optimization_group_box.setEnabled(False) self.text_dir_punc_none_button.setEnabled(False) self.text_dir_punc_button.setEnabled(False) self.text_dir_punc_kindle_button.setEnabled(False) else: self.optimization_group_box.setEnabled(True) self.text_dir_punc_none_button.setEnabled(True) self.text_dir_punc_button.setEnabled(True) self.text_dir_punc_kindle_button.setEnabled(True) def _ok_clicked(self): optimization_mode = 0 if self.text_dir_punc_button.isChecked(): optimization_mode = 1 #Readium if self.text_dir_punc_kindle_button.isChecked(): optimization_mode = 2 #Kindle self.criteria = (self.text_dir_combo.currentIndex(), optimization_mode) self.savePrefs() self.accept() def getCriteria(self): return self.criteria def prefsPrep(self): from calibre.utils.config import JSONConfig plugin_prefs = JSONConfig('plugins/{0}_ChineseConversion_settings'.format(PLUGIN_SAFE_NAME)) plugin_prefs.defaults['use_html_file'] = True plugin_prefs.defaults['use_entire_book'] = True plugin_prefs.defaults['orientation'] = 0 plugin_prefs.defaults['no_optimization'] = True plugin_prefs.defaults['readium_optimization'] = False plugin_prefs.defaults['kindle_optimization'] = False return plugin_prefs def savePrefs(self): self.prefs['use_html_file'] = self.file_source_button.isChecked() self.prefs['use_entire_book'] = self.book_source_button.isChecked() self.prefs['orientation'] = self.text_dir_combo.currentIndex() self.prefs['no_optimization'] = self.text_dir_punc_none_button.isChecked() self.prefs['readium_optimization'] = self.text_dir_punc_button.isChecked() self.prefs['kindle_optimization'] = self.text_dir_punc_kindle_button.isChecked()
class TableTab(TraceDocks): ## The constructor # initialize the super-class, assign a name and first configItems def __init__(self,parent): super(TableTab, self).__init__(parent,'TableTab') self.tabName = 'TableTab' self.parent = parent self.logger.logEvent('Creating Tab now: '+ self.tabName) # Set a couple of default-values, in case the configParser does not work self.snifferConfig.configAutoClearCheck = 1 self.snifferConfig.configFilterState = 'Local' self.snifferConfig.configFilterList = self.snifferFilter.filteredIdList # By parsing the config now, we assure that we re-load everything # the way we left it self.snifferConfig.parseConfigFromFile() self.lastSearchedText = 'nullthiswillneverbefound' self.lastMatch = 'purge' self.lastIndex = 0 ## Create the visible UI def setTableTabLayout(self): # Create Table Tab --------------------### # Create Layouts self.Vlayout = QVBoxLayout() self.H1layout = QHBoxLayout() self.H11layout = QHBoxLayout() self.H12layout = QHBoxLayout() self.H21layout = QHBoxLayout() self.V11layout = QVBoxLayout() self.V21layout = QVBoxLayout() # Create Widgets for H1layout # First buttons self.clearTableButt = QPushButton('Clear Table') self.clearTableButt.clicked.connect(self.clearTable) self.autoClearCheck = QCheckBox('Auto Clear') self.autoClearCheck.stateChanged.connect(self.checkAutoClearChanged) self.searchInputField = QLineEdit() self.searchInputField.setPlaceholderText('Enter search term, then click search') self.searchButt = QPushButton('Search Table') self.searchButt.clicked.connect(lambda: self.searchInTable(self.searchInputField.text(),2)) self.showSummaryButt = QPushButton('Show Summary') self.showSummaryButt.clicked.connect(self.showSummary) self.filterGroup = QButtonGroup() self.localFilterRadio = QRadioButton('Local',self) self.globalFilterRadio = QRadioButton('Global', self) self.configureFilterButt = QPushButton('Configure Filter') self.configureFilterButt.clicked.connect(self.configureFilter) self.localFilterRadio.clicked.connect(self.localRadioSelected) self.globalFilterRadio.clicked.connect(self.globalRadioSelected) self.H21layout.addWidget(self.localFilterRadio) self.H21layout.addWidget(self.globalFilterRadio) self.H21layout.addWidget(self.showSummaryButt) self.V21layout.addLayout(self.H21layout) self.V21layout.addWidget(self.configureFilterButt) # Add Widgets to H11layout self.H11layout.addWidget(self.clearTableButt) self.H11layout.addWidget(self.autoClearCheck) # Add Widgets to H12layout self.H12layout.addWidget(self.searchInputField) self.H12layout.addWidget(self.searchButt) self.H12layout.addStretch() self.V11layout.addLayout(self.H11layout) self.V11layout.addLayout(self.H12layout) self.H1layout.addLayout(self.V11layout) self.H1layout.addLayout(self.V21layout) self.syncUiToConfig() #------------------------------------ # Create Table self.detailTableIndex = 0 self.detailTable = QTableWidget() self.detailTableItem = QTableWidgetItem() self.detailTable.setRowCount(0) self.detailTable.setColumnCount(6) self.detailTable.setHorizontalHeaderLabels('packID;Tick;Timer;Type;Message;Length'.split(';')) self.detailTable.resizeColumnsToContents() self.detailTableHeader = self.detailTable.horizontalHeader() self.detailTableHeader.setSectionResizeMode(0, QHeaderView.ResizeToContents) self.detailTableHeader.setSectionResizeMode(1, QHeaderView.ResizeToContents) self.detailTableHeader.setSectionResizeMode(2, QHeaderView.ResizeToContents) self.detailTableHeader.setSectionResizeMode(3, QHeaderView.ResizeToContents) self.detailTableHeader.setSectionResizeMode(4, QHeaderView.Stretch) self.detailTableHeader.setSectionResizeMode(5, QHeaderView.ResizeToContents) #------------------------------------ self.Vlayout.addLayout(self.H1layout) self.Vlayout.addWidget(self.detailTable) self.dockContents.setLayout(self.Vlayout) ## Fill the main table with the entries from the given list # @param fillTableList the list which is to be parsed into the table def fillTable(self,fillTableList): print('Filling Table with all items in PayloadList') self.detailTable.scrollToTop() # Scrolls to the top of the table self.configAutoClearCheck = True if self.configAutoClearCheck == True: self.detailTable.setRowCount(0) self.detailTableIndex = 0 Globals.dockDict['dockStart'].progressShotBar.setMaximum(len(Globals.payloadList)) for self.tablePayload in fillTableList: self.detailTable.insertRow(self.detailTableIndex) self.detailTable.setItem(self.detailTableIndex,0,QTableWidgetItem(str(self.tablePayload.payloadHead.packetID))) self.detailTable.setItem(self.detailTableIndex,1,QTableWidgetItem(str(self.tablePayload.payloadHead.tickCountHigh << 8 | self.tablePayload.payloadHead.tickCountLow))) self.detailTable.setItem(self.detailTableIndex,2,QTableWidgetItem(str(self.tablePayload.payloadHead.timerByteHigh << 8 | self.tablePayload.payloadHead.timerByteLow))) self.detailTable.setItem(self.detailTableIndex,3,QTableWidgetItem(Globals.tspDict[self.tablePayload.payloadHead.informationID][0])) testPayload = self.tablePayload.toDict() # This a little messy: We check whether an objectDict is available, and if it is # we check for the different DataTypes there might be, since the message section is unified. # Basically, we need to handle all the different cases there can be, payload0-payload3 and the actual objectType if Globals.objectDict: if getDataType(self.tablePayload) == 1: if getObjectType(self.tablePayload) == 5: try: self.detailTable.setItem(self.detailTableIndex,4,QTableWidgetItem(str(self.tablePayload.data1)+': '+Globals.objectDict['TASK'][str(self.tablePayload.data1)])) except: self.detailTable.setItem(self.detailTableIndex,4,QTableWidgetItem('FAILED WITH KEYERROR - see printOutput')) print('KEYERROR DETAILS:') print(testPayload) elif getDataType(self.tablePayload) == 2: try: self.detailTable.setItem(self.detailTableIndex,4,QTableWidgetItem(str(self.tablePayload.data1)+': '+Globals.objectDict[Globals.objectTypeDict[self.tablePayload.data1]][str(self.tablePayload.data2)])) except: self.detailTable.setItem(self.detailTableIndex,4,QTableWidgetItem('FAILED WITH KEYERROR - see printOutput')) print('KEYERROR DETAILS:') print(testPayload) # If the objectDict does not exist, we can just dump the raw information into the message-section -> no lookup will be happening # and it is up to the user to interpret the data else: if getDataType(self.tablePayload) == 1: self.detailTable.setItem(self.detailTableIndex,4,QTableWidgetItem(str(self.tablePayload.data1))) elif getDataType(self.tablePayload) == 2: self.detailTable.setItem(self.detailTableIndex,4,QTableWidgetItem(str(self.tablePayload.data1)+';'+str(self.tablePayload.data2))) elif getDataType(self.tablePayload) == 3: self.detailTable.setItem(self.detailTableIndex,4,QTableWidgetItem(str(self.tablePayload.data1)+';'+str(self.tablePayload.data2)+';'+str(self.tablePayload.data3))) self.detailTable.setItem(self.detailTableIndex,5,QTableWidgetItem('payload'+str(getDataType(self.tablePayload)))) self.detailTableIndex+=1 Globals.dockDict['dockStart'].progressShotBar.setValue(self.detailTableIndex) Globals.dockDict['dockStart'].progressShotBar.setValue(len(Globals.payloadList)) Globals.dockDict['dockStart'].displayStatusMessage('Table filled completely, check the tab') self.lastSearchedText = 'thisisAWorkAround' # No idea why this is there... # --- DOCK-SPECIFIC UI FUNCTIONS --- # # -----------------------------------# ## Disable all UI-buttons belonging to the tab. This is implementation specific def disableButtons(self): self.clearTableButt.setEnabled(False) self.autoClearCheck.setEnabled(False) print('Disable TabTable Buttons') ## CB: clearTableButton // Clear the main table def clearTable(self): self.logger.logEvent('clear Table clicked') self.detailTable.setRowCount(0) self.detailTableIndex = 0 ## CB: autoClearCheckbox // set the configAutoClearCheck state according to the checkbox def checkAutoClearChanged(self): self.snifferConfig.configAutoClearCheck ^= 1 self.logger.logEvent('changed Auto Clear Checkbox to - '+ str(self.snifferConfig.configAutoClearCheck)) ## CB: localRadioButt // set the configFilterState to 'Local' def localRadioSelected(self): self.snifferConfig.configFilterState = 'Local' self.logger.logEvent('changed Filter Radio to - '+ str(self.snifferConfig.configFilterState)) ## CB: globalRadioButt // set the configFilterState to 'Global' def globalRadioSelected(self): self.snifferConfig.configFilterState = 'Global' self.logger.logEvent('changed Filter Radio to - '+ str(self.snifferConfig.configFilterState)) ## CB: Show Stats // opens the Stat-Dialog to show misc. information about the measurement def showSummary(self): self.snifferStats.show() self.snifferStats.showStats() ## Searches the table for a string and scrolls to the found item. # @param textToSearch the string that needs to be found in the table # @param column the column where the search needs to take place def searchInTable(self, textToSearch, column): if textToSearch not in Globals.IDList: QMessageBox.about(self,'Not Found','SearchText not Found!') return if self.lastSearchedText == textToSearch: self.detailTable.setCurrentItem(self.lastFound[self.lastIndex]) self.detailTable.scrollToItem(self.lastFound[self.lastIndex]) self.lastIndex = self.lastIndex + 1 if self.lastIndex == len(self.lastFound): self.lastIndex = 0 else: foundItems = self.detailTable.findItems(textToSearch, Qt.MatchExactly) self.lastSearchedText = textToSearch if len(foundItems) != 0: self.lastFound = foundItems self.lastIndex = 1 self.detailTable.setCurrentItem(foundItems[0]) self.detailTable.scrollToItem(self.lastFound[self.lastIndex]) # --- MANDATORY UI FUNCTIONS --- # # -------------------------------# ## Read out all components of snifferConfig and set the UI elements according to # the saved values. def syncUiToConfig(self): self.autoClearCheck.setChecked(self.snifferConfig.configAutoClearCheck) if self.snifferConfig.configFilterState == 'Local': self.localFilterRadio.click() elif self.snifferConfig.configFilterState == 'Global': self.globalFilterRadio.click() else: print('Error, neither local nor global in config') ## Open the correct filter based on the radioButton. If the Global-filter is checked # we assign its calledby variable in order to execute the right callback after the filter is saved. def configureFilter(self): if self.localFilterRadio.isChecked(): self.snifferFilter.show() elif self.globalFilterRadio.isChecked(): Globals.globalFilter.show() Globals.globalFilter.calledBy = 'dockTable' else: print('neither radios checked. Error!') ## CB: // Updates the UI-contents with after filtering has taken place. # This function should not be called by the tab itself, but by the filter # @param filteredIDs the IDs that are to be kept in the payloadList (obsolete) # @param filteredPayloads the new payloadList, which only contains the payloads filtered by the SnifferFilter def filterUpdated(self, filteredIDs, filteredPayloads): print('we arrive from SnifferFilter') self.clearTable() self.fillTable(filteredPayloads)
class ConversionDialog(Dialog): def __init__(self, parent, force_entire_book=False): self.prefs = self.prefsPrep() self.parent = parent self.force_entire_book = force_entire_book self.criteria = None Dialog.__init__(self, _('Chinese Conversion'), 'chinese_conversion_dialog', parent) def setup_ui(self): self.quote_for_trad_target = _("Update quotes: "",'' -> 「」,『』") self.quote_for_simp_target = _("Update quotes: 「」,『』 -> "",''") # Create layout for entire dialog layout = QVBoxLayout(self) self.setLayout(layout) #Create a scroll area for the top part of the dialog self.scrollArea = QScrollArea(self) self.scrollArea.setWidgetResizable(True) # Create widget for all the contents of the dialog except the OK and Cancel buttons self.scrollContentWidget = QWidget(self.scrollArea) self.scrollArea.setWidget(self.scrollContentWidget) widgetLayout = QVBoxLayout(self.scrollContentWidget) # Add scrollArea to dialog layout.addWidget(self.scrollArea) self.operation_group_box = QGroupBox(_('Conversion Direction')) widgetLayout.addWidget(self.operation_group_box) operation_group_box_layout = QVBoxLayout() self.operation_group_box.setLayout(operation_group_box_layout) operation_group = QButtonGroup(self) self.no_conversion_button = QRadioButton(_('No Conversion')) operation_group.addButton(self.no_conversion_button) self.trad_to_simp_button = QRadioButton(_('Traditional to Simplified')) operation_group.addButton(self.trad_to_simp_button) self.simp_to_trad_button = QRadioButton(_('Simplified to Traditional')) operation_group.addButton(self.simp_to_trad_button) self.trad_to_trad_button = QRadioButton( _('Traditional to Traditional')) operation_group.addButton(self.trad_to_trad_button) operation_group_box_layout.addWidget(self.no_conversion_button) operation_group_box_layout.addWidget(self.trad_to_simp_button) operation_group_box_layout.addWidget(self.simp_to_trad_button) operation_group_box_layout.addWidget(self.trad_to_trad_button) self.no_conversion_button.toggled.connect(self.update_gui) self.trad_to_simp_button.toggled.connect(self.update_gui) self.simp_to_trad_button.toggled.connect(self.update_gui) self.trad_to_trad_button.toggled.connect(self.update_gui) self.style_group_box = QGroupBox(_('Language Styles')) widgetLayout.addWidget(self.style_group_box) style_group_box_layout = QVBoxLayout() self.style_group_box.setLayout(style_group_box_layout) input_layout = QHBoxLayout() style_group_box_layout.addLayout(input_layout) self.input_region_label = QLabel(_('Input:')) input_layout.addWidget(self.input_region_label) self.input_combo = QComboBox() input_layout.addWidget(self.input_combo) self.input_combo.addItems([_('Mainland'), _('Hong Kong'), _('Taiwan')]) self.input_combo.setToolTip(_('Select the origin region of the input')) self.input_combo.currentIndexChanged.connect(self.update_gui) output_layout = QHBoxLayout() style_group_box_layout.addLayout(output_layout) self.output_region_label = QLabel(_('Output:')) output_layout.addWidget(self.output_region_label) self.output_combo = QComboBox() output_layout.addWidget(self.output_combo) self.output_combo.addItems( [_('Mainland'), _('Hong Kong'), _('Taiwan')]) self.output_combo.setToolTip( _('Select the desired region of the output')) self.output_combo.currentIndexChanged.connect(self.update_gui) self.use_target_phrases = QCheckBox( _('Use output target phrases if possible')) self.use_target_phrases.setToolTip( _('Check to allow region specific word replacements if available')) style_group_box_layout.addWidget(self.use_target_phrases) self.use_target_phrases.stateChanged.connect(self.update_gui) self.quotation_group_box = QGroupBox(_('Quotation Marks')) widgetLayout.addWidget(self.quotation_group_box) quotation_group_box_layout = QVBoxLayout() self.quotation_group_box.setLayout(quotation_group_box_layout) quotation_group = QButtonGroup(self) self.quotation_no_conversion_button = QRadioButton(_('No Conversion')) quotation_group.addButton(self.quotation_no_conversion_button) self.quotation_trad_to_simp_button = QRadioButton( self.quote_for_simp_target) quotation_group.addButton(self.quotation_trad_to_simp_button) self.quotation_simp_to_trad_button = QRadioButton( self.quote_for_trad_target) quotation_group.addButton(self.quotation_simp_to_trad_button) quotation_group_box_layout.addWidget( self.quotation_no_conversion_button) quotation_group_box_layout.addWidget( self.quotation_simp_to_trad_button) quotation_group_box_layout.addWidget( self.quotation_trad_to_simp_button) self.quotation_no_conversion_button.toggled.connect(self.update_gui) self.quotation_trad_to_simp_button.toggled.connect(self.update_gui) self.quotation_simp_to_trad_button.toggled.connect(self.update_gui) self.use_smart_quotes = QCheckBox( """Use curved 'Smart" quotes if applicable""") self.use_smart_quotes.setToolTip( _('Use smart curved half-width quotes rather than straight full-width quotes' )) quotation_group_box_layout.addWidget(self.use_smart_quotes) self.use_smart_quotes.stateChanged.connect(self.update_gui) self.other_group_box = QGroupBox(_('Other Changes')) widgetLayout.addWidget(self.other_group_box) other_group_box_layout = QVBoxLayout() self.other_group_box.setLayout(other_group_box_layout) text_dir_layout = QHBoxLayout() other_group_box_layout.addLayout(text_dir_layout) direction_label = QLabel(_('Text Direction:')) text_dir_layout.addWidget(direction_label) self.text_dir_combo = QComboBox() text_dir_layout.addWidget(self.text_dir_combo) self.text_dir_combo.addItems( [_('No Conversion'), _('Horizontal'), _('Vertical')]) self.text_dir_combo.setToolTip( _('Select the desired text orientation')) self.text_dir_combo.currentIndexChanged.connect(self.update_gui) self.optimization_group_box = QGroupBox( _('Reader Device Optimization')) other_group_box_layout.addWidget(self.optimization_group_box) optimization_group_box_layout = QVBoxLayout() self.optimization_group_box.setLayout(optimization_group_box_layout) punc_group = QButtonGroup(self) self.text_dir_punc_none_button = QRadioButton( """No presentation optimization""") optimization_group_box_layout.addWidget(self.text_dir_punc_none_button) self.text_dir_punc_button = QRadioButton( """Optimize presentation for Readium reader""") self.text_dir_punc_button.setToolTip( _('Use vert/horiz punctuation presentation forms for Chrome Readium Epub3 reader' )) optimization_group_box_layout.addWidget(self.text_dir_punc_button) self.text_dir_punc_kindle_button = QRadioButton( """Optimize presentation for Kindle reader""") self.text_dir_punc_kindle_button.setToolTip( _('Use vert/horiz puncuation presentation forms for Kindle reader') ) optimization_group_box_layout.addWidget( self.text_dir_punc_kindle_button) self.text_dir_punc_none_button.toggled.connect(self.update_gui) self.text_dir_punc_button.toggled.connect(self.update_gui) self.text_dir_punc_kindle_button.toggled.connect(self.update_gui) source_group = QButtonGroup(self) self.file_source_button = QRadioButton(_('Selected File Only')) self.book_source_button = QRadioButton(_('Entire eBook')) source_group.addButton(self.file_source_button) source_group.addButton(self.book_source_button) self.source_group_box = QGroupBox(_('Source')) if not self.force_entire_book: widgetLayout.addWidget(self.source_group_box) source_group_box_layout = QVBoxLayout() self.source_group_box.setLayout(source_group_box_layout) source_group_box_layout.addWidget(self.file_source_button) source_group_box_layout.addWidget(self.book_source_button) layout.addSpacing(10) self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.button_box.accepted.connect(self._ok_clicked) self.button_box.rejected.connect(self.reject) layout.addWidget(self.button_box) self.input_combo.setCurrentIndex(self.prefs['input_format']) self.output_combo.setCurrentIndex(self.prefs['output_format']) self.no_conversion_button.setChecked(self.prefs['no_conversion']) self.trad_to_simp_button.setChecked(self.prefs['trad_to_simp']) self.simp_to_trad_button.setChecked(self.prefs['simp_to_trad']) self.trad_to_trad_button.setChecked(self.prefs['trad_to_trad']) if not self.force_entire_book: self.file_source_button.setChecked(self.prefs['use_html_file']) self.book_source_button.setChecked(self.prefs['use_entire_book']) else: self.file_source_button.setChecked(False) self.book_source_button.setChecked(True) self.quotation_no_conversion_button.setChecked( self.prefs['quote_no_conversion']) self.quotation_trad_to_simp_button.setChecked( self.prefs['quote_trad_to_simp']) self.quotation_simp_to_trad_button.setChecked( self.prefs['quote_simp_to_trad']) self.use_smart_quotes.setChecked(self.prefs['use_smart_quotes']) self.text_dir_combo.setCurrentIndex(self.prefs['orientation']) self.text_dir_punc_none_button.setChecked( self.prefs['no_optimization']) self.text_dir_punc_button.setChecked( self.prefs['readium_optimization']) self.text_dir_punc_kindle_button.setChecked( self.prefs['kindle_optimization']) self.update_gui() def update_gui(self): if (self.quotation_trad_to_simp_button.isChecked()): self.use_smart_quotes.setEnabled(True) else: self.use_smart_quotes.setEnabled(False) if self.text_dir_combo.currentIndex() == 0: self.optimization_group_box.setEnabled(False) self.text_dir_punc_none_button.setEnabled(False) self.text_dir_punc_button.setEnabled(False) self.text_dir_punc_kindle_button.setEnabled(False) else: self.optimization_group_box.setEnabled(True) self.text_dir_punc_none_button.setEnabled(True) self.text_dir_punc_button.setEnabled(True) self.text_dir_punc_kindle_button.setEnabled(True) if self.no_conversion_button.isChecked(): self.input_combo.setEnabled(False) self.output_combo.setEnabled(False) self.use_target_phrases.setEnabled(False) self.output_region_label.setEnabled(False) self.input_region_label.setEnabled(False) self.style_group_box.setEnabled(False) elif self.trad_to_simp_button.isChecked(): self.input_combo.setEnabled(True) #only mainland output locale for simplified output self.output_combo.setCurrentIndex(0) self.output_combo.setEnabled(False) self.use_target_phrases.setEnabled(True) self.output_region_label.setEnabled(False) self.input_region_label.setEnabled(True) self.style_group_box.setEnabled(True) elif self.simp_to_trad_button.isChecked(): #only mainland input locale for simplified input self.input_combo.setCurrentIndex(0) self.input_combo.setEnabled(False) self.output_combo.setEnabled(True) self.use_target_phrases.setEnabled(True) self.output_region_label.setEnabled(True) self.input_region_label.setEnabled(False) self.style_group_box.setEnabled(True) elif self.trad_to_trad_button.isChecked(): #Trad->Trad #currently only mainland input locale for Trad->Trad self.input_combo.setCurrentIndex(0) self.input_combo.setEnabled(False) self.output_combo.setEnabled(True) self.use_target_phrases.setEnabled(True) self.output_region_label.setEnabled(True) self.input_region_label.setEnabled(False) self.style_group_box.setEnabled(True) else: self.input_combo.setEnabled(True) self.output_combo.setEnabled(True) self.use_target_phrases.setEnabled(True) self.style_group_box.setEnabled(True) self.output_region_label.setEnabled(True) self.input_region_label.setEnabled(True) def _ok_clicked(self): output_mode = 0 if self.trad_to_simp_button.isChecked(): output_mode = 1 #trad -> simp if self.simp_to_trad_button.isChecked(): output_mode = 2 #simp -> trad elif self.trad_to_trad_button.isChecked(): output_mode = 3 #trad -> trad quote_mode = 0 if self.quotation_trad_to_simp_button.isChecked(): quote_mode = 1 #trad -> simp if self.quotation_simp_to_trad_button.isChecked(): quote_mode = 2 #simp -> trad optimization_mode = 0 if self.text_dir_punc_button.isChecked(): optimization_mode = 1 #Readium if self.text_dir_punc_kindle_button.isChecked(): optimization_mode = 2 #Kindle self.criteria = (self.file_source_button.isChecked(), output_mode, self.input_combo.currentIndex(), self.output_combo.currentIndex(), self.use_target_phrases.isChecked(), quote_mode, self.use_smart_quotes.isChecked(), self.text_dir_combo.currentIndex(), optimization_mode) self.savePrefs() self.accept() def getCriteria(self): return self.criteria def prefsPrep(self): from calibre.utils.config import JSONConfig plugin_prefs = JSONConfig( 'plugins/{0}_ChineseConversion_settings'.format(PLUGIN_SAFE_NAME)) plugin_prefs.defaults['input_format'] = 0 plugin_prefs.defaults['output_format'] = 0 plugin_prefs.defaults['no_conversion'] = True plugin_prefs.defaults['trad_to_simp'] = False plugin_prefs.defaults['use_html_file'] = True plugin_prefs.defaults['simp_to_trad'] = False plugin_prefs.defaults['trad_to_trad'] = False plugin_prefs.defaults['use_entire_book'] = True plugin_prefs.defaults['use_target_phrases'] = True plugin_prefs.defaults['quote_no_conversion'] = True plugin_prefs.defaults['quote_trad_to_simp'] = False plugin_prefs.defaults['quote_simp_to_trad'] = False plugin_prefs.defaults['use_smart_quotes'] = False plugin_prefs.defaults['orientation'] = 0 plugin_prefs.defaults['no_optimization'] = True plugin_prefs.defaults['readium_optimization'] = False plugin_prefs.defaults['kindle_optimization'] = False return plugin_prefs def savePrefs(self): self.prefs['input_format'] = self.input_combo.currentIndex() self.prefs['output_format'] = self.output_combo.currentIndex() self.prefs['no_conversion'] = self.no_conversion_button.isChecked() self.prefs['trad_to_simp'] = self.trad_to_simp_button.isChecked() self.prefs['use_html_file'] = self.file_source_button.isChecked() self.prefs['simp_to_trad'] = self.simp_to_trad_button.isChecked() self.prefs['trad_to_trad'] = self.trad_to_trad_button.isChecked() self.prefs['use_entire_book'] = self.book_source_button.isChecked() self.prefs['use_target_phrases'] = self.use_target_phrases.isChecked() self.prefs[ 'quote_no_conversion'] = self.quotation_no_conversion_button.isChecked( ) self.prefs[ 'quote_trad_to_simp'] = self.quotation_trad_to_simp_button.isChecked( ) self.prefs[ 'quote_simp_to_trad'] = self.quotation_simp_to_trad_button.isChecked( ) self.prefs['use_smart_quotes'] = self.use_smart_quotes.isChecked() self.prefs['orientation'] = self.text_dir_combo.currentIndex() self.prefs[ 'no_optimization'] = self.text_dir_punc_none_button.isChecked() self.prefs[ 'readium_optimization'] = self.text_dir_punc_button.isChecked() self.prefs[ 'kindle_optimization'] = self.text_dir_punc_kindle_button.isChecked( )
class Editor(QFrame): # {{{ editing_done = pyqtSignal(object) def __init__(self, parent=None): QFrame.__init__(self, parent) self.setFocusPolicy(Qt.StrongFocus) self.setAutoFillBackground(True) self.capture = 0 self.setFrameShape(self.StyledPanel) self.setFrameShadow(self.Raised) self._layout = l = QGridLayout(self) self.setLayout(l) self.header = QLabel('') l.addWidget(self.header, 0, 0, 1, 2) self.use_default = QRadioButton('') self.use_custom = QRadioButton(_('Custom')) l.addWidget(self.use_default, 1, 0, 1, 3) l.addWidget(self.use_custom, 2, 0, 1, 3) self.use_custom.toggled.connect(self.custom_toggled) off = 2 for which in (1, 2): text = _('&Shortcut:') if which == 1 else _('&Alternate shortcut:') la = QLabel(text) la.setStyleSheet('QLabel { margin-left: 1.5em }') l.addWidget(la, off + which, 0, 1, 3) setattr(self, 'label%d' % which, la) button = QPushButton(_('None'), self) button.clicked.connect(partial(self.capture_clicked, which=which)) button.keyPressEvent = partial(self.key_press_event, which=which) setattr(self, 'button%d' % which, button) clear = QToolButton(self) clear.setIcon(QIcon(I('clear_left.png'))) clear.clicked.connect(partial(self.clear_clicked, which=which)) setattr(self, 'clear%d' % which, clear) l.addWidget(button, off + which, 1, 1, 1) l.addWidget(clear, off + which, 2, 1, 1) la.setBuddy(button) self.done_button = doneb = QPushButton(_('Done'), self) l.addWidget(doneb, 0, 2, 1, 1) doneb.clicked.connect(lambda: self.editing_done.emit(self)) l.setColumnStretch(0, 100) self.custom_toggled(False) def initialize(self, shortcut, all_shortcuts): self.header.setText('<b>%s: %s</b>' % (_('Customize'), shortcut['name'])) self.all_shortcuts = all_shortcuts self.shortcut = shortcut self.default_keys = [ QKeySequence(k, QKeySequence.PortableText) for k in shortcut['default_keys'] ] self.current_keys = list(shortcut['keys']) default = ', '.join( [unicode(k.toString(k.NativeText)) for k in self.default_keys]) if not default: default = _('None') current = ', '.join( [unicode(k.toString(k.NativeText)) for k in self.current_keys]) if not current: current = _('None') self.use_default.setText( _('Default: %(deflt)s [Currently not conflicting: %(curr)s]') % dict(deflt=default, curr=current)) if shortcut['set_to_default']: self.use_default.setChecked(True) else: self.use_custom.setChecked(True) for key, which in zip(self.current_keys, [1, 2]): button = getattr(self, 'button%d' % which) button.setText(key.toString(key.NativeText)) def custom_toggled(self, checked): for w in ('1', '2'): for o in ('label', 'button', 'clear'): getattr(self, o + w).setEnabled(checked) def capture_clicked(self, which=1): self.capture = which button = getattr(self, 'button%d' % which) button.setText(_('Press a key...')) button.setFocus(Qt.OtherFocusReason) button.setStyleSheet('QPushButton { font-weight: bold}') def clear_clicked(self, which=0): button = getattr(self, 'button%d' % which) button.setText(_('None')) def key_press_event(self, ev, which=0): code = ev.key() if self.capture == 0 or code in (0, Qt.Key_unknown, Qt.Key_Shift, Qt.Key_Control, Qt.Key_Alt, Qt.Key_Meta, Qt.Key_AltGr, Qt.Key_CapsLock, Qt.Key_NumLock, Qt.Key_ScrollLock): return QWidget.keyPressEvent(self, ev) button = getattr(self, 'button%d' % which) button.setStyleSheet('QPushButton { font-weight: normal}') mods = int(ev.modifiers()) & ~Qt.KeypadModifier txt = unicode(ev.text()) if txt and txt.lower() == txt.upper(): # We have a symbol like ! or > etc. In this case the value of code # already includes Shift, so remove it mods &= ~Qt.ShiftModifier sequence = QKeySequence(code | mods) button.setText(sequence.toString(QKeySequence.NativeText)) self.capture = 0 dup_desc = self.dup_check(sequence) if dup_desc is not None: error_dialog(self, _('Already assigned'), unicode(sequence.toString(QKeySequence.NativeText)) + ' ' + _('already assigned to') + ' ' + dup_desc, show=True) self.clear_clicked(which=which) def dup_check(self, sequence): for sc in self.all_shortcuts: if sc is self.shortcut: continue for k in sc['keys']: if k == sequence: return sc['name'] @property def custom_keys(self): if self.use_default.isChecked(): return None ans = [] for which in (1, 2): button = getattr(self, 'button%d' % which) t = unicode(button.text()) if t == _('None'): continue ks = QKeySequence(t, QKeySequence.NativeText) if not ks.isEmpty(): ans.append(ks) return tuple(ans)
class MakeBrickDialog(QDialog): def __init__(self, parent, modal=True, flags=Qt.WindowFlags()): QDialog.__init__(self, parent, flags) self.model = None self._model_dir = None self.setModal(modal) self.setWindowTitle("Convert sources to FITS brick") lo = QVBoxLayout(self) lo.setContentsMargins(10, 10, 10, 10) lo.setSpacing(5) # file selector self.wfile = FileSelector(self, label="FITS filename:", dialog_label="Output FITS file", default_suffix="fits", file_types="FITS files (*.fits *.FITS)", file_mode=QFileDialog.ExistingFile) lo.addWidget(self.wfile) # reference frequency lo1 = QHBoxLayout() lo.addLayout(lo1) lo1.setContentsMargins(0, 0, 0, 0) label = QLabel("Frequency, MHz:", self) lo1.addWidget(label) tip = """<P>If your sky model contains spectral information (such as spectral indices), then a brick may be generated for a specific frequency. If a frequency is not specified here, the reference frequency of the model sources will be assumed.</P>""" self.wfreq = QLineEdit(self) self.wfreq.setValidator(QDoubleValidator(self)) label.setToolTip(tip) self.wfreq.setToolTip(tip) lo1.addWidget(self.wfreq) # beam gain lo1 = QHBoxLayout() lo.addLayout(lo1) lo1.setContentsMargins(0, 0, 0, 0) self.wpb_apply = QCheckBox("Apply primary beam expression:", self) self.wpb_apply.setChecked(True) lo1.addWidget(self.wpb_apply) tip = """<P>If this option is specified, a primary power beam gain will be applied to the sources before inserting them into the brick. This can be any valid Python expression making use of the variables 'r' (corresponding to distance from field centre, in radians) and 'fq' (corresponding to frequency.)</P>""" self.wpb_exp = QLineEdit(self) self.wpb_apply.setToolTip(tip) self.wpb_exp.setToolTip(tip) lo1.addWidget(self.wpb_exp) # overwrite or add mode lo1 = QHBoxLayout() lo.addLayout(lo1) lo1.setContentsMargins(0, 0, 0, 0) self.woverwrite = QRadioButton("overwrite image", self) self.woverwrite.setChecked(True) lo1.addWidget(self.woverwrite) self.waddinto = QRadioButton("add into image", self) lo1.addWidget(self.waddinto) # add to model self.wadd = QCheckBox( "Add resulting brick to sky model as a FITS image component", self) lo.addWidget(self.wadd) lo1 = QHBoxLayout() lo.addLayout(lo1) lo1.setContentsMargins(0, 0, 0, 0) self.wpad = QLineEdit(self) self.wpad.setValidator(QDoubleValidator(self)) self.wpad.setText("1.1") lab = QLabel("...with padding factor:", self) lab.setToolTip( """<P>The padding factor determines the amount of null padding inserted around the image during the prediction stage. Padding alleviates the effects of tapering and detapering in the uv-brick, which can show up towards the edges of the image. For a factor of N, the image will be padded out to N times its original size. This increases memory use, so if you have no flux at the edges of the image anyway, then a pad factor of 1 is perfectly fine.</P>""") self.wpad.setToolTip(lab.toolTip()) self.wadd.toggled[bool].connect(self.wpad.setEnabled) self.wadd.toggled[bool].connect(lab.setEnabled) self.wpad.setEnabled(False) lab.setEnabled(False) lo1.addStretch(1) lo1.addWidget(lab, 0) lo1.addWidget(self.wpad, 1) self.wdel = QCheckBox( "Remove from the sky model sources that go into the brick", self) lo.addWidget(self.wdel) # OK/cancel buttons lo.addSpacing(10) lo2 = QHBoxLayout() lo.addLayout(lo2) lo2.setContentsMargins(5, 5, 5, 5) self.wokbtn = QPushButton("OK", self) self.wokbtn.setMinimumWidth(128) self.wokbtn.clicked.connect(self.accept) self.wokbtn.setEnabled(False) cancelbtn = QPushButton("Cancel", self) cancelbtn.setMinimumWidth(128) cancelbtn.clicked.connect(self.reject) lo2.addWidget(self.wokbtn) lo2.addStretch(1) lo2.addWidget(cancelbtn) self.setMinimumWidth(384) # signals self.wfile.filenameSelected.connect(self._fileSelected) # internal state self.qerrmsg = QErrorMessage(self) def setModel(self, model): self.model = model pb = self.model.primaryBeam() if pb: self.wpb_exp.setText(pb) else: self.wpb_apply.setChecked(False) self.wpb_exp.setText("") if model.filename(): self._model_dir = os.path.dirname(os.path.abspath( model.filename())) else: self._model_dir = os.path.abspath('.') self.wfile.setDirectory(self._model_dir) self._fileSelected(self.wfile.filename(), quiet=True) def _fileSelected(self, filename, quiet=False): self.wokbtn.setEnabled(False) if not filename: return None # check that filename matches model if not os.path.samefile(self._model_dir, os.path.dirname(filename)): self.wfile.setFilename('') if not quiet: QMessageBox.warning( self, "Directory mismatch", """<P>The FITS file must reside in the same directory as the current sky model.</P>""") self.wfile.setDirectory(self._model_dir) return None # read fits file busy = BusyIndicator() try: input_hdu = pyfits.open(filename)[0] hdr = input_hdu.header # get frequency, if specified for axis in range(1, hdr['NAXIS'] + 1): if hdr['CTYPE%d' % axis].upper() == 'FREQ': self.wfreq.setText(str(hdr['CRVAL%d' % axis] / 1e+6)) break except Exception as err: busy.reset_cursor() self.wfile.setFilename('') if not quiet: QMessageBox.warning( self, "Error reading FITS", "Error reading FITS file %s: %s" % (filename, str(err))) return None self.wokbtn.setEnabled(True) # if filename is not in model already, enable the "add to model" control for src in self.model.sources: if isinstance(getattr(src, 'shape', None), ModelClasses.FITSImage) \ and os.path.exists(src.shape.filename) and os.path.exists(filename) \ and os.path.samefile(src.shape.filename, filename): self.wadd.setChecked(True) self.wadd.setEnabled(False) self.wadd.setText("image already in sky model") break else: self.wadd.setText("add image to sky model") busy.reset_cursor() return filename def accept(self): """Tries to make a brick, and closes the dialog if successful.""" sources = [ src for src in self.model.sources if src.selected and src.typecode == 'pnt' ] filename = self.wfile.filename() if not self._fileSelected(filename): return # get PB expression pbfunc = None if self.wpb_apply.isChecked(): pbexp = str(self.wpb_exp.text()) try: pbfunc = eval("lambda r,fq:" + pbexp) except Exception as err: QMessageBox.warning( self, "Error parsing PB experssion", "Error parsing primary beam expression %s: %s" % (pbexp, str(err))) return # get frequency freq = str(self.wfreq.text()) freq = float(freq) * 1e+6 if freq else None # get pad factor pad = str(self.wpad.text()) pad = max(float(pad), 1) if pad else 1 # read fits file busy = BusyIndicator() try: input_hdu = pyfits.open(filename)[0] except Exception as err: busy.reset_cursor() QMessageBox.warning( self, "Error reading FITS", "Error reading FITS file %s: %s" % (filename, str(err))) return # reset data if asked to if self.woverwrite.isChecked(): input_hdu.data[...] = 0 # insert sources Imaging.restoreSources(input_hdu, sources, 0, primary_beam=pbfunc, freq=freq) # save fits file try: # pyfits seems to produce an exception: # TypeError: formatwarning() takes exactly 4 arguments (5 given) # when attempting to overwrite a file. As a workaround, remove the file first. if os.path.exists(filename): os.remove(filename) input_hdu.writeto(filename) except Exception as err: traceback.print_exc() busy.reset_cursor() QMessageBox.warning( self, "Error writing FITS", "Error writing FITS file %s: %s" % (filename, str(err))) return changed = False sources = self.model.sources # remove sources from model if asked to if self.wdel.isChecked(): sources = [ src for src in sources if not (src.selected and src.typecode == 'pnt') ] changed = True # add image to model if asked to if self.wadd.isChecked(): hdr = input_hdu.header # get image parameters max_flux = float(input_hdu.data.max()) wcs = WCS(hdr, mode='pyfits') # Get reference pixel coordinates # wcs.getCentreWCSCoords() doesn't work, as that gives us the middle of the image # So scan the header to get the CRPIX values ra0 = dec0 = 1 for iaxis in range(hdr['NAXIS']): axs = str(iaxis + 1) name = hdr.get('CTYPE' + axs, axs).upper() if name.startswith("RA"): ra0 = hdr.get('CRPIX' + axs, 1) - 1 elif name.startswith("DEC"): dec0 = hdr.get('CRPIX' + axs, 1) - 1 # convert pixel to degrees ra0, dec0 = wcs.pix2wcs(ra0, dec0) ra0 *= DEG dec0 *= DEG sx, sy = wcs.getHalfSizeDeg() sx *= DEG sy *= DEG nx, ny = input_hdu.data.shape[-1:-3:-1] # check if this image is already contained in the model for src in sources: if isinstance(getattr(src, 'shape', None), ModelClasses.FITSImage) and os.path.samefile( src.shape.filename, filename): # update source parameters src.pos.ra, src.pos.dec = ra0, dec0 src.flux.I = max_flux src.shape.ex, src.shape.ey = sx, sy src.shape.nx, src.shape.ny = nx, ny src.shape.pad = pad break # not contained, make new source object else: pos = ModelClasses.Position(ra0, dec0) flux = ModelClasses.Flux(max_flux) shape = ModelClasses.FITSImage(sx, sy, 0, os.path.basename(filename), nx, ny, pad=pad) img_src = SkyModel.Source(os.path.splitext( os.path.basename(filename))[0], pos, flux, shape=shape) sources.append(img_src) changed = True if changed: self.model.setSources(sources) self.model.emitUpdate(SkyModel.SkyModel.UpdateAll, origin=self) self.parent().showMessage("Wrote %d sources to FITS file %s" % (len(sources), filename)) busy.reset_cursor() return QDialog.accept(self)