def __init__(self, parent, config_name=None, buttons=[], *args): """Creates dialog. 'config_name' is used to get/set default window size from Config object 'buttons' can be a list of names or (QPixmapWrapper,name[,tooltip]) tuples to provide custom buttons at the bottom of the dialog. When a button is clicked, the dialog emits SIGNAL("name"). A "Close" button is always provided, this simply hides the dialog. """ QDialog.__init__(self, parent, *args) self.setModal(False) lo = QVBoxLayout(self) # create viewer self.label = QLabel(self) self.label.setMargin(5) self.label.setWordWrap(True) lo.addWidget(self.label) self.label.hide() self.viewer = QTextBrowser(self) lo.addWidget(self.viewer) # self.viewer.setReadOnly(True) self.viewer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) QObject.connect(self.viewer, SIGNAL("anchorClicked(const QUrl &)"), self._urlClicked) self._source = None lo.addSpacing(5) # create button bar btnfr = QFrame(self) btnfr.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed) # btnfr.setMargin(5) lo.addWidget(btnfr) lo.addSpacing(5) btnfr_lo = QHBoxLayout(btnfr) btnfr_lo.setMargin(5) # add user buttons self._user_buttons = {} for name in buttons: if isinstance(name, str): btn = QPushButton(name, btnfr) elif isinstance(name, (list, tuple)): if len(name) < 3: pixmap, name = name tip = None else: pixmap, name, tip = name btn = QPushButton(pixmap.icon(), name, btnfr) if tip: btn.setToolTip(tip) self._user_buttons[name] = btn btn._clicked = Kittens.utils.curry(self.emit, SIGNAL(name)) self.connect(btn, SIGNAL("clicked()"), btn._clicked) btnfr_lo.addWidget(btn, 1) # add a Close button btnfr_lo.addStretch(100) closebtn = QPushButton(pixmaps.grey_round_cross.icon(), "Close", btnfr) self.connect(closebtn, SIGNAL("clicked()"), self.hide) btnfr_lo.addWidget(closebtn, 1) # resize selves self.config_name = config_name or "html-viewer" width = Config.getint('%s-width' % self.config_name, 512) height = Config.getint('%s-height' % self.config_name, 512) self.resize(QSize(width, height))
def __init__(self, movie): self.movie = movie QDialog.__init__(self, None) self.setObjectName("movie_warning") self.text_browser = QTextBrowser(self) self.text_browser.setObjectName("movie_warning_textbrowser") self.text_browser.setMinimumSize(400, 40) self.setWindowTitle('Rewind your movie?') self.text_browser.setPlainText( "You may want to rewind the movie now. If you save the part without " + "rewinding the movie, the movie file will become invalid because it " + "depends upon the initial atom positions. The atoms move as the movie " + "progresses, and saving the part now will save the final positions, " + "which are incorrect for the movie you just watched.") self.ok_button = QPushButton(self) self.ok_button.setObjectName("ok_button") self.ok_button.setText("Rewind movie") self.cancel_button = QPushButton(self) self.cancel_button.setObjectName("cancel_button") self.cancel_button.setText("No thanks") layout = QGridLayout(self) layout.addWidget(self.text_browser,0,0,0,1) layout.addWidget(self.ok_button,1,0) layout.addWidget(self.cancel_button,1,1) self.connect(self.ok_button,SIGNAL("clicked()"),self.rewindMovie) self.connect(self.cancel_button,SIGNAL("clicked()"),self.noThanks)
def addIconButton(self, icon, func): button = QPushButton() button.setIcon(icon) setattr(self, "button%d" % self.buttonNum, button) self.buttonNum += 1 self.buttonLayout.addWidget(button) self.connect(button, SIGNAL('clicked()'), func)
def __init__(self, movie): self.movie = movie QDialog.__init__(self, None) self.setObjectName("movie_warning") self.text_browser = QTextBrowser(self) self.text_browser.setObjectName("movie_warning_textbrowser") self.text_browser.setMinimumSize(400, 40) self.setWindowTitle('Rewind your movie?') self.text_browser.setPlainText( #bruce 080827 revised text "You may want to rewind the movie now. The atoms move as the movie " "progresses, and saving the part without rewinding will save the " "current positions, which is sometimes useful, but will make the " "movie invalid, because .dpb files only store deltas relative to " "the initial atom positions, and don't store the initial positions " "themselves." ) self.ok_button = QPushButton(self) self.ok_button.setObjectName("ok_button") self.ok_button.setText("Rewind movie") self.cancel_button = QPushButton(self) self.cancel_button.setObjectName("cancel_button") self.cancel_button.setText("Exit command without rewinding") #bruce 080827 revised text # Note: this is not, in fact, a cancel button -- # there is no option in the caller to prevent exiting the command. # There is also no option to "forward to final position", # though for a minimize movie, that might be most useful. # [bruce 080827 comment] layout = QGridLayout(self) layout.addWidget(self.text_browser, 0, 0, 0, 1) layout.addWidget(self.ok_button, 1, 0) layout.addWidget(self.cancel_button, 1, 1) self.connect(self.ok_button, SIGNAL("clicked()"), self.rewindMovie) self.connect(self.cancel_button, SIGNAL("clicked()"), self.noThanks)
def __init__(self, process, calculation): """ """ QDialog.__init__(self, None, None, True) self.process = process self.setCaption("Please Wait") pbVBLayout = QVBoxLayout(self,11,6,"ProgressBarDialogLayout") msgLabel = QLabel(self,"msgLabel") msgLabel.setAlignment(QLabel.AlignCenter) if calculation == 'Energy': msgLabel.setText("Calculating Energy ...") else: msgLabel.setText("Optimizing ...") pbVBLayout.addWidget(msgLabel) self.msgLabel2 = QLabel(self,"msgLabel2") self.msgLabel2.setAlignment(QLabel.AlignCenter) self.msgLabel2.setText('') pbVBLayout.addWidget(self.msgLabel2) cancelButton = QPushButton(self,"canel") cancelButton.setText("Cancel") pbVBLayout.addWidget(cancelButton) self.resize(QSize(248,146).expandedTo(self.minimumSizeHint())) self.connect(cancelButton, SIGNAL("clicked()"), self.reject) return
def __init__(self, parent, modal=True, flags=Qt.WindowFlags(), caption="Select Tags", ok_button="Select"): QDialog.__init__(self, parent, flags) self.setModal(modal) self.setWindowTitle(caption) lo = QVBoxLayout(self) lo.setMargin(10) lo.setSpacing(5) # tag selector self.wtagsel = QListWidget(self) lo.addWidget(self.wtagsel) # self.wtagsel.setColumnMode(QListBox.FitToWidth) self.wtagsel.setSelectionMode(QListWidget.MultiSelection) QObject.connect(self.wtagsel, SIGNAL("itemSelectionChanged()"), self._check_tag) # buttons lo.addSpacing(10) lo2 = QHBoxLayout() lo.addLayout(lo2) lo2.setContentsMargins(0, 0, 0, 0) lo2.setMargin(5) self.wokbtn = QPushButton(ok_button, self) self.wokbtn.setMinimumWidth(128) QObject.connect(self.wokbtn, SIGNAL("clicked()"), self.accept) self.wokbtn.setEnabled(False) cancelbtn = QPushButton("Cancel", self) cancelbtn.setMinimumWidth(128) QObject.connect(cancelbtn, SIGNAL("clicked()"), self.reject) lo2.addWidget(self.wokbtn) lo2.addStretch(1) lo2.addWidget(cancelbtn) self.setMinimumWidth(384) self._tagnames = []
def __init__(self, gui, icon, do_user_config): QDialog.__init__(self, gui) self.gui = gui self.do_user_config = do_user_config # The current database shown in the GUI self.db = gui.current_db self.prefs = PrefsFacade(self.db) self.version = Downloader.version # The GUI, created and layouted by hand... self.layout = QVBoxLayout() self.setLayout(self.layout) self.setWindowTitle('Beam EBooks Downloader') self.setWindowIcon(icon) self.log_area = QTextEdit('Log output', self) self.log_area.setReadOnly(True) self.log_area.setLineWrapMode(QTextEdit.NoWrap); self.log_area.setText("") self.layout.addWidget(self.log_area) self.download_button = QPushButton('Download books', self) self.download_button.clicked.connect(self.download) self.layout.addWidget(self.download_button) self.conf_button = QPushButton('Configure this plugin', self) self.conf_button.clicked.connect(self.config) self.layout.addWidget(self.conf_button) self.resize(self.sizeHint())
class DebugDevice(QDialog): def __init__(self, parent=None): QDialog.__init__(self, parent) self._layout = QVBoxLayout(self) self.setLayout(self._layout) self.log = QPlainTextEdit(self) self._layout.addWidget(self.log) self.log.setPlainText(_('Getting debug information')+'...') self.copy = QPushButton(_('Copy to &clipboard')) self.copy.setDefault(True) self.setWindowTitle(_('Debug device detection')) self.setWindowIcon(QIcon(I('debug.png'))) self.copy.clicked.connect(self.copy_to_clipboard) self.ok = QPushButton('&OK') self.ok.setAutoDefault(False) self.ok.clicked.connect(self.accept) self.bbox = QDialogButtonBox(self) self.bbox.addButton(self.copy, QDialogButtonBox.ActionRole) self.bbox.addButton(self.ok, QDialogButtonBox.AcceptRole) self._layout.addWidget(self.bbox) self.resize(750, 500) self.bbox.setEnabled(False) QTimer.singleShot(1000, self.debug) def debug(self): try: from calibre.devices import debug raw = debug() self.log.setPlainText(raw) finally: self.bbox.setEnabled(True) def copy_to_clipboard(self): QApplication.clipboard().setText(self.log.toPlainText())
def setupUi(self, *args): # {{{ self.resize(990, 670) self.download_shortcut = QShortcut(self) self.download_shortcut.setKey(QKeySequence('Ctrl+D', QKeySequence.PortableText)) p = self.parent() if hasattr(p, 'keyboard'): kname = u'Interface Action: Edit Metadata (Edit Metadata) : menu action : download' sc = p.keyboard.keys_map.get(kname, None) if sc: self.download_shortcut.setKey(sc[0]) self.button_box = bb = QDialogButtonBox(self) self.button_box.accepted.connect(self.accept) self.button_box.rejected.connect(self.reject) self.next_button = QPushButton(QIcon(I('forward.png')), _('Next'), self) self.next_button.setShortcut(QKeySequence('Alt+Right')) self.next_button.clicked.connect(self.next_clicked) self.prev_button = QPushButton(QIcon(I('back.png')), _('Previous'), self) self.prev_button.setShortcut(QKeySequence('Alt+Left')) self.button_box.addButton(self.prev_button, bb.ActionRole) self.button_box.addButton(self.next_button, bb.ActionRole) self.prev_button.clicked.connect(self.prev_clicked) bb.setStandardButtons(bb.Ok|bb.Cancel) bb.button(bb.Ok).setDefault(True) self.scroll_area = QScrollArea(self) self.scroll_area.setFrameShape(QScrollArea.NoFrame) self.scroll_area.setWidgetResizable(True) self.central_widget = QTabWidget(self) self.scroll_area.setWidget(self.central_widget) self.l = QVBoxLayout(self) self.setLayout(self.l) self.l.addWidget(self.scroll_area) ll = self.button_box_layout = QHBoxLayout() self.l.addLayout(ll) ll.addSpacing(10) ll.addWidget(self.button_box) self.setWindowIcon(QIcon(I('edit_input.png'))) self.setWindowTitle(BASE_TITLE) self.create_basic_metadata_widgets() if len(self.db.custom_column_label_map): self.create_custom_metadata_widgets() self.do_layout() geom = gprefs.get('metasingle_window_geometry3', None) if geom is not None: self.restoreGeometry(bytes(geom))
def __init__(self, win): self.xmlfile = os.path.join(_sponsordir, 'sponsors.xml') self.win = win self.needToAsk = False self.downloadSponsors = False threading.Thread.__init__(self) if not self.refreshWanted(): return if env.prefs[sponsor_permanent_permission_prefs_key]: # We have a permanent answer so no need for a dialog if env.prefs[sponsor_download_permission_prefs_key]: self.downloadSponsors = True return self.needToAsk = True QDialog.__init__(self, None) self.setObjectName("Permission") self.setModal(True) #This fixes bug 2296. Mitigates bug 2297 layout = QGridLayout() self.setLayout(layout) layout.setMargin(0) layout.setSpacing(0) layout.setObjectName("PermissionLayout") self.text_browser = QTextBrowser(self) self.text_browser.setObjectName("text_browser") layout.addWidget(self.text_browser,0,0,1,4) self.text_browser.setMinimumSize(400, 80) self.setWindowTitle('May we use your network connection?') self.setWindowIcon(geticon('ui/border/MainWindow.png')) self.text_browser.setPlainText(self.text) self.accept_button = QPushButton(self) self.accept_button.setObjectName("accept_button") self.accept_button.setText("Always OK") self.accept_once_button = QPushButton(self) self.accept_once_button.setObjectName("accept_once_button") self.accept_once_button.setText("OK now") self.decline_once_button = QPushButton(self) self.decline_once_button.setObjectName("decline_once_button") self.decline_once_button.setText("Not now") self.decline_always_button = QPushButton(self) self.decline_always_button.setObjectName("decline_always_button") self.decline_always_button.setText("Never") layout.addWidget(self.accept_button,1,0) layout.addWidget(self.accept_once_button,1,1) layout.addWidget(self.decline_once_button,1,2) layout.addWidget(self.decline_always_button,1,3) self.connect(self.accept_button,SIGNAL("clicked()"),self.acceptAlways) self.connect(self.accept_once_button,SIGNAL("clicked()"),self.acceptJustOnce) self.connect(self.decline_once_button,SIGNAL("clicked()"),self.declineJustOnce) self.connect(self.decline_always_button,SIGNAL("clicked()"),self.declineAlways)
def __init__(self, data, name, text, parent): QPushButton.__init__(self, text, parent) self.ic = QPixmap(self.iconSize()) color = data[name] self.data, self.name = data, name if color is not None: self.current_color = read_color(color).color() self.ic.fill(self.current_color) else: self.ic.fill(Qt.transparent) self.current_color = color self.update_tooltip() self.setIcon(QIcon(self.ic)) self.clicked.connect(self.choose_color)
class DebugDevice(QDialog): def __init__(self, gui, parent=None): QDialog.__init__(self, parent) self.gui = gui self._layout = QVBoxLayout(self) self.setLayout(self._layout) self.log = QPlainTextEdit(self) self._layout.addWidget(self.log) self.log.setPlainText(_('Getting debug information, please wait')+'...') self.copy = QPushButton(_('Copy to &clipboard')) self.copy.setDefault(True) self.setWindowTitle(_('Debug device detection')) self.setWindowIcon(QIcon(I('debug.png'))) self.copy.clicked.connect(self.copy_to_clipboard) self.ok = QPushButton('&OK') self.ok.setAutoDefault(False) self.ok.clicked.connect(self.accept) self.bbox = QDialogButtonBox(self) self.bbox.addButton(self.copy, QDialogButtonBox.ActionRole) self.bbox.addButton(self.ok, QDialogButtonBox.AcceptRole) self._layout.addWidget(self.bbox) self.resize(750, 500) self.bbox.setEnabled(False) QTimer.singleShot(1000, self.debug) def debug(self): if self.gui.device_manager.is_device_connected: error_dialog(self, _('Device already detected'), _('A device (%s) is already detected by calibre.' ' If you wish to debug the detection of another device' ', first disconnect this device.')% self.gui.device_manager.connected_device.get_gui_name(), show=True) self.bbox.setEnabled(True) return self.gui.debug_detection(self) def __call__(self, job): if not self.isVisible(): return self.bbox.setEnabled(True) if job.failed: return error_dialog(self, _('Debugging failed'), _('Running debug device detection failed. Click Show ' 'Details for more information.'), det_msg=job.details, show=True) self.log.setPlainText(job.result) def copy_to_clipboard(self): QApplication.clipboard().setText(self.log.toPlainText())
def paint(self, painter, option, index): # If we are looking at a method then draw a button # http://www.qtcentre.org/threads/26916-inserting-custom-Widget-to-listview?p=128623#post128623 if not hasattr(self, "blue_button"): self.blue_button = QPushButton() self.blue_button.setStyleSheet("color: rgb(0, 0, 196)") if index.isValid() and \ isinstance(index.internalPointer().data, Method): item = index.internalPointer() opt = QStyleOptionButton() style = QApplication.style() # If method is running, draw sunken if item.argvalue: opt.state |= QStyle.State_Enabled opt.state |= QStyle.State_Sunken # if method is allowed, draw blue elif self.method_allowed(item): opt.state |= QStyle.State_Enabled style = self.blue_button.style() # if we are hovering, draw highlight if option.state & QStyle.State_MouseOver: opt.state |= QStyle.State_MouseOver opt.rect = option.rect opt.text = index.internalPointer().name style.drawControl(QStyle.CE_PushButton, opt, painter, self.blue_button) else: if option.state & QStyle.State_Selected: # Don't show delegates as highlighted option.state = option.state ^ QStyle.State_Selected QStyledItemDelegate.paint(self, painter, option, index)
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.layout = QVBoxLayout() self.setLayout(self.layout) self.setWindowTitle('Mendeley Plugin') self.setWindowIcon(icon) self.setMinimumWidth(500) self.resize(self.sizeHint()) self.startImportButton = QPushButton('Import documents from \'calibre\' Mendeley folder.') self.startImportButton.clicked.connect(self.startImport) self.layout.addWidget(self.startImportButton) self.helpl = QLabel('\n') self.helpl.setWordWrap(True) self.layout.addWidget(self.helpl) self.layout.setSizeConstraint(QLayout.SetFixedSize) plugin_prefs = JSONConfig('plugins/Mendeley') if not plugin_prefs.has_key('account') or not plugin_prefs.has_key('verification'): from calibre_plugins.mendeley_to_calibre import config as ConfigWidget dialog = ConfigWidget.ConfigWidget('plugin_option') dialog.add_ok_cancel_buttons() dialog.exec_() self.showErrorSignal.connect(self.show_dialog, type=Qt.QueuedConnection)
def __init__(self, parent = None, name = None, modal = 1, fl = 0): #QDialog.__init__(self,parent,name,modal,fl) QDialog.__init__(self,parent) self.setModal(modal) qt4todo("handle flags in TextMessageBox.__init__") if name is None: name = "TextMessageBox" self.setObjectName(name) self.setWindowTitle(name) TextMessageLayout = QVBoxLayout(self) TextMessageLayout.setMargin(5) TextMessageLayout.setSpacing(1) self.text_edit = QTextEdit(self) TextMessageLayout.addWidget(self.text_edit) self.close_button = QPushButton(self) self.close_button.setText("Close") TextMessageLayout.addWidget(self.close_button) self.resize(QSize(350, 300).expandedTo(self.minimumSizeHint())) # Width changed from 300 to 350. Now hscrollbar doesn't appear in # Help > Graphics Info textbox. mark 060322 qt4todo('self.clearWState(Qt.WState_Polished)') # what is this? self.connect(self.close_button, SIGNAL("clicked()"),self.close)
def __init__(self, parent, closeButton=True): QFrame.__init__(self, parent) self.setMaximumSize(QSize(9999999,22)) self.setObjectName("windowTitle") self.hboxlayout = QHBoxLayout(self) self.hboxlayout.setSpacing(0) self.hboxlayout.setContentsMargins(0,0,4,0) self.label = QLabel(self) self.label.setObjectName("label") self.label.setStyleSheet("padding-left:4px; font:bold 11px; color: #FFFFFF;") self.hboxlayout.addWidget(self.label) spacerItem = QSpacerItem(40,20,QSizePolicy.Expanding,QSizePolicy.Minimum) self.hboxlayout.addItem(spacerItem) if closeButton: self.pushButton = QPushButton(self) self.pushButton.setFocusPolicy(Qt.NoFocus) self.pushButton.setObjectName("pushButton") self.pushButton.setStyleSheet("font:bold;") self.pushButton.setText("X") self.hboxlayout.addWidget(self.pushButton) self.dragPosition = None self.mainwidget = self.parent() self.setStyleSheet(""" QFrame#windowTitle {background-color:#222222;color:#FFF;} """) # Initial position to top left self.dragPosition = self.mainwidget.frameGeometry().topLeft()
def __init__(self, text, parent=None, clicked_func=None, caption="(caption)", size=None): QDialog.__init__(self, parent) self.setWindowTitle(caption) self.setWindowIcon(QtGui.QIcon("ui/border/MainWindow")) self.setObjectName("WikiHelpBrowser") TextBrowserLayout = QGridLayout(self) TextBrowserLayout.setSpacing(5) TextBrowserLayout.setMargin(2) self.text_browser = QTextBrowser(self) self.text_browser.setOpenExternalLinks(True) self.text_browser.setObjectName("text_browser") TextBrowserLayout.addWidget(self.text_browser, 0, 0, 1, 0) self.text_browser.setMinimumSize(400, 200) # make it pale yellow like a post-it note self.text_browser.setHtml('<qt bgcolor="#FFFF80">' + text) self.close_button = QPushButton(self) self.close_button.setObjectName("close_button") self.close_button.setText("Close") TextBrowserLayout.addWidget(self.close_button, 1, 1) spacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) TextBrowserLayout.addItem(spacer, 1, 0) self.resize(QSize(300, 300).expandedTo(self.minimumSizeHint())) if size == 1: self.text_browser.setMinimumSize(200, 400) self.resize(QSize(300, 550).expandedTo(self.minimumSizeHint())) if size == 2: self.resize(QSize(650, 250).expandedTo(self.minimumSizeHint())) self.connect(self.close_button, SIGNAL("clicked()"), self.close) return
def __init__(self, parent): super(CharacterDialog, self).__init__(parent) self.setWindowTitle(_("KeepKey Seed Recovery")) self.character_pos = 0 self.word_pos = 0 self.loop = QEventLoop() self.word_help = QLabel() self.char_buttons = [] vbox = QVBoxLayout(self) vbox.addWidget(WWLabel(CHARACTER_RECOVERY)) hbox = QHBoxLayout() hbox.addWidget(self.word_help) for i in range(4): char_button = CharacterButton('*') char_button.setMaximumWidth(36) self.char_buttons.append(char_button) hbox.addWidget(char_button) self.accept_button = CharacterButton(_("Accept Word")) self.accept_button.clicked.connect(partial(self.process_key, 32)) self.rejected.connect(partial(self.loop.exit, 1)) hbox.addWidget(self.accept_button) hbox.addStretch(1) vbox.addLayout(hbox) self.finished_button = QPushButton(_("Seed Entered")) self.cancel_button = QPushButton(_("Cancel")) self.finished_button.clicked.connect(partial(self.process_key, Qt.Key_Return)) self.cancel_button.clicked.connect(self.rejected) buttons = Buttons(self.finished_button, self.cancel_button) vbox.addSpacing(40) vbox.addLayout(buttons) self.refresh() self.show()
def _getTitleButton(self, parentWidget = None, title = '', showExpanded = True ): """ Return the group box title push button. The push button is customized such that it appears as a title bar at the top of the group box. If the user clicks on this 'title bar' it sends a signal to open or close the group box. @param parentWidget: The parent dialog or group box containing this widget. @type parentWidget: PM_Dialog or PM_GroupBox @param title: The button title. @type title: str @param showExpanded: dDetermines whether the expand or collapse image is displayed on the title button. @type showExpanded: bool @see: L{_getTitleButtonStyleSheet()} @Note: Including a title button should only be legal if the parentWidget is a PM_Dialog. """ button = QPushButton(title, parentWidget) button.setFlat(False) button.setAutoFillBackground(True) button.setStyleSheet(self._getTitleButtonStyleSheet(showExpanded)) return button
class TextMessageBox(QDialog): """ The TextMessageBox class provides a modal dialog with a textedit widget and a close button. It is used as an option to QMessageBox when displaying a large amount of text. It also has the benefit of allowing the user to copy and paste the text from the textedit widget. Call the setText() method to insert text into the textedit widget. """ def __init__(self, parent = None, name = None, modal = 1, fl = 0): #QDialog.__init__(self,parent,name,modal,fl) QDialog.__init__(self,parent) self.setModal(modal) qt4todo("handle flags in TextMessageBox.__init__") if name is None: name = "TextMessageBox" self.setObjectName(name) self.setWindowTitle(name) TextMessageLayout = QVBoxLayout(self) TextMessageLayout.setMargin(5) TextMessageLayout.setSpacing(1) self.text_edit = QTextEdit(self) TextMessageLayout.addWidget(self.text_edit) self.close_button = QPushButton(self) self.close_button.setText("Close") TextMessageLayout.addWidget(self.close_button) self.resize(QSize(350, 300).expandedTo(self.minimumSizeHint())) # Width changed from 300 to 350. Now hscrollbar doesn't appear in # Help > Graphics Info textbox. mark 060322 qt4todo('self.clearWState(Qt.WState_Polished)') # what is this? self.connect(self.close_button, SIGNAL("clicked()"),self.close) def setText(self, txt): """ Sets the textedit's text to txt """ self.text_edit.setPlainText(txt) pass
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 __init__(self, parent=None, name=None, modal=0, fl=None): if fl is None: fl = Qt.Dialog | Qt.WindowTitleHint QDialog.__init__(self, parent, Qt.Dialog | Qt.WindowTitleHint) self.setModal(modal) image0 = pixmaps.tigger_logo.pm() # self.setSizeGripEnabled(0) LayoutWidget = QWidget(self) LayoutWidget.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) lo_top = QVBoxLayout(LayoutWidget) lo_title = QHBoxLayout(None) self.title_icon = QLabel(LayoutWidget) self.title_icon.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.title_icon.setPixmap(image0) self.title_icon.setAlignment(Qt.AlignCenter) lo_title.addWidget(self.title_icon) self.title_label = QLabel(LayoutWidget) self.title_label.setWordWrap(True) lo_title.addWidget(self.title_label) lo_top.addLayout(lo_title) lo_logos = QHBoxLayout(None) lo_top.addLayout(lo_logos) # for logo in ("astron",): # icon = QLabel(LayoutWidget) # icon.setSizePolicy(QSizePolicy.Fixed,QSizePolicy.Fixed) # icon.setPixmap(getattr(pixmaps,logo+"_logo").pm()) # icon.setAlignment(Qt.AlignCenter) # lo_logos.addWidget(icon) lo_mainbtn = QHBoxLayout(None) lo_mainbtn.addItem(QSpacerItem(20, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)) lo_top.addLayout(lo_mainbtn) self.btn_ok = QPushButton(LayoutWidget) self.btn_ok.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.btn_ok.setMinimumSize(QSize(60, 0)) self.btn_ok.setAutoDefault(1) self.btn_ok.setDefault(1) lo_mainbtn.addWidget(self.btn_ok) lo_mainbtn.addItem(QSpacerItem(20, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)) self.languageChange() LayoutWidget.adjustSize() # LayoutWidget.resize(QSize(489,330).expandedTo(LayoutWidget.minimumSizeHint())) # self.resize(QSize(489,330).expandedTo(self.minimumSizeHint())) # self.clearWState(Qt.WState_Polished) self.connect(self.btn_ok, SIGNAL("clicked()"), self.accept)
class FontFamilyChooser(QWidget): family_changed = pyqtSignal(object) def __init__(self, parent=None): QWidget.__init__(self, parent) self.l = l = QHBoxLayout() self.setLayout(l) self.button = QPushButton(self) self.button.setIcon(QIcon(I('font.png'))) l.addWidget(self.button) self.default_text = _('Choose &font family') self.font_family = None self.button.clicked.connect(self.show_chooser) self.clear_button = QToolButton(self) self.clear_button.setIcon(QIcon(I('clear_left.png'))) self.clear_button.clicked.connect(self.clear_family) l.addWidget(self.clear_button) self.setToolTip = self.button.setToolTip self.toolTip = self.button.toolTip self.clear_button.setToolTip(_('Clear the font family')) def clear_family(self): self.font_family = None @dynamic_property def font_family(self): def fget(self): return self._current_family def fset(self, val): if not val: val = None self._current_family = val self.button.setText(val or self.default_text) self.family_changed.emit(val) return property(fget=fget, fset=fset) def show_chooser(self): d = FontFamilyDialog(self.font_family, self) if d.exec_() == d.Accepted: self.font_family = d.font_family
def setupWidgets(self): self.setWindowTitle(self.title) menubar = QMenuBar(self) menu = QMenu("Title " + self.title) self._action_tmp = QAction("Reproduce", self) signal_connect(self._action_tmp, SIGNAL("activated()"), self.reproduce) menu.addAction(self._action_tmp) menubar.addMenu(menu) menubar.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.setMenuBar(menubar) cw = QWidget(self) layout = QVBoxLayout(cw) cw.setLayout(layout) button = QPushButton("Reproduce", self) button.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) layout.addWidget(button) self.button = button signal_connect(button, SIGNAL("clicked()"), self.reproduce) for o in ("menubar", "menu", "cw"): setattr(self, o, eval(o)) self.setCentralWidget(cw)
def __init__(self, parent, modal=True, flags=Qt.WindowFlags()): QDialog.__init__(self, parent, flags) self.setModal(modal) self.setWindowTitle("Add Tag") lo = QVBoxLayout(self) lo.setMargin(10) lo.setSpacing(5) # tag selector lo1 = QHBoxLayout() lo.addLayout(lo1) lo1.setSpacing(5) self.wtagsel = QComboBox(self) self.wtagsel.setEditable(True) wtagsel_lbl = QLabel("&Tag:", self) wtagsel_lbl.setBuddy(self.wtagsel) lo1.addWidget(wtagsel_lbl, 0) lo1.addWidget(self.wtagsel, 1) QObject.connect(self.wtagsel, SIGNAL("activated(int)"), self._check_tag) QObject.connect(self.wtagsel, SIGNAL("editTextChanged(const QString &)"), self._check_tag_text) # value editor self.valedit = ValueTypeEditor(self) lo.addWidget(self.valedit) # buttons lo.addSpacing(10) lo2 = QHBoxLayout() lo.addLayout(lo2) lo2.setContentsMargins(0, 0, 0, 0) lo2.setMargin(5) self.wokbtn = QPushButton("OK", self) self.wokbtn.setMinimumWidth(128) QObject.connect(self.wokbtn, SIGNAL("clicked()"), self.accept) self.wokbtn.setEnabled(False) cancelbtn = QPushButton("Cancel", self) cancelbtn.setMinimumWidth(128) QObject.connect(cancelbtn, SIGNAL("clicked()"), self.reject) lo2.addWidget(self.wokbtn) lo2.addStretch(1) lo2.addWidget(cancelbtn) self.setMinimumWidth(384)
def __init__(self, parent=None): QDialog.__init__(self, parent) self._layout = QVBoxLayout(self) self.setLayout(self._layout) self.log = QPlainTextEdit(self) self._layout.addWidget(self.log) self.log.setPlainText(_('Getting device information')+'...') self.copy = QPushButton(_('Copy to &clipboard')) self.copy.setDefault(True) self.setWindowTitle(_('User-defined device information')) self.setWindowIcon(QIcon(I('debug.png'))) self.copy.clicked.connect(self.copy_to_clipboard) self.ok = QPushButton('&OK') self.ok.setAutoDefault(False) self.ok.clicked.connect(self.accept) self.bbox = QDialogButtonBox(self) self.bbox.addButton(self.copy, QDialogButtonBox.ActionRole) self.bbox.addButton(self.ok, QDialogButtonBox.AcceptRole) self._layout.addWidget(self.bbox) self.resize(750, 500) self.bbox.setEnabled(False) QTimer.singleShot(1000, self.device_info)
class SelectTagsDialog(QDialog): def __init__(self, parent, modal=True, flags=Qt.WindowFlags(), caption="Select Tags", ok_button="Select"): QDialog.__init__(self, parent, flags) self.setModal(modal) self.setWindowTitle(caption) lo = QVBoxLayout(self) lo.setMargin(10) lo.setSpacing(5) # tag selector self.wtagsel = QListWidget(self) lo.addWidget(self.wtagsel) # self.wtagsel.setColumnMode(QListBox.FitToWidth) self.wtagsel.setSelectionMode(QListWidget.MultiSelection) QObject.connect(self.wtagsel, SIGNAL("itemSelectionChanged()"), self._check_tag) # buttons lo.addSpacing(10) lo2 = QHBoxLayout() lo.addLayout(lo2) lo2.setContentsMargins(0, 0, 0, 0) lo2.setMargin(5) self.wokbtn = QPushButton(ok_button, self) self.wokbtn.setMinimumWidth(128) QObject.connect(self.wokbtn, SIGNAL("clicked()"), self.accept) self.wokbtn.setEnabled(False) cancelbtn = QPushButton("Cancel", self) cancelbtn.setMinimumWidth(128) QObject.connect(cancelbtn, SIGNAL("clicked()"), self.reject) lo2.addWidget(self.wokbtn) lo2.addStretch(1) lo2.addWidget(cancelbtn) self.setMinimumWidth(384) self._tagnames = [] def setTags(self, tagnames): self._tagnames = tagnames self.wtagsel.clear() self.wtagsel.insertItems(0, list(tagnames)) def _check_tag(self): for i in range(len(self._tagnames)): if self.wtagsel.item(i).isSelected(): self.wokbtn.setEnabled(True) return else: self.wokbtn.setEnabled(False) def getSelectedTags(self): return [tag for i, tag in enumerate(self._tagnames) if self.wtagsel.item(i).isSelected()]
def __init__(self, parent, modal=True, flags=Qt.WindowFlags()): QDialog.__init__(self, parent, flags) self.setModal(modal) self.setWindowTitle("Export Karma annotations") lo = QVBoxLayout(self) lo.setMargin(10) lo.setSpacing(5) # file selector self.wfile = FileSelector(self, label="Filename:", dialog_label="Karma annotations filename", default_suffix="ann", file_types="Karma annotations (*.ann)") lo.addWidget(self.wfile) # selected sources checkbox self.wsel = QCheckBox("selected sources only", self) lo.addWidget(self.wsel) # OK/cancel buttons lo.addSpacing(10) lo2 = QHBoxLayout() lo.addLayout(lo2) lo2.setContentsMargins(0, 0, 0, 0) lo2.setMargin(5) self.wokbtn = QPushButton("OK", self) self.wokbtn.setMinimumWidth(128) QObject.connect(self.wokbtn, SIGNAL("clicked()"), self.accept) self.wokbtn.setEnabled(False) cancelbtn = QPushButton("Cancel", self) cancelbtn.setMinimumWidth(128) QObject.connect(cancelbtn, SIGNAL("clicked()"), self.reject) lo2.addWidget(self.wokbtn) lo2.addStretch(1) lo2.addWidget(cancelbtn) self.setMinimumWidth(384) # signals QObject.connect(self.wfile, SIGNAL("valid"), self.wokbtn.setEnabled) # internal state self.qerrmsg = QErrorMessage(self) self._model_filename = None
def reactivateBtns(self): try: self.gridLayout.removeWidget(self.stopBtn) self.stopBtn.deleteLater() except: pass self.downloadBtn = QPushButton(self.centralwidget) self.downloadBtn.setObjectName("downloadBtn") self.downloadBtn.setText("Download!") self.downloadBtn.clicked.connect(self.beginDownload) self.gridLayout.addWidget(self.downloadBtn, 6, 0, 1, 2) self.addUserBtn.setEnabled(True) self.addSubredditBtn.setEnabled(True) self.deleteUserBtn.setEnabled(True) self.deleteSubredditBtn.setEnabled(True) self._rddtDataExtractor.currentlyDownloading = False
def enterDownloadMode(self): self._rddtDataExtractor.currentlyDownloading = True self.logTextEdit.clear() self.stopBtn = QPushButton(self.centralwidget) self.stopBtn.setObjectName("stopBtn") self.stopBtn.setText("Downloading... Press here to stop the download (In progress downloads will continue until done).") self.stopBtn.clicked.connect(self.stopDownload) try: self.gridLayout.removeWidget(self.downloadBtn) self.downloadBtn.deleteLater() except: pass self.gridLayout.addWidget(self.stopBtn, 6, 0, 1, 2) self.addUserBtn.setEnabled(False) self.addSubredditBtn.setEnabled(False) self.deleteUserBtn.setEnabled(False) self.deleteSubredditBtn.setEnabled(False)
class BookInfo(QDialog): closed = pyqtSignal(object) def __init__(self, parent, view, row, link_delegate): QDialog.__init__(self, parent) self.normal_brush = QBrush(Qt.white) self.marked_brush = QBrush(Qt.lightGray) self.marked = None self.gui = parent self.splitter = QSplitter(self) self._l = l = QVBoxLayout(self) self.setLayout(l) l.addWidget(self.splitter) self.cover = CoverView(self) self.cover.resizeEvent = self.cover_view_resized self.cover.cover_changed.connect(self.cover_changed) self.cover_pixmap = None self.cover.sizeHint = self.details_size_hint self.splitter.addWidget(self.cover) self.details = QWebView(self) self.details.sizeHint = self.details_size_hint self.details.page().setLinkDelegationPolicy( self.details.page().DelegateAllLinks) self.details.linkClicked.connect(self.link_clicked) s = self.details.page().settings() s.setAttribute(s.JavascriptEnabled, False) self.css = css() self.link_delegate = link_delegate self.details.setAttribute(Qt.WA_OpaquePaintEvent, False) palette = self.details.palette() self.details.setAcceptDrops(False) palette.setBrush(QPalette.Base, Qt.transparent) self.details.page().setPalette(palette) self.c = QWidget(self) self.c.l = l2 = QGridLayout(self.c) self.c.setLayout(l2) l2.addWidget(self.details, 0, 0, 1, -1) self.splitter.addWidget(self.c) self.fit_cover = QCheckBox(_('Fit &cover within view'), self) self.fit_cover.setChecked( gprefs.get('book_info_dialog_fit_cover', True)) l2.addWidget(self.fit_cover, l2.rowCount(), 0, 1, -1) self.previous_button = QPushButton(QIcon(I('previous.png')), _('&Previous'), self) self.previous_button.clicked.connect(self.previous) l2.addWidget(self.previous_button, l2.rowCount(), 0) self.next_button = QPushButton(QIcon(I('next.png')), _('&Next'), self) self.next_button.clicked.connect(self.next) l2.addWidget(self.next_button, l2.rowCount() - 1, 1) self.view = view self.current_row = None self.refresh(row) self.view.selectionModel().currentChanged.connect(self.slave) self.fit_cover.stateChanged.connect(self.toggle_cover_fit) self.ns = QShortcut(QKeySequence('Alt+Right'), self) self.ns.activated.connect(self.next) self.ps = QShortcut(QKeySequence('Alt+Left'), self) self.ps.activated.connect(self.previous) self.next_button.setToolTip( _('Next [%s]') % unicode(self.ns.key().toString(QKeySequence.NativeText))) self.previous_button.setToolTip( _('Previous [%s]') % unicode(self.ps.key().toString(QKeySequence.NativeText))) geom = QCoreApplication.instance().desktop().availableGeometry(self) screen_height = geom.height() - 100 screen_width = geom.width() - 100 self.resize(max(int(screen_width / 2), 700), screen_height) saved_layout = gprefs.get('book_info_dialog_layout', None) if saved_layout is not None: try: self.restoreGeometry(saved_layout[0]) self.splitter.restoreState(saved_layout[1]) except Exception: pass def link_clicked(self, qurl): link = unicode(qurl.toString()) self.link_delegate(link) def done(self, r): saved_layout = (bytearray(self.saveGeometry()), bytearray(self.splitter.saveState())) gprefs.set('book_info_dialog_layout', saved_layout) ret = QDialog.done(self, r) self.view.selectionModel().currentChanged.disconnect(self.slave) self.view = self.link_delegate = self.gui = None self.closed.emit(self) return ret def cover_changed(self, data): if self.current_row is not None: id_ = self.view.model().id(self.current_row) self.view.model().db.set_cover(id_, data) if self.gui.cover_flow: self.gui.cover_flow.dataChanged() ci = self.view.currentIndex() if ci.isValid(): self.view.model().current_changed(ci, ci) self.cover_pixmap = QPixmap() self.cover_pixmap.loadFromData(data) if self.fit_cover.isChecked(): self.resize_cover() def details_size_hint(self): return QSize(350, 550) def toggle_cover_fit(self, state): gprefs.set('book_info_dialog_fit_cover', self.fit_cover.isChecked()) self.resize_cover() def cover_view_resized(self, event): QTimer.singleShot(1, self.resize_cover) def slave(self, current, previous): if current.row() != previous.row(): row = current.row() self.refresh(row) def move(self, delta=1): self.view.selectionModel().currentChanged.disconnect(self.slave) try: idx = self.view.currentIndex() if idx.isValid(): m = self.view.model() ni = m.index(idx.row() + delta, idx.column()) if ni.isValid(): self.view.setCurrentIndex(ni) self.refresh(ni.row()) if self.view.isVisible(): self.view.scrollTo(ni) finally: self.view.selectionModel().currentChanged.connect(self.slave) def next(self): self.move() def previous(self): self.move(-1) def resize_cover(self): if self.cover_pixmap is None: return pixmap = self.cover_pixmap if self.fit_cover.isChecked(): scaled, new_width, new_height = fit_image( pixmap.width(), pixmap.height(), self.cover.size().width() - 10, self.cover.size().height() - 10) if scaled: pixmap = pixmap.scaled(new_width, new_height, Qt.KeepAspectRatio, Qt.SmoothTransformation) self.cover.set_pixmap(pixmap) self.update_cover_tooltip() def update_cover_tooltip(self): tt = '' if self.marked: tt = _('This book is marked') if self.marked in { True, 'true' } else _('This book is marked as: %s') % self.marked tt += '\n\n' if self.cover_pixmap is not None: sz = self.cover_pixmap.size() tt += _('Cover size: %(width)d x %(height)d') % dict( width=sz.width(), height=sz.height()) self.cover.setToolTip(tt) def refresh(self, row): if isinstance(row, QModelIndex): row = row.row() if row == self.current_row: return mi = self.view.model().get_book_display_info(row) if mi is None: # Indicates books was deleted from library, or row numbers have # changed return self.previous_button.setEnabled(False if row == 0 else True) self.next_button.setEnabled(False if row == self.view.model().rowCount(QModelIndex()) - 1 else True) self.current_row = row self.setWindowTitle(mi.title) self.cover_pixmap = QPixmap.fromImage(mi.cover_data[1]) self.resize_cover() html = render_html(mi, self.css, True, self, all_fields=True) self.details.setHtml(html) self.marked = mi.marked self.cover.setBackgroundBrush( self.marked_brush if mi.marked else self.normal_brush) self.update_cover_tooltip()
def genesis(self, gui): self.gui = gui db = gui.library_view.model().db r = self.register r('gui_layout', config, restart_required=True, choices=[(_('Wide'), 'wide'), (_('Narrow'), 'narrow')]) r('ui_style', gprefs, restart_required=True, choices=[(_('System default'), 'system'), (_('Calibre style'), 'calibre')]) r('book_list_tooltips', gprefs) r('tag_browser_old_look', gprefs, restart_required=True) r('bd_show_cover', gprefs) r('bd_overlay_cover_size', gprefs) r('cover_grid_width', gprefs) r('cover_grid_height', gprefs) r('cover_grid_cache_size', gprefs) r('cover_grid_disk_cache_size', gprefs) r('cover_grid_spacing', gprefs) r('cover_grid_show_title', gprefs) r('cover_flow_queue_length', config, restart_required=True) r('cover_browser_reflections', gprefs) r('extra_row_spacing', gprefs) def get_esc_lang(l): if l == 'en': return 'English' return get_language(l) lang = get_lang() if lang is None or lang not in available_translations(): lang = 'en' items = [(l, get_esc_lang(l)) for l in available_translations() if l != lang] if lang != 'en': items.append(('en', get_esc_lang('en'))) items.sort(cmp=lambda x, y: cmp(x[1].lower(), y[1].lower())) choices = [(y, x) for x, y in items] # Default language is the autodetected one choices = [(get_language(lang), lang)] + choices r('language', prefs, choices=choices, restart_required=True) r('show_avg_rating', config) r('disable_animations', config) r('systray_icon', config, restart_required=True) r('show_splash_screen', gprefs) r('disable_tray_notification', config) r('use_roman_numerals_for_series_number', config) r('separate_cover_flow', config, restart_required=True) r('cb_fullscreen', gprefs) r('cb_preserve_aspect_ratio', gprefs) choices = [(_('Off'), 'off'), (_('Small'), 'small'), (_('Medium'), 'medium'), (_('Large'), 'large')] r('toolbar_icon_size', gprefs, choices=choices) choices = [(_('If there is enough room'), 'auto'), (_('Always'), 'always'), (_('Never'), 'never')] r('toolbar_text', gprefs, choices=choices) choices = [(_('Disabled'), 'disable'), (_('By first letter'), 'first letter'), (_('Partitioned'), 'partition')] r('tags_browser_partition_method', gprefs, choices=choices) r('tags_browser_collapse_at', gprefs) r('default_author_link', gprefs) r('tag_browser_dont_collapse', gprefs, setting=CommaSeparatedList) choices = set([ k for k in db.field_metadata.all_field_keys() if (db.field_metadata[k]['is_category'] and (db.field_metadata[k]['datatype'] in ['text', 'series', 'enumeration']) and not db.field_metadata[k]['display'].get('is_names', False)) or (db.field_metadata[k]['datatype'] in ['composite'] and db.field_metadata[k]['display'].get('make_category', False)) ]) choices -= set( ['authors', 'publisher', 'formats', 'news', 'identifiers']) choices |= set(['search']) self.opt_categories_using_hierarchy.update_items_cache(choices) r('categories_using_hierarchy', db.prefs, setting=CommaSeparatedList, choices=sorted(list(choices), key=sort_key)) fm = db.field_metadata choices = sorted( ((fm[k]['name'], k) for k in fm.displayable_field_keys() if fm[k]['name']), key=lambda x: sort_key(x[0])) r('field_under_covers_in_grid', db.prefs, choices=choices) self.current_font = self.initial_font = None self.change_font_button.clicked.connect(self.change_font) self.display_model = DisplayedFields(self.gui.current_db, self.field_display_order) self.display_model.dataChanged.connect(self.changed_signal) self.field_display_order.setModel(self.display_model) self.df_up_button.clicked.connect(self.move_df_up) self.df_down_button.clicked.connect(self.move_df_down) self.edit_rules = EditRules(self.tabWidget) self.edit_rules.changed.connect(self.changed_signal) self.tabWidget.addTab(self.edit_rules, QIcon(I('format-fill-color.png')), _('Column coloring')) self.icon_rules = EditRules(self.tabWidget) self.icon_rules.changed.connect(self.changed_signal) self.tabWidget.addTab(self.icon_rules, QIcon(I('icon_choose.png')), _('Column icons')) self.tabWidget.setCurrentIndex(0) keys = [ QKeySequence('F11', QKeySequence.PortableText), QKeySequence('Ctrl+Shift+F', QKeySequence.PortableText) ] keys = [unicode(x.toString(QKeySequence.NativeText)) for x in keys] self.fs_help_msg.setText( unicode(self.fs_help_msg.text()) % (_(' or ').join(keys))) self.size_calculated.connect(self.update_cg_cache_size, type=Qt.QueuedConnection) self.tabWidget.currentChanged.connect(self.tab_changed) l = self.cg_background_box.layout() self.cg_bg_widget = w = Background(self) l.addWidget(w, 0, 0, 3, 1) self.cover_grid_color_button = b = QPushButton(_('Change &color'), self) b.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) l.addWidget(b, 0, 1) b.clicked.connect(self.change_cover_grid_color) self.cover_grid_texture_button = b = QPushButton( _('Change &background image'), self) b.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) l.addWidget(b, 1, 1) b.clicked.connect(self.change_cover_grid_texture) self.cover_grid_default_appearance_button = b = QPushButton( _('Restore &default appearance'), self) b.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) l.addWidget(b, 2, 1) b.clicked.connect(self.restore_cover_grid_appearance) self.cover_grid_empty_cache.clicked.connect(self.empty_cache) self.cover_grid_open_cache.clicked.connect(self.open_cg_cache) self.cover_grid_smaller_cover.clicked.connect( partial(self.resize_cover, True)) self.cover_grid_larger_cover.clicked.connect( partial(self.resize_cover, False)) self.cover_grid_reset_size.clicked.connect(self.cg_reset_size) self.opt_cover_grid_disk_cache_size.setMinimum( self.gui.grid_view.thumbnail_cache.min_disk_cache) self.opt_cover_grid_disk_cache_size.setMaximum( self.gui.grid_view.thumbnail_cache.min_disk_cache * 100) self.opt_cover_grid_width.valueChanged.connect( self.update_aspect_ratio) self.opt_cover_grid_height.valueChanged.connect( self.update_aspect_ratio)
class UserDefinedDevice(QDialog): def __init__(self, parent=None): QDialog.__init__(self, parent) self._layout = QVBoxLayout(self) self.setLayout(self._layout) self.log = QPlainTextEdit(self) self._layout.addWidget(self.log) self.log.setPlainText(_('Getting device information')+'...') self.copy = QPushButton(_('Copy to &clipboard')) self.copy.setDefault(True) self.setWindowTitle(_('User-defined device information')) self.setWindowIcon(QIcon(I('debug.png'))) self.copy.clicked.connect(self.copy_to_clipboard) self.ok = QPushButton('&OK') self.ok.setAutoDefault(False) self.ok.clicked.connect(self.accept) self.bbox = QDialogButtonBox(self) self.bbox.addButton(self.copy, QDialogButtonBox.ActionRole) self.bbox.addButton(self.ok, QDialogButtonBox.AcceptRole) self._layout.addWidget(self.bbox) self.resize(750, 500) self.bbox.setEnabled(False) QTimer.singleShot(1000, self.device_info) def device_info(self): try: from calibre.devices import device_info r = step_dialog(self.parent(), _('Device Detection'), _('Ensure your device is disconnected, then press OK')) if r: self.close() return before = device_info() r = step_dialog(self.parent(), _('Device Detection'), _('Ensure your device is connected, then press OK')) if r: self.close() return after = device_info() new_drives = after['drive_set'] - before['drive_set'] new_devices = after['device_set'] - before['device_set'] res = '' if (not iswindows or len(new_drives)) and len(new_devices) == 1: def fmtid(x): if not x.startswith('0x'): x = '0x' + x return x for d in new_devices: res = _('USB Vendor ID (in hex)') + ': ' + \ fmtid(after['device_details'][d][0]) + '\n' res += _('USB Product ID (in hex)') + ': ' + \ fmtid(after['device_details'][d][1]) + '\n' res += _('USB Revision ID (in hex)') + ': ' + \ fmtid(after['device_details'][d][2]) + '\n' if iswindows: # sort the drives by the order number for i,d in enumerate(sorted(new_drives, key=lambda x: after['drive_details'][x][0])): if i == 0: res += _('Windows main memory vendor string') + ': ' + \ after['drive_details'][d][1] + '\n' res += _('Windows main memory ID string') + ': ' + \ after['drive_details'][d][2] + '\n' else: res += _('Windows card A vendor string') + ': ' + \ after['drive_details'][d][1] + '\n' res += _('Windows card A ID string') + ': ' + \ after['drive_details'][d][2] + '\n' trailer = _( 'Copy these values to the clipboard, paste them into an ' 'editor, then enter them into the USER_DEVICE by ' 'customizing the device plugin in Preferences->Plugins. ' 'Remember to also enter the folders where you want the books to ' 'be put. You must restart calibre for your changes ' 'to take effect.\n') self.log.setPlainText(res + '\n\n' + trailer) finally: self.bbox.setEnabled(True) def copy_to_clipboard(self): QApplication.clipboard().setText(self.log.toPlainText())
class AddBrickDialog(QDialog): def __init__(self, parent, modal=True, flags=Qt.WindowFlags()): QDialog.__init__(self, parent, flags) self.setModal(modal) self.setWindowTitle("Add FITS brick") lo = QVBoxLayout(self) lo.setMargin(10) lo.setSpacing(5) # file selector self.wfile = FileSelector(self, label="FITS filename:", dialog_label="FITS file", default_suffix="fits", file_types="FITS files (*.fits *.FITS)", file_mode=QFileDialog.ExistingFile) lo.addWidget(self.wfile) # overwrite or add mode lo1 = QGridLayout() lo.addLayout(lo1) lo1.setContentsMargins(0, 0, 0, 0) lo1.addWidget(QLabel("Padding factor:", self), 0, 0) self.wpad = QLineEdit("2", self) self.wpad.setValidator(QDoubleValidator(self)) lo1.addWidget(self.wpad, 0, 1) lo1.addWidget(QLabel("Assign source name:", self), 1, 0) self.wname = QLineEdit(self) lo1.addWidget(self.wname, 1, 1) # OK/cancel buttons lo.addSpacing(10) lo2 = QHBoxLayout() lo.addLayout(lo2) lo2.setContentsMargins(0, 0, 0, 0) lo2.setMargin(5) self.wokbtn = QPushButton("OK", self) self.wokbtn.setMinimumWidth(128) QObject.connect(self.wokbtn, SIGNAL("clicked()"), self.accept) self.wokbtn.setEnabled(False) cancelbtn = QPushButton("Cancel", self) cancelbtn.setMinimumWidth(128) QObject.connect(cancelbtn, SIGNAL("clicked()"), self.reject) lo2.addWidget(self.wokbtn) lo2.addStretch(1) lo2.addWidget(cancelbtn) self.setMinimumWidth(384) # signals QObject.connect(self.wfile, SIGNAL("filenameSelected"), self._fileSelected) # internal state self.qerrmsg = QErrorMessage(self) def setModel(self, model): self.model = model 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 # 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): if os.path.exists(src.shape.filename) and os.path.samefile( src.shape.filename, filename): if not quiet: QMessageBox.warning( self, "Already in model", "This FITS brick is already present in the model.") self.wfile.setFilename('') return None if not str(self.wname.text()): self.wname.setText( os.path.splitext(os.path.basename(str(filename)))[0]) self.wokbtn.setEnabled(True) return filename def accept(self): """Tries to add brick, and closes the dialog if successful.""" filename = self.wfile.filename() # read fits file busy = BusyIndicator() try: input_hdu = pyfits.open(filename)[0] except Exception as err: busy = None QMessageBox.warning( self, "Error reading FITS", "Error reading FITS file %s: %s" % (filename, str(err))) return # check name srcname = str(self.wname.text()) or os.path.splitext( os.path.basename(str(filename)))[0] if srcname in set([src.name for src in self.model.sources]): QMessageBox.warning( self, "Already in model", "<p>The model already contains a source named '%s'. Please select a different name.</p>" % srcname) return # get image parameters hdr = input_hdu.header 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 # print ra0,dec0 ra0, dec0 = wcs.pix2wcs(ra0, dec0) ra0 *= DEG dec0 *= DEG # print ModelClasses.Position.ra_hms_static(ra0) # print ModelClasses.Position.dec_sdms_static(dec0) sx, sy = wcs.getHalfSizeDeg() sx *= DEG sy *= DEG nx, ny = input_hdu.data.shape[-1:-3:-1] pos = ModelClasses.Position(ra0, dec0) flux = ModelClasses.Flux(max_flux) shape = ModelClasses.FITSImage(sx, sy, 0, os.path.basename(filename), nx, ny, pad=float(str(self.wpad.text()) or "1")) img_src = SkyModel.Source(srcname, pos, flux, shape=shape) self.model.setSources(self.model.sources + [img_src]) self.model.emitUpdate(SkyModel.SkyModel.UpdateAll, origin=self) busy = None return QDialog.accept(self)
def setup_ui(self): self.block_show = False self.properties = [] self.l = l = QVBoxLayout(self) self.setLayout(l) h = QHBoxLayout() l.addLayout(h) self.la = la = QLabel(_('&Edit theme:')) h.addWidget(la) self.theme = t = QComboBox(self) la.setBuddy(t) t.addItems(sorted(custom_theme_names())) t.setMinimumWidth(200) if t.count() > 0: t.setCurrentIndex(0) t.currentIndexChanged[int].connect(self.show_theme) h.addWidget(t) self.add_button = b = QPushButton(QIcon(I('plus.png')), _('Add &new theme'), self) b.clicked.connect(self.create_new_theme) h.addWidget(b) self.remove_button = b = QPushButton(QIcon(I('minus.png')), _('&Remove theme'), self) b.clicked.connect(self.remove_theme) h.addWidget(b) h.addStretch(1) self.scroll = s = QScrollArea(self) self.w = w = QWidget(self) s.setWidget(w), s.setWidgetResizable(True) self.cl = cl = QVBoxLayout() w.setLayout(cl) from calibre.gui2.tweak_book.editor.text import TextEdit self.preview = p = TextEdit(self, expected_geometry=(73, 50)) p.load_text( textwrap.dedent( _('''\ <h2>Creating a custom theme</h2> <p id="attribute" lang="und">You can create a custom syntax highlighting theme, with your own colors and font styles. The most important types of highlighting rules are described below. Note that not every rule supports every kind of customization, for example, changing font or underline styles for the <code>Cursor</code> rule does not have any effect as that rule is used only for the color of the blinking cursor.</p> <p>As you make changes to your them on the left, the changes will be reflected live in this panel.</p> <p xml:lang="und"> {0} The most important rule. Sets the foreground and background colors for the editor as well as the style of "normal" text, that is, text that does not match any special syntax. {1} Defines the colors for text selected by the mouse. {2} Defines the color for the line containing the cursor. {3} Defines the colors for the line numbers on the left. {4} Defines the colors for matching tags in HTML and matching braces in CSS. {5} Used for highlighting tags in HTML {6} Used for highlighting attributes in HTML {7} Tag names in HTML {8} Namespace prefixes in XML and constants in CSS {9} Non-breaking spaces/hyphens in HTML {10} Syntax errors such as <this <> {11} Misspelled words such as <span lang="en">thisword</span> {12} Comments like <!-- this one --> </p> <style type="text/css"> /* Some CSS so you can see how the highlighting rules affect it */ p.someclass {{ font-family: serif; font-size: 12px; line-height: 1.2; }} </style> ''')).format(*[ '<b>%s</b>' % x for x in ('Normal', 'Visual', 'CursorLine', 'LineNr', 'MatchParen', 'Function', 'Type', 'Statement', 'Constant', 'SpecialCharacter', 'Error', 'SpellError', 'Comment') ])) p.setMaximumWidth(p.size_hint.width() + 5) s.setMinimumWidth(600) self.splitter = sp = QSplitter(self) l.addWidget(sp) sp.addWidget(s), sp.addWidget(p) self.bb.clear() self.bb.addButton(self.bb.Close) l.addWidget(self.bb) if self.theme.count() > 0: self.show_theme()
def modify_pin(): # twd.start("Please confirm pin change on Trezor") try: status = self.wallet.get_client().change_pin() print_error(status) self.show_message(status) except Exception, e: give_error(e) finally: twd.emit(SIGNAL('trezor_done')) # twd.stop() remove_pin_button = QPushButton("Remove Pin") remove_pin_button.clicked.connect(remove_pin) layout.addWidget(remove_pin_button, 4, 0) change_pin_button = QPushButton("Change Pin") change_pin_button.clicked.connect(modify_pin) layout.addWidget(change_pin_button, 4, 1) if d.exec_(): return True else: return False from chainkey.wallet import pw_decode, bip32_private_derivation, bip32_root
def __init__(self, text=None): QPushButton.__init__(self, text)
def create_basic_metadata_widgets(self): # {{{ self.basic_metadata_widgets = [] self.languages = LanguagesEdit(self) self.basic_metadata_widgets.append(self.languages) self.title = TitleEdit(self) self.title.textChanged.connect(self.update_window_title) self.deduce_title_sort_button = QToolButton(self) self.deduce_title_sort_button.setToolTip( _('Automatically create the title sort entry based on the current ' 'title entry.\nUsing this button to create title sort will ' 'change title sort from red to green.')) self.deduce_title_sort_button.setWhatsThis( self.deduce_title_sort_button.toolTip()) self.title_sort = TitleSortEdit(self, self.title, self.deduce_title_sort_button, self.languages) self.basic_metadata_widgets.extend([self.title, self.title_sort]) self.deduce_author_sort_button = b = QToolButton(self) b.setToolTip('<p>' + _('Automatically create the author sort entry based on the current ' 'author entry. Using this button to create author sort will ' 'change author sort from red to green. There is a menu of ' 'functions available under this button. Click and hold ' 'on the button to see it.') + '</p>') b.m = m = QMenu() ac = m.addAction(QIcon(I('forward.png')), _('Set author sort from author')) ac2 = m.addAction(QIcon(I('back.png')), _('Set author from author sort')) ac3 = m.addAction(QIcon(I('user_profile.png')), _('Manage authors')) ac4 = m.addAction(QIcon(I('next.png')), _('Copy author to author sort')) ac5 = m.addAction(QIcon(I('previous.png')), _('Copy author sort to author')) b.setMenu(m) self.authors = AuthorsEdit(self, ac3) self.author_sort = AuthorSortEdit(self, self.authors, b, self.db, ac, ac2, ac4, ac5) self.basic_metadata_widgets.extend([self.authors, self.author_sort]) self.swap_title_author_button = QToolButton(self) self.swap_title_author_button.setIcon(QIcon(I('swap.png'))) self.swap_title_author_button.setToolTip(_( 'Swap the author and title')) self.swap_title_author_button.clicked.connect(self.swap_title_author) self.manage_authors_button = QToolButton(self) self.manage_authors_button.setIcon(QIcon(I('user_profile.png'))) self.manage_authors_button.setToolTip('<p>' + _( 'Manage authors. Use to rename authors and correct ' 'individual author\'s sort values') + '</p>') self.manage_authors_button.clicked.connect(self.authors.manage_authors) self.series = SeriesEdit(self) self.clear_series_button = QToolButton(self) self.clear_series_button.setToolTip( _('Clear series') ) self.clear_series_button.clicked.connect(self.series.clear) self.series_index = SeriesIndexEdit(self, self.series) self.basic_metadata_widgets.extend([self.series, self.series_index]) self.formats_manager = FormatsManager(self, self.copy_fmt) # We want formats changes to be committed before title/author, as # otherwise we could have data loss if the title/author changed and the # user was trying to add an extra file from the old books directory. self.basic_metadata_widgets.insert(0, self.formats_manager) self.formats_manager.metadata_from_format_button.clicked.connect( self.metadata_from_format) self.formats_manager.cover_from_format_button.clicked.connect( self.cover_from_format) self.cover = Cover(self) self.cover.download_cover.connect(self.download_cover) self.basic_metadata_widgets.append(self.cover) self.comments = CommentsEdit(self, self.one_line_comments_toolbar) self.basic_metadata_widgets.append(self.comments) self.rating = RatingEdit(self) self.basic_metadata_widgets.append(self.rating) self.tags = TagsEdit(self) self.tags_editor_button = QToolButton(self) self.tags_editor_button.setToolTip(_('Open Tag Editor')) self.tags_editor_button.setIcon(QIcon(I('chapters.png'))) self.tags_editor_button.clicked.connect(self.tags_editor) self.basic_metadata_widgets.append(self.tags) self.identifiers = IdentifiersEdit(self) self.basic_metadata_widgets.append(self.identifiers) self.clear_identifiers_button = QToolButton(self) self.clear_identifiers_button.setIcon(QIcon(I('trash.png'))) self.clear_identifiers_button.setToolTip(_('Clear Ids')) self.clear_identifiers_button.clicked.connect(self.identifiers.clear) self.paste_isbn_button = QToolButton(self) self.paste_isbn_button.setToolTip('<p>' + _('Paste the contents of the clipboard into the ' 'identifiers box prefixed with isbn:') + '</p>') self.paste_isbn_button.setIcon(QIcon(I('edit-paste.png'))) self.paste_isbn_button.clicked.connect(self.identifiers.paste_isbn) self.publisher = PublisherEdit(self) self.basic_metadata_widgets.append(self.publisher) self.timestamp = DateEdit(self) self.pubdate = PubdateEdit(self) self.basic_metadata_widgets.extend([self.timestamp, self.pubdate]) self.fetch_metadata_button = QPushButton( _('&Download metadata'), self) self.fetch_metadata_button.clicked.connect(self.fetch_metadata) self.download_shortcut.activated.connect(self.fetch_metadata_button.click) font = self.fmb_font = QFont() font.setBold(True) self.fetch_metadata_button.setFont(font) if self.use_toolbutton_for_config_metadata: self.config_metadata_button = QToolButton(self) self.config_metadata_button.setIcon(QIcon(I('config.png'))) else: self.config_metadata_button = QPushButton(self) self.config_metadata_button.setText(_('Configure download metadata')) self.config_metadata_button.setIcon(QIcon(I('config.png'))) self.config_metadata_button.clicked.connect(self.configure_metadata) self.config_metadata_button.setToolTip( _('Change how calibre downloads metadata'))
def __init__(self, text, action, parent): QPushButton.__init__(self, text, parent) self.clicked.connect(lambda: parent.search_triggered.emit(action))
def __init__(self, fm, pref_name, parent=None): QDialog.__init__(self, parent) self.fm = fm if pref_name == 'column_color_rules': self.rule_kind = 'color' rule_text = _('coloring') else: self.rule_kind = 'icon' rule_text = _('icon') self.setWindowIcon(QIcon(I('format-fill-color.png'))) self.setWindowTitle( _('Create/edit a column {0} rule').format(rule_text)) self.l = l = QGridLayout(self) self.setLayout(l) self.l1 = l1 = QLabel( _('Create a column {0} rule by' ' filling in the boxes below'.format(rule_text))) l.addWidget(l1, 0, 0, 1, 8) self.f1 = QFrame(self) self.f1.setFrameShape(QFrame.HLine) l.addWidget(self.f1, 1, 0, 1, 8) self.l2 = l2 = QLabel(_('Set the')) l.addWidget(l2, 2, 0) if self.rule_kind == 'color': l.addWidget(QLabel(_('color'))) else: self.kind_box = QComboBox(self) for tt, t in icon_rule_kinds: self.kind_box.addItem(tt, t) l.addWidget(self.kind_box, 2, 1) self.l3 = l3 = QLabel(_('of the column:')) l.addWidget(l3, 2, 2) self.column_box = QComboBox(self) l.addWidget(self.column_box, 2, 3) self.l4 = l4 = QLabel(_('to')) l.addWidget(l4, 2, 4) if self.rule_kind == 'color': self.color_box = QComboBox(self) self.color_label = QLabel('Sample text Sample text') self.color_label.setTextFormat(Qt.RichText) l.addWidget(self.color_box, 2, 5) l.addWidget(self.color_label, 2, 6) l.addItem(QSpacerItem(10, 10, QSizePolicy.Expanding), 2, 7) else: self.filename_box = QComboBox() self.filename_box.setInsertPolicy( self.filename_box.InsertAlphabetically) d = os.path.join(config_dir, 'cc_icons') self.icon_file_names = [] if os.path.exists(d): for icon_file in os.listdir(d): icon_file = lower(icon_file) if os.path.exists(os.path.join(d, icon_file)): if icon_file.endswith('.png'): self.icon_file_names.append(icon_file) self.icon_file_names.sort(key=sort_key) self.update_filename_box() l.addWidget(self.filename_box, 2, 5) self.filename_button = QPushButton(QIcon(I('document_open.png')), _('&Add icon')) l.addWidget(self.filename_button, 2, 6) l.addWidget(QLabel(_('Icons should be square or landscape')), 2, 7) l.setColumnStretch(7, 10) self.l5 = l5 = QLabel( _('Only if the following conditions are all satisfied:')) l.addWidget(l5, 3, 0, 1, 7) self.scroll_area = sa = QScrollArea(self) sa.setMinimumHeight(300) sa.setMinimumWidth(950) sa.setWidgetResizable(True) l.addWidget(sa, 4, 0, 1, 8) self.add_button = b = QPushButton(QIcon(I('plus.png')), _('Add another condition')) l.addWidget(b, 5, 0, 1, 8) b.clicked.connect(self.add_blank_condition) self.l6 = l6 = QLabel( _('You can disable a condition by' ' blanking all of its boxes')) l.addWidget(l6, 6, 0, 1, 8) self.bb = bb = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) bb.accepted.connect(self.accept) bb.rejected.connect(self.reject) l.addWidget(bb, 7, 0, 1, 8) self.conditions_widget = QWidget(self) sa.setWidget(self.conditions_widget) self.conditions_widget.setLayout(QVBoxLayout()) self.conditions_widget.layout().setAlignment(Qt.AlignTop) self.conditions = [] if self.rule_kind == 'color': for b in (self.column_box, self.color_box): b.setSizeAdjustPolicy(b.AdjustToMinimumContentsLengthWithIcon) b.setMinimumContentsLength(15) for key in sorted(displayable_columns(fm), key=lambda (k): sort_key(fm[k]['name']) if k != color_row_key else 0): if key == color_row_key and self.rule_kind != 'color': continue name = all_columns_string if key == color_row_key else fm[key][ 'name'] if name: self.column_box.addItem(name, key) self.column_box.setCurrentIndex(0) if self.rule_kind == 'color': self.color_box.addItems(QColor.colorNames()) self.color_box.setCurrentIndex(0) self.update_color_label() self.color_box.currentIndexChanged.connect(self.update_color_label) else: self.filename_button.clicked.connect(self.filename_button_clicked) self.resize(self.sizeHint())
def __init__(self, parent): QWidget.__init__(self, parent) self.parent = parent self._layout = QVBoxLayout() self.setLayout(self._layout) self._layout.setContentsMargins(0, 0, 0, 0) # Set up the find box & button search_layout = QHBoxLayout() self._layout.addLayout(search_layout) self.item_search = HistoryLineEdit(parent) self.item_search.setMinimumContentsLength(5) self.item_search.setSizeAdjustPolicy( self.item_search.AdjustToMinimumContentsLengthWithIcon) try: self.item_search.lineEdit().setPlaceholderText( _('Find item in tag browser')) except: pass # Using Qt < 4.7 self.item_search.setToolTip( _('Search for items. This is a "contains" search; items containing the\n' 'text anywhere in the name will be found. You can limit the search\n' 'to particular categories using syntax similar to search. For example,\n' 'tags:foo will find foo in any tag, but not in authors etc. Entering\n' '*foo will filter all categories at once, showing only those items\n' 'containing the text "foo"')) search_layout.addWidget(self.item_search) # Not sure if the shortcut should be translatable ... sc = QShortcut(QKeySequence(_('ALT+f')), parent) sc.activated.connect(self.set_focus_to_find_box) self.search_button = QToolButton() self.search_button.setText(_('F&ind')) self.search_button.setToolTip(_('Find the first/next matching item')) search_layout.addWidget(self.search_button) self.expand_button = QToolButton() self.expand_button.setText('-') self.expand_button.setToolTip(_('Collapse all categories')) search_layout.addWidget(self.expand_button) search_layout.setStretch(0, 10) search_layout.setStretch(1, 1) search_layout.setStretch(2, 1) self.current_find_position = None self.search_button.clicked.connect(self.find) self.item_search.initialize('tag_browser_search') self.item_search.lineEdit().returnPressed.connect(self.do_find) self.item_search.lineEdit().textEdited.connect(self.find_text_changed) self.item_search.activated[QString].connect(self.do_find) self.item_search.completer().setCaseSensitivity(Qt.CaseSensitive) parent.tags_view = TagsView(parent) self.tags_view = parent.tags_view self.expand_button.clicked.connect(self.tags_view.collapseAll) self._layout.addWidget(parent.tags_view) # Now the floating 'not found' box l = QLabel(self.tags_view) self.not_found_label = l l.setFrameStyle(QFrame.StyledPanel) l.setAutoFillBackground(True) l.setText( '<p><b>' + _('No More Matches.</b><p> Click Find again to go to first match')) l.setAlignment(Qt.AlignVCenter) l.setWordWrap(True) l.resize(l.sizeHint()) l.move(10, 20) l.setVisible(False) self.not_found_label_timer = QTimer() self.not_found_label_timer.setSingleShot(True) self.not_found_label_timer.timeout.connect( self.not_found_label_timer_event, type=Qt.QueuedConnection) parent.alter_tb = l = QPushButton(parent) l.setText(_('Alter Tag Browser')) l.setIcon(QIcon(I('tags.png'))) l.m = QMenu() l.setMenu(l.m) self._layout.addWidget(l) sb = l.m.addAction(_('Sort by')) sb.m = l.sort_menu = QMenu(l.m) sb.setMenu(sb.m) sb.bg = QActionGroup(sb) # Must be in the same order as db2.CATEGORY_SORTS for i, x in enumerate((_('Sort by name'), _('Sort by number of books'), _('Sort by average rating'))): a = sb.m.addAction(x) sb.bg.addAction(a) a.setCheckable(True) if i == 0: a.setChecked(True) sb.setToolTip(_('Set the sort order for entries in the Tag Browser')) sb.setStatusTip(sb.toolTip()) ma = l.m.addAction(_('Search type when selecting multiple items')) ma.m = l.match_menu = QMenu(l.m) ma.setMenu(ma.m) ma.ag = QActionGroup(ma) # Must be in the same order as db2.MATCH_TYPE for i, x in enumerate( (_('Match any of the items'), _('Match all of the items'))): a = ma.m.addAction(x) ma.ag.addAction(a) a.setCheckable(True) if i == 0: a.setChecked(True) ma.setToolTip( _('When selecting multiple entries in the Tag Browser ' 'match any or all of them')) ma.setStatusTip(ma.toolTip()) mt = l.m.addAction(_('Manage authors, tags, etc')) mt.setToolTip( _('All of these category_managers are available by right-clicking ' 'on items in the tag browser above')) mt.m = l.manage_menu = QMenu(l.m) mt.setMenu(mt.m)
def __init__(self, parent): QWidget.__init__(self, parent) l = self.l = QGridLayout() self.setLayout(l) self.tocw = t = TreeWidget(self) self.tocw.edit_item.connect(self.edit_item) l.addWidget(t, 0, 0, 7, 3) self.up_button = b = QToolButton(self) b.setIcon(QIcon(I('arrow-up.png'))) b.setIconSize(QSize(ICON_SIZE, ICON_SIZE)) l.addWidget(b, 0, 3) b.setToolTip(_('Move current entry up [Ctrl+Up]')) b.clicked.connect(self.move_up) self.left_button = b = QToolButton(self) b.setIcon(QIcon(I('back.png'))) b.setIconSize(QSize(ICON_SIZE, ICON_SIZE)) l.addWidget(b, 2, 3) b.setToolTip(_('Unindent the current entry [Ctrl+Left]')) b.clicked.connect(self.tocw.move_left) self.del_button = b = QToolButton(self) b.setIcon(QIcon(I('trash.png'))) b.setIconSize(QSize(ICON_SIZE, ICON_SIZE)) l.addWidget(b, 3, 3) b.setToolTip(_('Remove all selected entries')) b.clicked.connect(self.del_items) self.right_button = b = QToolButton(self) b.setIcon(QIcon(I('forward.png'))) b.setIconSize(QSize(ICON_SIZE, ICON_SIZE)) l.addWidget(b, 4, 3) b.setToolTip(_('Indent the current entry [Ctrl+Right]')) b.clicked.connect(self.tocw.move_right) self.down_button = b = QToolButton(self) b.setIcon(QIcon(I('arrow-down.png'))) b.setIconSize(QSize(ICON_SIZE, ICON_SIZE)) l.addWidget(b, 6, 3) b.setToolTip(_('Move current entry down [Ctrl+Down]')) b.clicked.connect(self.move_down) self.expand_all_button = b = QPushButton(_('&Expand all')) col = 7 l.addWidget(b, col, 0) b.clicked.connect(self.tocw.expandAll) self.collapse_all_button = b = QPushButton(_('&Collapse all')) b.clicked.connect(self.tocw.collapseAll) l.addWidget(b, col, 1) self.default_msg = _('Double click on an entry to change the text') self.hl = hl = QLabel(self.default_msg) l.addWidget(hl, col, 2, 1, -1) self.item_view = i = ItemView(self) self.item_view.delete_item.connect(self.delete_current_item) i.add_new_item.connect(self.add_new_item) i.create_from_xpath.connect(self.create_from_xpath) i.create_from_links.connect(self.create_from_links) i.create_from_files.connect(self.create_from_files) i.flatten_item.connect(self.flatten_item) i.flatten_toc.connect(self.flatten_toc) i.go_to_root.connect(self.go_to_root) l.addWidget(i, 0, 4, col, 1) l.setColumnStretch(2, 10)
def __init__(self, parent): QFrame.__init__(self, parent) self.setFrameShape(QFrame.StyledPanel) self.setMinimumWidth(250) self.stack = s = QStackedWidget(self) self.l = l = QVBoxLayout() self.setLayout(l) l.addWidget(s) self.root_pane = rp = QWidget(self) self.item_pane = ip = QWidget(self) self.current_item = None s.addWidget(rp) s.addWidget(ip) self.l1 = la = QLabel('<p>' + _( 'You can edit existing entries in the Table of Contents by clicking them' ' in the panel to the left.' ) + '<p>' + _( 'Entries with a green tick next to them point to a location that has ' 'been verified to exist. Entries with a red dot are broken and may need' ' to be fixed.')) la.setStyleSheet('QLabel { margin-bottom: 20px }') la.setWordWrap(True) l = rp.l = QVBoxLayout() rp.setLayout(l) l.addWidget(la) self.add_new_to_root_button = b = QPushButton(_('Create a &new entry')) b.clicked.connect(self.add_new_to_root) l.addWidget(b) l.addStretch() self.cfmhb = b = QPushButton(_('Generate ToC from &major headings')) b.clicked.connect(self.create_from_major_headings) b.setToolTip( textwrap.fill( _('Generate a Table of Contents from the major headings in the book.' ' This will work if the book identifies its headings using HTML' ' heading tags. Uses the <h1>, <h2> and <h3> tags.'))) l.addWidget(b) self.cfmab = b = QPushButton(_('Generate ToC from &all headings')) b.clicked.connect(self.create_from_all_headings) b.setToolTip( textwrap.fill( _('Generate a Table of Contents from all the headings in the book.' ' This will work if the book identifies its headings using HTML' ' heading tags. Uses the <h1-6> tags.'))) l.addWidget(b) self.lb = b = QPushButton(_('Generate ToC from &links')) b.clicked.connect(self.create_from_links) b.setToolTip( textwrap.fill( _('Generate a Table of Contents from all the links in the book.' ' Links that point to destinations that do not exist in the book are' ' ignored. Also multiple links with the same destination or the same' ' text are ignored.'))) l.addWidget(b) self.cfb = b = QPushButton(_('Generate ToC from &files')) b.clicked.connect(self.create_from_files) b.setToolTip( textwrap.fill( _('Generate a Table of Contents from individual files in the book.' ' Each entry in the ToC will point to the start of the file, the' ' text of the entry will be the "first line" of text from the file.' ))) l.addWidget(b) self.xpb = b = QPushButton(_('Generate ToC from &XPath')) b.clicked.connect(self.create_from_user_xpath) b.setToolTip( textwrap.fill( _('Generate a Table of Contents from arbitrary XPath expressions.' ))) l.addWidget(b) self.fal = b = QPushButton(_('&Flatten the ToC')) b.clicked.connect(self.flatten_toc) b.setToolTip( textwrap.fill( _('Flatten the Table of Contents, putting all entries at the top level' ))) l.addWidget(b) l.addStretch() self.w1 = la = QLabel( _('<b>WARNING:</b> calibre only supports the ' 'creation of linear ToCs in AZW3 files. In a ' 'linear ToC every entry must point to a ' 'location after the previous entry. If you ' 'create a non-linear ToC it will be ' 'automatically re-arranged inside the AZW3 file.')) la.setWordWrap(True) l.addWidget(la) l = ip.l = QGridLayout() ip.setLayout(l) la = ip.heading = QLabel('') l.addWidget(la, 0, 0, 1, 2) la.setWordWrap(True) la = ip.la = QLabel( _('You can move this entry around the Table of Contents by drag ' 'and drop or using the up and down buttons to the left')) la.setWordWrap(True) l.addWidget(la, 1, 0, 1, 2) # Item status ip.hl1 = hl = QFrame() hl.setFrameShape(hl.HLine) l.addWidget(hl, l.rowCount(), 0, 1, 2) self.icon_label = QLabel() self.status_label = QLabel() self.status_label.setWordWrap(True) l.addWidget(self.icon_label, l.rowCount(), 0) l.addWidget(self.status_label, l.rowCount() - 1, 1) ip.hl2 = hl = QFrame() hl.setFrameShape(hl.HLine) l.addWidget(hl, l.rowCount(), 0, 1, 2) # Edit/remove item rs = l.rowCount() ip.b1 = b = QPushButton(QIcon(I('edit_input.png')), _('Change the &location this entry points to'), self) b.clicked.connect(self.edit_item) l.addWidget(b, l.rowCount() + 1, 0, 1, 2) ip.b2 = b = QPushButton(QIcon(I('trash.png')), _('&Remove this entry'), self) l.addWidget(b, l.rowCount(), 0, 1, 2) b.clicked.connect(self.delete_item) ip.hl3 = hl = QFrame() hl.setFrameShape(hl.HLine) l.addWidget(hl, l.rowCount(), 0, 1, 2) l.setRowMinimumHeight(rs, 20) # Add new item rs = l.rowCount() ip.b3 = b = QPushButton(QIcon(I('plus.png')), _('New entry &inside this entry')) b.clicked.connect(partial(self.add_new, 'inside')) l.addWidget(b, l.rowCount() + 1, 0, 1, 2) ip.b4 = b = QPushButton(QIcon(I('plus.png')), _('New entry &above this entry')) b.clicked.connect(partial(self.add_new, 'before')) l.addWidget(b, l.rowCount(), 0, 1, 2) ip.b5 = b = QPushButton(QIcon(I('plus.png')), _('New entry &below this entry')) b.clicked.connect(partial(self.add_new, 'after')) l.addWidget(b, l.rowCount(), 0, 1, 2) # Flatten entry ip.b3 = b = QPushButton(QIcon(I('heuristics.png')), _('&Flatten this entry')) b.clicked.connect(self.flatten_item) b.setToolTip( _('All children of this entry are brought to the same ' 'level as this entry.')) l.addWidget(b, l.rowCount() + 1, 0, 1, 2) ip.hl4 = hl = QFrame() hl.setFrameShape(hl.HLine) l.addWidget(hl, l.rowCount(), 0, 1, 2) l.setRowMinimumHeight(rs, 20) # Return to welcome rs = l.rowCount() ip.b4 = b = QPushButton(QIcon(I('back.png')), _('&Return to welcome screen')) b.clicked.connect(self.go_to_root) b.setToolTip(_('Go back to the top level view')) l.addWidget(b, l.rowCount() + 1, 0, 1, 2) l.setRowMinimumHeight(rs, 20) l.addWidget(QLabel(), l.rowCount(), 0, 1, 2) l.setColumnStretch(1, 10) l.setRowStretch(l.rowCount() - 1, 10) self.w2 = la = QLabel(self.w1.text()) self.w2.setWordWrap(True) l.addWidget(la, l.rowCount(), 0, 1, 2)
def __init__(self, parent, view, row, link_delegate): QDialog.__init__(self, parent) self.normal_brush = QBrush(Qt.white) self.marked_brush = QBrush(Qt.lightGray) self.marked = None self.gui = parent self.splitter = QSplitter(self) self._l = l = QVBoxLayout(self) self.setLayout(l) l.addWidget(self.splitter) self.cover = CoverView(self) self.cover.resizeEvent = self.cover_view_resized self.cover.cover_changed.connect(self.cover_changed) self.cover_pixmap = None self.cover.sizeHint = self.details_size_hint self.splitter.addWidget(self.cover) self.details = QWebView(self) self.details.sizeHint = self.details_size_hint self.details.page().setLinkDelegationPolicy( self.details.page().DelegateAllLinks) self.details.linkClicked.connect(self.link_clicked) s = self.details.page().settings() s.setAttribute(s.JavascriptEnabled, False) self.css = css() self.link_delegate = link_delegate self.details.setAttribute(Qt.WA_OpaquePaintEvent, False) palette = self.details.palette() self.details.setAcceptDrops(False) palette.setBrush(QPalette.Base, Qt.transparent) self.details.page().setPalette(palette) self.c = QWidget(self) self.c.l = l2 = QGridLayout(self.c) self.c.setLayout(l2) l2.addWidget(self.details, 0, 0, 1, -1) self.splitter.addWidget(self.c) self.fit_cover = QCheckBox(_('Fit &cover within view'), self) self.fit_cover.setChecked( gprefs.get('book_info_dialog_fit_cover', True)) l2.addWidget(self.fit_cover, l2.rowCount(), 0, 1, -1) self.previous_button = QPushButton(QIcon(I('previous.png')), _('&Previous'), self) self.previous_button.clicked.connect(self.previous) l2.addWidget(self.previous_button, l2.rowCount(), 0) self.next_button = QPushButton(QIcon(I('next.png')), _('&Next'), self) self.next_button.clicked.connect(self.next) l2.addWidget(self.next_button, l2.rowCount() - 1, 1) self.view = view self.current_row = None self.refresh(row) self.view.selectionModel().currentChanged.connect(self.slave) self.fit_cover.stateChanged.connect(self.toggle_cover_fit) self.ns = QShortcut(QKeySequence('Alt+Right'), self) self.ns.activated.connect(self.next) self.ps = QShortcut(QKeySequence('Alt+Left'), self) self.ps.activated.connect(self.previous) self.next_button.setToolTip( _('Next [%s]') % unicode(self.ns.key().toString(QKeySequence.NativeText))) self.previous_button.setToolTip( _('Previous [%s]') % unicode(self.ps.key().toString(QKeySequence.NativeText))) geom = QCoreApplication.instance().desktop().availableGeometry(self) screen_height = geom.height() - 100 screen_width = geom.width() - 100 self.resize(max(int(screen_width / 2), 700), screen_height) saved_layout = gprefs.get('book_info_dialog_layout', None) if saved_layout is not None: try: self.restoreGeometry(saved_layout[0]) self.splitter.restoreState(saved_layout[1]) except Exception: pass
def __init__(self, window, plugin, keystore, device_id): title = _("%s Settings") % plugin.device super(SettingsDialog, self).__init__(window, title) self.setMaximumWidth(540) devmgr = plugin.device_manager() config = devmgr.config handler = keystore.handler thread = keystore.thread hs_rows, hs_cols = (64, 128) def invoke_client(method, *args, **kw_args): unpair_after = kw_args.pop('unpair_after', False) def task(): client = devmgr.client_by_id(device_id) if not client: raise RuntimeError("Device not connected") if method: getattr(client, method)(*args, **kw_args) if unpair_after: devmgr.unpair_id(device_id) return client.features thread.add(task, on_success=update) def update(features): self.features = features set_label_enabled() bl_hash = features.bootloader_hash.encode('hex') bl_hash = "\n".join([bl_hash[:32], bl_hash[32:]]) noyes = [_("No"), _("Yes")] endis = [_("Enable Passphrases"), _("Disable Passphrases")] disen = [_("Disabled"), _("Enabled")] setchange = [_("Set a PIN"), _("Change PIN")] version = "%d.%d.%d" % (features.major_version, features.minor_version, features.patch_version) coins = ", ".join(coin.coin_name for coin in features.coins) device_label.setText(features.label) pin_set_label.setText(noyes[features.pin_protection]) passphrases_label.setText(disen[features.passphrase_protection]) bl_hash_label.setText(bl_hash) label_edit.setText(features.label) device_id_label.setText(features.device_id) initialized_label.setText(noyes[features.initialized]) version_label.setText(version) coins_label.setText(coins) clear_pin_button.setVisible(features.pin_protection) clear_pin_warning.setVisible(features.pin_protection) pin_button.setText(setchange[features.pin_protection]) pin_msg.setVisible(not features.pin_protection) passphrase_button.setText(endis[features.passphrase_protection]) language_label.setText(features.language) def set_label_enabled(): label_apply.setEnabled(label_edit.text() != self.features.label) def rename(): invoke_client('change_label', unicode(label_edit.text())) def toggle_passphrase(): title = _("Confirm Toggle Passphrase Protection") currently_enabled = self.features.passphrase_protection if currently_enabled: msg = _("After disabling passphrases, you can only pair this " "Fermatum wallet if it had an empty passphrase. " "If its passphrase was not empty, you will need to " "create a new wallet with the install wizard. You " "can use this wallet again at any time by re-enabling " "passphrases and entering its passphrase.") else: msg = _("Your current Fermatum wallet can only be used with " "an empty passphrase. You must create a separate " "wallet with the install wizard for other passphrases " "as each one generates a new set of addresses.") msg += "\n\n" + _("Are you sure you want to proceed?") if not self.question(msg, title=title): return invoke_client('toggle_passphrase', unpair_after=currently_enabled) def change_homescreen(): from PIL import Image # FIXME dialog = QFileDialog(self, _("Choose Homescreen")) filename = dialog.getOpenFileName() if filename: im = Image.open(str(filename)) if im.size != (hs_cols, hs_rows): raise Exception('Image must be 64 x 128 pixels') im = im.convert('1') pix = im.load() img = '' for j in range(hs_rows): for i in range(hs_cols): img += '1' if pix[i, j] else '0' img = ''.join(chr(int(img[i:i + 8], 2)) for i in range(0, len(img), 8)) invoke_client('change_homescreen', img) def clear_homescreen(): invoke_client('change_homescreen', '\x00') def set_pin(): invoke_client('set_pin', remove=False) def clear_pin(): invoke_client('set_pin', remove=True) def wipe_device(): wallet = window.wallet if wallet and sum(wallet.get_balance()): title = _("Confirm Device Wipe") msg = _("Are you SURE you want to wipe the device?\n" "Your wallet still has iops in it!") if not self.question(msg, title=title, icon=QMessageBox.Critical): return invoke_client('wipe_device', unpair_after=True) def slider_moved(): mins = timeout_slider.sliderPosition() timeout_minutes.setText(_("%2d minutes") % mins) def slider_released(): config.set_session_timeout(timeout_slider.sliderPosition() * 60) # Information tab info_tab = QWidget() info_layout = QVBoxLayout(info_tab) info_glayout = QGridLayout() info_glayout.setColumnStretch(2, 1) device_label = QLabel() pin_set_label = QLabel() passphrases_label = QLabel() version_label = QLabel() device_id_label = QLabel() bl_hash_label = QLabel() bl_hash_label.setWordWrap(True) coins_label = QLabel() coins_label.setWordWrap(True) language_label = QLabel() initialized_label = QLabel() rows = [ (_("Device Label"), device_label), (_("PIN set"), pin_set_label), (_("Passphrases"), passphrases_label), (_("Firmware Version"), version_label), (_("Device ID"), device_id_label), (_("Bootloader Hash"), bl_hash_label), (_("Supported Coins"), coins_label), (_("Language"), language_label), (_("Initialized"), initialized_label), ] for row_num, (label, widget) in enumerate(rows): info_glayout.addWidget(QLabel(label), row_num, 0) info_glayout.addWidget(widget, row_num, 1) info_layout.addLayout(info_glayout) # Settings tab settings_tab = QWidget() settings_layout = QVBoxLayout(settings_tab) settings_glayout = QGridLayout() # Settings tab - Label label_msg = QLabel(_("Name this %s. If you have mutiple devices " "their labels help distinguish them.") % plugin.device) label_msg.setWordWrap(True) label_label = QLabel(_("Device Label")) label_edit = QLineEdit() label_edit.setMinimumWidth(150) label_edit.setMaxLength(plugin.MAX_LABEL_LEN) label_apply = QPushButton(_("Apply")) label_apply.clicked.connect(rename) label_edit.textChanged.connect(set_label_enabled) settings_glayout.addWidget(label_label, 0, 0) settings_glayout.addWidget(label_edit, 0, 1, 1, 2) settings_glayout.addWidget(label_apply, 0, 3) settings_glayout.addWidget(label_msg, 1, 1, 1, -1) # Settings tab - PIN pin_label = QLabel(_("PIN Protection")) pin_button = QPushButton() pin_button.clicked.connect(set_pin) settings_glayout.addWidget(pin_label, 2, 0) settings_glayout.addWidget(pin_button, 2, 1) pin_msg = QLabel(_("PIN protection is strongly recommended. " "A PIN is your only protection against someone " "stealing your iops if they obtain physical " "access to your %s.") % plugin.device) pin_msg.setWordWrap(True) pin_msg.setStyleSheet("color: red") settings_glayout.addWidget(pin_msg, 3, 1, 1, -1) # Settings tab - Homescreen if plugin.device != 'KeepKey': # Not yet supported by KK firmware homescreen_layout = QHBoxLayout() homescreen_label = QLabel(_("Homescreen")) homescreen_change_button = QPushButton(_("Change...")) homescreen_clear_button = QPushButton(_("Reset")) homescreen_change_button.clicked.connect(change_homescreen) homescreen_clear_button.clicked.connect(clear_homescreen) homescreen_msg = QLabel(_("You can set the homescreen on your " "device to personalize it. You must " "choose a %d x %d monochrome black and " "white image.") % (hs_rows, hs_cols)) homescreen_msg.setWordWrap(True) settings_glayout.addWidget(homescreen_label, 4, 0) settings_glayout.addWidget(homescreen_change_button, 4, 1) settings_glayout.addWidget(homescreen_clear_button, 4, 2) settings_glayout.addWidget(homescreen_msg, 5, 1, 1, -1) # Settings tab - Session Timeout timeout_label = QLabel(_("Session Timeout")) timeout_minutes = QLabel() timeout_slider = QSlider(Qt.Horizontal) timeout_slider.setRange(1, 60) timeout_slider.setSingleStep(1) timeout_slider.setTickInterval(5) timeout_slider.setTickPosition(QSlider.TicksBelow) timeout_slider.setTracking(True) timeout_msg = QLabel( _("Clear the session after the specified period " "of inactivity. Once a session has timed out, " "your PIN and passphrase (if enabled) must be " "re-entered to use the device.")) timeout_msg.setWordWrap(True) timeout_slider.setSliderPosition(config.get_session_timeout() // 60) slider_moved() timeout_slider.valueChanged.connect(slider_moved) timeout_slider.sliderReleased.connect(slider_released) settings_glayout.addWidget(timeout_label, 6, 0) settings_glayout.addWidget(timeout_slider, 6, 1, 1, 3) settings_glayout.addWidget(timeout_minutes, 6, 4) settings_glayout.addWidget(timeout_msg, 7, 1, 1, -1) settings_layout.addLayout(settings_glayout) settings_layout.addStretch(1) # Advanced tab advanced_tab = QWidget() advanced_layout = QVBoxLayout(advanced_tab) advanced_glayout = QGridLayout() # Advanced tab - clear PIN clear_pin_button = QPushButton(_("Disable PIN")) clear_pin_button.clicked.connect(clear_pin) clear_pin_warning = QLabel( _("If you disable your PIN, anyone with physical access to your " "%s device can spend your iops.") % plugin.device) clear_pin_warning.setWordWrap(True) clear_pin_warning.setStyleSheet("color: red") advanced_glayout.addWidget(clear_pin_button, 0, 2) advanced_glayout.addWidget(clear_pin_warning, 1, 0, 1, 5) # Advanced tab - toggle passphrase protection passphrase_button = QPushButton() passphrase_button.clicked.connect(toggle_passphrase) passphrase_msg = WWLabel(PASSPHRASE_HELP) passphrase_warning = WWLabel(PASSPHRASE_NOT_PIN) passphrase_warning.setStyleSheet("color: red") advanced_glayout.addWidget(passphrase_button, 3, 2) advanced_glayout.addWidget(passphrase_msg, 4, 0, 1, 5) advanced_glayout.addWidget(passphrase_warning, 5, 0, 1, 5) # Advanced tab - wipe device wipe_device_button = QPushButton(_("Wipe Device")) wipe_device_button.clicked.connect(wipe_device) wipe_device_msg = QLabel( _("Wipe the device, removing all data from it. The firmware " "is left unchanged.")) wipe_device_msg.setWordWrap(True) wipe_device_warning = QLabel( _("Only wipe a device if you have the recovery seed written down " "and the device wallet(s) are empty, otherwise the iops " "will be lost forever.")) wipe_device_warning.setWordWrap(True) wipe_device_warning.setStyleSheet("color: red") advanced_glayout.addWidget(wipe_device_button, 6, 2) advanced_glayout.addWidget(wipe_device_msg, 7, 0, 1, 5) advanced_glayout.addWidget(wipe_device_warning, 8, 0, 1, 5) advanced_layout.addLayout(advanced_glayout) advanced_layout.addStretch(1) tabs = QTabWidget(self) tabs.addTab(info_tab, _("Information")) tabs.addTab(settings_tab, _("Settings")) tabs.addTab(advanced_tab, _("Advanced")) dialog_vbox = QVBoxLayout(self) dialog_vbox.addWidget(tabs) dialog_vbox.addLayout(Buttons(CloseButton(self))) # Update information invoke_client(None)
class TweakBook(QDialog): def __init__(self, parent, book_id, fmts, db): QDialog.__init__(self, parent) self.book_id, self.fmts, self.db_ref = book_id, fmts, weakref.ref(db) self._exploded = None self._cleanup_dirs = [] self._cleanup_files = [] self.setup_ui() self.setWindowTitle( _('Tweak Book') + ' - ' + db.title(book_id, index_is_id=True)) button = self.fmt_choice_buttons[0] button_map = {unicode(x.text()): x for x in self.fmt_choice_buttons} of = prefs['output_format'].upper() df = tweaks.get('default_tweak_format', None) lf = gprefs.get('last_tweak_format', None) if df and df.lower() == 'remember' and lf in button_map: button = button_map[lf] elif df and df.upper() in button_map: button = button_map[df.upper()] elif of in button_map: button = button_map[of] button.setChecked(True) self.init_state() for button in self.fmt_choice_buttons: button.toggled.connect(self.init_state) def init_state(self, *args): self._exploded = None self.preview_button.setEnabled(False) self.rebuild_button.setEnabled(False) self.explode_button.setEnabled(True) def setup_ui(self): # {{{ self._g = g = QHBoxLayout(self) self.setLayout(g) self._l = l = QVBoxLayout() g.addLayout(l) fmts = sorted(x.upper() for x in self.fmts) self.fmt_choice_box = QGroupBox(_('Choose the format to tweak:'), self) self._fl = fl = QHBoxLayout() self.fmt_choice_box.setLayout(self._fl) self.fmt_choice_buttons = [QRadioButton(x, self) for x in fmts] for x in self.fmt_choice_buttons: fl.addWidget(x, stretch=10 if x is self.fmt_choice_buttons[-1] else 0) l.addWidget(self.fmt_choice_box) self.fmt_choice_box.setVisible(len(fmts) > 1) self.help_label = QLabel( _('''\ <h2>About Tweak Book</h2> <p>Tweak Book allows you to fine tune the appearance of an ebook by making small changes to its internals. In order to use Tweak Book, you need to know a little bit about HTML and CSS, technologies that are used in ebooks. Follow the steps:</p> <br> <ol> <li>Click "Explode Book": This will "explode" the book into its individual internal components.<br></li> <li>Right click on any individual file and select "Open with..." to edit it in your favorite text editor.<br></li> <li>When you are done Tweaking: <b>close the file browser window and the editor windows you used to make your tweaks</b>. Then click the "Rebuild Book" button, to update the book in your calibre library.</li> </ol>''')) self.help_label.setWordWrap(True) self._fr = QFrame() self._fr.setFrameShape(QFrame.VLine) g.addWidget(self._fr) g.addWidget(self.help_label) self._b = b = QGridLayout() left, top, right, bottom = b.getContentsMargins() top += top b.setContentsMargins(left, top, right, bottom) l.addLayout(b, stretch=10) self.explode_button = QPushButton(QIcon(I('wizard.png')), _('&Explode Book')) self.preview_button = QPushButton(QIcon(I('view.png')), _('&Preview Book')) self.cancel_button = QPushButton(QIcon(I('window-close.png')), _('&Cancel')) self.rebuild_button = QPushButton(QIcon(I('exec.png')), _('&Rebuild Book')) self.explode_button.setToolTip( _('Explode the book to edit its components')) self.preview_button.setToolTip(_('Preview the result of your tweaks')) self.cancel_button.setToolTip(_('Abort without saving any changes')) self.rebuild_button.setToolTip( _('Save your changes and update the book in the calibre library')) a = b.addWidget a(self.explode_button, 0, 0, 1, 1) a(self.preview_button, 0, 1, 1, 1) a(self.cancel_button, 1, 0, 1, 1) a(self.rebuild_button, 1, 1, 1, 1) for x in ('explode', 'preview', 'cancel', 'rebuild'): getattr(self, x + '_button').clicked.connect(getattr(self, x)) self.msg = QLabel('dummy', self) self.msg.setVisible(False) self.msg.setStyleSheet(''' QLabel { text-align: center; background-color: white; color: black; border-width: 1px; border-style: solid; border-radius: 20px; font-size: x-large; font-weight: bold; } ''') self.resize(self.sizeHint() + QSize(40, 10)) # }}} def show_msg(self, msg): self.msg.setText(msg) self.msg.resize(self.size() - QSize(50, 25)) self.msg.move((self.width() - self.msg.width()) // 2, (self.height() - self.msg.height()) // 2) self.msg.setVisible(True) def hide_msg(self): self.msg.setVisible(False) def explode(self): self.show_msg(_('Exploding, please wait...')) if len(self.fmt_choice_buttons) > 1: gprefs.set('last_tweak_format', self.current_format.upper()) QTimer.singleShot(5, self.do_explode) def ask_question(self, msg): return question_dialog(self, _('Are you sure?'), msg) def do_explode(self): from calibre.ebooks.tweak import get_tools, Error, WorkerError tdir = PersistentTemporaryDirectory('_tweak_explode') self._cleanup_dirs.append(tdir) det_msg = None try: src = self.db.format(self.book_id, self.current_format, index_is_id=True, as_path=True) self._cleanup_files.append(src) exploder = get_tools(self.current_format)[0] opf = exploder(src, tdir, question=self.ask_question) except WorkerError as e: det_msg = e.orig_tb except Error as e: return error_dialog( self, _('Failed to unpack'), (_('Could not explode the %s file.') % self.current_format) + ' ' + as_unicode(e), show=True) except: import traceback det_msg = traceback.format_exc() finally: self.hide_msg() if det_msg is not None: return error_dialog( self, _('Failed to unpack'), _('Could not explode the %s file. Click "Show Details" for ' 'more information.') % self.current_format, det_msg=det_msg, show=True) if opf is None: # The question was answered with No return self._exploded = tdir self.explode_button.setEnabled(False) self.preview_button.setEnabled(True) self.rebuild_button.setEnabled(True) open_local_file(tdir) def rebuild_it(self): from calibre.ebooks.tweak import get_tools, WorkerError src_dir = self._exploded det_msg = None of = PersistentTemporaryFile('_tweak_rebuild.' + self.current_format.lower()) of.close() of = of.name self._cleanup_files.append(of) try: rebuilder = get_tools(self.current_format)[1] rebuilder(src_dir, of) except WorkerError as e: det_msg = e.orig_tb except: import traceback det_msg = traceback.format_exc() finally: self.hide_msg() if det_msg is not None: error_dialog(self, _('Failed to rebuild file'), _('Failed to rebuild %s. For more information, click ' '"Show details".') % self.current_format, det_msg=det_msg, show=True) return None return of def preview(self): self.show_msg(_('Rebuilding, please wait...')) QTimer.singleShot(5, self.do_preview) def do_preview(self): rebuilt = self.rebuild_it() if rebuilt is not None: self.parent().iactions['View']._view_file(rebuilt) def rebuild(self): self.show_msg(_('Rebuilding, please wait...')) QTimer.singleShot(5, self.do_rebuild) def do_rebuild(self): rebuilt = self.rebuild_it() if rebuilt is not None: fmt = os.path.splitext(rebuilt)[1][1:].upper() with open(rebuilt, 'rb') as f: self.db.add_format(self.book_id, fmt, f, index_is_id=True) self.accept() def cancel(self): self.reject() def cleanup(self): if isosx and self._exploded: try: import appscript self.finder = appscript.app('Finder') self.finder.Finder_windows[os.path.basename( self._exploded)].close() except: pass for f in self._cleanup_files: try: os.remove(f) except: pass for d in self._cleanup_dirs: try: shutil.rmtree(d) except: pass @property def db(self): return self.db_ref() @property def current_format(self): for b in self.fmt_choice_buttons: if b.isChecked(): return unicode(b.text())
class CharacterDialog(WindowModalDialog): def __init__(self, parent): super(CharacterDialog, self).__init__(parent) self.setWindowTitle(_("KeepKey Seed Recovery")) self.character_pos = 0 self.word_pos = 0 self.loop = QEventLoop() self.word_help = QLabel() self.char_buttons = [] vbox = QVBoxLayout(self) vbox.addWidget(WWLabel(CHARACTER_RECOVERY)) hbox = QHBoxLayout() hbox.addWidget(self.word_help) for i in range(4): char_button = CharacterButton('*') char_button.setMaximumWidth(36) self.char_buttons.append(char_button) hbox.addWidget(char_button) self.accept_button = CharacterButton(_("Accept Word")) self.accept_button.clicked.connect(partial(self.process_key, 32)) self.rejected.connect(partial(self.loop.exit, 1)) hbox.addWidget(self.accept_button) hbox.addStretch(1) vbox.addLayout(hbox) self.finished_button = QPushButton(_("Seed Entered")) self.cancel_button = QPushButton(_("Cancel")) self.finished_button.clicked.connect(partial(self.process_key, Qt.Key_Return)) self.cancel_button.clicked.connect(self.rejected) buttons = Buttons(self.finished_button, self.cancel_button) vbox.addSpacing(40) vbox.addLayout(buttons) self.refresh() self.show() def refresh(self): self.word_help.setText("Enter seed word %2d:" % (self.word_pos + 1)) self.accept_button.setEnabled(self.character_pos >= 3) self.finished_button.setEnabled((self.word_pos in (11, 17, 23) and self.character_pos >= 3)) for n, button in enumerate(self.char_buttons): button.setEnabled(n == self.character_pos) if n == self.character_pos: button.setFocus() def is_valid_alpha_space(self, key): # Auto-completion requires at least 3 characters if key == ord(' ') and self.character_pos >= 3: return True # Firmware aborts protocol if the 5th character is non-space if self.character_pos >= 4: return False return (key >= ord('a') and key <= ord('z') or (key >= ord('A') and key <= ord('Z'))) def process_key(self, key): self.data = None if key == Qt.Key_Return and self.finished_button.isEnabled(): self.data = {'done': True} elif key == Qt.Key_Backspace and (self.word_pos or self.character_pos): self.data = {'delete': True} elif self.is_valid_alpha_space(key): self.data = {'character': chr(key).lower()} if self.data: self.loop.exit(0) def keyPressEvent(self, event): self.process_key(event.key()) if not self.data: QDialog.keyPressEvent(self, event) def get_char(self, word_pos, character_pos): self.word_pos = word_pos self.character_pos = character_pos self.refresh() if self.loop.exec_(): self.data = None # User cancelled
class RddtDataExtractorGUI(QMainWindow, Ui_RddtDataExtractorMainWindow): def __init__(self, rddtDataExtractor, queue, recv): """ Main GUI Window that the user interacts with. :type rddtDataExtractor: RedditDataExtractor.redditDataExtractor.RedditDataExtractor :type queue: Queue.queue :type recv: RedditDataExtractor.main.QueueMessageReceiver """ QMainWindow.__init__(self) # Set up the user interface from Designer. self.setupUi(self) # The model for the view self._rddtDataExtractor = rddtDataExtractor # Bool to keep track of changes that have occurred that haven't been saved self._unsavedChanges = False self.queue = queue self.recv = recv # Custom Set ups self.setup() def setup(self): self.init() self.directoryBox.setText(str(self._rddtDataExtractor.defaultPath)) self.directorySelectBtn.clicked.connect(self.selectDirectory) self.addUserBtn.clicked.connect(self.userList.addToList) self.addSubredditBtn.clicked.connect(self.subredditList.addToList) self.deleteUserBtn.clicked.connect(self.userList.deleteFromList) self.deleteSubredditBtn.clicked.connect(self.subredditList.deleteFromList) self.actionSettings_2.triggered.connect(self.showSettings) self.actionExit.triggered.connect(self.close) self.actionSubreddit_List.triggered.connect(self.subredditList.makeNewList) self.actionUser_List.triggered.connect(self.userList.makeNewList) self.actionSave.triggered.connect(self.saveState) self.actionRemove_Subreddit_List.triggered.connect(self.subredditList.removeLst) self.actionRemove_User_List.triggered.connect(self.userList.removeLst) self.userListChooser.addAction(self.actionUser_List) self.subredditListChooser.addAction(self.actionSubreddit_List) self.userListChooser.addAction(self.actionRemove_User_List) self.subredditListChooser.addAction(self.actionRemove_Subreddit_List) self.userListChooser.activated.connect(self.userList.chooseNewList) self.subredditListChooser.activated.connect(self.subredditList.chooseNewList) self.userList.addAction(self.actionDownloaded_Reddit_User_Posts) self.userList.addAction(self.actionNew_User) self.userList.addAction(self.actionRemove_Selected_User) self.actionDownloaded_Reddit_User_Posts.triggered.connect(self.userList.viewDownloadedContent) self.actionNew_User.triggered.connect(self.userList.addToList) self.actionRemove_Selected_User.triggered.connect(self.userList.deleteFromList) self.subredditList.addAction(self.actionDownloaded_Subreddit_Posts) self.subredditList.addAction(self.actionNew_Subreddit) self.subredditList.addAction(self.actionRemove_Selected_Subreddit) self.actionDownloaded_Subreddit_Posts.triggered.connect(self.subredditList.viewDownloadedContent) self.actionNew_Subreddit.triggered.connect(self.subredditList.addToList) self.actionRemove_Selected_Subreddit.triggered.connect(self.subredditList.deleteFromList) self.actionRemaining_Imgur_Requests.triggered.connect(self.viewRemainingImgurRequests) self.downloadBtn.clicked.connect(self.beginDownload) self.userSubBtn.clicked.connect( lambda: self._rddtDataExtractor.changeDownloadType(DownloadType.USER_SUBREDDIT_CONSTRAINED)) self.allUserBtn.clicked.connect( lambda: self._rddtDataExtractor.changeDownloadType(DownloadType.USER_SUBREDDIT_ALL)) self.allSubBtn.clicked.connect( lambda: self._rddtDataExtractor.changeDownloadType(DownloadType.SUBREDDIT_CONTENT)) self.actionAbout.triggered.connect(self.displayAbout) def initUserList(self): self.userList = UserListViewAndChooser(self) self.gridLayout.addWidget(self.userList, 1, 0, 1, 1) def initSubredditList(self): self.subredditList = SubredditListViewAndChooser(self) self.gridLayout.addWidget(self.subredditList, 1, 1, 1, 1) def init(self): self.initUserList() self.initSubredditList() if (self._rddtDataExtractor.downloadType is DownloadType.USER_SUBREDDIT_CONSTRAINED): self.userSubBtn.setChecked(True) elif (self._rddtDataExtractor.downloadType is DownloadType.USER_SUBREDDIT_ALL): self.allUserBtn.setChecked(True) elif (self._rddtDataExtractor.downloadType is DownloadType.SUBREDDIT_CONTENT): self.allSubBtn.setChecked(True) icon = QIcon() icon.addPixmap(QPixmap("RedditDataExtractor/images/logo.png"), QIcon.Normal, QIcon.Off) self.setWindowIcon(icon) def stopDownload(self): try: self.redditorValidator.stop() except AttributeError: # the redditorValidator object hasn't been made pass try: self.subredditValidator.stop() except AttributeError: # the subredditValidator object hasn't been made pass try: self.downloader.stop() except AttributeError: # the downloader object hasn't been made pass # Try to save the current downloads, just in case it never stops the download (rare cases of network problems) self._rddtDataExtractor.currentlyDownloading = False self._rddtDataExtractor.saveState() self._rddtDataExtractor.currentlyDownloading = True self.stopBtn.setEnabled(False) @pyqtSlot() def reactivateBtns(self): try: self.gridLayout.removeWidget(self.stopBtn) self.stopBtn.deleteLater() except: pass self.downloadBtn = QPushButton(self.centralwidget) self.downloadBtn.setObjectName("downloadBtn") self.downloadBtn.setText("Download!") self.downloadBtn.clicked.connect(self.beginDownload) self.gridLayout.addWidget(self.downloadBtn, 6, 0, 1, 2) self.addUserBtn.setEnabled(True) self.addSubredditBtn.setEnabled(True) self.deleteUserBtn.setEnabled(True) self.deleteSubredditBtn.setEnabled(True) self._rddtDataExtractor.currentlyDownloading = False def enterDownloadMode(self): self._rddtDataExtractor.currentlyDownloading = True self.logTextEdit.clear() self.stopBtn = QPushButton(self.centralwidget) self.stopBtn.setObjectName("stopBtn") self.stopBtn.setText("Downloading... Press here to stop the download (In progress downloads will continue until done).") self.stopBtn.clicked.connect(self.stopDownload) try: self.gridLayout.removeWidget(self.downloadBtn) self.downloadBtn.deleteLater() except: pass self.gridLayout.addWidget(self.stopBtn, 6, 0, 1, 2) self.addUserBtn.setEnabled(False) self.addSubredditBtn.setEnabled(False) self.deleteUserBtn.setEnabled(False) self.deleteSubredditBtn.setEnabled(False) @pyqtSlot() def beginDownload(self): self.enterDownloadMode() if self._rddtDataExtractor.downloadType is DownloadType.USER_SUBREDDIT_CONSTRAINED: # need to validate both subreddits and redditors, start downloading user data once done self.getValidSubreddits() self.getValidRedditors(startDownload=True) elif self._rddtDataExtractor.downloadType is DownloadType.USER_SUBREDDIT_ALL: self.getValidRedditors(startDownload=True) elif self._rddtDataExtractor.downloadType is DownloadType.SUBREDDIT_CONTENT: self.getValidSubreddits(startDownload=True) @pyqtSlot(list) def downloadValidUserOrSub(self, validUsersOrSubs): """ Begin the download process for the validated users or subreddits :type validUsersOrSubs: list """ if self._rddtDataExtractor.downloadType is DownloadType.USER_SUBREDDIT_CONSTRAINED or self._rddtDataExtractor.downloadType is DownloadType.USER_SUBREDDIT_ALL: self.downloader = Downloader(self._rddtDataExtractor, validUsersOrSubs, self.queue, ListType.USER) elif self._rddtDataExtractor.downloadType is DownloadType.SUBREDDIT_CONTENT: self.downloader = Downloader(self._rddtDataExtractor, validUsersOrSubs, self.queue, ListType.SUBREDDIT) self.thread = QThread() self.downloader.moveToThread(self.thread) self.thread.started.connect(self.downloader.run) self.downloader.finished.connect(self.thread.quit) self.downloader.finished.connect(self.reactivateBtns) self.downloader.finished.connect(self.downloader.deleteLater) self.downloader.finished.connect(lambda: self.setUnsavedChanges(True)) self.thread.finished.connect(self.thread.deleteLater) self.thread.start() @pyqtSlot(str) def append_text(self, text): """ Add text to the text area in the GUI, sent by the Receiver thread that picks up messages from other threads :type text: str """ self.logTextEdit.moveCursor(QTextCursor.End) self.logTextEdit.insertPlainText(text) def getValidRedditors(self, startDownload=False): """ Validate the users in the user list :param startDownload: Indicates whether or not the download should start when the validation is done :type startDownload: bool """ model = self.userList.model() users = set(model.lst) # create a new set so we don't change set size during iteration if we remove a user # These are class variables so that they don't get destroyed when we return from getValidRedditors() self.redditorValidatorThread = QThread() self.redditorValidator = Validator(self._rddtDataExtractor, self.queue, users, ListType.USER) self.redditorValidator.moveToThread(self.redditorValidatorThread) self.redditorValidatorThread.started.connect(self.redditorValidator.run) self.redditorValidator.invalid.connect(self.notifyInvalidRedditor) # When the validation finishes, start the downloading process on the validated users if startDownload: self.redditorValidator.download.connect(self.downloadValidUserOrSub) self.redditorValidator.finished.connect(self.redditorValidatorThread.quit) self.redditorValidator.finished.connect(self.redditorValidator.deleteLater) self.redditorValidatorThread.finished.connect(self.redditorValidatorThread.deleteLater) self.redditorValidator.stopped.connect(self.reactivateBtns) self.redditorValidatorThread.start() @pyqtSlot(str) def notifyInvalidRedditor(self, userName): """ Ask the user if we should delete the redditor :type userName: str """ model = self.userList.model() msgBox = confirmDialog("The user " + userName + " does not exist. Remove from list?") ret = msgBox.exec_() if ret == QMessageBox.Yes: index = model.getIndexOfName(userName) if index != -1: model.removeRows(index, 1) def getValidSubreddits(self, startDownload=False): """ Validate the subreddits in the subreddit list :param startDownload: Indicates whether or not the download should start when the validation is done :type startDownload: bool """ model = self.subredditList.model() subreddits = set(model.lst) self.subredditValidatorThread = QThread() self.subredditValidator = Validator(self._rddtDataExtractor, self.queue, subreddits, ListType.SUBREDDIT) self.subredditValidator.moveToThread(self.subredditValidatorThread) self.subredditValidatorThread.started.connect(self.subredditValidator.run) self.subredditValidator.invalid.connect(self.notifyInvalidSubreddit) if startDownload: self.subredditValidator.download.connect(self.downloadValidUserOrSub) self.subredditValidator.finished.connect(self.subredditValidatorThread.quit) self.subredditValidator.finished.connect(self.subredditValidator.deleteLater) self.subredditValidatorThread.finished.connect(self.subredditValidatorThread.deleteLater) self.subredditValidator.stopped.connect(self.reactivateBtns) self.subredditValidatorThread.start() @pyqtSlot(str) def notifyInvalidSubreddit(self, subredditName): """ Ask the user if we should delete the redditor :type userName: str """ model = self.subredditList.model() msgBox = confirmDialog("The subreddit " + subredditName + " does not exist. Remove from list?") ret = msgBox.exec_() if ret == QMessageBox.Yes: index = model.getIndexOfName(subredditName) if index != -1: model.removeRows(index, 1) def selectDirectory(self): directory = QFileDialog.getExistingDirectory(QFileDialog()) if len(directory) > 0: directory = pathlib.Path(directory).resolve() if directory.exists(): self._rddtDataExtractor.defaultPath = directory self.directoryBox.setText(str(directory)) self.setUnsavedChanges(True) def convertFilterTableToFilters(self, settings): """ Take the filter table settings from the settings GUI and convert them into actionable filter functions and parameters that _submissionPassesFilter() can use :type settings: RedditDataExtractor.GUI.settingsGUI.SettingsGUI """ filterTable = settings.filterTable submissionFilts = [] commentFilts = [] connector = None if filterTable.rowCount() > 0: connectorWidget = filterTable.cellWidget(0, settings.filtTableConnectCol) if connectorWidget is not None: connector = self._rddtDataExtractor.mapConnectorTextToOper(connectorWidget.currentText()) else: connector = None # We are just filtering by a single thing for row in range(filterTable.rowCount()): type = filterTable.cellWidget(row, settings.filtTableTypeCol).currentText() prop = filterTable.cellWidget(row, settings.filtTablePropCol).currentText() oper = self._rddtDataExtractor.mapFilterTextToOper( filterTable.cellWidget(row, settings.filtTableOperCol).currentText()) val = filterTable.cellWidget(row, settings.filtTableValCol).toPlainText() if val.lower() == "false": val = False elif val.lower() == "true": val = True elif isNumber(val): val = float(val) filt = (prop, oper, val) if type == "Submission": submissionFilts.append(filt) elif type == "Comment": commentFilts.append(filt) return submissionFilts, commentFilts, connector def showSettings(self): settings = SettingsGUI(self._rddtDataExtractor, self.notifyImgurAPI) ret = settings.exec_() if ret == QDialog.Accepted: self._rddtDataExtractor.defaultUserListName = settings.currentUserListName self._rddtDataExtractor.defaultSubredditListName = settings.currentSubredditListName self._rddtDataExtractor.avoidDuplicates = settings.avoidDuplicates self._rddtDataExtractor.getExternalContent = settings.getExternalContent self._rddtDataExtractor.getCommentExternalContent = settings.getCommentExternalContent self._rddtDataExtractor.getSelftextExternalContent = settings.getSelftextExternalContent self._rddtDataExtractor.getSubmissionContent = settings.getSubmissionContent self._rddtDataExtractor.subSort = settings.subSort self._rddtDataExtractor.subLimit = settings.subLimit self._rddtDataExtractor.filterExternalContent = settings.filterExternalContent self._rddtDataExtractor.filterSubmissionContent = settings.filterSubmissionContent if settings.filterExternalContent or settings.filterSubmissionContent: self._rddtDataExtractor.submissionFilts, self._rddtDataExtractor.commentFilts, self._rddtDataExtractor.connector = self.convertFilterTableToFilters( settings) self._rddtDataExtractor.restrictDownloadsByCreationDate = settings.restrictDownloadsByCreationDate self._rddtDataExtractor.showImgurAPINotification = settings.showImgurAPINotification self._rddtDataExtractor.avoidVideos = settings.avoidVideos self._rddtDataExtractor.getAuthorsCommentsOnly = settings.getAuthorsCommentsOnly self.saveState() def notifyImgurAPI(self): self._rddtDataExtractor.imgurAPIClientID = None imgurClientIdGUI = ImgurClientIdGUI() ret = imgurClientIdGUI.exec_() if ret == QDialog.Accepted: self._rddtDataExtractor.imgurAPIClientID = imgurClientIdGUI.imgurAPIClientID self._rddtDataExtractor.saveState() def displayAbout(self): msgBox = QMessageBox() msgBox.setTextFormat(Qt.RichText) msgBox.setWindowTitle("Data Extractor for reddit") msgBox.setText(""" <p>This program uses the following open source software:<br> <a href="http://www.riverbankcomputing.co.uk/software/pyqt/intro">PyQt</a> under the GNU GPL v3 license <br> <a href="https://praw.readthedocs.org/en/v2.1.16/">PRAW (Python Reddit API Wrapper)</a> under the GNU GPL v3 license <br> <a href="http://docs.python-requests.org/en/latest/">Requests</a> under the Apache2 license <br> <a href="http://www.crummy.com/software/BeautifulSoup/">Beautiful Soup</a> under a simplified BSD licence <br> <a href="https://github.com/rg3/youtube-dl">youtube-dl</a> under an unlicense (public domain) </p> <p>This program makes use of a modified version of <a href="https://www.videolan.org/vlc/">VLC's</a> logo:<br> Copyright (c) 1996-2013 VideoLAN. This logo or a modified version may<br> be used or modified by anyone to refer to the VideoLAN project or any<br> product developed by the VideoLAN team, but does not indicate<br> endorsement by the project. </p> <p>This program makes use of a modified version of Microsoft Window's<br> .txt file icon. This is solely the property of Microsoft Windows<br> and I claim no ownership. </p> <p>This program is released under the GNU GPL v3 license<br> <a href="https://www.gnu.org/licenses/quick-guide-gplv3.html">GNU GPL v3 license page</a><br> See <a href="https://github.com/NSchrading/redditDataExtractor/blob/master/LICENSE.txt">LICENSE.txt</a> for more information. </p> """) msgBox.exec() def viewRemainingImgurRequests(self): if self._rddtDataExtractor.currentlyDownloading: QMessageBox.warning(QMessageBox(), "Data Extractor for reddit", "Cannot view imgur requests while currently downloading. Please wait.") return msgBox = QMessageBox() msgBox.setWindowTitle("Data Extractor for reddit") if self._rddtDataExtractor.imgurAPIClientID is not None: headers = {'Authorization': 'Client-ID ' + self._rddtDataExtractor.imgurAPIClientID} apiURL = "https://api.imgur.com/3/credits" requestsSession = requests.session() requestsSession.headers[ 'User-Agent'] = 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36' json = exceptionSafeJsonRequest(requestsSession, apiURL, headers=headers, stream=True, verify='RedditDataExtractor/cacert.pem') if json is not None and json.get('data') is not None and json.get('data').get('ClientRemaining'): msgBox.setText("You have " + str(json.get('data').get('ClientRemaining')) + " requests remaining.") else: msgBox.setText( "A problem occurred using the Imgur API. Check that you are connected to the internet and make sure your client-id is correct.") else: msgBox.setText( "You do not currently have an Imgur client-id set. To set one, go to settings and check 'Change / Reset Client-id'") msgBox.exec() def setUnsavedChanges(self, unsaved): """ If there are unsaved changes, indicate to the user by adding an asterisk to the window title """ self._unsavedChanges = unsaved if self._unsavedChanges: self.setWindowTitle("Data Extractor for reddit *") else: self.setWindowTitle("Data Extractor for reddit") def checkSaveState(self): close = False if self._unsavedChanges: msgBox = QMessageBox() msgBox.setText("A list or setting has been changed.") msgBox.setInformativeText("Do you want to save your changes?") msgBox.setStandardButtons(QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel) msgBox.setDefaultButton(QMessageBox.Save) ret = msgBox.exec_() if ret == QMessageBox.Save: self.saveState() close = True elif ret == QMessageBox.Discard: close = True elif ret == QMessageBox.Cancel: close = False else: close = False else: close = True return close def closeEvent(self, event): """ If there are unsaved changes, let the user know before closing the window """ close = self.checkSaveState() if close: self.recv.stop() event.accept() else: event.ignore() def saveState(self): successful = self._rddtDataExtractor.saveState() self.setUnsavedChanges(not successful)
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.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.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 = QDialogButtonBox(self) self.bbox.addButton(self.check_button, QDialogButtonBox.ActionRole) self.bbox.addButton(self.delete_button, QDialogButtonBox.ActionRole) self.bbox.addButton(self.fix_button, QDialogButtonBox.ActionRole) self.bbox.addButton(self.copy_button, QDialogButtonBox.ActionRole) self.bbox.addButton(self.ok_button, QDialogButtonBox.AcceptRole) 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.addWidget(self.bbox) self.resize(950, 500) self.bbox.setEnabled(True) 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.prefs['check_library_ignore_extensions'] = \ unicode(self.ext_ignores.text()) self.db.prefs['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) 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.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 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 = item.data(0, Qt.UserRole).toInt()[0] 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 = item.data(0, Qt.UserRole).toInt()[0] 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 = item.data(0, Qt.UserRole).toInt()[0] 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 MetadataSingleDialogBase(ResizableDialog): view_format = pyqtSignal(object, object) cc_two_column = tweaks['metadata_single_use_2_cols_for_custom_fields'] one_line_comments_toolbar = False use_toolbutton_for_config_metadata = True def __init__(self, db, parent=None): self.db = db self.changed = set() self.books_to_refresh = set() self.rows_to_refresh = set() ResizableDialog.__init__(self, parent) def setupUi(self, *args): # {{{ self.resize(990, 670) self.download_shortcut = QShortcut(self) self.download_shortcut.setKey(QKeySequence('Ctrl+D', QKeySequence.PortableText)) p = self.parent() if hasattr(p, 'keyboard'): kname = u'Interface Action: Edit Metadata (Edit Metadata) : menu action : download' sc = p.keyboard.keys_map.get(kname, None) if sc: self.download_shortcut.setKey(sc[0]) self.button_box = bb = QDialogButtonBox(self) self.button_box.accepted.connect(self.accept) self.button_box.rejected.connect(self.reject) self.next_button = QPushButton(QIcon(I('forward.png')), _('Next'), self) self.next_button.setShortcut(QKeySequence('Alt+Right')) self.next_button.clicked.connect(self.next_clicked) self.prev_button = QPushButton(QIcon(I('back.png')), _('Previous'), self) self.prev_button.setShortcut(QKeySequence('Alt+Left')) self.button_box.addButton(self.prev_button, bb.ActionRole) self.button_box.addButton(self.next_button, bb.ActionRole) self.prev_button.clicked.connect(self.prev_clicked) bb.setStandardButtons(bb.Ok|bb.Cancel) bb.button(bb.Ok).setDefault(True) self.scroll_area = QScrollArea(self) self.scroll_area.setFrameShape(QScrollArea.NoFrame) self.scroll_area.setWidgetResizable(True) self.central_widget = QTabWidget(self) self.scroll_area.setWidget(self.central_widget) self.l = QVBoxLayout(self) self.setLayout(self.l) self.l.addWidget(self.scroll_area) ll = self.button_box_layout = QHBoxLayout() self.l.addLayout(ll) ll.addSpacing(10) ll.addWidget(self.button_box) self.setWindowIcon(QIcon(I('edit_input.png'))) self.setWindowTitle(BASE_TITLE) self.create_basic_metadata_widgets() if len(self.db.custom_column_label_map): self.create_custom_metadata_widgets() self.do_layout() geom = gprefs.get('metasingle_window_geometry3', None) if geom is not None: self.restoreGeometry(bytes(geom)) # }}} def create_basic_metadata_widgets(self): # {{{ self.basic_metadata_widgets = [] self.languages = LanguagesEdit(self) self.basic_metadata_widgets.append(self.languages) self.title = TitleEdit(self) self.title.textChanged.connect(self.update_window_title) self.deduce_title_sort_button = QToolButton(self) self.deduce_title_sort_button.setToolTip( _('Automatically create the title sort entry based on the current ' 'title entry.\nUsing this button to create title sort will ' 'change title sort from red to green.')) self.deduce_title_sort_button.setWhatsThis( self.deduce_title_sort_button.toolTip()) self.title_sort = TitleSortEdit(self, self.title, self.deduce_title_sort_button, self.languages) self.basic_metadata_widgets.extend([self.title, self.title_sort]) self.deduce_author_sort_button = b = QToolButton(self) b.setToolTip('<p>' + _('Automatically create the author sort entry based on the current ' 'author entry. Using this button to create author sort will ' 'change author sort from red to green. There is a menu of ' 'functions available under this button. Click and hold ' 'on the button to see it.') + '</p>') b.m = m = QMenu() ac = m.addAction(QIcon(I('forward.png')), _('Set author sort from author')) ac2 = m.addAction(QIcon(I('back.png')), _('Set author from author sort')) ac3 = m.addAction(QIcon(I('user_profile.png')), _('Manage authors')) ac4 = m.addAction(QIcon(I('next.png')), _('Copy author to author sort')) ac5 = m.addAction(QIcon(I('previous.png')), _('Copy author sort to author')) b.setMenu(m) self.authors = AuthorsEdit(self, ac3) self.author_sort = AuthorSortEdit(self, self.authors, b, self.db, ac, ac2, ac4, ac5) self.basic_metadata_widgets.extend([self.authors, self.author_sort]) self.swap_title_author_button = QToolButton(self) self.swap_title_author_button.setIcon(QIcon(I('swap.png'))) self.swap_title_author_button.setToolTip(_( 'Swap the author and title')) self.swap_title_author_button.clicked.connect(self.swap_title_author) self.manage_authors_button = QToolButton(self) self.manage_authors_button.setIcon(QIcon(I('user_profile.png'))) self.manage_authors_button.setToolTip('<p>' + _( 'Manage authors. Use to rename authors and correct ' 'individual author\'s sort values') + '</p>') self.manage_authors_button.clicked.connect(self.authors.manage_authors) self.series = SeriesEdit(self) self.clear_series_button = QToolButton(self) self.clear_series_button.setToolTip( _('Clear series') ) self.clear_series_button.clicked.connect(self.series.clear) self.series_index = SeriesIndexEdit(self, self.series) self.basic_metadata_widgets.extend([self.series, self.series_index]) self.formats_manager = FormatsManager(self, self.copy_fmt) # We want formats changes to be committed before title/author, as # otherwise we could have data loss if the title/author changed and the # user was trying to add an extra file from the old books directory. self.basic_metadata_widgets.insert(0, self.formats_manager) self.formats_manager.metadata_from_format_button.clicked.connect( self.metadata_from_format) self.formats_manager.cover_from_format_button.clicked.connect( self.cover_from_format) self.cover = Cover(self) self.cover.download_cover.connect(self.download_cover) self.basic_metadata_widgets.append(self.cover) self.comments = CommentsEdit(self, self.one_line_comments_toolbar) self.basic_metadata_widgets.append(self.comments) self.rating = RatingEdit(self) self.basic_metadata_widgets.append(self.rating) self.tags = TagsEdit(self) self.tags_editor_button = QToolButton(self) self.tags_editor_button.setToolTip(_('Open Tag Editor')) self.tags_editor_button.setIcon(QIcon(I('chapters.png'))) self.tags_editor_button.clicked.connect(self.tags_editor) self.basic_metadata_widgets.append(self.tags) self.identifiers = IdentifiersEdit(self) self.basic_metadata_widgets.append(self.identifiers) self.clear_identifiers_button = QToolButton(self) self.clear_identifiers_button.setIcon(QIcon(I('trash.png'))) self.clear_identifiers_button.setToolTip(_('Clear Ids')) self.clear_identifiers_button.clicked.connect(self.identifiers.clear) self.paste_isbn_button = QToolButton(self) self.paste_isbn_button.setToolTip('<p>' + _('Paste the contents of the clipboard into the ' 'identifiers box prefixed with isbn:') + '</p>') self.paste_isbn_button.setIcon(QIcon(I('edit-paste.png'))) self.paste_isbn_button.clicked.connect(self.identifiers.paste_isbn) self.publisher = PublisherEdit(self) self.basic_metadata_widgets.append(self.publisher) self.timestamp = DateEdit(self) self.pubdate = PubdateEdit(self) self.basic_metadata_widgets.extend([self.timestamp, self.pubdate]) self.fetch_metadata_button = QPushButton( _('&Download metadata'), self) self.fetch_metadata_button.clicked.connect(self.fetch_metadata) self.download_shortcut.activated.connect(self.fetch_metadata_button.click) font = self.fmb_font = QFont() font.setBold(True) self.fetch_metadata_button.setFont(font) if self.use_toolbutton_for_config_metadata: self.config_metadata_button = QToolButton(self) self.config_metadata_button.setIcon(QIcon(I('config.png'))) else: self.config_metadata_button = QPushButton(self) self.config_metadata_button.setText(_('Configure download metadata')) self.config_metadata_button.setIcon(QIcon(I('config.png'))) self.config_metadata_button.clicked.connect(self.configure_metadata) self.config_metadata_button.setToolTip( _('Change how calibre downloads metadata')) # }}} def create_custom_metadata_widgets(self): # {{{ self.custom_metadata_widgets_parent = w = QWidget(self) layout = QGridLayout() w.setLayout(layout) self.custom_metadata_widgets, self.__cc_spacers = \ populate_metadata_page(layout, self.db, None, parent=w, bulk=False, two_column=self.cc_two_column) self.__custom_col_layouts = [layout] # }}} def set_custom_metadata_tab_order(self, before=None, after=None): # {{{ sto = QWidget.setTabOrder if getattr(self, 'custom_metadata_widgets', []): ans = self.custom_metadata_widgets for i in range(len(ans)-1): if before is not None and i == 0: pass if len(ans[i+1].widgets) == 2: sto(ans[i].widgets[-1], ans[i+1].widgets[1]) else: sto(ans[i].widgets[-1], ans[i+1].widgets[0]) for c in range(2, len(ans[i].widgets), 2): sto(ans[i].widgets[c-1], ans[i].widgets[c+1]) if after is not None: pass # }}} def do_view_format(self, path, fmt): if path: self.view_format.emit(None, path) else: self.view_format.emit(self.book_id, fmt) def copy_fmt(self, fmt, f): self.db.copy_format_to(self.book_id, fmt, f, index_is_id=True) def do_layout(self): raise NotImplementedError() def __call__(self, id_): self.book_id = id_ self.books_to_refresh = set([]) for widget in self.basic_metadata_widgets: widget.initialize(self.db, id_) for widget in getattr(self, 'custom_metadata_widgets', []): widget.initialize(id_) if callable(self.set_current_callback): self.set_current_callback(id_) # Commented out as it doesn't play nice with Next, Prev buttons #self.fetch_metadata_button.setFocus(Qt.OtherFocusReason) # Miscellaneous interaction methods {{{ def update_window_title(self, *args): title = self.title.current_val if len(title) > 50: title = title[:50] + u'\u2026' self.setWindowTitle(BASE_TITLE + ' - ' + title + ' - ' + _(' [%(num)d of %(tot)d]')%dict(num= self.current_row+1, tot=len(self.row_list))) def swap_title_author(self, *args): title = self.title.current_val self.title.current_val = authors_to_string(self.authors.current_val) self.authors.current_val = string_to_authors(title) self.title_sort.auto_generate() self.author_sort.auto_generate() def tags_editor(self, *args): self.tags.edit(self.db, self.book_id) def metadata_from_format(self, *args): mi, ext = self.formats_manager.get_selected_format_metadata(self.db, self.book_id) if mi is not None: self.update_from_mi(mi) def cover_from_format(self, *args): mi, ext = self.formats_manager.get_selected_format_metadata(self.db, self.book_id) if mi is None: return cdata = None if mi.cover and os.access(mi.cover, os.R_OK): cdata = open(mi.cover).read() elif mi.cover_data[1] is not None: cdata = mi.cover_data[1] if cdata is None: error_dialog(self, _('Could not read cover'), _('Could not read cover from %s format')%ext).exec_() return orig = self.cover.current_val self.cover.current_val = cdata if self.cover.current_val is None: self.cover.current_val = orig return error_dialog(self, _('Could not read cover'), _('The cover in the %s format is invalid')%ext, show=True) return def update_from_mi(self, mi, update_sorts=True, merge_tags=True): if not mi.is_null('title'): self.title.current_val = mi.title if update_sorts: self.title_sort.auto_generate() if not mi.is_null('authors'): self.authors.current_val = mi.authors if not mi.is_null('author_sort'): self.author_sort.current_val = mi.author_sort elif update_sorts: self.author_sort.auto_generate() if not mi.is_null('rating'): try: self.rating.current_val = mi.rating except: pass if not mi.is_null('publisher'): self.publisher.current_val = mi.publisher if not mi.is_null('tags'): old_tags = self.tags.current_val tags = mi.tags if mi.tags else [] if old_tags and merge_tags: ltags, lotags = {t.lower() for t in tags}, {t.lower() for t in old_tags} tags = [t for t in tags if t.lower() in ltags-lotags] + old_tags self.tags.current_val = tags if not mi.is_null('identifiers'): current = self.identifiers.current_val current.update(mi.identifiers) self.identifiers.current_val = current if not mi.is_null('pubdate'): self.pubdate.current_val = mi.pubdate if not mi.is_null('series') and mi.series.strip(): self.series.current_val = mi.series if mi.series_index is not None: self.series_index.reset_original() self.series_index.current_val = float(mi.series_index) if not mi.is_null('languages'): langs = [canonicalize_lang(x) for x in mi.languages] langs = [x for x in langs if x is not None] if langs: self.languages.current_val = langs if mi.comments and mi.comments.strip(): self.comments.current_val = mi.comments def fetch_metadata(self, *args): d = FullFetch(self.cover.pixmap(), self) ret = d.start(title=self.title.current_val, authors=self.authors.current_val, identifiers=self.identifiers.current_val) if ret == d.Accepted: from calibre.ebooks.metadata.sources.base import msprefs mi = d.book dummy = Metadata(_('Unknown')) for f in msprefs['ignore_fields']: if ':' not in f: setattr(mi, f, getattr(dummy, f)) if mi is not None: pd = mi.pubdate if pd is not None: # Put the downloaded published date into the local timezone # as we discard time info and the date is timezone # invariant. This prevents the as_local_timezone() call in # update_from_mi from changing the pubdate mi.pubdate = datetime(pd.year, pd.month, pd.day, tzinfo=local_tz) self.update_from_mi(mi) if d.cover_pixmap is not None: self.cover.current_val = pixmap_to_data(d.cover_pixmap) def configure_metadata(self): from calibre.gui2.preferences import show_config_widget gui = self.parent() show_config_widget('Sharing', 'Metadata download', parent=self, gui=gui, never_shutdown=True) def download_cover(self, *args): from calibre.gui2.metadata.single_download import CoverFetch d = CoverFetch(self.cover.pixmap(), self) ret = d.start(self.title.current_val, self.authors.current_val, self.identifiers.current_val) if ret == d.Accepted: if d.cover_pixmap is not None: self.cover.current_val = pixmap_to_data(d.cover_pixmap) # }}} def apply_changes(self): self.changed.add(self.book_id) if self.db is None: # break_cycles has already been called, don't know why this should # happen but a user reported it return True for widget in self.basic_metadata_widgets: try: if not widget.commit(self.db, self.book_id): return False self.books_to_refresh |= getattr(widget, 'books_to_refresh', set([])) except (IOError, OSError) as err: if getattr(err, 'errno', None) == errno.EACCES: # Permission denied import traceback fname = err.filename if err.filename else 'file' error_dialog(self, _('Permission denied'), _('Could not open %s. Is it being used by another' ' program?')%fname, det_msg=traceback.format_exc(), show=True) return False raise for widget in getattr(self, 'custom_metadata_widgets', []): self.books_to_refresh |= widget.commit(self.book_id) self.db.commit() rows = self.db.refresh_ids(list(self.books_to_refresh)) if rows: self.rows_to_refresh |= set(rows) return True def accept(self): self.save_state() if not self.apply_changes(): return ResizableDialog.accept(self) def reject(self): self.save_state() ResizableDialog.reject(self) def save_state(self): try: gprefs['metasingle_window_geometry3'] = bytearray(self.saveGeometry()) except: # Weird failure, see https://bugs.launchpad.net/bugs/995271 import traceback traceback.print_exc() # Dialog use methods {{{ def start(self, row_list, current_row, view_slot=None, set_current_callback=None): self.row_list = row_list self.current_row = current_row if view_slot is not None: self.view_format.connect(view_slot) self.set_current_callback = set_current_callback self.do_one(apply_changes=False) ret = self.exec_() self.break_cycles() return ret def next_clicked(self): if not self.apply_changes(): return self.do_one(delta=1, apply_changes=False) def prev_clicked(self): if not self.apply_changes(): return self.do_one(delta=-1, apply_changes=False) def do_one(self, delta=0, apply_changes=True): if apply_changes: self.apply_changes() self.current_row += delta prev = next_ = None if self.current_row > 0: prev = self.db.title(self.row_list[self.current_row-1]) if self.current_row < len(self.row_list) - 1: next_ = self.db.title(self.row_list[self.current_row+1]) if next_ is not None: tip = (_('Save changes and edit the metadata of %s')+ ' [Alt+Right]')%next_ self.next_button.setToolTip(tip) self.next_button.setEnabled(next_ is not None) if prev is not None: tip = (_('Save changes and edit the metadata of %s')+ ' [Alt+Left]')%prev self.prev_button.setToolTip(tip) self.prev_button.setEnabled(prev is not None) self.button_box.button(self.button_box.Ok).setDefault(True) self.button_box.button(self.button_box.Ok).setFocus(Qt.OtherFocusReason) self(self.db.id(self.row_list[self.current_row])) def break_cycles(self): # Break any reference cycles that could prevent python # from garbage collecting this dialog self.set_current_callback = self.db = None def disconnect(signal): try: signal.disconnect() except: pass # Fails if view format was never connected disconnect(self.view_format) for b in ('next_button', 'prev_button'): x = getattr(self, b, None) if x is not None: disconnect(x.clicked) for widget in self.basic_metadata_widgets: bc = getattr(widget, 'break_cycles', None) if bc is not None and callable(bc): bc() for widget in getattr(self, 'custom_metadata_widgets', []): widget.break_cycles()
class GAppLayerW(QgsMapCanvas): def __init__(self, layer, rawLayer, linkedCanvas, dock): # Canvas params QgsMapCanvas.__init__(self, dock) self.setCanvasColor(Qt.white) self.setWheelAction(QgsMapCanvas.WheelZoom, factor=0) self._dock = dock # Add buttons buttonWidth = self.width() / 2.5 self._buttonUp = QToolButton(self) self._buttonUp.setArrowType(Qt.UpArrow) self._buttonUp.setMaximumWidth(buttonWidth) self._buttonUp.setVisible(False) self._buttonUp.connect(self._buttonUp, SIGNAL("clicked()"), self.moveUp) self._buttonHide = QPushButton("Hide") self._buttonHide.setMaximumWidth(buttonWidth) self._buttonHide.setVisible(False) self._buttonHide.setFocusPolicy(Qt.NoFocus) self._buttonHide.connect(self._buttonHide, SIGNAL("clicked()"), self.toggleLayer) self._buttonDown = QToolButton(self) self._buttonDown.setArrowType(Qt.DownArrow) self._buttonDown.setMaximumWidth(buttonWidth) self._buttonDown.setVisible(False) self._buttonDown.connect(self._buttonDown, SIGNAL("clicked()"), self.moveDown) self._layout = QVBoxLayout(self) self._layout.setMargin(1) self._layout.addWidget(self._buttonUp) self._layout.addWidget(self._buttonHide) self._layout.addWidget(self._buttonDown) self._rawLayer = rawLayer self._layer = layer self.setLayerSet([layer]) self._linkedCanvas = linkedCanvas self._extent = self._linkedCanvas.extent() self.setExtent(self._extent) self.setFocusPolicy(Qt.StrongFocus) self._hidden = False def toggleLayer(self): if not self._hidden: self._linkedCanvas.hideLayer(self._layer) self._buttonHide.setText("Show") self._hidden = True else: self._linkedCanvas.showLayer(self._layer) self._buttonHide.setText("Hide") self._hidden = False def moveUp(self): if not self._hidden: self._linkedCanvas.moveLayerUp(self._layer) self._dock.moveWidgetUp(self) def moveDown(self): if not self._hidden: self._linkedCanvas.moveLayerDown(self._layer) self._dock.moveWidgetDown(self) def refreshExtent(self): self._extent = self._linkedCanvas.extent() self.zoomToFeatureExtent(self._extent) def isCurrentLayer(self): return self._linkedCanvas.currentLayer == self._rawLayer def setAsCurrentLayer(self): self._linkedCanvas.setCurrentLayer(self._rawLayer) def focusInEvent(self, focusEvent): self._buttonUp.setVisible(True) self._buttonHide.setVisible(True) self._buttonDown.setVisible(True) # if not self.isCurrentLayer(): # self.setAsCurrentLayer() QgsMapCanvas.focusInEvent(self, focusEvent) def focusOutEvent(self, focusEvent): if not self._buttonHide.hasFocus(): self._buttonUp.setVisible(False) self._buttonHide.setVisible(False) self._buttonDown.setVisible(False) QgsMapCanvas.focusOutEvent(self, focusEvent) def keyPressEvent(self, keyEvent): if keyEvent.key() == Qt.Key_Down: self.moveDown() elif keyEvent.key() == Qt.Key_Up: self.moveUp() elif keyEvent.key() == Qt.Key_H: self.toggleLayer() def keyPressed(self, keyEvent): pass def keyReleased(self, QKeyEvent): pass def keyReleaseEvent(self, QKeyEvent): pass def _getCanvas(self): return self._canvas canvas = property(_getCanvas)
def addButton(self, func): button = QPushButton(func.__doc__) setattr(self, "button%d" % self.buttonNum, button) self.buttonNum += 1 self.buttonLayout.addWidget(button) self.connect(button, SIGNAL('clicked()'), func)
def setup_ui(self): # {{{ self._g = g = QHBoxLayout(self) self.setLayout(g) self._l = l = QVBoxLayout() g.addLayout(l) fmts = sorted(x.upper() for x in self.fmts) self.fmt_choice_box = QGroupBox(_('Choose the format to tweak:'), self) self._fl = fl = QHBoxLayout() self.fmt_choice_box.setLayout(self._fl) self.fmt_choice_buttons = [QRadioButton(x, self) for x in fmts] for x in self.fmt_choice_buttons: fl.addWidget(x, stretch=10 if x is self.fmt_choice_buttons[-1] else 0) l.addWidget(self.fmt_choice_box) self.fmt_choice_box.setVisible(len(fmts) > 1) self.help_label = QLabel( _('''\ <h2>About Tweak Book</h2> <p>Tweak Book allows you to fine tune the appearance of an ebook by making small changes to its internals. In order to use Tweak Book, you need to know a little bit about HTML and CSS, technologies that are used in ebooks. Follow the steps:</p> <br> <ol> <li>Click "Explode Book": This will "explode" the book into its individual internal components.<br></li> <li>Right click on any individual file and select "Open with..." to edit it in your favorite text editor.<br></li> <li>When you are done Tweaking: <b>close the file browser window and the editor windows you used to make your tweaks</b>. Then click the "Rebuild Book" button, to update the book in your calibre library.</li> </ol>''')) self.help_label.setWordWrap(True) self._fr = QFrame() self._fr.setFrameShape(QFrame.VLine) g.addWidget(self._fr) g.addWidget(self.help_label) self._b = b = QGridLayout() left, top, right, bottom = b.getContentsMargins() top += top b.setContentsMargins(left, top, right, bottom) l.addLayout(b, stretch=10) self.explode_button = QPushButton(QIcon(I('wizard.png')), _('&Explode Book')) self.preview_button = QPushButton(QIcon(I('view.png')), _('&Preview Book')) self.cancel_button = QPushButton(QIcon(I('window-close.png')), _('&Cancel')) self.rebuild_button = QPushButton(QIcon(I('exec.png')), _('&Rebuild Book')) self.explode_button.setToolTip( _('Explode the book to edit its components')) self.preview_button.setToolTip(_('Preview the result of your tweaks')) self.cancel_button.setToolTip(_('Abort without saving any changes')) self.rebuild_button.setToolTip( _('Save your changes and update the book in the calibre library')) a = b.addWidget a(self.explode_button, 0, 0, 1, 1) a(self.preview_button, 0, 1, 1, 1) a(self.cancel_button, 1, 0, 1, 1) a(self.rebuild_button, 1, 1, 1, 1) for x in ('explode', 'preview', 'cancel', 'rebuild'): getattr(self, x + '_button').clicked.connect(getattr(self, x)) self.msg = QLabel('dummy', self) self.msg.setVisible(False) self.msg.setStyleSheet(''' QLabel { text-align: center; background-color: white; color: black; border-width: 1px; border-style: solid; border-radius: 20px; font-size: x-large; font-weight: bold; } ''') self.resize(self.sizeHint() + QSize(40, 10))
QPushButton, QTimer, QIcon) app = Application([], force_calibre_style=True) d = QDialog() d.l = l = QGridLayout() d.setLayout(l) lw = QListWidget() lw.addItem('Some text guy') l.addWidget(lw, 0, 0, 2, 1) bb = QDialogButtonBox() bb.setStandardButtons(bb.Close) bb.accepted.connect(d.accept) bb.rejected.connect(d.reject) b = bb.addButton('Action', bb.ActionRole) b.setIcon(QIcon(I('wizard.png'))) l.addWidget(bb, 2, 0, 1, 2) bb.button(bb.Close).setDefault(True) b = QPushButton('Normal') l.addWidget(b, 0, 1, 1, 1) def print_button_sizes(): for b in d.findChildren(QPushButton): print(unicode(b.text()), b.height(), b.iconSize()) QTimer.singleShot(5, print_button_sizes) d.exec_()
class AddTagDialog(QDialog): def __init__(self, parent, modal=True, flags=Qt.WindowFlags()): QDialog.__init__(self, parent, flags) self.setModal(modal) self.setWindowTitle("Add Tag") lo = QVBoxLayout(self) lo.setMargin(10) lo.setSpacing(5) # tag selector lo1 = QHBoxLayout() lo.addLayout(lo1) lo1.setSpacing(5) self.wtagsel = QComboBox(self) self.wtagsel.setEditable(True) wtagsel_lbl = QLabel("&Tag:", self) wtagsel_lbl.setBuddy(self.wtagsel) lo1.addWidget(wtagsel_lbl, 0) lo1.addWidget(self.wtagsel, 1) QObject.connect(self.wtagsel, SIGNAL("activated(int)"), self._check_tag) QObject.connect(self.wtagsel, SIGNAL("editTextChanged(const QString &)"), self._check_tag_text) # value editor self.valedit = ValueTypeEditor(self) lo.addWidget(self.valedit) # buttons lo.addSpacing(10) lo2 = QHBoxLayout() lo.addLayout(lo2) lo2.setContentsMargins(0, 0, 0, 0) lo2.setMargin(5) self.wokbtn = QPushButton("OK", self) self.wokbtn.setMinimumWidth(128) QObject.connect(self.wokbtn, SIGNAL("clicked()"), self.accept) self.wokbtn.setEnabled(False) cancelbtn = QPushButton("Cancel", self) cancelbtn.setMinimumWidth(128) QObject.connect(cancelbtn, SIGNAL("clicked()"), self.reject) lo2.addWidget(self.wokbtn) lo2.addStretch(1) lo2.addWidget(cancelbtn) self.setMinimumWidth(384) def setTags(self, tagnames): self.wtagsel.clear() self.wtagsel.addItems(list(tagnames)) self.wtagsel.addItem("") self.wtagsel.setCurrentIndex(len(tagnames)) def setValue(self, value): self.valedit.setValue(value) def _check_tag(self, tag): self.wokbtn.setEnabled(True) def _check_tag_text(self, text): self.wokbtn.setEnabled(bool(str(text) != "")) def accept(self): """When dialog is accepted with a default (bool) tag type, check if the user hasn't entered a name=value entry in the tag name field. This is a common mistake, and should be treated as a shortcut for setting string tags.""" if isinstance(self.valedit.getValue(), bool): tagval = str(self.wtagsel.currentText()).split("=", 1) if len(tagval) > 1: # print tagval if QMessageBox.warning( self, "Set a string tag instead?", """<P>You have included an "=" sign in the tag name. Perhaps you actually mean to set tag "%s" to the string value "%s"?</P>""" % tuple(tagval), QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) == QMessageBox.No: return self.wtagsel.setEditText(tagval[0]) self.valedit.setValue(tagval[1]) return QDialog.accept(self) def getTag(self): return str(self.wtagsel.currentText()), self.valedit.getValue()
class ModelGroupsTable(QWidget): EditableAttrs = [ attr for attr in PlotStyles.StyleAttributes if attr in PlotStyles.StyleAttributeOptions ] ColList = 3 ColPlot = 4 ColApply = 5 AttrByCol = dict([(i + 6, attr) for i, attr in enumerate(EditableAttrs)]) def __init__(self, parent, *args): QWidget.__init__(self, parent, *args) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) lo = QVBoxLayout(self) lo.setContentsMargins(0, 0, 0, 0) lo1 = QHBoxLayout() lo.addLayout(lo1) lo1.setContentsMargins(0, 0, 0, 0) lbl = QLabel(QString("<nobr><b>Source groupings:</b></nobr>"), self) lo1.addWidget(lbl, 0) lo1.addStretch(1) # add show/hide button self._showattrbtn = QPushButton(self) self._showattrbtn.setMinimumWidth(256) lo1.addWidget(self._showattrbtn, 0) lo1.addStretch() QObject.connect(self._showattrbtn, SIGNAL("clicked()"), self._togglePlotControlsVisibility) # add table self.table = QTableWidget(self) lo.addWidget(self.table) QObject.connect(self.table, SIGNAL("cellChanged(int,int)"), self._valueChanged) self.table.setSelectionMode(QTableWidget.NoSelection) # setup basic columns self.table.setColumnCount(6 + len(self.EditableAttrs)) for i, label in enumerate( ("grouping", "total", "selection", "list", "plot", "style")): self.table.setHorizontalHeaderItem(i, QTableWidgetItem(label)) self.table.horizontalHeader().setSectionHidden(self.ColApply, True) # setup columns for editable grouping attributes for i, attr in self.AttrByCol.items(): self.table.setHorizontalHeaderItem( i, QTableWidgetItem(PlotStyles.StyleAttributeLabels[attr])) self.table.horizontalHeader().setSectionHidden(i, True) self.table.verticalHeader().hide() # other internal init self._attrs_shown = False self._togglePlotControlsVisibility() self.model = None self._setting_model = False self._currier = PersistentCurrier() # row of 'selected' grouping self._irow_selgroup = 0 def clear(self): self.table.setRowCount(0) self.model = None # setup mappings from the group.show_plot attribute to check state ShowAttrToCheckState = { PlotStyles.ShowNot: Qt.Unchecked, PlotStyles.ShowDefault: Qt.PartiallyChecked, PlotStyles.ShowAlways: Qt.Checked } CheckStateToShowAttr = dict([(val, key) for key, val in ShowAttrToCheckState.items()]) def _makeCheckItem(self, name, group, attr): item = QTableWidgetItem(name) if group is self.model.defgroup: item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsUserCheckable) item.setCheckState( Qt.Checked if getattr(group.style, attr) else Qt.Unchecked) else: item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsUserCheckable | Qt.ItemIsTristate) item.setCheckState(self.ShowAttrToCheckState[getattr( group.style, attr)]) return item def _updateModel(self, what=SkyModel.UpdateAll, origin=None): if origin is self or not what & (SkyModel.UpdateTags | SkyModel.UpdateGroupStyle): return model = self.model self._setting_model = True # to ignore cellChanged() signals (in valueChanged()) # _item_cb is a dict (with row,col keys) containing the widgets (CheckBoxes ComboBoxes) per each cell self._item_cb = {} # lists of "list" and "plot" checkboxes per each grouping (excepting the default grouping); each entry is an (row,col,item) tuple. # used as argument to self._showControls() self._list_controls = [] self._plot_controls = [] # list of selection callbacks (to which signals are connected) self._callbacks = [] # set requisite number of rows,and start filling self.table.setRowCount(len(model.groupings)) for irow, group in enumerate(model.groupings): self.table.setItem(irow, 0, QTableWidgetItem(group.name)) if group is model.selgroup: self._irow_selgroup = irow # total # source in group: skip for "current" if group is not model.curgroup: self.table.setItem(irow, 1, QTableWidgetItem(str(group.total))) # selection controls: skip for current and selection if group not in (model.curgroup, model.selgroup): btns = QWidget() lo = QHBoxLayout(btns) lo.setContentsMargins(0, 0, 0, 0) lo.setSpacing(0) # make selector buttons (depending on which group we're in) if group is model.defgroup: Buttons = (("+", lambda src, grp=group: True, "select all sources"), ("-", lambda src, grp=group: False, "unselect all sources")) else: Buttons = ( ("=", lambda src, grp=group: grp.func(src), "select only this grouping"), ("+", lambda src, grp=group: src.selected or grp.func(src), "add grouping to selection"), ("-", lambda src, grp=group: src.selected and not grp. func(src), "remove grouping from selection"), ("&&", lambda src, grp=group: src.selected and grp.func(src), "intersect selection with grouping")) lo.addStretch(1) for label, predicate, tooltip in Buttons: btn = QToolButton(btns) btn.setText(label) btn.setMinimumWidth(24) btn.setMaximumWidth(24) btn.setToolTip(tooltip) lo.addWidget(btn) # add callback QObject.connect( btn, SIGNAL("clicked()"), self._currier.curry(self.selectSources, predicate)) lo.addStretch(1) self.table.setCellWidget(irow, 2, btns) # "list" checkbox (not for current and selected groupings: these are always listed) if group not in (model.curgroup, model.selgroup): item = self._makeCheckItem("", group, "show_list") self.table.setItem(irow, self.ColList, item) item.setToolTip( """<P>If checked, sources in this grouping will be listed in the source table. If un-checked, sources will be excluded from the table. If partially checked, then the default list/no list setting of "all sources" will be in effect. </P>""") # "plot" checkbox (not for the current grouping, since that's always plotted) if group is not model.curgroup: item = self._makeCheckItem("", group, "show_plot") self.table.setItem(irow, self.ColPlot, item) item.setToolTip( """<P>If checked, sources in this grouping will be included in the plot. If un-checked, sources will be excluded from the plot. If partially checked, then the default plot/no plot setting of "all sources" will be in effect. </P>""") # custom style control # for default, current and selected, this is just a text label if group is model.defgroup: item = QTableWidgetItem("default:") item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter) item.setToolTip( """<P>This is the default plot style used for all sources for which a custom grouping style is not selected.</P>""" ) self.table.setItem(irow, self.ColApply, item) elif group is model.curgroup: item = QTableWidgetItem("") item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter) item.setToolTip( """<P>This is the plot style used for the highlighted source, if any.</P>""" ) self.table.setItem(irow, self.ColApply, item) elif group is model.selgroup: item = QTableWidgetItem("") item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter) item.setToolTip( """<P>This is the plot style used for the currently selected sources.</P>""" ) self.table.setItem(irow, self.ColApply, item) # for the rest, a combobox with custom priorities else: cb = QComboBox() cb.addItems(["default"] + ["custom %d" % p for p in range(1, 10)]) index = max(0, min(group.style.apply, 9)) # dprint(0,group.name,"apply",index) cb.setCurrentIndex(index) QObject.connect( cb, SIGNAL("activated(int)"), self._currier.xcurry(self._valueChanged, (irow, self.ColApply))) self.table.setCellWidget(irow, self.ColApply, cb) cb.setToolTip( """<P>This controls whether sources within this group are plotted with a customized plot style. Customized styles have numeric priority; if a source belongs to multiple groups, then the style with the lowest priority takes precedence.<P>""") # attribute comboboxes for icol, attr in self.AttrByCol.items(): # get list of options for this style attribute. If dealing with first grouping (i==0), which is # the "all sources" grouping, then remove the "default" option (which is always first in the list) options = PlotStyles.StyleAttributeOptions[attr] if irow == 0: options = options[1:] # make combobox cb = QComboBox() cb.addItems(list(map(str, options))) # the "label" option is also editable if attr == "label": cb.setEditable(True) try: index = options.index(getattr(group.style, attr)) cb.setCurrentIndex(index) except ValueError: cb.setEditText(str(getattr(group.style, attr))) slot = self._currier.xcurry(self._valueChanged, (irow, icol)) QObject.connect(cb, SIGNAL("activated(int)"), slot) QObject.connect(cb, SIGNAL("editTextChanged(const QString &)"), slot) cb.setEnabled(group is model.defgroup or group.style.apply) self.table.setCellWidget(irow, icol, cb) label = attr if irow: cb.setToolTip( """<P>This is the %s used to plot sources in this group, when a "custom" style for the group is enabled via the style control.<P>""" % label) else: cb.setToolTip( "<P>This is the default %s used for all sources for which a custom style is not specified below.<P>" % label) self.table.resizeColumnsToContents() # re-enable processing of cellChanged() signals self._setting_model = False def setModel(self, model): self.model = model self.model.connect("updated", self._updateModel) self.model.connect("selected", self.updateModelSelection) self._updateModel(SkyModel.UpdateAll) def _valueChanged(self, row, col): """Called when a cell has been edited""" if self._setting_model: return group = self.model.groupings[row] item = self.table.item(row, col) if col == self.ColList: if group is not self.model.defgroup: # tri-state items go from unchecked to checked when user clicks them. Make them partially checked instead. if group.style.show_list == PlotStyles.ShowNot and item.checkState( ) == Qt.Checked: item.setCheckState(Qt.PartiallyChecked) group.style.show_list = self.CheckStateToShowAttr[ item.checkState()] self.model.emitChangeGroupingVisibility(group, origin=self) return elif col == self.ColPlot: if group is not self.model.defgroup: # tri-state items go from unchecked to checked by default. Make them partially checked instead. if group.style.show_plot == PlotStyles.ShowNot and item.checkState( ) == Qt.Checked: item.setCheckState(Qt.PartiallyChecked) group.style.show_plot = self.CheckStateToShowAttr[ item.checkState()] elif col == self.ColApply: group.style.apply = self.table.cellWidget(row, col).currentIndex() # enable/disable editable cells for j in list(self.AttrByCol.keys()): item1 = self.table.item(row, j) if item1: fl = item1.flags() & ~Qt.ItemIsEnabled if group.style.apply: fl |= Qt.ItemIsEnabled item1.setFlags(fl) cw = self.table.cellWidget(row, j) cw and cw.setEnabled(group.style.apply) elif col in self.AttrByCol: cb = self.table.cellWidget(row, col) txt = str(cb.currentText()) attr = self.AttrByCol[col] if txt == "default": setattr(group.style, attr, PlotStyles.DefaultValue) else: setattr(group.style, attr, PlotStyles.StyleAttributeTypes.get(attr, str)(txt)) # all other columns: return so we don't emit a signal else: return # in all cases emit a signal self.model.emitChangeGroupingStyle(group, origin=self) def selectSources(self, predicate): """Selects sources according to predicate(src)""" busy = BusyIndicator() for src in self.model.sources: src.selected = predicate(src) self.model.emitSelection(origin=self) busy = None def updateModelSelection(self, nsel, origin=None): """This is called when some other widget changes the set of selected model sources""" self.table.clearSelection() if self.model: self.table.item(self._irow_selgroup, 1).setText(str(nsel)) def _togglePlotControlsVisibility(self): if self._attrs_shown: self._attrs_shown = False self.table.hideColumn(self.ColApply) for col in self.AttrByCol.keys(): self.table.hideColumn(col) self._showattrbtn.setText("Show plot styles >>") else: self._attrs_shown = True self.table.showColumn(self.ColApply) for col in self.AttrByCol.keys(): self.table.showColumn(col) self._showattrbtn.setText("<< Hide plot styles")
def pb(text, tooltip=None): b = QPushButton(text, self) b.setToolTip(tooltip or '') b.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed) return b
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.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.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 = QDialogButtonBox(self) self.bbox.addButton(self.check_button, QDialogButtonBox.ActionRole) self.bbox.addButton(self.delete_button, QDialogButtonBox.ActionRole) self.bbox.addButton(self.fix_button, QDialogButtonBox.ActionRole) self.bbox.addButton(self.copy_button, QDialogButtonBox.ActionRole) self.bbox.addButton(self.ok_button, QDialogButtonBox.AcceptRole) 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.addWidget(self.bbox) self.resize(950, 500) self.bbox.setEnabled(True)
def __init__(self, parentWidget, label='', labelColumn=0, text='', setAsDefault=True, spanWidth=False): """ Appends a QPushButton (Qt) widget to the bottom of I{parentWidget}, a Property Manager group box. @param parentWidget: The parent group box containing this widget. @type parentWidget: PM_GroupBox @param label: The label that appears to the left or right of the checkbox. If spanWidth is True, the label will be displayed on its own row directly above the list widget. To suppress the label, set I{label} to an empty string. @type label: str @param labelColumn: The column number of the label in the group box grid layout. The only valid values are 0 (left column) and 1 (right column). The default is 0 (left column). @type labelColumn: int @param text: The button's text. @type text: str @param setAsDefault: if True, will restore <text> as the button's text when the "Restore Defaults" button is clicked. @type setAsDefault: bool @param spanWidth: if True, the widget and its label will span the width of the group box. Its label will appear directly above the widget (unless the label is empty) and is left justified. @type spanWidth: bool @see: U{B{QPushButton}<http://doc.trolltech.com/4/qpushbutton.html>} """ if 0: # Debugging code print "PM_PushButton.__init__():" print " label = ", label print " labelColumn = ", labelColumn print " text = ", text print " setAsDefault = ", setAsDefault print " spanWidth = ", spanWidth QPushButton.__init__(self) self.parentWidget = parentWidget self.label = label self.labelColumn = labelColumn self.setAsDefault = setAsDefault self.spanWidth = spanWidth if label: # Create this widget's QLabel. self.labelWidget = QLabel() self.labelWidget.setText(label) # Set text self.setText(text) # Set default text self.defaultText = text self.setAsDefault = setAsDefault parentWidget.addPmWidget(self)
def __init__(self, win=None): import string QDialog.__init__(self, win) self.setWindowTitle('Manually edit sim parameters') layout = QGridLayout(self) layout.setMargin(1) layout.setSpacing(-1) layout.setObjectName("SimParameterDialog") for i in range(len(_sim_param_table)): attr, paramtype = _sim_param_table[i] current = sim_param_values[attr] currentStr = str(current) label = QLabel(attr + ' (' + paramtype + ')', self) layout.addWidget(label, i, 0) if paramtype == BOOLEAN: label = QLabel(currentStr, self) layout.addWidget(label, i, 1) def falseFunc(attr=attr, label=label): sim_param_values[attr] = False label.setText('False') def trueFunc(attr=attr, label=label): sim_param_values[attr] = True label.setText('True') btn = QPushButton(self) btn.setText('True') layout.addWidget(btn, i, 2) self.connect(btn, SIGNAL("clicked()"), trueFunc) btn = QPushButton(self) btn.setText('False') layout.addWidget(btn, i, 3) self.connect(btn, SIGNAL("clicked()"), falseFunc) else: label = QLabel(self) label.setText(currentStr) layout.addWidget(label, i, 1) linedit = QLineEdit(self) linedit.setText(currentStr) layout.addWidget(linedit, i, 2) def change(attr=attr, linedit=linedit, paramtype=paramtype, label=label): txt = str(linedit.text()) label.setText(txt) if paramtype == STRING: sim_param_values[attr] = txt elif paramtype == INT: if txt.startswith('0x') or txt.startswith('0X'): n = string.atoi(txt[2:], 16) else: n = string.atoi(txt) sim_param_values[attr] = n elif paramtype == FLOAT: sim_param_values[attr] = string.atof(txt) btn = QPushButton(self) btn.setText('OK') layout.addWidget(btn, i, 3) self.connect(btn, SIGNAL("clicked()"), change) btn = QPushButton(self) btn.setText('Done') layout.addWidget(btn, len(_sim_param_table), 0, len(_sim_param_table), 4) def done(self=self): global sim_params_set sim_params_set = True #import pprint #pprint.pprint(sim_param_values) self.close() self.connect(btn, SIGNAL("clicked()"), done)