def insertHtmlMessage(self, text, setAsDefault=True, minLines=4, maxLines=10, replace=True): """Insert <text> (HTML) into the message groupbox. <minLines> - The minimum number of lines (of text) to display in the TextEdit. if <minLines>=0 the TextEdit will fit its own height to fit <text>. The default height is 4 (lines of text). <maxLines> - The maximum number of lines to display in the TextEdit widget. <replace> should be set to False if you do not wish to replace the current text. It will append <text> instead. Shows the message groupbox if it is hidden. """ if setAsDefault: self.defaultText = text self.setAsDefault = True if replace: self.MessageTextEdit.clear() if text: self._setHeight(minLines, maxLines) QTextEdit.insertHtml(self.MessageTextEdit, text) self.show() else: # Hide the message groupbox if it contains no text. self.hide()
def mousePressEvent(self, event): if event.button() == Qt.RightButton: # Rewrite the mouse event to a left button event so the cursor is # moved to the location of the pointer. event = QMouseEvent(QEvent.MouseButtonPress, event.pos(), Qt.LeftButton, Qt.LeftButton, Qt.NoModifier) QTextEdit.mousePressEvent(self, event)
def main(args=sys.argv): app = QApplication(args) editor = QTextEdit() nb=NumberBar(edit=editor) nb.show() editor.show() return app.exec_()
def __init__(self, *args): QTextEdit.__init__(self, *args) self._active = False # Default dictionary based on the current locale. self.dict = enchant.Dict() self.highlighter = Highlighter(self.document()) self.highlighter.setDict(self.dict) self.highlighter.setActivateSpellCheck(self._active)
def __init__(self, prompt='>>> ', continuation='... ', parent=None): QTextEdit.__init__(self, parent) self.shutting_down = False self.compiler = CommandCompiler() self.buf = self.old_buf = [] self.history = History([''], dynamic.get('console_history', [])) self.prompt_frame = None self.allow_output = False self.prompt_frame_format = QTextFrameFormat() self.prompt_frame_format.setBorder(1) self.prompt_frame_format.setBorderStyle(QTextFrameFormat.BorderStyle_Solid) self.prompt_len = len(prompt) self.doc.setMaximumBlockCount(int(prefs['scrollback'])) self.lexer = PythonLexer(ensurenl=False) self.tb_lexer = PythonTracebackLexer() self.context_menu = cm = QMenu(self) # {{{ cm.theme = ThemeMenu(cm) # }}} self.formatter = Formatter(prompt, continuation, style=prefs['theme']) p = QPalette() p.setColor(p.Base, QColor(self.formatter.background_color)) p.setColor(p.Text, QColor(self.formatter.color)) self.setPalette(p) self.key_dispatcher = { # {{{ Qt.Key_Enter : self.enter_pressed, Qt.Key_Return : self.enter_pressed, Qt.Key_Up : self.up_pressed, Qt.Key_Down : self.down_pressed, Qt.Key_Home : self.home_pressed, Qt.Key_End : self.end_pressed, Qt.Key_Left : self.left_pressed, Qt.Key_Right : self.right_pressed, Qt.Key_Backspace : self.backspace_pressed, Qt.Key_Delete : self.delete_pressed, } # }}} motd = textwrap.dedent('''\ # Python {0} # {1} {2} '''.format(sys.version.splitlines()[0], __appname__, __version__)) sys.excepthook = self.unhandled_exception self.controllers = [] QTimer.singleShot(0, self.launch_controller) with EditBlock(self.cursor): self.render_block(motd)
class LineTextWidget(QFrame): def __init__(self, *args): QFrame.__init__(self, *args) self.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken) self.edit = QTextEdit() self.edit.setFrameStyle(QFrame.NoFrame) self.edit.setAcceptRichText(False) self.number_bar = NumberBar() self.number_bar.setTextEdit(self.edit) hbox = QHBoxLayout(self) hbox.setSpacing(0) hbox.setMargin(0) hbox.addWidget(self.number_bar) hbox.addWidget(self.edit) self.edit.installEventFilter(self) self.edit.viewport().installEventFilter(self) def eventFilter(self, object, event): # Update the line numbers for all events on the text edit and the viewport. # This is easier than connecting all necessary singals. if object in (self.edit, self.edit.viewport()): self.number_bar.update() return False return QFrame.eventFilter(object, event) def getTextEdit(self): return self.edit
def show(self): if self.controller: self.controller.setSponsor() if self.typ == "QDialog": QDialog.show(self) elif self.typ == "QTextEdit": QTextEdit.show(self) elif self.typ == "QFrame": QFrame.show(self) else: print "don't know about self.typ == %r" % (self.typ,)
def createLogDisplay(self): logTextDisplay = QTextEdit() logTextDisplay.setFont(GETFONT('Fixed', 8)) w,h = relaxedSizeNChar(logTextDisplay, 68)[0], int(12 * 8.2) logTextDisplay.setMinimumWidth(w) logTextDisplay.setMinimumHeight(h) logTextDisplay.setReadOnly(True) return logTextDisplay
def keyPressEvent(self, ev): text = unicode(ev.text()) key = ev.key() action = self.key_dispatcher.get(key, None) if callable(action): action() elif key in (Qt.Key_Escape,): QTextEdit.keyPressEvent(self, ev) elif text: self.text_typed(text) else: QTextEdit.keyPressEvent(self, ev)
def keyPressEvent(self, event): """ Overrides the superclass method. """ #If user hits 'Enter' key (return key), don't do anything. if event.key() == Qt.Key_Return: #there is no obvious way to allow only a single line in a #QTextEdit (we can use some methods that restrict the columnt width #, line wrapping etc but this is untested when the line contains # huge umber of characters. Anyway, the following always works #and fixes bug 2713 if not self._permit_enter_keystroke: return QTextEdit.keyPressEvent(self, event)
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())
def _init_controls(self): layout = QVBoxLayout(self) self.setLayout(layout) ml = QHBoxLayout() layout.addLayout(ml, 1) self.keys_list = QListWidget(self) self.keys_list.setSelectionMode(QAbstractItemView.SingleSelection) self.keys_list.setFixedWidth(150) self.keys_list.setAlternatingRowColors(True) ml.addWidget(self.keys_list) self.value_text = QTextEdit(self) self.value_text.setTabStopWidth(24) self.value_text.setReadOnly(False) ml.addWidget(self.value_text, 1) button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) button_box.accepted.connect(self._apply_changes) button_box.rejected.connect(self.reject) self.clear_button = button_box.addButton('Clear', QDialogButtonBox.ResetRole) self.clear_button.setIcon(get_icon('trash.png')) self.clear_button.setToolTip('Clear all settings for this plugin') self.clear_button.clicked.connect(self._clear_settings) layout.addWidget(button_box)
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)
class PrefsViewerDialog(SizePersistedDialog): def __init__(self, gui, namespace): SizePersistedDialog.__init__(self, gui, 'Prefs Viewer dialog') self.setWindowTitle('Preferences for: '+namespace) self.db = gui.current_db self.namespace = namespace self._init_controls() self.resize_dialog() self._populate_settings() if self.keys_list.count(): self.keys_list.setCurrentRow(0) def _init_controls(self): layout = QVBoxLayout(self) self.setLayout(layout) ml = QHBoxLayout() layout.addLayout(ml, 1) self.keys_list = QListWidget(self) self.keys_list.setSelectionMode(QAbstractItemView.SingleSelection) self.keys_list.setFixedWidth(150) self.keys_list.setAlternatingRowColors(True) ml.addWidget(self.keys_list) self.value_text = QTextEdit(self) self.value_text.setTabStopWidth(24) self.value_text.setReadOnly(True) ml.addWidget(self.value_text, 1) button_box = QDialogButtonBox(QDialogButtonBox.Ok) button_box.accepted.connect(self.accept) button_box.setCenterButtons(True) layout.addWidget(button_box) def _populate_settings(self): self.keys_list.clear() ns_prefix = 'namespaced:%s:'% self.namespace keys = sorted([k[len(ns_prefix):] for k in self.db.prefs.iterkeys() if k.startswith(ns_prefix)]) for key in keys: self.keys_list.addItem(key) self.keys_list.setMinimumWidth(self.keys_list.sizeHintForColumn(0)) self.keys_list.currentRowChanged[int].connect(self._current_row_changed) def _current_row_changed(self, new_row): if new_row < 0: self.value_text.clear() return key = unicode(self.keys_list.currentItem().text()) val = self.db.prefs.get_namespaced(self.namespace, key, '') self.value_text.setPlainText(self.db.prefs.to_raw(val))
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
class VersionHistoryDialog(SizePersistedDialog): def __init__(self, parent, plugin_name, html): SizePersistedDialog.__init__(self, parent, "Plugin Updater plugin:version history dialog") self.setWindowTitle(_("Version History for %s") % plugin_name) layout = QVBoxLayout(self) self.setLayout(layout) self.notes = QTextEdit(html, self) self.notes.setReadOnly(True) layout.addWidget(self.notes) self.button_box = QDialogButtonBox(QDialogButtonBox.Close) self.button_box.rejected.connect(self.reject) layout.addWidget(self.button_box) # Cause our dialog size to be restored from prefs or created on first usage self.resize_dialog()
def insertHtml(self, text, setAsDefault = False, minLines = 4, maxLines = 6, replace = True ): """ Insert <text> (HTML) into the Prop Mgr's message groupbox. <minLines> is the minimum number of lines to display, even if the text takes up fewer lines. The default number of lines is 4. <maxLines> is the maximum number of lines to diplay before adding a vertical scrollbar. <replace> should be set to False if you do not wish to replace the current text. It will append <text> instead. """ if setAsDefault: self.defaultText = text self.setAsDefault = True if replace: # Replace the text by selecting effectively all text and # insert the new text 'over' it (overwrite). :jbirac: 20070629 cursor = self.textCursor() cursor.setPosition( 0, QTextCursor.MoveAnchor ) cursor.setPosition( len(self.toPlainText()), QTextCursor.KeepAnchor ) self.setTextCursor( cursor ) QTextEdit.insertHtml(self, text) if replace: # Restore the previous cursor position/selection and mode. cursor.setPosition( len(self.toPlainText()), QTextCursor.MoveAnchor ) self.setTextCursor( cursor )
def __init__(self, *args): QFrame.__init__(self, *args) self.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken) self.edit = QTextEdit() self.edit.setFrameStyle(QFrame.NoFrame) self.edit.setAcceptRichText(False) self.number_bar = NumberBar() self.number_bar.setTextEdit(self.edit) hbox = QHBoxLayout(self) hbox.setSpacing(0) hbox.setMargin(0) hbox.addWidget(self.number_bar) hbox.addWidget(self.edit) self.edit.installEventFilter(self) self.edit.viewport().installEventFilter(self)
def _init_controls(self): layout = QVBoxLayout(self) self.setLayout(layout) ml = QHBoxLayout() layout.addLayout(ml, 1) self.keys_list = QListWidget(self) self.keys_list.setSelectionMode(QAbstractItemView.SingleSelection) self.keys_list.setFixedWidth(150) self.keys_list.setAlternatingRowColors(True) ml.addWidget(self.keys_list) self.value_text = QTextEdit(self) self.value_text.setTabStopWidth(24) self.value_text.setReadOnly(True) ml.addWidget(self.value_text, 1) button_box = QDialogButtonBox(QDialogButtonBox.Ok) button_box.accepted.connect(self.accept) button_box.setCenterButtons(True) layout.addWidget(button_box)
def __init__(self, html, width=600, timeout=None): QDialog.__init__(self) self.setMinimumWidth(width) self.setObjectName("About NanoEngineer-1") TextEditLayout = QVBoxLayout(self) TextEditLayout.setSpacing(0) TextEditLayout.setMargin(0) self.text_edit = QTextEdit(self) self.text_edit.setHtml(html) self.text_edit.setReadOnly(True) self.text_edit.setWordWrapMode(QTextOption.WordWrap) TextEditLayout.addWidget(self.text_edit) self.quit_button = QPushButton("OK") TextEditLayout.addWidget(self.quit_button) self.connect(self.quit_button, SIGNAL("clicked()"), self.close) if timeout is not None: self.qt = QTimer() self.qt.setInterval(1000*timeout) self.qt.start() self.connect(self.qt, SIGNAL("timeout()"), self.close) self.show() self.exec_()
def highlight_cursor_line(self): sel = QTextEdit.ExtraSelection() sel.format.setBackground(self.palette().alternateBase()) sel.format.setProperty(QTextFormat.FullWidthSelection, True) sel.cursor = self.textCursor() sel.cursor.clearSelection() self.current_cursor_line = sel self.update_extra_selections() # Update the cursor line's line number in the line number area try: self.line_number_area.update(0, self.last_current_lnum[0], self.line_number_area.width(), self.last_current_lnum[1]) except AttributeError: pass block = self.textCursor().block() top = int( self.blockBoundingGeometry(block).translated( self.contentOffset()).top()) height = int(self.blockBoundingRect(block).height()) self.line_number_area.update(0, top, self.line_number_area.width(), height)
class AboutWindow(QDialog): def __init__(self, html, width=600, timeout=None): QDialog.__init__(self) self.setMinimumWidth(width) self.setObjectName("About NanoEngineer-1") TextEditLayout = QVBoxLayout(self) TextEditLayout.setSpacing(0) TextEditLayout.setMargin(0) self.text_edit = QTextEdit(self) self.text_edit.setHtml(html) self.text_edit.setReadOnly(True) self.text_edit.setWordWrapMode(QTextOption.WordWrap) TextEditLayout.addWidget(self.text_edit) self.quit_button = QPushButton("OK") TextEditLayout.addWidget(self.quit_button) self.connect(self.quit_button, SIGNAL("clicked()"), self.close) if timeout is not None: self.qt = QTimer() self.qt.setInterval(1000 * timeout) self.qt.start() self.connect(self.qt, SIGNAL("timeout()"), self.close) self.show() self.exec_()
def add_tag(tag): a = QTextEdit.ExtraSelection() a.cursor, a.format = editor.textCursor(), editor.match_paren_format a.cursor.setPosition(tag.start_block.position() + tag.start_offset) a.cursor.setPosition(tag.end_block.position() + tag.end_offset + 1, a.cursor.KeepAnchor) ans.append(a)
def __init__(self, parentWidget, label = '', labelColumn = 0, spanWidth = False, addToParent = True, permit_enter_keystroke = True ): """ Appends a QTextEdit (Qt) widget to the bottom of I{parentWidget}, a Property Manager group box. The QTextEdit is empty (has no text) by default. Use insertHtml() to insert HTML text into the TextEdit. @param parentWidget: the parent group box containing this widget. @type parentWidget: PM_GroupBox @param label: The label that appears to the left of (or above) the spin box. To suppress the label, set I{label} to an empty string. @type label: str @param spanWidth: If True, the spin box and its label will span the width of the group box. The label will appear directly above the spin box and is left justified. @type spanWidth: bool @param addToParent: If True (the default), self will be added to parentWidget by passing it to parentWidget.addPmWidget. If False, self will not be added to parentWidget. Typically, when this is False, the caller will add self to parent in some other way. @type addToParent: bool @param permit_enter_keystroke: If set to True, this PM_textEdit can have multiple lines. Otherwise, it will block the 'Enter' keypress within the text editor. Note that caller needs to make sure that linewrapping option is a propriately set, (in addition to this flag) so as to permit/ not permit multiple lines in the text edit. @see: U{B{QTextEdit}<http://doc.trolltech.com/4/qtextedit.html>} """ if 0: # Debugging code print "QTextEdit.__init__():" print " label =", label print " labelColumn =", labelColumn print " spanWidth =", spanWidth QTextEdit.__init__(self) self.parentWidget = parentWidget self.label = label self.labelColumn = labelColumn self.spanWidth = spanWidth self._permit_enter_keystroke = permit_enter_keystroke if label: # Create this widget's QLabel. self.labelWidget = QLabel() self.labelWidget.setText(label) self._setHeight() # Default height is 4 lines high. ## from PM.PM_MessageGroupBox import PM_MessageGroupBox ## if isinstance(parentWidget, PM_MessageGroupBox): ## # Add to parentWidget's vBoxLayout if <parentWidget> is a MessageGroupBox. ## parentWidget.vBoxLayout.addWidget(self) ## # We should be calling the PM's getMessageTextEditPalette() method, ## # but that will take some extra work which I will do soon. Mark 2007-06-21 ## self.setPalette(getPalette( None, ## QPalette.Base, ## pmMessageBoxColor)) ## self.setReadOnly(True) ## #@self.labelWidget = None # Never has one. Mark 2007-05-31 ## parentWidget._widgetList.append(self) ## parentWidget._rowCount += 1 ## else: ## parentWidget.addPmWidget(self) # bruce 071103 refactored the above into the new addToParent option and # code added to PM_MessageGroupBox.__init__ after it calls this method. if addToParent: parentWidget.addPmWidget(self) return
def __init__(self, *args): QTextEdit.__init__(self, *args) self.lastFolder = QtGui.QDesktopServices.storageLocation(QtGui.QDesktopServices.DocumentsLocation) self.docName = None self.initDict()
def __init__(self, parent=None): self.ip='' super(ServiceControl, self).__init__(parent) try: self.ip = ni.ifaddresses('eth0')[2][0]['addr'] except: self.ip = 'No Network ' # servis etiketleri........................... SSHLabel = QLabel('<center><font color="#2980B9" size="4"> SSH </font></center>') HTTPLabel = QLabel('<center><font color="#2980B9" size="4"> HTTP </font></center>') FTPLabel = QLabel('<center><font color="#2980B9" size="4"> FTP </font></center>') # servis calisiyor etiketleri .............................. self.SSHRLabel = QLabel() self.HTTPRLabel =QLabel() self.FTPRLabel =QLabel() # servis Buttonlari..................................... self.SSHBtn=QPushButton() self.HTTPBtn=QPushButton() self.FTPBtn=QPushButton() # servis Buttonlarina tiklandigi zaman gidecekleri fonsiyonlar................... self.SSHBtn.clicked.connect(self.SSH_Change) self.HTTPBtn.clicked.connect(self.HTTP_Change) self.FTPBtn.clicked.connect(self.FTP_Change) self.HTTPRestart = QPushButton('Restart') self.FTPRestart = QPushButton('Restart') self.SSHRestart = QPushButton('Restart') self.SSHRestart.clicked.connect(self.SSH_Restart) self.HTTPRestart.clicked.connect(self.Http_Restart) self.FTPRestart.clicked.connect(self.Ftp_Restart) # logo................................... self.label = QLabel() self.pixmap = QPixmap(os.getcwd() + '/p2.png') self.label.setPixmap(self.pixmap) # ssh kapaliysa kapali yaz... Stringssh = commands.getoutput('/etc/init.d/ssh status') Stringhttp = commands.getoutput('/etc/init.d/lighttpd status') Stringftp = os.system('/etc/init.d/vsftpd status') StringCheck = 'Active: inactive' Cssh = Stringssh.find('Active: active') Chttp = Stringhttp.find('Active: active') Cftp = Stringssh.find('Active: active') if Cssh != -1: self.SSHBtn.setText('DURDUR') self.SSHRLabel.setText('<center><font color="#2980B9" size="2"> Runing... </font></center>') print('Aktif') else: self.SSHBtn.setText('BASLAT') if Chttp !=-1: self.HTTPBtn.setText('DURDUR') self.HTTPRLabel.setText('<center><font color="#2980B9" size="2"> Runing... </font></center>') print('Aktif') else: self.HTTPBtn.setText('BASLAT') if Cftp !=-1: self.FTPBtn.setText('DURDUR') self.FTPRLabel.setText('<center><font color="#2980B9" size="2"> Runing... </font></center>') print('Aktif') else: self.FTPBtn.setText('BASLAT') # alt kisim. ip ve kernel surumleri............. self.Text = QTextEdit() self.Text.setText('Pardus ARM Servis Kontrol'+'\n'+ 'Host Name : ' + str(commands.getoutput("hostname")) + '\n'+ 'Kernel : '+ str(commands.getoutput("uname -r")) +'\n' +'IP Adress : '+ self.ip) self.Text.setReadOnly(True) # grid layout etiketleri ve buttonlari ekliyoruz..................... kutu = QHBoxLayout() kutu.addStretch(1) kutu.addWidget(self.label) grid = QGridLayout() # grid.addWidget(kutu,3,0,0,0) grid.addWidget(self.label,3,0,4,4) grid.addWidget(SSHLabel,3,1) grid.addWidget(HTTPLabel,4,1) grid.addWidget(FTPLabel,5,1) grid.addWidget(self.SSHRLabel,3,2) grid.addWidget(self.HTTPRLabel,4,2) grid.addWidget(self.FTPRLabel,5,2) grid.addWidget(self.SSHBtn,3,3) grid.addWidget(self.HTTPBtn,4,3) grid.addWidget(self.FTPBtn,5,3) grid.addWidget(self.SSHRestart,3,4) grid.addWidget(self.HTTPRestart,4,4) grid.addWidget(self.FTPRestart,5,4) grid.addWidget(self.Text, 8, 0, 7, 4) self.setLayout(grid) self.setWindowTitle('Pardus Servis Control') self.setFixedSize(650, 350)
class LineTextWidget(QFrame): class NumberBar(QWidget): def __init__(self, *args): QWidget.__init__(self, *args) self.edit = None # This is used to update the width of the control. # It is the highest line that is currently visibile. self.highest_line = 0 def setTextEdit(self, edit): self.edit = edit def update(self, *args): ''' Updates the number bar to display the current set of numbers. Also, adjusts the width of the number bar if necessary. ''' # The + 4 is used to compensate for the current line being bold. width = self.fontMetrics().width(str(self.highest_line)) + 4 if self.width() != width: self.setFixedWidth(width) QWidget.update(self, *args) def paintEvent(self, event): contents_y = self.edit.verticalScrollBar().value() page_bottom = contents_y + self.edit.viewport().height() font_metrics = self.fontMetrics() current_block = self.edit.document().findBlock( self.edit.textCursor().position()) painter = QPainter(self) line_count = 0 # Iterate over all text blocks in the document. block = self.edit.document().begin() while block.isValid(): line_count += 1 # The top left position of the block in the document position = self.edit.document().documentLayout( ).blockBoundingRect(block).topLeft() # Check if the position of the block is out side of the visible # area. if position.y() > page_bottom: break # We want the line number for the selected line to be bold. bold = False if block == current_block: bold = True font = painter.font() font.setBold(True) painter.setFont(font) # Draw the line number right justified at the y position of the # line. 3 is a magic padding number. drawText(x, y, text). painter.drawText( self.width() - font_metrics.width(str(line_count)) - 3, round(position.y()) - contents_y + font_metrics.ascent(), str(line_count)) # Remove the bold style if it was set previously. if bold: font = painter.font() font.setBold(False) painter.setFont(font) block = block.next() self.highest_line = line_count painter.end() QWidget.paintEvent(self, event) def __init__(self, *args): QFrame.__init__(self, *args) self.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken) self.edit = QTextEdit() self.edit.setFrameStyle(QFrame.NoFrame) self.edit.setAcceptRichText(False) self.number_bar = self.NumberBar() self.number_bar.setTextEdit(self.edit) hbox = QHBoxLayout(self) hbox.setSpacing(0) hbox.setMargin(0) hbox.addWidget(self.number_bar) hbox.addWidget(self.edit) self.edit.installEventFilter(self) self.edit.viewport().installEventFilter(self) def eventFilter(self, object, event): # Update the line numbers for all events on the text edit and the viewport. # This is easier than connecting all necessary singals. if object in (self.edit, self.edit.viewport()): self.number_bar.update() return False return QFrame.eventFilter(object, event) def getTextEdit(self): return self.edit
def createEditor(self, parent, option, index): editor = QTextEdit(parent) editor.setMinimumHeight(24) return editor
def __init__(self, parent=None, desc=None, name=None, modal=0, fl=0, env=None, type="QDialog"): if env is None: import foundation.env as env # this is a little weird... probably it'll be ok, and logically it seems correct. self.desc = desc self.typ = type if type == "QDialog": QDialog.__init__(self, parent, name, modal, fl) elif type == "QTextEdit": QTextEdit.__init__(self, parent, name) elif type == "QFrame": QFrame.__init__(self, parent, name) else: print "don't know about type == %r" % (type, ) self.image1 = QPixmap() self.image1.loadFromData(image1_data, "PNG") # should be: title_icon #### self.image3 = QPixmap() self.image3.loadFromData(image3_data, "PNG") self.image4 = QPixmap() self.image4.loadFromData(image4_data, "PNG") self.image5 = QPixmap() self.image5.loadFromData(image5_data, "PNG") self.image6 = QPixmap() self.image6.loadFromData(image6_data, "PNG") self.image7 = QPixmap() self.image7.loadFromData(image7_data, "PNG") self.image0 = QPixmap(image0_data) # should be: border_icon #### self.image2 = QPixmap(image2_data) # should be: sponsor_pixmap #### try: ####@@@@ title_icon_name = self.desc.options.get('title_icon') border_icon_name = self.desc.options.get('border_icon') if title_icon_name: self.image1 = imagename_to_pixmap( title_icon_name) ###@@@ pass icon_path ###@@@ import imagename_to_pixmap or use env function # or let that func itself be an arg, or have an env arg for it ###e rename it icon_name_to_pixmap, or find_icon? (the latter only if it's ok if it returns an iconset) ###e use iconset instead? if border_icon_name: self.image0 = imagename_to_pixmap(border_icon_name) except: print_compact_traceback( "bug in icon-setting code, using fallback icons: ") pass if not name: self.setName("parameter_dialog_or_frame") ### ###k guess this will need: if type == 'QDialog' self.setIcon(self.image0) # should be: border_icon #### nanotube_dialogLayout = QVBoxLayout(self, 0, 0, "nanotube_dialogLayout") self.heading_frame = QFrame(self, "heading_frame") self.heading_frame.setPaletteBackgroundColor(QColor(122, 122, 122)) self.heading_frame.setFrameShape(QFrame.NoFrame) self.heading_frame.setFrameShadow(QFrame.Plain) heading_frameLayout = QHBoxLayout(self.heading_frame, 0, 3, "heading_frameLayout") self.heading_pixmap = QLabel(self.heading_frame, "heading_pixmap") self.heading_pixmap.setSizePolicy( QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed, 0, 0, self.heading_pixmap.sizePolicy().hasHeightForWidth())) self.heading_pixmap.setPixmap( self.image1) # should be: title_icon #### self.heading_pixmap.setScaledContents(1) heading_frameLayout.addWidget(self.heading_pixmap) self.heading_label = QLabel(self.heading_frame, "heading_label") self.heading_label.setPaletteForegroundColor(QColor(255, 255, 255)) heading_label_font = QFont(self.heading_label.font()) heading_label_font.setPointSize(12) heading_label_font.setBold(1) self.heading_label.setFont(heading_label_font) heading_frameLayout.addWidget(self.heading_label) nanotube_dialogLayout.addWidget(self.heading_frame) self.body_frame = QFrame(self, "body_frame") self.body_frame.setFrameShape(QFrame.StyledPanel) self.body_frame.setFrameShadow(QFrame.Raised) body_frameLayout = QVBoxLayout(self.body_frame, 3, 3, "body_frameLayout") self.sponsor_frame = QFrame(self.body_frame, "sponsor_frame") self.sponsor_frame.setPaletteBackgroundColor(QColor(255, 255, 255)) self.sponsor_frame.setFrameShape(QFrame.StyledPanel) self.sponsor_frame.setFrameShadow(QFrame.Raised) sponsor_frameLayout = QHBoxLayout(self.sponsor_frame, 0, 0, "sponsor_frameLayout") self.sponsor_btn = QPushButton(self.sponsor_frame, "sponsor_btn") self.sponsor_btn.setAutoDefault(0) #bruce 060703 bugfix self.sponsor_btn.setSizePolicy( QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred, 0, 0, self.sponsor_btn.sizePolicy().hasHeightForWidth())) self.sponsor_btn.setPaletteBackgroundColor(QColor(255, 255, 255)) self.sponsor_btn.setPixmap( self.image2 ) # should be: sponsor_pixmap #### [also we'll need to support >1 sponsor] self.sponsor_btn.setFlat(1) sponsor_frameLayout.addWidget(self.sponsor_btn) body_frameLayout.addWidget(self.sponsor_frame) layout59 = QHBoxLayout(None, 0, 6, "layout59") left_spacer = QSpacerItem(20, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) layout59.addItem(left_spacer) self.done_btn = QToolButton(self.body_frame, "done_btn") self.done_btn.setIcon(QIcon(self.image3)) layout59.addWidget(self.done_btn) self.abort_btn = QToolButton(self.body_frame, "abort_btn") self.abort_btn.setIcon(QIcon(self.image4)) layout59.addWidget(self.abort_btn) self.preview_btn = QToolButton(self.body_frame, "preview_btn") self.preview_btn.setIcon(QIcon(self.image5)) layout59.addWidget(self.preview_btn) self.whatsthis_btn = QToolButton(self.body_frame, "whatsthis_btn") self.whatsthis_btn.setIcon(QIcon(self.image6)) layout59.addWidget(self.whatsthis_btn) right_spacer = QSpacerItem(20, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) layout59.addItem(right_spacer) body_frameLayout.addLayout(layout59) self.groups = [] self.param_getters = { } # map from param name to get-function (which gets current value out of its widget or controller) for group_desc in self.desc.kids('group'): # == start parameters_grpbox ### this will differ for Windows style header_refs = [ ] # keep python refcounted refs to all objects we make (at least the ones pyuic stored in self attrs) self.parameters_grpbox = QGroupBox(self.body_frame, "parameters_grpbox") self.parameters_grpbox.setFrameShape(QGroupBox.StyledPanel) self.parameters_grpbox.setFrameShadow(QGroupBox.Sunken) self.parameters_grpbox.setMargin(0) self.parameters_grpbox.setColumnLayout(0, Qt.Vertical) self.parameters_grpbox.layout().setSpacing(1) self.parameters_grpbox.layout().setMargin(4) parameters_grpboxLayout = QVBoxLayout( self.parameters_grpbox.layout()) parameters_grpboxLayout.setAlignment(Qt.AlignTop) layout20 = QHBoxLayout(None, 0, 6, "layout20") self.nt_parameters_grpbtn = QPushButton(self.parameters_grpbox, "nt_parameters_grpbtn") self.nt_parameters_grpbtn.setSizePolicy( QSizePolicy( QSizePolicy.Minimum, QSizePolicy.Fixed, 0, 0, self.nt_parameters_grpbtn.sizePolicy().hasHeightForWidth()) ) self.nt_parameters_grpbtn.setMaximumSize(QSize(16, 16)) self.nt_parameters_grpbtn.setAutoDefault(0) self.nt_parameters_grpbtn.setIcon(QIcon( self.image7)) ### not always right, but doesn't matter self.nt_parameters_grpbtn.setFlat(1) layout20.addWidget(self.nt_parameters_grpbtn) self.parameters_grpbox_label = QLabel(self.parameters_grpbox, "parameters_grpbox_label") self.parameters_grpbox_label.setSizePolicy( QSizePolicy( QSizePolicy.Preferred, QSizePolicy.Minimum, 0, 0, self.parameters_grpbox_label.sizePolicy(). hasHeightForWidth())) self.parameters_grpbox_label.setAlignment(QLabel.AlignVCenter) layout20.addWidget(self.parameters_grpbox_label) gbx_spacer1 = QSpacerItem(67, 16, QSizePolicy.Expanding, QSizePolicy.Minimum) layout20.addItem(gbx_spacer1) parameters_grpboxLayout.addLayout(layout20) nt_parameters_body_layout = QGridLayout( None, 1, 1, 0, 6, "nt_parameters_body_layout" ) ### what is 6 -- is it related to number of items??? # is it 6 in all the ones we got, but that could be a designer error so i better look it up sometime. # == start its kids # will use from above: self.parameters_grpbox, nt_parameters_body_layout nextrow = 0 # which row of the QGridLayout to start filling next (loop variable) hidethese = [ ] # set of objects to hide or show, when this group is closed or opened for param in group_desc.kids('parameter'): # param (a group subobj desc) is always a parameter, but we already plan to extend this beyond that, # so we redundantly test for this here. getter = None paramname = None # set these for use by uniform code at the end (e.g. for tooltips) editfield = None label = None if param.isa('parameter'): label = QLabel(self.parameters_grpbox, "members_label") label.setAlignment(QLabel.AlignVCenter | QLabel.AlignRight) nt_parameters_body_layout.addWidget(label, nextrow, 0) hidethese.append(label) thisrow = nextrow nextrow += 1 #e following should be known in a place that knows the input language, not here paramname = param.options.get('name') or ( param.args and param.args[0]) or "?" paramlabel = param.options.get( 'label' ) or paramname ##e wrong, label "" or none ought to be possible # QtGui.QApplication.translate(self.__class__.__name__, "xyz") label.setText( QtGui.QApplication.translate(self.__class__.__name__, paramlabel)) if param.isa('parameter', widget='combobox', type=('str', None)): self.members_combox = QComboBox( 0, self.parameters_grpbox, "members_combox") ###k what's 0? editfield = self.members_combox #### it probably needs a handler class, and then that could do this setup self.members_combox.clear() default = param.options.get( 'default', None) # None is not equal to any string thewidgetkid = param.kids( 'widget' )[-1] # kluge; need to think what the desc method for this should be for item in thewidgetkid.kids('item'): itemval = item.args[0] itemtext = itemval self.members_combox.insertItem( QtGui.QApplication.translate( self.__class__.__name__, itemtext)) #k __tr ok?? if itemval == default: #k or itemtext? pass ##k i find no setItem in our py code, so not sure yet what to do for this. nt_parameters_body_layout.addWidget( self.members_combox, thisrow, 1) hidethese.append(self.members_combox) getter = (lambda combobox=self.members_combox: str( combobox.currentText())) ##e due to __tr or non-str values, it might be better to use currentIndex and look it up in a table # (though whether __tr is good here might depend on what it's used for) elif param.isa('parameter', widget=('lineedit', None), type=('str', None)): # this covers explicit str|lineedit, and 3 default cases str, lineedit, neither. # (i.e. if you say parameter and nothing else, it's str lineedit by default.) self.length_linedit = QLineEdit(self.parameters_grpbox, "length_linedit") editfield = self.length_linedit nt_parameters_body_layout.addWidget( self.length_linedit, thisrow, 1) hidethese.append(self.length_linedit) default = str(param.options.get('default', "")) self.length_linedit.setText( QtGui.QApplication.translate(self.__class__.__name__, default)) # __tr ok? getter = (lambda lineedit=self.length_linedit: str( lineedit.text())) elif param.isa('parameter', widget=('lineedit', None), type='float'): self.length_linedit = QLineEdit(self.parameters_grpbox, "length_linedit") editfield = self.length_linedit nt_parameters_body_layout.addWidget( self.length_linedit, thisrow, 1) hidethese.append(self.length_linedit) controller = FloatLineeditController_Qt( self, param, self.length_linedit) header_refs.append(controller) getter = controller.get_value elif param.isa('parameter', widget = ('spinbox', None), type = 'int') or \ param.isa('parameter', widget = ('spinbox'), type = None): self.chirality_N_spinbox = QSpinBox( self.parameters_grpbox, "chirality_N_spinbox" ) # was chirality_m_spinbox, now chirality_N_spinbox editfield = self.chirality_N_spinbox ### seems like Qt defaults for min and max are 0,100 -- way too small a range! if param.options.has_key('min') or 1: self.chirality_N_spinbox.setMinimum( param.options.get('min', -999999999)) # was 0 if param.options.has_key('max') or 1: self.chirality_N_spinbox.setMaximum( param.options.get( 'max', +999999999)) # wasn't in egcode, but needed self.chirality_N_spinbox.setValue( param.options.get('default', 0)) # was 5 ##e note: i suspect this default 0 should come from something that knows this desc grammar. suffix = param.options.get('suffix', '') if suffix: self.chirality_N_spinbox.setSuffix( QtGui.QApplication.translate( self.__class__.__name__, suffix)) else: self.chirality_N_spinbox.setSuffix( QString.null) # probably not needed nt_parameters_body_layout.addWidget( self.chirality_N_spinbox, thisrow, 1) hidethese.append(self.chirality_N_spinbox) getter = self.chirality_N_spinbox.value # note: it also has .text, which includes suffix else: print "didn't match:", param ###e improve this # things done the same way for all kinds of param-editing widgets if 1: #bruce 060703 moved this down here, as bugfix # set tooltip (same one for editfield and label) tooltip = param.options.get('tooltip', '') ###e do it for more kinds of params; share the code somehow; do it in controller, or setup-aid? ###k QToolTip appropriateness; tooltip option might be entirely untested if tooltip and label: QToolTip.add( label, QtGui.QApplication.translate( self.__class__.__name__, tooltip)) if tooltip and editfield: QToolTip.add( editfield, QtGui.QApplication.translate( self.__class__.__name__, tooltip) ) ##k ok?? review once not all params have same-row labels. if getter and paramname and paramname != '?': self.param_getters[paramname] = getter ### also bind these params to actions... continue # next param header_refs.extend([ self.parameters_grpbox, self.nt_parameters_grpbtn, self.parameters_grpbox_label ]) # now create the logic/control object for the group group = CollapsibleGroupController_Qt(self, group_desc, header_refs, hidethese, self.nt_parameters_grpbtn) ### maybe ask env for the class to use for this? self.groups.append( group ) ### needed?? only for scanning the params, AFAIK -- oh, and to maintain a python refcount. # from languageChange: if 1: # i don't know if these are needed: self.parameters_grpbox.setTitle(QString.null) self.nt_parameters_grpbtn.setText(QString.null) self.parameters_grpbox_label.setText( QtGui.QApplication.translate( self.__class__.__name__, group_desc.args[0])) # was "Nanotube Parameters" ##e note that it's questionable in the syntax design for this property of a group (overall group label) # to be in that position (desc arg 0). # == end its kids parameters_grpboxLayout.addLayout(nt_parameters_body_layout) body_frameLayout.addWidget(self.parameters_grpbox) # == end parameters groupbox continue # next group nanotube_dialogLayout.addWidget(self.body_frame) spacer14 = QSpacerItem(20, 20, QSizePolicy.Minimum, QSizePolicy.Expanding) nanotube_dialogLayout.addItem(spacer14) layout42 = QHBoxLayout(None, 4, 6, "layout42") btm_spacer = QSpacerItem(59, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) layout42.addItem(btm_spacer) self.cancel_btn = QPushButton(self, "cancel_btn") self.cancel_btn.setAutoDefault(0) #bruce 060703 bugfix layout42.addWidget(self.cancel_btn) self.ok_btn = QPushButton(self, "ok_btn") self.ok_btn.setAutoDefault(0) #bruce 060703 bugfix layout42.addWidget(self.ok_btn) nanotube_dialogLayout.addLayout(layout42) self.languageChange() self.resize( QSize(246, 618).expandedTo(self.minimumSizeHint()) ) ### this size will need to be adjusted (guess -- it's only place overall size is set) qt4todo('self.clearWState(Qt.WState_Polished)') ## self.connect(self.nt_parameters_grpbtn,SIGNAL("clicked()"),self.toggle_nt_parameters_grpbtn) #### # new: for button, methodname in ( (self.sponsor_btn, 'do_sponsor_btn'), #e generalize to more than one sponsor button (self.done_btn, 'do_done_btn'), (self.abort_btn, 'do_abort_btn'), (self.preview_btn, 'do_preview_btn'), (self.whatsthis_btn, 'do_whatsthis_btn'), (self.cancel_btn, 'do_cancel_btn'), (self.ok_btn, 'do_ok_btn')): if hasattr(self, methodname): self.connect(button, SIGNAL("clicked()"), getattr(self, methodname)) return
class IPETLogFileView(QWidget): StyleSheet = """ QComboBox { color: darkblue; } QTextEdit { font-family : monospace; font-size : 12px; } """ """ a view of a log file, with selection mechanisms for the desired test run and problem that should be shown """ def __init__(self, parent=None): super(IPETLogFileView, self).__init__(parent) vlayout = QVBoxLayout(self) self.textbrowser = QTextEdit(self) self.textbrowser.setReadOnly(True) self.testrunselection = OptionsComboBox(self) self.problemselection = OptionsComboBox(self) vlayout.addWidget(self.textbrowser) self.setStyleSheet(self.StyleSheet) hlayout = QHBoxLayout() testrunselectionlabel = QLabel("Select a test run", self) problemselectionlabel = QLabel("Select an instance", self) testrunselectionlabel.setBuddy(self.testrunselection) problemselectionlabel.setBuddy(self.problemselection) for l, s in [(testrunselectionlabel, self.testrunselection), (problemselectionlabel, self.problemselection)]: v = QVBoxLayout(self) v.addWidget(l) v.addWidget(s) hlayout.addLayout(v) vlayout.addLayout(hlayout) self.setLayout(vlayout) self.initConnections() def getProblem(self): problem = self.problemselection.currentText() return problem def getSelectedText(self): return self.textbrowser.textCursor().selection() def getLineSelectionIndex(self): c = self.textbrowser.textCursor() if c.anchor() < c.position(): # normal left to right selection return (c.positionInBlock() - (c.selectionEnd() - c.selectionStart()), c.positionInBlock()) else: return (c.positionInBlock(), c.positionInBlock() + (c.selectionEnd() - c.selectionStart())) def getSelectedLine(self): cursor = self.textbrowser.textCursor() cursor.select(QTextCursor.LineUnderCursor) return str(cursor.selectedText()) def getTestRun(self): testruntext = self.testrunselection.currentText() for t in ExperimentManagement.getExperiment().getTestRuns(): if t.getName() == testruntext: return t return None def initConnections(self): for s in (self.testrunselection, self.problemselection): self.connect(s, SIGNAL("currentIndexChanged(int)"), self.updateView) def updateExperimentData(self): self.testrunselection.clear() self.problemselection.clear() testruns = ExperimentManagement.getExperiment().getTestRuns() problems = ExperimentManagement.getExperiment().getProblemNames() self.testrunselection.addItems([t.getName() for t in testruns]) self.problemselection.addItems([p for p in problems]) def updateView(self): selectedtestrun = self.getTestRun() selectedproblem = self.getProblem() if selectedtestrun is None: return outfile = selectedtestrun.getLogFile() with open(outfile, 'r') as in_file: linesstart = selectedtestrun.getProblemDataById( str(selectedproblem), "LineNumbers_BeginLogFile") linesend = selectedtestrun.getProblemDataById( str(selectedproblem), "LineNumbers_EndLogFile") self.text = [] for idx, line in enumerate(in_file): if idx > linesend: break elif idx >= linesstart: self.text.append(line) self.text = "".join(self.text) self.textbrowser.setText(self.text) self.textbrowser.update()
class DownloadDialog(QDialog): 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()) def config(self): self.do_user_config(parent=self) # Apply the changes # Not necessary, the downloader will obtain fresh config anyway... # self.label.setText(prefs['hello_world_msg']) def notify(self, message = None): if message is not None: # insertPlainText inserts at the beginning of the log area... self.log_area.append(message) sb = self.log_area.verticalScrollBar() sb.setValue(sb.maximum()) def download(self): prefs = self.prefs self.download_button.setEnabled(False) self.conf_button.setEnabled(False) downloader = BeamEbooksDownloader(self.prefs, self.version, caller = self) self.notify("Downloader is: %s" % (downloader)) # Loop over all accounts until we have support for selection for account_id in prefs[prefs.ACCOUNTS]: account = prefs[prefs.ACCOUNTS][account_id] account[prefs.ACCOUNT_ID] = account_id if account[prefs.ENABLED]: self.enqueue(account, downloader) self.hide() def enqueue(self, account, downloader): prefs = self.prefs self.notify("Account: '%s'" % account[prefs.USERNAME]) # downloader.login(account) func = 'arbitrary_n' # func = 'arbitrary' cpus = self.gui.job_manager.server.pool_size print "CPUs: %s" % (cpus) args = ['calibre_plugins.beam_ebooks_downloader.jobs', 'do_obtain_new_books', (cpus, account)] desc = 'Beam EBooks Downloader' job = self.gui.job_manager.run_job(Dispatcher(self._done), func, args=args, description=desc) print "Job: %s" % (job) self.notify(" Start parsing OPDS catalog") # if downloader.successful_login == False: # self.notify("Failed to log in...") # else: # self.notify("Scanning (beam) private library now...") # downloader.recursive_descent(norms(prefs[prefs.URLBASE])) def _done(self, job): print "Done Downloading" print "Self: %s" % (self) print "Job: %s" % (job) # print " Result: %s" % (job.result) # print " Result: %s" % (len(job.result)) self.notify(" Finished download catalog...") if job.result is not None: for entry in job.result: beamebooks_id = entry['id'] message = "<br/>Ebook # %s" % (beamebooks_id) self.notify(message) self.download_button.setEnabled(True) self.conf_button.setEnabled(True) payload = job.result msg = "Parsing OPDS Catalog complete, found %s ebooks." % (len(job.result)) msg = msg + "<br>Do you want to import the books into the library?" # question_dialog self.gui.proceed_question(self._add_ebooks, payload, job.details, 'OPDS Download Log', 'OPDS parse complete', msg, show_copy_button = False) def _done_2(self, job): print "Done Downloading" print "Self: %s" % (self) print "Job: %s" % (job) # print " Result: %s" % (job.result) # print " Result: %s" % (len(job.result)) self.notify(" Finished download book...") def _add_ebooks(self, payload): print "Done Downloading, Step 2" print "Self: %s" % (self) # print "Payload: %s" % (payload) # Printing the complete payload at once gives [IOError 12] - Out of space (memory, that is...) self.notify(" Finished adding books...") for entry in payload: print "Entry: %s" % entry # TODO allow for checkbox list of entries to download func = 'arbitrary_n' # func = 'arbitrary' cpus = self.gui.job_manager.server.pool_size print "CPUs: %s" % (cpus) args = ['calibre_plugins.beam_ebooks_downloader.jobs', 'do_download_book', (cpus, entry)] desc = 'Beam EBooks Downloader' job = self.gui.job_manager.run_job(Dispatcher(self._done_2), func, args=args, description=desc) print "Job: %s" % (job)
def __init__(self, main): self.searchThread = None self.passPhraseFinder = None self.isSearchOver = False def updateResults(resultStr): self.resultStr += resultStr def updateDisplay(): if len(self.resultStr) > 0: self.resultsDisplay.append(self.resultStr) self.resultStr = '' self.resultsDisplay.moveCursor(QtGui.QTextCursor.End) self.resultsDisplay.repaint() if self.isSearchOver: endSearch() # Call this from another thread to end the search def terminateSearch(): self.isSearchOver = True # Call this from the main thread to end the search def endSearch(): self.main.extraHeartbeatAlways.remove(updateDisplay) self.searchButton.setEnabled(True) self.stopButton.setEnabled(False) # If the thread is still searching tell the pass phrase finder to stop if self.passPhraseFinder and self.searchThread and not self.searchThread.isFinished(): self.passPhraseFinder.isStopped = True def searchForPassphrase(): # Get the selected wallet from the main screen wlt = self.getSelectedWlt() if wlt and not wlt.watchingOnly and wlt.isLocked: self.resultStr = '' self.passPhraseFinder = PassPhraseFinder(wlt) self.resultsDisplay.setText(QString('')) self.main.extraHeartbeatAlways.append(updateDisplay) if len(self.segOrdStrSet) > 0: # From self.segOrdStrList, create a list of lists of indexes that describe the segment orderings to search # In other words convert all of the strings in orderings list to lists of integers segOrdIntListList = [] for ordStr in self.segOrdStrSet: # The indexes provided by the users are 1 based, and the list indexes ought to be 0 based segOrdIntListList.append([int(indexStr)-1 for indexStr in ordStr.split(',')]) self.searchThread = PyBackgroundThread(self.passPhraseFinder.searchForPassPhrase, [segDef.getSegList() for segDef in self.segDefList], segOrdIntListList, updateResults, terminateSearch ) # Reset the isSearchOver flag self.isSearchOver = False self.searchThread.start() # Disable search button adn enabled stop button self.stopButton.setEnabled(True) self.searchButton.setEnabled(False) else: QMessageBox.warning(self.main, tr('Invalid'), tr(""" There are no valid segment combinations to search. Please add at least one segment and ordering to search."""), QMessageBox.Ok) else: QMessageBox.warning(self.main, tr('Invalid'), tr(""" No valid wallet is selected. Please select a locked non-watching-only from Available Wallets."""), QMessageBox.Ok) def addKnownSegment(): dlgEnterSegment = DlgEnterSegment(main, main) if dlgEnterSegment.exec_(): segmentText = str(dlgEnterSegment.editSegment.text()) if len(segmentText)>0: self.segDefList.append(KnownSeg(segmentText)) self.segDefTableModel.updateSegList(self.segDefList) def addUnknownCaseSegment(): dlgEnterSegment = DlgEnterSegment(main, main) if dlgEnterSegment.exec_(): segmentText = str(dlgEnterSegment.editSegment.text()) if len(segmentText)>0: self.segDefList.append(UnknownCaseSeg(segmentText)) self.segDefTableModel.updateSegList(self.segDefList) def addUnknownOrderSegment(): dlgEnterSegment = DlgEnterSegment(main, main, isUnknownOrder=True) if dlgEnterSegment.exec_(): segmentText = str(dlgEnterSegment.editSegment.text()) minLen = int(str(dlgEnterSegment.minSelector.currentText())) maxLen = int(str(dlgEnterSegment.maxSelector.currentText())) if len(segmentText)>0: self.segDefList.append(UnknownSeg(segmentText, minLen, maxLen)) self.segDefTableModel.updateSegList(self.segDefList) def addOrdering(): if len(self.segDefList) > 0: dlgSpecifyOrdering = DlgSpecifyOrdering(main, main, len(self.segDefList)) if dlgSpecifyOrdering.exec_(): self.segOrdStrSet.add(str(dlgSpecifyOrdering.parseOrderingList()).strip('[]')) self.updateOrderingListBox() else: QMessageBox.warning(self.main, tr('Not Ready'), tr(""" No segments have been entered. You must enter some segments before you can order them."""), QMessageBox.Ok) self.main = main self.segDefList = [] self.segOrdStrSet = set() segmentHeader = QRichLabel(tr("""<b>Build segments for pass phrase search: </b>"""), doWrap=False) self.knownButton = QPushButton("Add Known Segment") self.unknownCaseButton = QPushButton("Add Unknown Case Segment") self.unknownOrderButton = QPushButton("Add Unknown Order Segment") self.main.connect(self.knownButton, SIGNAL('clicked()'), addKnownSegment) self.main.connect(self.unknownCaseButton, SIGNAL('clicked()'), addUnknownCaseSegment) self.main.connect(self.unknownOrderButton, SIGNAL('clicked()'), addUnknownOrderSegment) topRow = makeHorizFrame([segmentHeader, self.knownButton, self.unknownCaseButton, self.unknownOrderButton, 'stretch']) self.segDefTableModel = SegDefDisplayModel() self.segDefTableView = QTableView() self.segDefTableView.setModel(self.segDefTableModel) self.segDefTableView.setSelectionBehavior(QTableView.SelectRows) self.segDefTableView.setSelectionMode(QTableView.SingleSelection) self.segDefTableView.verticalHeader().setDefaultSectionSize(20) self.segDefTableView.verticalHeader().hide() h = tightSizeNChar(self.segDefTableView, 1)[1] self.segDefTableView.setMinimumHeight(2 * (1.3 * h)) self.segDefTableView.setMaximumHeight(10 * (1.3 * h)) initialColResize(self.segDefTableView, [.1, .2, .4, .1, .1, .1]) self.segDefTableView.customContextMenuRequested.connect(self.showSegContextMenu) self.segDefTableView.setContextMenuPolicy(Qt.CustomContextMenu) segmentOrderingsHeader = QRichLabel(tr("""<b>Specify orderings for pass phrase search: </b>"""), doWrap=False) self.addOrderingButton = QPushButton("Add Ordering") self.main.connect(self.addOrderingButton, SIGNAL('clicked()'), addOrdering) orderingButtonPanel = makeHorizFrame([segmentOrderingsHeader, self.addOrderingButton, 'stretch']) self.segOrdListBox = QListWidget() self.segOrdListBox.customContextMenuRequested.connect(self.showOrdContextMenu) self.segOrdListBox.setContextMenuPolicy(Qt.CustomContextMenu) self.searchButton = QPushButton("Search") self.main.connect(self.searchButton, SIGNAL('clicked()'), searchForPassphrase) self.stopButton = QPushButton("Stop Searching") self.stopButton.setEnabled(False) self.main.connect(self.stopButton, SIGNAL('clicked()'), endSearch) totalSearchLabel = QRichLabel(tr("""<b>Total Passphrase Tries To Search: </b>"""), doWrap=False) self.totalSearchTriesDisplay = QLineEdit() self.totalSearchTriesDisplay.setReadOnly(True) self.totalSearchTriesDisplay.setText(QString('0')) self.totalSearchTriesDisplay.setFont(GETFONT('Fixed')) self.totalSearchTriesDisplay.setMinimumWidth(tightSizeNChar(self.totalSearchTriesDisplay, 6)[0]) self.totalSearchTriesDisplay.setMaximumWidth(tightSizeNChar(self.totalSearchTriesDisplay, 12)[0]) searchButtonPanel = makeHorizFrame([self.searchButton, self.stopButton, 'stretch', totalSearchLabel, self.totalSearchTriesDisplay]) self.resultsDisplay = QTextEdit() self.resultsDisplay.setReadOnly(True) self.resultsDisplay.setFont(GETFONT('Fixed')) self.resultsDisplay.setMinimumHeight(100) self.searchPanel = makeVertFrame([topRow, self.segDefTableView, orderingButtonPanel, self.segOrdListBox, searchButtonPanel, self.resultsDisplay, 'stretch']) # Now set the scrollarea widget to the layout self.tabToDisplay = QScrollArea() self.tabToDisplay.setWidgetResizable(True) self.tabToDisplay.setWidget(self.searchPanel)
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, handler, data): '''Ask user for 2nd factor authentication. Support text, security card and paired mobile methods. Use last method from settings, but support new pairing and downgrade. ''' QDialog.__init__(self, handler.top_level_window()) self.handler = handler self.txdata = data self.idxs = self.txdata[ 'keycardData'] if self.txdata['confirmationType'] > 1 else '' self.setMinimumWidth(600) self.setWindowTitle(_("Ledger Wallet Authentication")) self.cfg = copy.deepcopy(self.handler.win.wallet.get_keystore().cfg) self.dongle = self.handler.win.wallet.get_keystore().get_client( ).dongle self.ws = None self.pin = '' self.devmode = self.getDevice2FAMode() if self.devmode == 0x11 or self.txdata['confirmationType'] == 1: self.cfg['mode'] = 0 vbox = QVBoxLayout() self.setLayout(vbox) def on_change_mode(idx): if idx < 2 and self.ws: self.ws.stop() self.ws = None self.cfg[ 'mode'] = 0 if self.devmode == 0x11 else idx if idx > 0 else 1 if self.cfg['mode'] > 1 and self.cfg['pair'] and not self.ws: self.req_validation() if self.cfg['mode'] > 0: self.handler.win.wallet.get_keystore().cfg = self.cfg self.handler.win.wallet.save_keystore() self.update_dlg() def add_pairing(): self.do_pairing() def return_pin(): self.pin = self.pintxt.text( ) if self.txdata['confirmationType'] == 1 else self.cardtxt.text() if self.cfg['mode'] == 1: self.pin = ''.join(chr(int(str(i), 16)) for i in self.pin) self.accept() self.modebox = QWidget() modelayout = QHBoxLayout() self.modebox.setLayout(modelayout) modelayout.addWidget(QLabel(_("Method:"))) self.modes = QComboBox() modelayout.addWidget(self.modes, 2) self.addPair = QPushButton(_("Pair")) self.addPair.setMaximumWidth(60) modelayout.addWidget(self.addPair) modelayout.addStretch(1) self.modebox.setMaximumHeight(50) vbox.addWidget(self.modebox) self.populate_modes() self.modes.currentIndexChanged.connect(on_change_mode) self.addPair.clicked.connect(add_pairing) self.helpmsg = QTextEdit() self.helpmsg.setStyleSheet( "QTextEdit { background-color: lightgray; }") self.helpmsg.setReadOnly(True) vbox.addWidget(self.helpmsg) self.pinbox = QWidget() pinlayout = QHBoxLayout() self.pinbox.setLayout(pinlayout) self.pintxt = QLineEdit() self.pintxt.setEchoMode(2) self.pintxt.setMaxLength(4) self.pintxt.returnPressed.connect(return_pin) pinlayout.addWidget(QLabel(_("Enter PIN:"))) pinlayout.addWidget(self.pintxt) pinlayout.addWidget(QLabel(_("NOT DEVICE PIN - see above"))) pinlayout.addStretch(1) self.pinbox.setVisible(self.cfg['mode'] == 0) vbox.addWidget(self.pinbox) self.cardbox = QWidget() card = QVBoxLayout() self.cardbox.setLayout(card) self.addrtext = QTextEdit() self.addrtext.setStyleSheet( "QTextEdit { color:blue; background-color:lightgray; padding:15px 10px; border:none; font-size:20pt; }" ) self.addrtext.setReadOnly(True) self.addrtext.setMaximumHeight(120) card.addWidget(self.addrtext) def pin_changed(s): if len(s) < len(self.idxs): i = self.idxs[len(s)] addr = self.txdata['address'] addr = addr[:i] + '<u><b>' + addr[i:i + 1] + '</u></b>' + addr[i + 1:] self.addrtext.setHtml(str(addr)) else: self.addrtext.setHtml(_("Press Enter")) pin_changed('') cardpin = QHBoxLayout() cardpin.addWidget(QLabel(_("Enter PIN:"))) self.cardtxt = QLineEdit() self.cardtxt.setEchoMode(2) self.cardtxt.setMaxLength(len(self.idxs)) self.cardtxt.textChanged.connect(pin_changed) self.cardtxt.returnPressed.connect(return_pin) cardpin.addWidget(self.cardtxt) cardpin.addWidget(QLabel(_("NOT DEVICE PIN - see above"))) cardpin.addStretch(1) card.addLayout(cardpin) self.cardbox.setVisible(self.cfg['mode'] == 1) vbox.addWidget(self.cardbox) self.pairbox = QWidget() pairlayout = QVBoxLayout() self.pairbox.setLayout(pairlayout) pairhelp = QTextEdit(helpTxt[5]) pairhelp.setStyleSheet("QTextEdit { background-color: lightgray; }") pairhelp.setReadOnly(True) pairlayout.addWidget(pairhelp, 1) self.pairqr = QRCodeWidget() pairlayout.addWidget(self.pairqr, 4) self.pairbox.setVisible(False) vbox.addWidget(self.pairbox) self.update_dlg() if self.cfg['mode'] > 1 and not self.ws: self.req_validation()
def __init__(self, parent = None, desc = None, name = None, modal = 0, fl = 0, env = None, type = "QDialog"): if env is None: import foundation.env as env # this is a little weird... probably it'll be ok, and logically it seems correct. self.desc = desc self.typ = type if type == "QDialog": QDialog.__init__(self,parent,name,modal,fl) elif type == "QTextEdit": QTextEdit.__init__(self, parent, name) elif type == "QFrame": QFrame.__init__(self,parent,name) else: print "don't know about type == %r" % (type,) self.image1 = QPixmap() self.image1.loadFromData(image1_data,"PNG") # should be: title_icon #### self.image3 = QPixmap() self.image3.loadFromData(image3_data,"PNG") self.image4 = QPixmap() self.image4.loadFromData(image4_data,"PNG") self.image5 = QPixmap() self.image5.loadFromData(image5_data,"PNG") self.image6 = QPixmap() self.image6.loadFromData(image6_data,"PNG") self.image7 = QPixmap() self.image7.loadFromData(image7_data,"PNG") self.image0 = QPixmap(image0_data) # should be: border_icon #### self.image2 = QPixmap(image2_data) # should be: sponsor_pixmap #### try: ####@@@@ title_icon_name = self.desc.options.get('title_icon') border_icon_name = self.desc.options.get('border_icon') if title_icon_name: self.image1 = imagename_to_pixmap(title_icon_name) ###@@@ pass icon_path ###@@@ import imagename_to_pixmap or use env function # or let that func itself be an arg, or have an env arg for it ###e rename it icon_name_to_pixmap, or find_icon? (the latter only if it's ok if it returns an iconset) ###e use iconset instead? if border_icon_name: self.image0 = imagename_to_pixmap(border_icon_name) except: print_compact_traceback("bug in icon-setting code, using fallback icons: ") pass if not name: self.setName("parameter_dialog_or_frame") ### ###k guess this will need: if type == 'QDialog' self.setIcon(self.image0) # should be: border_icon #### nanotube_dialogLayout = QVBoxLayout(self,0,0,"nanotube_dialogLayout") self.heading_frame = QFrame(self,"heading_frame") self.heading_frame.setPaletteBackgroundColor(QColor(122,122,122)) self.heading_frame.setFrameShape(QFrame.NoFrame) self.heading_frame.setFrameShadow(QFrame.Plain) heading_frameLayout = QHBoxLayout(self.heading_frame,0,3,"heading_frameLayout") self.heading_pixmap = QLabel(self.heading_frame,"heading_pixmap") self.heading_pixmap.setSizePolicy(QSizePolicy(QSizePolicy.Fixed,QSizePolicy.Fixed,0,0,self.heading_pixmap.sizePolicy().hasHeightForWidth())) self.heading_pixmap.setPixmap(self.image1) # should be: title_icon #### self.heading_pixmap.setScaledContents(1) heading_frameLayout.addWidget(self.heading_pixmap) self.heading_label = QLabel(self.heading_frame,"heading_label") self.heading_label.setPaletteForegroundColor(QColor(255,255,255)) heading_label_font = QFont(self.heading_label.font()) heading_label_font.setPointSize(12) heading_label_font.setBold(1) self.heading_label.setFont(heading_label_font) heading_frameLayout.addWidget(self.heading_label) nanotube_dialogLayout.addWidget(self.heading_frame) self.body_frame = QFrame(self,"body_frame") self.body_frame.setFrameShape(QFrame.StyledPanel) self.body_frame.setFrameShadow(QFrame.Raised) body_frameLayout = QVBoxLayout(self.body_frame,3,3,"body_frameLayout") self.sponsor_frame = QFrame(self.body_frame,"sponsor_frame") self.sponsor_frame.setPaletteBackgroundColor(QColor(255,255,255)) self.sponsor_frame.setFrameShape(QFrame.StyledPanel) self.sponsor_frame.setFrameShadow(QFrame.Raised) sponsor_frameLayout = QHBoxLayout(self.sponsor_frame,0,0,"sponsor_frameLayout") self.sponsor_btn = QPushButton(self.sponsor_frame,"sponsor_btn") self.sponsor_btn.setAutoDefault(0) #bruce 060703 bugfix self.sponsor_btn.setSizePolicy(QSizePolicy(QSizePolicy.Preferred,QSizePolicy.Preferred,0,0,self.sponsor_btn.sizePolicy().hasHeightForWidth())) self.sponsor_btn.setPaletteBackgroundColor(QColor(255,255,255)) self.sponsor_btn.setPixmap(self.image2) # should be: sponsor_pixmap #### [also we'll need to support >1 sponsor] self.sponsor_btn.setFlat(1) sponsor_frameLayout.addWidget(self.sponsor_btn) body_frameLayout.addWidget(self.sponsor_frame) layout59 = QHBoxLayout(None,0,6,"layout59") left_spacer = QSpacerItem(20,20,QSizePolicy.Expanding,QSizePolicy.Minimum) layout59.addItem(left_spacer) self.done_btn = QToolButton(self.body_frame,"done_btn") self.done_btn.setIcon(QIcon(self.image3)) layout59.addWidget(self.done_btn) self.abort_btn = QToolButton(self.body_frame,"abort_btn") self.abort_btn.setIcon(QIcon(self.image4)) layout59.addWidget(self.abort_btn) self.preview_btn = QToolButton(self.body_frame,"preview_btn") self.preview_btn.setIcon(QIcon(self.image5)) layout59.addWidget(self.preview_btn) self.whatsthis_btn = QToolButton(self.body_frame,"whatsthis_btn") self.whatsthis_btn.setIcon(QIcon(self.image6)) layout59.addWidget(self.whatsthis_btn) right_spacer = QSpacerItem(20,20,QSizePolicy.Expanding,QSizePolicy.Minimum) layout59.addItem(right_spacer) body_frameLayout.addLayout(layout59) self.groups = [] self.param_getters = {} # map from param name to get-function (which gets current value out of its widget or controller) for group_desc in self.desc.kids('group'): # == start parameters_grpbox ### this will differ for Windows style header_refs = [] # keep python refcounted refs to all objects we make (at least the ones pyuic stored in self attrs) self.parameters_grpbox = QGroupBox(self.body_frame,"parameters_grpbox") self.parameters_grpbox.setFrameShape(QGroupBox.StyledPanel) self.parameters_grpbox.setFrameShadow(QGroupBox.Sunken) self.parameters_grpbox.setMargin(0) self.parameters_grpbox.setColumnLayout(0,Qt.Vertical) self.parameters_grpbox.layout().setSpacing(1) self.parameters_grpbox.layout().setMargin(4) parameters_grpboxLayout = QVBoxLayout(self.parameters_grpbox.layout()) parameters_grpboxLayout.setAlignment(Qt.AlignTop) layout20 = QHBoxLayout(None,0,6,"layout20") self.nt_parameters_grpbtn = QPushButton(self.parameters_grpbox,"nt_parameters_grpbtn") self.nt_parameters_grpbtn.setSizePolicy(QSizePolicy(QSizePolicy.Minimum,QSizePolicy.Fixed,0,0,self.nt_parameters_grpbtn.sizePolicy().hasHeightForWidth())) self.nt_parameters_grpbtn.setMaximumSize(QSize(16,16)) self.nt_parameters_grpbtn.setAutoDefault(0) self.nt_parameters_grpbtn.setIcon(QIcon(self.image7)) ### not always right, but doesn't matter self.nt_parameters_grpbtn.setFlat(1) layout20.addWidget(self.nt_parameters_grpbtn) self.parameters_grpbox_label = QLabel(self.parameters_grpbox,"parameters_grpbox_label") self.parameters_grpbox_label.setSizePolicy(QSizePolicy(QSizePolicy.Preferred,QSizePolicy.Minimum,0,0,self.parameters_grpbox_label.sizePolicy().hasHeightForWidth())) self.parameters_grpbox_label.setAlignment(QLabel.AlignVCenter) layout20.addWidget(self.parameters_grpbox_label) gbx_spacer1 = QSpacerItem(67,16,QSizePolicy.Expanding,QSizePolicy.Minimum) layout20.addItem(gbx_spacer1) parameters_grpboxLayout.addLayout(layout20) nt_parameters_body_layout = QGridLayout(None,1,1,0,6,"nt_parameters_body_layout") ### what is 6 -- is it related to number of items??? # is it 6 in all the ones we got, but that could be a designer error so i better look it up sometime. # == start its kids # will use from above: self.parameters_grpbox, nt_parameters_body_layout nextrow = 0 # which row of the QGridLayout to start filling next (loop variable) hidethese = [] # set of objects to hide or show, when this group is closed or opened for param in group_desc.kids('parameter'): # param (a group subobj desc) is always a parameter, but we already plan to extend this beyond that, # so we redundantly test for this here. getter = None paramname = None # set these for use by uniform code at the end (e.g. for tooltips) editfield = None label = None if param.isa('parameter'): label = QLabel(self.parameters_grpbox,"members_label") label.setAlignment(QLabel.AlignVCenter | QLabel.AlignRight) nt_parameters_body_layout.addWidget(label,nextrow,0) hidethese.append(label) thisrow = nextrow nextrow += 1 #e following should be known in a place that knows the input language, not here paramname = param.options.get('name') or (param.args and param.args[0]) or "?" paramlabel = param.options.get('label') or paramname ##e wrong, label "" or none ought to be possible # QtGui.QApplication.translate(self.__class__.__name__, "xyz") label.setText(QtGui.QApplication.translate(self.__class__.__name__, paramlabel)) if param.isa('parameter', widget = 'combobox', type = ('str',None)): self.members_combox = QComboBox(0,self.parameters_grpbox,"members_combox") ###k what's 0? editfield = self.members_combox #### it probably needs a handler class, and then that could do this setup self.members_combox.clear() default = param.options.get('default', None) # None is not equal to any string thewidgetkid = param.kids('widget')[-1] # kluge; need to think what the desc method for this should be for item in thewidgetkid.kids('item'): itemval = item.args[0] itemtext = itemval self.members_combox.insertItem(QtGui.QApplication.translate(self.__class__.__name__, itemtext)) #k __tr ok?? if itemval == default: #k or itemtext? pass ##k i find no setItem in our py code, so not sure yet what to do for this. nt_parameters_body_layout.addWidget(self.members_combox,thisrow,1) hidethese.append(self.members_combox) getter = (lambda combobox = self.members_combox: str(combobox.currentText())) ##e due to __tr or non-str values, it might be better to use currentIndex and look it up in a table # (though whether __tr is good here might depend on what it's used for) elif param.isa('parameter', widget = ('lineedit', None), type = ('str',None)): # this covers explicit str|lineedit, and 3 default cases str, lineedit, neither. # (i.e. if you say parameter and nothing else, it's str lineedit by default.) self.length_linedit = QLineEdit(self.parameters_grpbox,"length_linedit") editfield = self.length_linedit nt_parameters_body_layout.addWidget(self.length_linedit,thisrow,1) hidethese.append(self.length_linedit) default = str(param.options.get('default', "")) self.length_linedit.setText(QtGui.QApplication.translate(self.__class__.__name__, default)) # __tr ok? getter = (lambda lineedit = self.length_linedit: str(lineedit.text())) elif param.isa('parameter', widget = ('lineedit', None), type = 'float'): self.length_linedit = QLineEdit(self.parameters_grpbox,"length_linedit") editfield = self.length_linedit nt_parameters_body_layout.addWidget(self.length_linedit,thisrow,1) hidethese.append(self.length_linedit) controller = FloatLineeditController_Qt(self, param, self.length_linedit) header_refs.append(controller) getter = controller.get_value elif param.isa('parameter', widget = ('spinbox', None), type = 'int') or \ param.isa('parameter', widget = ('spinbox'), type = None): self.chirality_N_spinbox = QSpinBox(self.parameters_grpbox,"chirality_N_spinbox") # was chirality_m_spinbox, now chirality_N_spinbox editfield = self.chirality_N_spinbox ### seems like Qt defaults for min and max are 0,100 -- way too small a range! if param.options.has_key('min') or 1: self.chirality_N_spinbox.setMinimum(param.options.get('min', -999999999)) # was 0 if param.options.has_key('max') or 1: self.chirality_N_spinbox.setMaximum(param.options.get('max', +999999999)) # wasn't in egcode, but needed self.chirality_N_spinbox.setValue(param.options.get('default', 0)) # was 5 ##e note: i suspect this default 0 should come from something that knows this desc grammar. suffix = param.options.get('suffix', '') if suffix: self.chirality_N_spinbox.setSuffix(QtGui.QApplication.translate(self.__class__.__name__, suffix)) else: self.chirality_N_spinbox.setSuffix(QString.null) # probably not needed nt_parameters_body_layout.addWidget(self.chirality_N_spinbox,thisrow,1) hidethese.append(self.chirality_N_spinbox) getter = self.chirality_N_spinbox.value # note: it also has .text, which includes suffix else: print "didn't match:",param ###e improve this # things done the same way for all kinds of param-editing widgets if 1: #bruce 060703 moved this down here, as bugfix # set tooltip (same one for editfield and label) tooltip = param.options.get('tooltip', '') ###e do it for more kinds of params; share the code somehow; do it in controller, or setup-aid? ###k QToolTip appropriateness; tooltip option might be entirely untested if tooltip and label: QToolTip.add(label, QtGui.QApplication.translate(self.__class__.__name__, tooltip)) if tooltip and editfield: QToolTip.add(editfield, QtGui.QApplication.translate(self.__class__.__name__, tooltip)) ##k ok?? review once not all params have same-row labels. if getter and paramname and paramname != '?': self.param_getters[paramname] = getter ### also bind these params to actions... continue # next param header_refs.extend( [self.parameters_grpbox, self.nt_parameters_grpbtn, self.parameters_grpbox_label] ) # now create the logic/control object for the group group = CollapsibleGroupController_Qt(self, group_desc, header_refs, hidethese, self.nt_parameters_grpbtn) ### maybe ask env for the class to use for this? self.groups.append(group) ### needed?? only for scanning the params, AFAIK -- oh, and to maintain a python refcount. # from languageChange: if 1: # i don't know if these are needed: self.parameters_grpbox.setTitle(QString.null) self.nt_parameters_grpbtn.setText(QString.null) self.parameters_grpbox_label.setText(QtGui.QApplication.translate(self.__class__.__name__, group_desc.args[0])) # was "Nanotube Parameters" ##e note that it's questionable in the syntax design for this property of a group (overall group label) # to be in that position (desc arg 0). # == end its kids parameters_grpboxLayout.addLayout(nt_parameters_body_layout) body_frameLayout.addWidget(self.parameters_grpbox) # == end parameters groupbox continue # next group nanotube_dialogLayout.addWidget(self.body_frame) spacer14 = QSpacerItem(20,20,QSizePolicy.Minimum,QSizePolicy.Expanding) nanotube_dialogLayout.addItem(spacer14) layout42 = QHBoxLayout(None,4,6,"layout42") btm_spacer = QSpacerItem(59,20,QSizePolicy.Expanding,QSizePolicy.Minimum) layout42.addItem(btm_spacer) self.cancel_btn = QPushButton(self,"cancel_btn") self.cancel_btn.setAutoDefault(0) #bruce 060703 bugfix layout42.addWidget(self.cancel_btn) self.ok_btn = QPushButton(self,"ok_btn") self.ok_btn.setAutoDefault(0) #bruce 060703 bugfix layout42.addWidget(self.ok_btn) nanotube_dialogLayout.addLayout(layout42) self.languageChange() self.resize(QSize(246,618).expandedTo(self.minimumSizeHint())) ### this size will need to be adjusted (guess -- it's only place overall size is set) qt4todo('self.clearWState(Qt.WState_Polished)') ## self.connect(self.nt_parameters_grpbtn,SIGNAL("clicked()"),self.toggle_nt_parameters_grpbtn) #### # new: for button, methodname in ((self.sponsor_btn, 'do_sponsor_btn'), #e generalize to more than one sponsor button (self.done_btn, 'do_done_btn'), (self.abort_btn, 'do_abort_btn'), (self.preview_btn, 'do_preview_btn'), (self.whatsthis_btn, 'do_whatsthis_btn'), (self.cancel_btn, 'do_cancel_btn'), (self.ok_btn, 'do_ok_btn')): if hasattr(self, methodname): self.connect(button, SIGNAL("clicked()"), getattr(self, methodname)) return
def __init__(self, *args): QTextEdit.__init__(self, *args)
def __init__(self, *args): QTextEdit.__init__(self, *args) self.lastFolder = QtGui.QDesktopServices.storageLocation( QtGui.QDesktopServices.DocumentsLocation) self.docName = None self.initDict()
class LedgerAuthDialog(QDialog): def __init__(self, handler, data): '''Ask user for 2nd factor authentication. Support text, security card and paired mobile methods. Use last method from settings, but support new pairing and downgrade. ''' QDialog.__init__(self, handler.top_level_window()) self.handler = handler self.txdata = data self.idxs = self.txdata[ 'keycardData'] if self.txdata['confirmationType'] > 1 else '' self.setMinimumWidth(600) self.setWindowTitle(_("Ledger Wallet Authentication")) self.cfg = copy.deepcopy(self.handler.win.wallet.get_keystore().cfg) self.dongle = self.handler.win.wallet.get_keystore().get_client( ).dongle self.ws = None self.pin = '' self.devmode = self.getDevice2FAMode() if self.devmode == 0x11 or self.txdata['confirmationType'] == 1: self.cfg['mode'] = 0 vbox = QVBoxLayout() self.setLayout(vbox) def on_change_mode(idx): if idx < 2 and self.ws: self.ws.stop() self.ws = None self.cfg[ 'mode'] = 0 if self.devmode == 0x11 else idx if idx > 0 else 1 if self.cfg['mode'] > 1 and self.cfg['pair'] and not self.ws: self.req_validation() if self.cfg['mode'] > 0: self.handler.win.wallet.get_keystore().cfg = self.cfg self.handler.win.wallet.save_keystore() self.update_dlg() def add_pairing(): self.do_pairing() def return_pin(): self.pin = self.pintxt.text( ) if self.txdata['confirmationType'] == 1 else self.cardtxt.text() if self.cfg['mode'] == 1: self.pin = ''.join(chr(int(str(i), 16)) for i in self.pin) self.accept() self.modebox = QWidget() modelayout = QHBoxLayout() self.modebox.setLayout(modelayout) modelayout.addWidget(QLabel(_("Method:"))) self.modes = QComboBox() modelayout.addWidget(self.modes, 2) self.addPair = QPushButton(_("Pair")) self.addPair.setMaximumWidth(60) modelayout.addWidget(self.addPair) modelayout.addStretch(1) self.modebox.setMaximumHeight(50) vbox.addWidget(self.modebox) self.populate_modes() self.modes.currentIndexChanged.connect(on_change_mode) self.addPair.clicked.connect(add_pairing) self.helpmsg = QTextEdit() self.helpmsg.setStyleSheet( "QTextEdit { background-color: lightgray; }") self.helpmsg.setReadOnly(True) vbox.addWidget(self.helpmsg) self.pinbox = QWidget() pinlayout = QHBoxLayout() self.pinbox.setLayout(pinlayout) self.pintxt = QLineEdit() self.pintxt.setEchoMode(2) self.pintxt.setMaxLength(4) self.pintxt.returnPressed.connect(return_pin) pinlayout.addWidget(QLabel(_("Enter PIN:"))) pinlayout.addWidget(self.pintxt) pinlayout.addWidget(QLabel(_("NOT DEVICE PIN - see above"))) pinlayout.addStretch(1) self.pinbox.setVisible(self.cfg['mode'] == 0) vbox.addWidget(self.pinbox) self.cardbox = QWidget() card = QVBoxLayout() self.cardbox.setLayout(card) self.addrtext = QTextEdit() self.addrtext.setStyleSheet( "QTextEdit { color:blue; background-color:lightgray; padding:15px 10px; border:none; font-size:20pt; }" ) self.addrtext.setReadOnly(True) self.addrtext.setMaximumHeight(120) card.addWidget(self.addrtext) def pin_changed(s): if len(s) < len(self.idxs): i = self.idxs[len(s)] addr = self.txdata['address'] addr = addr[:i] + '<u><b>' + addr[i:i + 1] + '</u></b>' + addr[i + 1:] self.addrtext.setHtml(str(addr)) else: self.addrtext.setHtml(_("Press Enter")) pin_changed('') cardpin = QHBoxLayout() cardpin.addWidget(QLabel(_("Enter PIN:"))) self.cardtxt = QLineEdit() self.cardtxt.setEchoMode(2) self.cardtxt.setMaxLength(len(self.idxs)) self.cardtxt.textChanged.connect(pin_changed) self.cardtxt.returnPressed.connect(return_pin) cardpin.addWidget(self.cardtxt) cardpin.addWidget(QLabel(_("NOT DEVICE PIN - see above"))) cardpin.addStretch(1) card.addLayout(cardpin) self.cardbox.setVisible(self.cfg['mode'] == 1) vbox.addWidget(self.cardbox) self.pairbox = QWidget() pairlayout = QVBoxLayout() self.pairbox.setLayout(pairlayout) pairhelp = QTextEdit(helpTxt[5]) pairhelp.setStyleSheet("QTextEdit { background-color: lightgray; }") pairhelp.setReadOnly(True) pairlayout.addWidget(pairhelp, 1) self.pairqr = QRCodeWidget() pairlayout.addWidget(self.pairqr, 4) self.pairbox.setVisible(False) vbox.addWidget(self.pairbox) self.update_dlg() if self.cfg['mode'] > 1 and not self.ws: self.req_validation() def populate_modes(self): self.modes.blockSignals(True) self.modes.clear() self.modes.addItem( _("Summary Text PIN (requires dongle replugging)" ) if self.txdata['confirmationType'] == 1 else _("Summary Text PIN is Disabled")) if self.txdata['confirmationType'] > 1: self.modes.addItem(_("Security Card Challenge")) if not self.cfg['pair']: self.modes.addItem(_("Mobile - Not paired")) else: self.modes.addItem(_("Mobile - %s") % self.cfg['pair'][1]) self.modes.blockSignals(False) def update_dlg(self): self.modes.setCurrentIndex(self.cfg['mode']) self.modebox.setVisible(True) self.addPair.setText( _("Pair") if not self.cfg['pair'] else _("Re-Pair")) self.addPair.setVisible(self.txdata['confirmationType'] > 2) self.helpmsg.setText( helpTxt[self.cfg['mode'] if self.cfg['mode'] < 2 else 2 if self. cfg['pair'] else 4]) self.helpmsg.setMinimumHeight(180 if self.txdata['confirmationType'] == 1 else 100) self.pairbox.setVisible(False) self.helpmsg.setVisible(True) self.pinbox.setVisible(self.cfg['mode'] == 0) self.cardbox.setVisible(self.cfg['mode'] == 1) self.pintxt.setFocus( True) if self.cfg['mode'] == 0 else self.cardtxt.setFocus(True) self.setMaximumHeight(200) def do_pairing(self): rng = os.urandom(16) pairID = rng.encode('hex') + hashlib.sha256(rng).digest()[0].encode( 'hex') self.pairqr.setData(pairID) self.modebox.setVisible(False) self.helpmsg.setVisible(False) self.pinbox.setVisible(False) self.cardbox.setVisible(False) self.pairbox.setVisible(True) self.pairqr.setMinimumSize(300, 300) if self.ws: self.ws.stop() self.ws = LedgerWebSocket(self, pairID) self.ws.pairing_done.connect(self.pairing_done) self.ws.start() def pairing_done(self, data): if data is not None: self.cfg['pair'] = [data['pairid'], data['name'], data['platform']] self.cfg['mode'] = 2 self.handler.win.wallet.get_keystore().cfg = self.cfg self.handler.win.wallet.save_keystore() self.pin = 'paired' self.accept() def req_validation(self): if self.cfg['pair'] and 'secureScreenData' in self.txdata: if self.ws: self.ws.stop() self.ws = LedgerWebSocket(self, self.cfg['pair'][0], self.txdata) self.ws.req_updated.connect(self.req_updated) self.ws.start() def req_updated(self, pin): if pin == 'accepted': self.helpmsg.setText(helpTxt[3]) else: self.pin = str(pin) self.accept() def getDevice2FAMode(self): apdu = [0xe0, 0x24, 0x01, 0x00, 0x00, 0x01] # get 2fa mode try: mode = self.dongle.exchange(bytearray(apdu)) return mode except BTChipException, e: debug_msg('Device getMode Failed') return 0x11
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 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)
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)
class PrefsViewerDialog(SizePersistedDialog): def __init__(self, gui, namespace): SizePersistedDialog.__init__(self, gui, 'Prefs Viewer dialog') self.setWindowTitle('Preferences for: ' + namespace) self.gui = gui self.db = gui.current_db self.namespace = namespace self._init_controls() self.resize_dialog() self._populate_settings() if self.keys_list.count(): self.keys_list.setCurrentRow(0) def _init_controls(self): layout = QVBoxLayout(self) self.setLayout(layout) ml = QHBoxLayout() layout.addLayout(ml, 1) self.keys_list = QListWidget(self) self.keys_list.setSelectionMode(QAbstractItemView.SingleSelection) self.keys_list.setFixedWidth(150) self.keys_list.setAlternatingRowColors(True) ml.addWidget(self.keys_list) self.value_text = QTextEdit(self) self.value_text.setTabStopWidth(24) self.value_text.setReadOnly(False) ml.addWidget(self.value_text, 1) button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) button_box.accepted.connect(self._apply_changes) button_box.rejected.connect(self.reject) self.clear_button = button_box.addButton('Clear', QDialogButtonBox.ResetRole) self.clear_button.setIcon(get_icon('trash.png')) self.clear_button.setToolTip('Clear all settings for this plugin') self.clear_button.clicked.connect(self._clear_settings) layout.addWidget(button_box) def _populate_settings(self): self.keys_list.clear() ns_prefix = self._get_ns_prefix() keys = sorted([ k[len(ns_prefix):] for k in self.db.prefs.iterkeys() if k.startswith(ns_prefix) ]) for key in keys: self.keys_list.addItem(key) self.keys_list.setMinimumWidth(self.keys_list.sizeHintForColumn(0)) self.keys_list.currentRowChanged[int].connect( self._current_row_changed) def _current_row_changed(self, new_row): if new_row < 0: self.value_text.clear() return key = unicode(self.keys_list.currentItem().text()) val = self.db.prefs.get_namespaced(self.namespace, key, '') self.value_text.setPlainText(self.db.prefs.to_raw(val)) def _get_ns_prefix(self): return 'namespaced:%s:' % self.namespace def _apply_changes(self): from calibre.gui2.dialogs.confirm_delete import confirm message = '<p>Are you sure you want to change your settings in this library for this plugin?</p>' \ '<p>Any settings in other libraries or stored in a JSON file in your calibre plugins ' \ 'folder will not be touched.</p>' \ '<p>You must restart calibre afterwards.</p>' if not confirm(message, self.namespace + '_clear_settings', self): return val = self.db.prefs.raw_to_object( unicode(self.value_text.toPlainText())) key = unicode(self.keys_list.currentItem().text()) self.db.prefs.set_namespaced(self.namespace, key, val) restart = prompt_for_restart( self, 'Settings changed', '<p>Settings for this plugin in this library have been changed.</p>' '<p>Please restart calibre now.</p>') self.close() if restart: self.gui.quit(restart=True) def _clear_settings(self): from calibre.gui2.dialogs.confirm_delete import confirm message = '<p>Are you sure you want to clear your settings in this library for this plugin?</p>' \ '<p>Any settings in other libraries or stored in a JSON file in your calibre plugins ' \ 'folder will not be touched.</p>' \ '<p>You must restart calibre afterwards.</p>' if not confirm(message, self.namespace + '_clear_settings', self): return ns_prefix = self._get_ns_prefix() keys = [k for k in self.db.prefs.iterkeys() if k.startswith(ns_prefix)] for k in keys: del self.db.prefs[k] self._populate_settings() restart = prompt_for_restart( self, 'Settings deleted', '<p>All settings for this plugin in this library have been cleared.</p>' '<p>Please restart calibre now.</p>') self.close() if restart: self.gui.quit(restart=True)
def __init__(self, parentWidget, label='', labelColumn=0, spanWidth=False, addToParent=True, permit_enter_keystroke=True): """ Appends a QTextEdit (Qt) widget to the bottom of I{parentWidget}, a Property Manager group box. The QTextEdit is empty (has no text) by default. Use insertHtml() to insert HTML text into the TextEdit. @param parentWidget: the parent group box containing this widget. @type parentWidget: PM_GroupBox @param label: The label that appears to the left of (or above) the spin box. To suppress the label, set I{label} to an empty string. @type label: str @param spanWidth: If True, the spin box and its label will span the width of the group box. The label will appear directly above the spin box and is left justified. @type spanWidth: bool @param addToParent: If True (the default), self will be added to parentWidget by passing it to parentWidget.addPmWidget. If False, self will not be added to parentWidget. Typically, when this is False, the caller will add self to parent in some other way. @type addToParent: bool @param permit_enter_keystroke: If set to True, this PM_textEdit can have multiple lines. Otherwise, it will block the 'Enter' keypress within the text editor. Note that caller needs to make sure that linewrapping option is appropriately set, (in addition to this flag) so as to permit/ not permit multiple lines in the text edit. @see: U{B{QTextEdit}<http://doc.trolltech.com/4/qtextedit.html>} """ if 0: # Debugging code print "QTextEdit.__init__():" print " label =", label print " labelColumn =", labelColumn print " spanWidth =", spanWidth QTextEdit.__init__(self) self.parentWidget = parentWidget self.label = label self.labelColumn = labelColumn self.spanWidth = spanWidth self._permit_enter_keystroke = permit_enter_keystroke if label: # Create this widget's QLabel. self.labelWidget = QLabel() self.labelWidget.setText(label) self._setHeight() # Default height is 4 lines high. ## from PM.PM_MessageGroupBox import PM_MessageGroupBox ## if isinstance(parentWidget, PM_MessageGroupBox): ## # Add to parentWidget's vBoxLayout if <parentWidget> is a MessageGroupBox. ## parentWidget.vBoxLayout.addWidget(self) ## # We should be calling the PM's getMessageTextEditPalette() method, ## # but that will take some extra work which I will do soon. Mark 2007-06-21 ## self.setPalette(getPalette( None, ## QPalette.Base, ## pmMessageBoxColor)) ## self.setReadOnly(True) ## #@self.labelWidget = None # Never has one. Mark 2007-05-31 ## parentWidget._widgetList.append(self) ## parentWidget._rowCount += 1 ## else: ## parentWidget.addPmWidget(self) # bruce 071103 refactored the above into the new addToParent option and # code added to PM_MessageGroupBox.__init__ after it calls this method. if addToParent: parentWidget.addPmWidget(self) return
class PrefsViewerDialog(SizePersistedDialog): def __init__(self, gui, namespace): SizePersistedDialog.__init__(self, gui, 'Prefs Viewer dialog') self.setWindowTitle('Preferences for: '+namespace) self.gui = gui self.db = gui.current_db self.namespace = namespace self._init_controls() self.resize_dialog() self._populate_settings() if self.keys_list.count(): self.keys_list.setCurrentRow(0) def _init_controls(self): layout = QVBoxLayout(self) self.setLayout(layout) ml = QHBoxLayout() layout.addLayout(ml, 1) self.keys_list = QListWidget(self) self.keys_list.setSelectionMode(QAbstractItemView.SingleSelection) self.keys_list.setFixedWidth(150) self.keys_list.setAlternatingRowColors(True) ml.addWidget(self.keys_list) self.value_text = QTextEdit(self) self.value_text.setTabStopWidth(24) self.value_text.setReadOnly(True) ml.addWidget(self.value_text, 1) button_box = QDialogButtonBox(QDialogButtonBox.Ok) button_box.accepted.connect(self.accept) self.clear_button = button_box.addButton('Clear', QDialogButtonBox.ResetRole) self.clear_button.setIcon(get_icon('trash.png')) self.clear_button.setToolTip('Clear all settings for this plugin') self.clear_button.clicked.connect(self._clear_settings) layout.addWidget(button_box) def _populate_settings(self): self.keys_list.clear() ns_prefix = self._get_ns_prefix() keys = sorted([k[len(ns_prefix):] for k in self.db.prefs.iterkeys() if k.startswith(ns_prefix)]) for key in keys: self.keys_list.addItem(key) self.keys_list.setMinimumWidth(self.keys_list.sizeHintForColumn(0)) self.keys_list.currentRowChanged[int].connect(self._current_row_changed) def _current_row_changed(self, new_row): if new_row < 0: self.value_text.clear() return key = unicode(self.keys_list.currentItem().text()) val = self.db.prefs.get_namespaced(self.namespace, key, '') self.value_text.setPlainText(self.db.prefs.to_raw(val)) def _get_ns_prefix(self): return 'namespaced:%s:'% self.namespace def _clear_settings(self): from calibre.gui2.dialogs.confirm_delete import confirm message = '<p>Are you sure you want to clear your settings in this library for this plugin?</p>' \ '<p>Any settings in other libraries or stored in a JSON file in your calibre plugins ' \ 'folder will not be touched.</p>' \ '<p>You must restart calibre afterwards.</p>' if not confirm(message, self.namespace+'_clear_settings', self): return ns_prefix = self._get_ns_prefix() keys = [k for k in self.db.prefs.iterkeys() if k.startswith(ns_prefix)] for k in keys: del self.db.prefs[k] self._populate_settings() d = info_dialog(self, 'Settings deleted', '<p>All settings for this plugin in this library have been cleared.</p>' '<p>Please restart calibre now.</p>', show_copy_button=False) b = d.bb.addButton(_('Restart calibre now'), d.bb.AcceptRole) b.setIcon(QIcon(I('lt.png'))) d.do_restart = False def rf(): d.do_restart = True b.clicked.connect(rf) d.set_details('') d.exec_() b.clicked.disconnect() self.close() if d.do_restart: self.gui.quit(restart=True)
class DownloadDialog(QDialog): 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()) def config(self): self.do_user_config(parent=self) # Apply the changes # Not necessary, the downloader will obtain fresh config anyway... # self.label.setText(prefs['hello_world_msg']) def notify(self, message=None): if message is not None: # insertPlainText inserts at the beginning of the log area... self.log_area.append(message) sb = self.log_area.verticalScrollBar() sb.setValue(sb.maximum()) def download(self): prefs = self.prefs self.download_button.setEnabled(False) self.conf_button.setEnabled(False) downloader = BeamEbooksDownloader(self.prefs, self.version, caller=self) self.notify("Downloader is: %s" % (downloader)) # Loop over all accounts until we have support for selection for account_id in prefs[prefs.ACCOUNTS]: account = prefs[prefs.ACCOUNTS][account_id] account[prefs.ACCOUNT_ID] = account_id if account[prefs.ENABLED]: self.enqueue(account, downloader) self.hide() def enqueue(self, account, downloader): prefs = self.prefs self.notify("Account: '%s'" % account[prefs.USERNAME]) # downloader.login(account) func = 'arbitrary_n' # func = 'arbitrary' cpus = self.gui.job_manager.server.pool_size print "CPUs: %s" % (cpus) args = [ 'calibre_plugins.beam_ebooks_downloader.jobs', 'do_obtain_new_books', (cpus, account) ] desc = 'Beam EBooks Downloader' job = self.gui.job_manager.run_job(Dispatcher(self._done), func, args=args, description=desc) print "Job: %s" % (job) self.notify(" Start parsing OPDS catalog") # if downloader.successful_login == False: # self.notify("Failed to log in...") # else: # self.notify("Scanning (beam) private library now...") # downloader.recursive_descent(norms(prefs[prefs.URLBASE])) def _done(self, job): print "Done Downloading" print "Self: %s" % (self) print "Job: %s" % (job) # print " Result: %s" % (job.result) # print " Result: %s" % (len(job.result)) self.notify(" Finished download catalog...") if job.result is not None: for entry in job.result: beamebooks_id = entry['id'] message = "<br/>Ebook # %s" % (beamebooks_id) self.notify(message) self.download_button.setEnabled(True) self.conf_button.setEnabled(True) payload = job.result msg = "Parsing OPDS Catalog complete, found %s ebooks." % (len( job.result)) msg = msg + "<br>Do you want to import the books into the library?" # question_dialog self.gui.proceed_question(self._add_ebooks, payload, job.details, 'OPDS Download Log', 'OPDS parse complete', msg, show_copy_button=False) def _done_2(self, job): print "Done Downloading" print "Self: %s" % (self) print "Job: %s" % (job) # print " Result: %s" % (job.result) # print " Result: %s" % (len(job.result)) self.notify(" Finished download book...") def _add_ebooks(self, payload): print "Done Downloading, Step 2" print "Self: %s" % (self) # print "Payload: %s" % (payload) # Printing the complete payload at once gives [IOError 12] - Out of space (memory, that is...) self.notify(" Finished adding books...") for entry in payload: print "Entry: %s" % entry # TODO allow for checkbox list of entries to download func = 'arbitrary_n' # func = 'arbitrary' cpus = self.gui.job_manager.server.pool_size print "CPUs: %s" % (cpus) args = [ 'calibre_plugins.beam_ebooks_downloader.jobs', 'do_download_book', (cpus, entry) ] desc = 'Beam EBooks Downloader' job = self.gui.job_manager.run_job(Dispatcher(self._done_2), func, args=args, description=desc) print "Job: %s" % (job)