class IssueBook(QWidget): session = Session() def __init__(self): super().__init__() self.title = "Admin Panel" self.width = 640 self.height = 400 self.top = 100 self.left = 100 self.initUI() def initUI(self): self.lblIssuedTo = QLabel(self) self.lblIssuedTo.setText("Issued To") self.lblIssuedTo.move(180, 100) self.lblBook = QLabel(self) self.lblBook.setText("Book") self.lblBook.move(180, 130) self.issuedTo = QComboBox(self) self.issuedTo.move(220, 100) self.populateUsers(self.issuedTo) self.Book = QComboBox(self) self.Book.move(220, 130) self.populateBooks(self.Book) self.saveBtn = QPushButton("Issue Book", self) self.saveBtn.move(250, 200) self.saveBtn.clicked.connect(self.saveIssue) self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, self.width, self.height) self.show() def populateUsers(self, combobox): users = self.session.query(User).filter(User.role == "Student") for user in users: combobox.addItem(user.username) def populateBooks(self, combobox): books = self.session.query(Book).all() for book in books: combobox.addItem(book.name) def saveIssue(self): issued_to = self.session.query(User).filter( User.username == self.issuedTo.currentText()).first() book = self.session.query(Book).filter( Book.name == self.Book.currentText()).first() try: issued = Issue(issued_to.id, book.id) self.session.add(issued) self.session.commit() except: print("An error occured")
def set_module_layout(self, module_name: str = "screenshot"): """Sets the layout which can execute modules.""" self._current_layout = "Module" self._clear_layout() command_type_label = QLabel("Command type: ") command_type_combobox = QComboBox() command_type_combobox.addItem("Module") command_type_combobox.addItem("Shell") module_label = QLabel("Module name: ") module_combobox = QComboBox() for module_name in modules.get_names(): module_combobox.addItem(module_name) module_combobox.currentTextChanged.connect(self._on_module_change) command_type_combobox.currentTextChanged.connect( self._on_command_type_change) self._layout.setColumnStretch(1, 1) self._layout.addWidget(command_type_label, 0, 0) self._layout.addWidget(command_type_combobox, 0, 1) self._layout.addWidget(module_label, 1, 0) self._layout.addWidget(module_combobox, 1, 1) # Module layout cached_module = modules.get_module(module_name) if not cached_module: cached_module = modules.load_module(module_name, self._module_view, self._model) input_fields = [] for option_name in cached_module.get_setup_messages(): input_field = QLineEdit() self._sub_layout.addWidget(QLabel(option_name)) self._sub_layout.addWidget(input_field) input_fields.append(input_field) run_button = QPushButton("Run") run_button.setMaximumWidth(250) run_button.setMinimumHeight(25) run_button.pressed.connect(lambda: self._on_module_run( module_combobox.currentText(), input_fields)) self._sub_layout.addWidget(QLabel("")) self._sub_layout.addWidget(run_button) self._sub_layout.setContentsMargins(0, 15, 0, 0) self._layout.addLayout(self._sub_layout, self._layout.rowCount() + 2, 0, 1, 2) self._on_module_change(module_combobox.currentText())
class ConfigWidget(QWidget): def __init__(self, plugin_action): QWidget.__init__(self) self.plugin_action = plugin_action layout = QVBoxLayout(self) self.setLayout(layout) # copy of preferences self.tmpserials = plugin_prefs['kobo_serials'] combo_label = QLabel(_('When should Obok try to insert EPUBs into existing calibre entries?'), self) layout.addWidget(combo_label) self.find_homes = QComboBox() self.find_homes.setToolTip(_('<p>Default behavior when duplicates are detected. None of the choices will cause calibre ebooks to be overwritten')) layout.addWidget(self.find_homes) self.find_homes.addItems([_('Ask'), _('Always'), _('Never')]) index = self.find_homes.findText(plugin_prefs['finding_homes_for_formats']) self.find_homes.setCurrentIndex(index) self.serials_button = QtGui.QPushButton(self) self.serials_button.setToolTip(_(u"Click to manage Kobo serial numbers for Kobo ebooks")) self.serials_button.setText(u"Kobo devices serials") self.serials_button.clicked.connect(self.edit_serials) layout.addWidget(self.serials_button) def edit_serials(self): d = ManageKeysDialog(self,u"Kobo device serial numbers",self.tmpserials, AddSerialDialog) d.exec_() def save_settings(self): plugin_prefs['finding_homes_for_formats'] = unicode(self.find_homes.currentText()) plugin_prefs['kobo_serials'] = self.tmpserials
class HashGeneratorTab(QWidget): def __init__(self, console): super().__init__() self.layout = QVBoxLayout(self) self.titleText = QLabel("<H1>HashGenerator</H1>\nText Einfügen") self.layout.addWidget(self.titleText) self.textField = QTextEdit() self.layout.addWidget(self.textField) self.selectHash = QComboBox() self.selectHash.addItems( ["SHA1", "SHA224", "SHA256", "SHA384", "SHA512", "MD5"]) self.selectHash.setCurrentText("SHA512") self.layout.addWidget(self.selectHash) self.genButton = QPushButton("Generiere Hash") self.hashGenerator = HashGenerator() self.genButton.clicked.connect(self.generateHash) self.layout.addWidget(self.genButton) self.output = QTextEdit("Output") self.output.setReadOnly(True) self.layout.addWidget(self.output) self.layout.addStretch(1) def generateHash(self): text = self.textField.toPlainText() hash = self.selectHash.currentText() self.output.setText(self.hashGenerator.getHash(text, hash))
class ConfigWidget(QWidget): def __init__(self): QWidget.__init__(self) self.l = QVBoxLayout() self.setLayout(self.l) self.label = QLabel("Voice") self.l.addWidget(self.label) self.voiceOptions = QComboBox() self.l.addWidget(self.voiceOptions) self.rateLabel = QLabel("Rate (-10 to 10)") self.l.addWidget(self.rateLabel) self.rateEdit = QLineEdit() self.rateEdit.setValidator(QIntValidator(-10, 10)) self.rateEdit.setText(str(prefs['rate'])) self.l.addWidget(self.rateEdit) self.volumeLabel = QLabel("Volume (0 to 100)") self.volumeEdit = QLineEdit() self.volumeEdit.setValidator(QIntValidator(0, 100)) self.volumeEdit.setText(str(prefs['volume'])) self.l.addWidget(self.volumeLabel) self.l.addWidget(self.volumeEdit) import win32com.client self.spVoice = win32com.client.Dispatch("SAPI.SpVoice") voices = self.spVoice.GetVoices("", "") for i in range(voices.Count): self.voiceOptions.addItem(voices.Item(i).GetDescription()) if voices.Item(i).GetDescription() == prefs['voice']: self.voiceOptions.setCurrentIndex(i) self.pauseHotKey = HotkeyWidget(prefs, "pause", "Enable Pause/Play hotkey") self.l.addWidget(self.pauseHotKey) self.stopHotKey = HotkeyWidget(prefs, "stop", "Enable Stop hotkey") self.l.addWidget(self.stopHotKey) self.selectHotKey = HotkeyWidget(prefs, "select", "Enable Select Mode hotkey") self.l.addWidget(self.selectHotKey) def save_settings(self): from calibre_plugins.tts_ebook_viewer.hotkeys import keycodes prefs['voice'] = unicode(self.voiceOptions.currentText()) prefs['rate'] = int(self.rateEdit.text()) prefs['volume'] = int(self.volumeEdit.text()) self.pauseHotKey.save_settings(prefs) self.stopHotKey.save_settings(prefs) self.selectHotKey.save_settings(prefs)
class ConfigWidget(QWidget): def __init__(self): super().__init__() vl = QVBoxLayout() self.setLayout(vl) self.search_people_box = QCheckBox('Search people') self.search_people_box.setChecked(prefs['search_people']) vl.addWidget(self.search_people_box) model_size_hl = QHBoxLayout() model_size_label = QLabel('spaCy model size') self.model_size_box = QComboBox() for size in ['sm', 'md', 'lg']: self.model_size_box.addItem(size) self.model_size_box.setCurrentText(prefs['model_size']) model_size_hl.addWidget(model_size_label) model_size_hl.addWidget(self.model_size_box) vl.addLayout(model_size_hl) zh_wiki_hl = QHBoxLayout() zh_label = QLabel('Chinese Wikipedia variant') self.zh_wiki_box = QComboBox() zh_variants = { 'cn': '大陆简体', 'hk': '香港繁體', 'mo': '澳門繁體', 'my': '大马简体', 'sg': '新加坡简体', 'tw': '臺灣正體' } for variant, text in zh_variants.items(): self.zh_wiki_box.addItem(text, variant) self.zh_wiki_box.setCurrentText(zh_variants[prefs['zh_wiki_variant']]) zh_wiki_hl.addWidget(zh_label) zh_wiki_hl.addWidget(self.zh_wiki_box) vl.addLayout(zh_wiki_hl) donate_button = QPushButton('Donate') donate_button.clicked.connect(self.donate) vl.addWidget(donate_button) github_button = QPushButton('Source code') github_button.clicked.connect(self.github) vl.addWidget(github_button) @staticmethod def donate(): webbrowser.open('https://liberapay.com/xxyzz/donate') def github(self): webbrowser.open('https://github.com/xxyzz/WordDumb') def save_settings(self): prefs['search_people'] = self.search_people_box.isChecked() prefs['model_size'] = self.model_size_box.currentText() prefs['zh_wiki_variant'] = self.zh_wiki_box.currentData()
class ConfigWidget(QWidget): def __init__(self, plugin_action): QWidget.__init__(self) self.plugin_action = plugin_action layout = QVBoxLayout(self) self.setLayout(layout) # copy of preferences self.tmpserials = plugin_prefs['kobo_serials'] self.kobodirectory = plugin_prefs['kobo_directory'] combo_label = QLabel( _('When should Obok try to insert EPUBs into existing calibre entries?' ), self) layout.addWidget(combo_label) self.find_homes = QComboBox() self.find_homes.setToolTip( _('<p>Default behavior when duplicates are detected. None of the choices will cause calibre ebooks to be overwritten' )) layout.addWidget(self.find_homes) self.find_homes.addItems([_('Ask'), _('Always'), _('Never')]) index = self.find_homes.findText( plugin_prefs['finding_homes_for_formats']) self.find_homes.setCurrentIndex(index) self.serials_button = QtGui.QPushButton(self) self.serials_button.setToolTip( _("Click to manage Kobo serial numbers for Kobo ebooks")) self.serials_button.setText("Kobo devices serials") self.serials_button.clicked.connect(self.edit_serials) layout.addWidget(self.serials_button) self.kobo_directory_button = QtGui.QPushButton(self) self.kobo_directory_button.setToolTip( _("Click to specify the Kobo directory")) self.kobo_directory_button.setText("Kobo directory") self.kobo_directory_button.clicked.connect(self.edit_kobo_directory) layout.addWidget(self.kobo_directory_button) def edit_serials(self): d = ManageKeysDialog(self, "Kobo device serial number", self.tmpserials, AddSerialDialog) d.exec_() def edit_kobo_directory(self): tmpkobodirectory = QFileDialog.getExistingDirectory( self, "Select Kobo directory", self.kobodirectory or "/home", QFileDialog.Option.ShowDirsOnly) if tmpkobodirectory != u"" and tmpkobodirectory is not None: self.kobodirectory = tmpkobodirectory def save_settings(self): plugin_prefs[ 'finding_homes_for_formats'] = self.find_homes.currentText() plugin_prefs['kobo_serials'] = self.tmpserials plugin_prefs['kobo_directory'] = self.kobodirectory
def __init__(self): super().__init__() self._layout = QVBoxLayout() host_label = QLabel("Server host (where EvilOSX will connect to):") self._host_field = QLineEdit() self._layout.addWidget(host_label) self._layout.addWidget(self._host_field) port_label = QLabel("Server port:") self._port_field = QLineEdit() self._layout.addWidget(port_label) self._layout.addWidget(self._port_field) live_label = QLabel( "Where should EvilOSX live? (Leave empty for ~/Library/Containers/.<RANDOM>): " ) self._live_field = QLineEdit() self._layout.addWidget(live_label) self._layout.addWidget(self._live_field) launcher_label = QLabel("Launcher name:") self._launcher_combobox = QComboBox() for launcher_name in launchers.get_names(): self._launcher_combobox.addItem(launcher_name) self._layout.addWidget(launcher_label) self._layout.addWidget(self._launcher_combobox) loader_label = QLabel("Loader name:") loader_combobox = QComboBox() self._loader_layout = QVBoxLayout() for loader_name in loaders.get_names(): loader_combobox.addItem(loader_name) self._layout.addWidget(loader_label) self._layout.addWidget(loader_combobox) loader_combobox.currentTextChanged.connect(self._set_on_loader_change) # Dynamically loaded loader layout self._layout.addLayout(self._loader_layout) self._set_on_loader_change(loader_combobox.currentText()) self._layout.setContentsMargins(10, 10, 10, 0) self._layout.setAlignment(Qt.AlignTop) self.setLayout(self._layout)
class editpopup(QWidget): user_id = "" session = Session() def __init__(self): super().__init__() self.title = "Edit Pop Up" self.width = 500 self.height = 500 self.top = 100 self.left = 100 self.initUI() def initUI(self): self.username = QLineEdit(self) self.username.move(200, 200) self.password = QLineEdit(self) self.password.move(200, 240) self.password.setEchoMode(QLineEdit.Password) self.role = QComboBox(self) self.role.addItem("Student") self.role.addItem("Librarian") self.role.addItem("Admin") self.role.move(200, 280) self.savechanges = QPushButton("Save changes", self) self.savechanges.move(250, 350) self.savechanges.clicked.connect(self.updateValues) self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, self.width, self.height) def setValues(self, user_id, user_name, user_password, user_role): self.user_id = user_id self.username.setText(user_name) self.password.setText(user_password) self.role.setCurrentText(user_role) def updateValues(self): result = self.session.query(User).filter( User.id == self.user_id).first() result.username = self.username.text() result.password = self.password.text() result.role = self.role.currentText() try: self.session.add(result) self.session.commit() self.close() except: print("Error occured while saving")
class AddUserUI(QMainWindow): def __init__(self): super().__init__() self.width = 640 self.height = 400 self.top = 100 self.left = 100 self.initUI() def initUI(self): self.labelUsername = QLabel(self) self.labelUsername.setText("Username") self.labelPassword = QLabel(self) self.labelPassword.setText("Password") self.labelRole = QLabel(self) self.labelRole.setText("Role") self.labelUsername.move(250, 100) self.labelPassword.move(250, 130) self.labelRole.move(250, 160) self.username = QLineEdit(self) self.username.move(300, 100) self.password = QLineEdit(self) self.password.setEchoMode(QLineEdit.Password) self.password.move(300, 130) self.role = QComboBox(self) self.role.addItem("Student") self.role.addItem("Librarian") self.role.addItem("Admin") self.role.move(300, 160) button = QPushButton("Add User", self) button.move(300, 190) button.clicked.connect(self.add_user) self.setWindowTitle("Add Users") self.setGeometry(self.left, self.top, self.width, self.height) def add_user(self): uname = self.username.text() password = self.password.text() role = self.role.currentText() print(uname) print(password) print(role) user = User(uname, password, role) session.add(user) session.commit() self.statusBar().showMessage("User Saved Successfully")
class ConfigWidget(QWidget): def __init__(self, plugin_action): QWidget.__init__(self) self.plugin_action = plugin_action layout = QVBoxLayout(self) self.setLayout(layout) combo_label = QLabel(_('When should Obok try to insert EPUBs into existing calibre entries?'), self) layout.addWidget(combo_label) self.find_homes = QComboBox() self.find_homes.setToolTip(_('<p>Default behavior when duplicates are detected. None of the choices will cause calibre ebooks to be overwritten')) layout.addWidget(self.find_homes) self.find_homes.addItems([_('Ask'), _('Always'), _('Never')]) index = self.find_homes.findText(plugin_prefs['finding_homes_for_formats']) self.find_homes.setCurrentIndex(index) def save_settings(self): plugin_prefs['finding_homes_for_formats'] = unicode(self.find_homes.currentText())
class ConfigWidget(QWidget): def __init__(self): QWidget.__init__(self) self.l = QVBoxLayout() self.setLayout(self.l) self.cbox1 = QCheckBox("Reformatting") self.cbox1.setChecked(prefs["reformat"]) self.l.addWidget(self.cbox1) self.cbox5 = QCheckBox("Paragraph by Ending mark") self.cbox5.setChecked(prefs["para_by_mark"]) self.l.addWidget(self.cbox5) self.cbox2 = QCheckBox("Pretty Quote char") self.cbox2.setChecked(prefs["pretty_quote"]) self.l.addWidget(self.cbox2) self.cbox3 = QCheckBox("Guess Chapter") self.cbox3.setChecked(prefs["guess_chapter"]) self.l.addWidget(self.cbox3) self.cbox4 = QCheckBox("Allow Empty Paragraph") self.cbox4.setChecked(prefs["insert_empty_paragraph"]) self.l.addWidget(self.cbox4) cl = QHBoxLayout() cl.addWidget(QLabel("Broken Word over lines")) self.combo1 = QComboBox() wbrk_list = ["None", "Pattern", "Naver"] self.combo1.addItems(wbrk_list) self.combo1.setCurrentIndex(wbrk_list.index(prefs["correct_word_break"])) cl.addWidget(self.combo1) self.l.addLayout(cl) def save_settings(self): prefs["reformat"] = self.cbox1.isChecked() prefs["para_by_mark"] = self.cbox5.isChecked() prefs["pretty_quote"] = self.cbox2.isChecked() prefs["guess_chapter"] = self.cbox3.isChecked() prefs["insert_empty_paragraph"] = self.cbox4.isChecked() prefs["correct_word_break"] = str(self.combo1.currentText())
class ConfigWidget(QWidget): def __init__(self, plugin_action): QWidget.__init__(self) self.plugin_action = plugin_action layout = QVBoxLayout(self) self.setLayout(layout) combo_label = QLabel(_("When should Obok try to insert EPUBs into existing calibre entries?"), self) layout.addWidget(combo_label) self.find_homes = QComboBox() self.find_homes.setToolTip( _( "<p>Default behavior when duplicates are detected. None of the choices will cause calibre ebooks to be overwritten" ) ) layout.addWidget(self.find_homes) self.find_homes.addItems([_("Ask"), _("Always"), _("Never")]) index = self.find_homes.findText(plugin_prefs["finding_homes_for_formats"]) self.find_homes.setCurrentIndex(index) def save_settings(self): plugin_prefs["finding_homes_for_formats"] = unicode(self.find_homes.currentText())
class AddTagDialog(QDialog): def __init__(self, parent, modal=True, flags=Qt.WindowFlags()): QDialog.__init__(self, parent, flags) self.setModal(modal) self.setWindowTitle("Add Tag") lo = QVBoxLayout(self) lo.setContentsMargins(10, 10, 10, 10) lo.setSpacing(5) # tag selector lo1 = QHBoxLayout() lo.addLayout(lo1) lo1.setSpacing(5) self.wtagsel = QComboBox(self) self.wtagsel.setEditable(True) wtagsel_lbl = QLabel("&Tag:", self) wtagsel_lbl.setBuddy(self.wtagsel) lo1.addWidget(wtagsel_lbl, 0) lo1.addWidget(self.wtagsel, 1) self.wtagsel.activated[int].connect(self._check_tag) self.wtagsel.editTextChanged['QString'].connect(self._check_tag_text) # value editor self.valedit = ValueTypeEditor(self) lo.addWidget(self.valedit) # buttons lo.addSpacing(10) lo2 = QHBoxLayout() lo.addLayout(lo2) lo2.setContentsMargins(0, 0, 0, 0) lo2.setContentsMargins(5, 5, 5, 5) self.wokbtn = QPushButton("OK", self) self.wokbtn.setMinimumWidth(128) self.wokbtn.clicked.connect(self.accept) self.wokbtn.setEnabled(False) cancelbtn = QPushButton("Cancel", self) cancelbtn.setMinimumWidth(128) cancelbtn.clicked.connect(self.reject) lo2.addWidget(self.wokbtn) lo2.addStretch(1) lo2.addWidget(cancelbtn) self.setMinimumWidth(384) def setTags(self, tagnames): self.wtagsel.clear() self.wtagsel.addItems(list(tagnames)) self.wtagsel.addItem("") self.wtagsel.setCurrentIndex(len(tagnames)) def setValue(self, value): self.valedit.setValue(value) def _check_tag(self, tag): self.wokbtn.setEnabled(True) def _check_tag_text(self, text): self.wokbtn.setEnabled(bool(str(text) != "")) def accept(self): """When dialog is accepted with a default (bool) tag type, check if the user hasn't entered a name=value entry in the tag name field. This is a common mistake, and should be treated as a shortcut for setting string tags.""" if isinstance(self.valedit.getValue(), bool): tagval = str(self.wtagsel.currentText()).split("=", 1) if len(tagval) > 1: # print tagval if QMessageBox.warning(self, "Set a string tag instead?", """<P>You have included an "=" sign in the tag name. Perhaps you actually mean to set tag "%s" to the string value "%s"?</P>""" % tuple(tagval), QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) == QMessageBox.No: return self.wtagsel.setEditText(tagval[0]) self.valedit.setValue(tagval[1]) return QDialog.accept(self) def getTag(self): return str(self.wtagsel.currentText()), self.valedit.getValue()
class RecollFulltextSearchDialog(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 # db is an instance of the class LibraryDatabase2 from database.py # This class has many, many methods that allow you to do a lot of # things. self.db = gui.current_db self.l = QVBoxLayout() self.setLayout(self.l) # Label self.labelText = QLabel('Use "and" and "or" for the search.') self.l.addWidget(self.labelText) # Title self.setWindowTitle('Recoll Full Text Search') self.setWindowIcon(icon) # Search window self.searchTextWindow = QComboBox() self.searchTextWindow.setEditable(True) self.l.addWidget(self.searchTextWindow) self.searchTextWindow.setFocus() self.searchTextWindow.setInsertPolicy(QComboBox.NoInsert) self.searchTextWindow.setDuplicatesEnabled(False) #Completer for the seach window self.completer = QCompleter() self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.searchTextWindow.setCompleter(self.completer) # output window self.outputWindow = QLabel() self.l.addWidget(self.outputWindow) # search button 1 self.doSearchButton = QPushButton('Search and replace the filter', self) self.doSearchButton.clicked.connect(self.recollSearchNew) self.l.addWidget(self.doSearchButton) self.doSearchButton.setDefault(True) # search button 2 self.doSearchButton = QPushButton('Search and add to filter', self) self.doSearchButton.clicked.connect(self.recollSearchAdd) self.l.addWidget(self.doSearchButton) # update database button 1 self.updateDatabaseButton = QPushButton('Update recoll database', self) self.updateDatabaseButton.clicked.connect(self.updateDatabase) self.l.addWidget(self.updateDatabaseButton) # update database button 2 self.newDatabaseButton = QPushButton('Make new recoll database', self) self.newDatabaseButton.clicked.connect(self.newDatabase) self.l.addWidget(self.newDatabaseButton) # config button self.configButton = QPushButton('Configure this plugin', self) self.configButton.clicked.connect(self.config) self.l.addWidget(self.configButton) # about button self.aboutButton = QPushButton('About', self) self.aboutButton.clicked.connect(self.about) self.l.addWidget(self.aboutButton) self.resize(self.sizeHint()) #self.resize(500, self.height()) def about(self): # Get the about text from a file inside the plugin zip file # The get_resources function is a builtin function defined for all your # plugin code. It loads files from the plugin zip file. It returns # the bytes from the specified file. text = get_resources('about.txt') #box = QMessageBox() #box.about(self, 'About the Recoll Full Text Search \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t',text.decode('utf-8')) #self.resize(600, self.height()) self.box = AboutWindow() self.box.setWindowTitle("About the Recoll Full Text Search Plugin") self.box.textWindow.setText(text) self.box.textWindow.setReadOnly(True) self.box.resize(600, 500) self.box.show() def updateDatabase(self): self.replaceDatabase = False self.makeDatabase() def newDatabase(self): self.replaceDatabase = True self.makeDatabase() def recollSearchNew(self): self.searchAdd = False self.recollSearch() def recollSearchAdd(self): self.searchAdd = True self.recollSearch() def makeDatabase(self): '''Runs recollindex outside calibre like in a terminal. Look for recollindex for more information about the flags and options''' self.cmd = [ prefs['pathToRecoll'] + '/recollindex', '-c', prefs['pathToCofig'] + '/plugins/recollFullTextSearchPlugin' ] #TODO: Fix for Linux #self.cmd = 'LD_LIBRARY_PATH="" ' + prefs['pathToRecoll'] + '/recollindex -c ' + prefs['pathToCofig'] + '/plugins/recollFullTextSearchPlugin' if self.replaceDatabase == True: self.cmd += [' -z'] self.p = Popen(self.cmd, shell=False) # TODO: Was close_fds nessesary? check it on linux #self.p = Popen(self.cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True) box = QMessageBox() box.about( self, 'Please read! \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t', 'Depending on you library size this operation can take a lot of time.\nThe process runs outside calibre so you can use or close it, but do not use this plugin.\nFor now there is no information about when recoll finishs,\nso look up, whether a recoll of recollindex process is running on you system.' ) def recollSearch(self): '''Runs recoll outside calibre like in a terminal. Look for recollindex for more information about the flags and options''' self.searchText = str(self.searchTextWindow.currentText() ) # search text from the plugin gui self.searchTextWindow.insertItem(0, self.searchText) #TODO: Fix Linux #self.cmd = 'LD_LIBRARY_PATH="" ' + prefs['pathToRecoll'] + '/recoll -c ' + prefs['pathToCofig'] + '/plugins/recollFullTextSearchPlugin -b -t ' self.cmd = [ prefs['pathToRecoll'] + '/recoll', '-c', prefs['pathToCofig'] + '/plugins/recollFullTextSearchPlugin', '-b', '-t' ] self.cmdString = self.cmd + [self.searchText] # TODO: Was close_fds nessesary? check it on linux #self.p = Popen(self.cmdString, shell=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True) self.p = Popen(self.cmdString, shell=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT) self.output = self.p.stdout.read() # output from the recoll search self.found = list(set(re.findall( r" \((\d+)\)\/[^/]*", self.output))) # regex to find the calibre ids in the folder names self.wholeString = '' if len(self.found) == 0: self.outputWindow.setText('no books found' + ' for ' + self.searchText) else: for elem in self.found[:400]: self.wholeString += 'id:=' + elem + ' or ' self.wholeString = self.wholeString[:-4] if len(self.found) > 400: self.outputWindow.setText( str(len(self.found)) + ' books found' + ' for ' + self.searchText + '. Only the first 400 books are shown') else: self.outputWindow.setText( str(len(self.found)) + ' books found' + ' for ' + self.searchText) if self.searchAdd == True: self.oldFilter = self.gui.search.text() self.wholeString = self.oldFilter + ' and (' + self.wholeString + ')' self.searchTextWindow.clearEditText() self.gui.search.setEditText( self.wholeString ) # set calibre search to the string found by recoll self.gui.search.do_search() def config(self): self.do_user_config(parent=self)
class ConvertDialog(QDialog): hide_text = _('&Hide styles') show_text = _('&Show styles') prince_log = '' prince_file = '' prince_css = '' # GUI definition def __init__(self, mi, fmt, opf, oeb, icon): ''' :param mi: The book metadata :param fmt: The source format used for conversion :param opf: The path to the OPF file :param oeb: An OEB object for the unpacked book :param icon: The window icon ''' self.opf = opf self.oeb = oeb self.mi = mi # The unpacked book needs to be parsed before, to read the contents # of the prince-style file, if it exists self.parse() QDialog.__init__(self) self.setAttribute(Qt.WA_DeleteOnClose) self.setWindowTitle(_('Convert to PDF with Prince')) self.setWindowIcon(icon) self.l = QVBoxLayout() self.setLayout(self.l) self.title_label = QLabel(_('<b>Title:</b> %s') % self.mi.title) self.l.addWidget(self.title_label) self.format_label = QLabel(_('<b>Source format:</b> %s') % fmt) self.l.addWidget(self.format_label) self.add_book = QCheckBox(_('&Add PDF to the book record')) self.add_book.setToolTip(_('<qt>Add the converted PDF to the selected book record</qt>')) self.add_book.setChecked(prefs['add_book']) self.add_book.stateChanged.connect(self.set_add_book) self.l.addWidget(self.add_book) self.ll = QHBoxLayout() self.ll.setAlignment(Qt.AlignLeft) self.l.addLayout(self.ll) self.label_css = QLabel(_('&Custom style:')) self.ll.addWidget(self.label_css) self.css_list = QComboBox() self.css_list.setToolTip(_('<qt>Select one style to use. Additional styles can be created in the plugin configuration</qt>')) for key in sorted(prefs['custom_CSS_list'], key=lambda x: x.lower()): self.css_list.addItem(key, key) self.css_list.setCurrentIndex(self.css_list.findText(prefs['default_CSS'])) self.css_list.currentIndexChanged.connect(self.set_css) self.ll.addWidget(self.css_list) self.label_css.setBuddy(self.css_list) self.ll_ = QHBoxLayout() self.l.addLayout(self.ll_) self.label_args = QLabel(_('A&dditional command-line arguments:')) self.ll_.addWidget(self.label_args) self.args = QLineEdit(self) self.args.setText(prefs['custom_args_list'][prefs['default_CSS']]) self.args.setToolTip(_('<qt>Specify additional command-line arguments for the conversion</qt>')) self.ll_.addWidget(self.args) self.label_args.setBuddy(self.args) self.css = QTabWidget() self.l.addWidget(self.css) self.css1 = TextEditWithTooltip(self, expected_geometry=(80,20)) self.css1.setLineWrapMode(TextEditWithTooltip.NoWrap) self.css1.load_text(self.replace_templates(prefs['custom_CSS_list'][prefs['default_CSS']]),'css') self.css1.setToolTip(_('<qt>This stylesheet can be modified<br/>The default can be configured</qt>')) i = self.css.addTab(self.css1, _('C&ustom CSS')) self.css.setTabToolTip(i, _('<qt>Custom CSS stylesheet to be used for this conversion</qt>')) monofont = QFont('') monofont.setStyleHint(QFont.TypeWriter) if (self.prince_css): self.css2 = QPlainTextEdit() self.css2.setStyleSheet('* { font-family: monospace }') self.css2.setLineWrapMode(QPlainTextEdit.NoWrap) self.css2.setPlainText(self.prince_css) self.css2.setReadOnly(True) self.css2.setToolTip(_('<qt>This stylesheet cannot be modified</qt>')) i = self.css.addTab(self.css2, _('&Book CSS')) self.css.setTabToolTip(i, _('<qt>Book-specific CSS stylesheet included in the ebook file</qt>')) self.ll = QHBoxLayout() self.l.addLayout(self.ll) if (prefs['show_CSS']): self.toggle = QPushButton(self.hide_text, self) else: self.toggle = QPushButton(self.show_text, self) self.toggle.setToolTip(_('<qt>Show/hide the additional styles used for the conversion</qt>')) self.toggle.clicked.connect(self.toggle_tabs) self.convert = QPushButton(_('Con&vert'), self) self.convert.setToolTip(_('<qt>Run the conversion with Prince</qt>')) self.convert.setDefault(True) self.buttons = QDialogButtonBox(QDialogButtonBox.Cancel) self.buttons.addButton(self.toggle, QDialogButtonBox.ResetRole) self.buttons.addButton(self.convert, QDialogButtonBox.AcceptRole) self.l.addWidget(self.buttons) self.buttons.accepted.connect(self.prince_convert) self.buttons.rejected.connect(self.reject) if (not prefs['show_CSS']): self.css.hide() self.adjustSize() def toggle_tabs(self): ''' Enable/disable the CSS tabs, and store the setting ''' if (self.css.isVisible()): self.css.hide() self.label_args.hide() self.args.hide() self.toggle.setText(self.show_text) self.adjustSize() else: self.css.show() self.label_args.show() self.args.show() self.toggle.setText(self.hide_text) self.adjustSize() prefs['show_CSS'] = self.css.isVisible() def set_add_book(self): ''' Save the status of the add_book checkbox ''' prefs['add_book'] = self.add_book.isChecked() def set_css(self): ''' Fill the custom CSS text box with the selected stylesheet (and command-line arguments) ''' style = unicode(self.css_list.currentText()) self.css1.load_text(self.replace_templates(prefs['custom_CSS_list'][style]),'css') self.args.setText(prefs['custom_args_list'][style]) prefs['default_CSS'] = style def parse(self): ''' Parse the unpacked OPF file to find and read the prince-style file ''' from calibre.constants import DEBUG from os.path import dirname, join from lxml import etree import codecs if DEBUG: print(_('Parsing book...')) opf_dir = dirname(self.opf) root = etree.parse(self.opf).getroot() metadata = root.find('{*}metadata') for meta in metadata.findall("{*}meta[@name='prince-style']"): prince_id = meta.get('content') for item in self.oeb.manifest: if (item.id == prince_id): self.prince_file = item.href break if (self.prince_file): fl = codecs.open(join(opf_dir, self.prince_file), 'rb', 'utf-8') self.prince_css = fl.read() fl.close() def replace_templates(self, text): ''' Replace templates (enclosed by '@{@', '@}@') in the input text ''' import re import json from calibre.ebooks.metadata.book.formatter import SafeFormat from calibre.constants import DEBUG matches = list(re.finditer('@{@(.+?)@}@',text,re.DOTALL)) results = {} for match in reversed(matches): result = SafeFormat().safe_format(match.group(1), self.mi, ('EXCEPTION: '), self.mi) # Escape quotes, backslashes and newlines result = re.sub(r'''['"\\]''', r'\\\g<0>', result) result = re.sub('\n', r'\A ', result) results[match.group(1)] = result text = text[:match.start(0)] + result + text[match.end(0):] if DEBUG: print(_('Replacing templates')) for match in matches: print(_('Found: %s (%d-%d)') % (match.group(1), match.start(0), match.end(0))) print(_('Replace with: %s') % results[match.group(1)]) return text def prince_convert(self): ''' Call the actual Prince command to convert to PDF ''' from os import makedirs from os.path import dirname, join, exists from calibre.ptempfile import PersistentTemporaryFile from calibre.constants import DEBUG from shlex import split as shsplit # All files are relative to the OPF location opf_dir = dirname(self.opf) base_dir = dirname(self.pdf_file) base_dir = join(opf_dir, base_dir) try: makedirs(base_dir) except BaseException: if not exists(base_dir): raise # Create a temporary CSS file with the box contents custom_CSS = PersistentTemporaryFile() custom_CSS.write(unicode(self.css1.toPlainText())) custom_CSS.close() # Create a temporary file with the list of input files file_list = PersistentTemporaryFile() for item in self.oeb.spine: file_list.write(item.href + "\n") file_list.close() # Build the command line command = prefs['prince_exe'] args = ['-v'] if self.prince_file: args.append('-s') args.append(self.prince_file) args.append('-s') args.append(custom_CSS.name) args.append('-l') args.append(file_list.name) args.append('-o') args.append(self.pdf_file) # Additional command-line arguments args.extend(shsplit(self.args.text())) # Hide the convert button and show a busy indicator self.convert.setEnabled(False) self.progress_bar = QProgressBar() self.progress_bar.setRange(0,0) self.progress_bar.setValue(0) self.l.addWidget(self.progress_bar) # Run the command and return the path to the PDF file if DEBUG: print(_('Converting book...')) process = QProcess(self) process.setWorkingDirectory(opf_dir) process.setProcessChannelMode(QProcess.MergedChannels); process.error.connect(self.error) process.finished.connect(self.end) self.process = process if DEBUG: from subprocess import list2cmdline line = list2cmdline([command] + args) print(_('Command line: %s') % line) process.start(command, args) def error(self, rc): ''' Show a message when there is an error in the command :param rc: The error code ''' from calibre.gui2 import error_dialog # Remove the progress bar while the error message is displayed self.progress_bar.hide() self.progress_bar.deleteLater() error_dialog(self, _('Process error'), _('<p>Error code: %s' '<p>make sure Prince (<a href="http://www.princexml.com">www.princexml.com</a>) is installed ' 'and the correct command-line-interface executable is set in the configuration of this plugin, ' 'which is usually:' '<ul><li>In Windows: <code><i>Prince_folder</i>\\Engine\\bin\\prince.exe</code>' ' <li>In Linux: <code>prince</code>' '</ul>') % rc, show=True) self.pdf_file = None self.accept() def end(self, rc): ''' Close and return the filename when the process ends :param rc: The return code (0 if successful) ''' from os.path import join self.prince_log = unicode(self.process.readAllStandardOutput().data()) opf_dir = unicode(self.process.workingDirectory()) if (rc == 0): self.pdf_file = join(opf_dir, self.pdf_file) else: self.pdf_file = None self.accept()
class XArmUI(object): def __init__(self, ui, layout): self.main_ui = ui self.layout = layout super(XArmUI, self).__init__() self.handler = XArmHandler(self) self.lang = self.main_ui.lang self.set_ui() self.set_disable(True) def set_ui(self): self._set_common_top_ui() self._set_tab() self._set_common_down_ui() self.connect_slot() def _set_common_top_ui(self): top_frame = QFrame() top_frame.setMaximumHeight(60) top_layout = QVBoxLayout(top_frame) self.layout.addWidget(top_frame) common_top_frame = QFrame() common_top_frame.setMinimumHeight(50) common_top_layout = QHBoxLayout(common_top_frame) top_layout.addWidget(common_top_frame) label_1 = QLabel(i18n[self.lang]['Connected'] + ':') self.label_connected = QLabel() img = QImage() self.label_connected.setMaximumHeight(20) self.label_connected.setMaximumWidth(20) self.label_connected.setScaledContents(True) if img.load(disconnect_icon_path): self.label_connected.setPixmap(QPixmap.fromImage(img)) label_2 = QLabel(i18n[self.lang]['Reported'] + ':') self.label_reported = QLabel() img = QImage() self.label_reported.setMaximumHeight(20) self.label_reported.setMaximumWidth(20) self.label_reported.setScaledContents(True) if img.load(disconnect_icon_path): self.label_reported.setPixmap(QPixmap.fromImage(img)) self.lnt_addr = QLineEdit('192.168.1.182') self.lnt_addr.setMaximumWidth(100) self.lnt_addr.setMinimumWidth(60) self.btn_connect = QPushButton(i18n[self.lang]['Connect']) # self.btn_connect.setMaximumWidth(50) # common_top_layout.addStretch(0) common_top_layout.setSpacing(10) common_top_layout.addWidget(label_1) common_top_layout.addWidget(self.label_connected) common_top_layout.addWidget(label_2) common_top_layout.addWidget(self.label_reported) common_top_layout.addWidget(self.lnt_addr) common_top_layout.addWidget(self.btn_connect) # common_down_frame = QFrame() # common_down_layout = QHBoxLayout(common_down_frame) # common_down_layout.setSpacing(0) # top_layout.addWidget(common_down_frame) common_down_layout = common_top_layout label = QLabel(i18n[self.lang]['WarnCode'] + ':') self.label_warn_code = QLabel('0') self.label_warn_code.setStyleSheet('''color: gray;font:bold;''') common_down_layout.addWidget(label) common_down_layout.addWidget(self.label_warn_code) label = QLabel(i18n[self.lang]['ErrorCode'] + ':') self.label_error_code = QLabel('0') self.label_error_code.setStyleSheet('''color: gray;font:bold;''') common_down_layout.addWidget(label) common_down_layout.addWidget(self.label_error_code) label = QLabel(i18n[self.lang]['CmdCount'] + ':') self.label_cmd_count = QLabel('0') self.label_cmd_count.setStyleSheet('''color: gray;font:bold;''') common_down_layout.addWidget(label) common_down_layout.addWidget(self.label_cmd_count) label = QLabel(i18n[self.lang]['State'] + ':') self.label_state = QLabel('4') self.label_state.setStyleSheet('''color: gray;font:bold;''') common_down_layout.addWidget(label) common_down_layout.addWidget(self.label_state) label = QLabel(i18n[self.lang]['Maable'] + ':') self.label_maable = QLabel('128') self.label_maable.setStyleSheet('''color: gray;font:bold;''') common_down_layout.addWidget(label) common_down_layout.addWidget(self.label_maable) label = QLabel(i18n[self.lang]['Mtbrake'] + ':') self.label_mtbrake = QLabel('128') self.label_mtbrake.setStyleSheet('''color: gray;font:bold;''') common_down_layout.addWidget(label) common_down_layout.addWidget(self.label_mtbrake) def _set_tab(self): tab_widget = QTabWidget() tab_widget.setMaximumHeight(self.main_ui.window.geometry().height() // 2) self.layout.addWidget(tab_widget) toolBox1 = QToolBox() toolBox2 = QToolBox() groupBox1 = QGroupBox() groupBox2 = QGroupBox() toolBox1.addItem(groupBox1, "") toolBox2.addItem(groupBox2, "") tab_widget.addTab(toolBox1, i18n[self.lang]['Joint']) tab_widget.addTab(toolBox2, i18n[self.lang]['Cartesian']) joint_layout = QVBoxLayout(groupBox1) cartesian_layout = QVBoxLayout(groupBox2) self.cartesian_ui = CartesianUI(self, cartesian_layout) self.axis_ui = JointUI(self, joint_layout) def _set_common_down_ui(self): slider_frame = QFrame() slider_layout = QGridLayout(slider_frame) self.layout.addWidget(slider_frame) label = QLabel(i18n[self.lang]['Speed'] + ':') self.slider_speed = QSlider(Qt.Horizontal) self.spinbox_speed = QSpinBox() self.slider_speed.setMinimum(1) self.slider_speed.setMaximum(1000) self.slider_speed.setValue(50) self.spinbox_speed.setSingleStep(1) self.spinbox_speed.setMinimum(1) self.spinbox_speed.setMaximum(1000) self.spinbox_speed.setValue(50) slider_layout.addWidget(label, 0, 0) slider_layout.addWidget(self.slider_speed, 0, 1) slider_layout.addWidget(self.spinbox_speed, 0, 2) label = QLabel(i18n[self.lang]['Acc'] + ':') self.slider_acc = QSlider(Qt.Horizontal) self.spinbox_acc = QSpinBox() self.slider_acc.setMinimum(1) self.slider_acc.setMaximum(100000) self.slider_acc.setValue(5000) self.spinbox_acc.setSingleStep(1) self.spinbox_acc.setMinimum(1) self.spinbox_acc.setMaximum(100000) self.spinbox_acc.setValue(5000) slider_layout.addWidget(label, 0, 3) slider_layout.addWidget(self.slider_acc, 0, 4) slider_layout.addWidget(self.spinbox_acc, 0, 5) common_frame = QFrame() common_layout = QGridLayout(common_frame) self.layout.addWidget(common_frame) self.btn_stop = QPushButton(i18n[self.lang]['Stop']) self.btn_clean = QPushButton(i18n[self.lang]['CleanErrorWarn']) self.btn_reset = QPushButton(i18n[self.lang]['Reset']) self.btn_get_servo_dbmsg = QPushButton( i18n[self.lang]['GetServoDebugMsg']) common_layout.addWidget(self.btn_stop, 0, 0) common_layout.addWidget(self.btn_clean, 0, 2) common_layout.addWidget(self.btn_reset, 0, 3) common_layout.addWidget(self.btn_get_servo_dbmsg, 0, 4) btn_frame = QFrame() btn_layout = QGridLayout(btn_frame) self.layout.addWidget(btn_frame) self.combobox_servo = QComboBox() self.combobox_servo.setStyleSheet('''color: blue;''') for item in [ 'axis-all', 'axis-1', 'axis-2', 'axis-3', 'axis-4', 'axis-5', 'axis-6', 'axis-7' ]: self.combobox_servo.addItem(item) self.combobox_servo.setCurrentIndex(1) btn_layout.addWidget(self.combobox_servo, 0, 0) self.btn_motion_enable = QPushButton(i18n[self.lang]['MotionEnable']) self.btn_motion_disable = QPushButton(i18n[self.lang]['MotionDisable']) self.btn_servo_attach = QPushButton(i18n[self.lang]['ServoAttach']) self.btn_servo_detach = QPushButton(i18n[self.lang]['ServoDetach']) self.combobox_state = QComboBox() self.combobox_state.setStyleSheet('''color: blue;''') for item in ['sport', 'pause', 'stop']: self.combobox_state.addItem(item) self.combobox_state.setCurrentIndex(0) self.btn_set_state = QPushButton(i18n[self.lang]['SetState']) btn_layout.addWidget(self.btn_motion_enable, 0, 1) btn_layout.addWidget(self.btn_motion_disable, 0, 2) btn_layout.addWidget(self.btn_servo_attach, 0, 3) btn_layout.addWidget(self.btn_servo_detach, 0, 4) btn_layout.addWidget(self.combobox_state, 0, 5) btn_layout.addWidget(self.btn_set_state, 0, 6) self.lnt_servo_addr = QLineEdit('servo_addr') self.lnt_servo_addr_value = QLineEdit('value') self.btn_get_servo_addr16 = QPushButton( i18n[self.lang]['GetServoAddr16']) self.btn_set_servo_addr16 = QPushButton( i18n[self.lang]['SetServoAddr16']) self.btn_get_servo_addr32 = QPushButton( i18n[self.lang]['GetServoAddr32']) self.btn_set_servo_addr32 = QPushButton( i18n[self.lang]['SetServoAddr32']) self.btn_set_servo_zero = QPushButton(i18n[self.lang]['SetServoZero']) btn_layout.addWidget(self.lnt_servo_addr, 1, 0) btn_layout.addWidget(self.lnt_servo_addr_value, 1, 1) btn_layout.addWidget(self.btn_get_servo_addr16, 1, 2) btn_layout.addWidget(self.btn_set_servo_addr16, 1, 3) btn_layout.addWidget(self.btn_get_servo_addr32, 1, 4) btn_layout.addWidget(self.btn_set_servo_addr32, 1, 5) btn_layout.addWidget(self.btn_set_servo_zero, 1, 6) def connect_slot(self): self.btn_connect.clicked.connect(self.connect) self.slider_speed.valueChanged.connect( functools.partial(self.slider_spinbox_related, slave=self.spinbox_speed, scale=1)) self.spinbox_speed.valueChanged.connect( functools.partial(self.slider_spinbox_related, slave=self.slider_speed, scale=1)) self.slider_acc.valueChanged.connect( functools.partial(self.slider_spinbox_related, slave=self.spinbox_acc, scale=1)) self.spinbox_acc.valueChanged.connect( functools.partial(self.slider_spinbox_related, slave=self.slider_acc, scale=1)) self.btn_stop.clicked.connect(self.stop) self.btn_clean.clicked.connect(self.clean) self.btn_reset.clicked.connect(self.reset) self.btn_get_servo_dbmsg.clicked.connect( functools.partial(self.handler.get_servo_debug_msg, only_log_error_servo=False)) self.btn_motion_enable.clicked.connect(self.motion_enable) self.btn_motion_disable.clicked.connect(self.motion_disable) self.btn_servo_attach.clicked.connect(self.set_servo_attach) self.btn_servo_detach.clicked.connect(self.set_servo_detach) self.btn_set_state.clicked.connect(self.set_state) self.btn_get_servo_addr16.clicked.connect(self.get_servo_addr_16) self.btn_set_servo_addr16.clicked.connect(self.set_servo_addr_16) self.btn_get_servo_addr32.clicked.connect(self.get_servo_addr_32) self.btn_set_servo_addr32.clicked.connect(self.set_servo_addr_32) self.btn_set_servo_zero.clicked.connect(self.set_servo_zero) @staticmethod def slider_spinbox_related(value, master=None, slave=None, scale=1): try: slave.setValue(value * scale) except Exception as e: print(e) def reset_flag(self): self.cartesian_ui.reset_flag() self.axis_ui.reset_flag() def update_maable_mtbrake(self, maable, mtbrake): try: self.label_maable.setText(str(maable)) self.label_mtbrake.setText(str(mtbrake)) self.label_maable.setStyleSheet('''color: green;font:bold;''') self.label_mtbrake.setStyleSheet('''color: green;font:bold;''') except Exception as e: print(e) def update_cmd_count(self, cmdnum): try: self.label_cmd_count.setText(str(cmdnum)) self.label_cmd_count.setStyleSheet('''color: green;font:bold;''') except Exception as e: print(e) def update_state(self, state): try: if state == 1: state_str = 'sport' self.label_state.setText(state_str) self.label_state.setStyleSheet('''color: green;font:bold;''') elif state == 2: state_str = 'sleep' self.label_state.setText(state_str) self.label_state.setStyleSheet('''color: gray;font:bold;''') elif state == 3: state_str = 'pause' self.label_state.setText(state_str) self.label_state.setStyleSheet('''color: orange;font:bold;''') elif state == 4: state_str = 'stop' self.label_state.setText(state_str) self.label_state.setStyleSheet('''color: red;font:bold;''') except Exception as e: print(e) # getattr(self, 'label_state').setText(state_str) # getattr(self, 'label_state').setText(str(state)) if state != 1: self.reset_flag() def update_warn_error(self, item): try: warn, error = item self.label_warn_code.setText(str(warn)) self.label_error_code.setText(str(error)) if warn != 0: self.label_warn_code.setStyleSheet('''color: red;font:bold;''') else: self.label_warn_code.setStyleSheet( '''color: green;font:bold;''') if error != 0: self.label_error_code.setStyleSheet( '''color: red;font:bold;''') else: self.label_error_code.setStyleSheet( '''color: green;font:bold;''') except Exception as e: print(e) def update_connect_status(self, item): try: img = QImage() if item[0]: logger.info('connect to {} success, report: {}'.format( self.handler.addr, self.handler.report_type)) if img.load(connect_icon_path): self.label_connected.setPixmap(QPixmap.fromImage(img)) self.btn_connect.setText(i18n[self.lang]['Disconnect']) self.btn_connect.setStyleSheet('''color: red;font:bold;''') self.set_disable(False) else: logger.info('disconnect from or failed connect {}'.format( self.handler.addr)) self.handler.cmd_que.queue.clear() if img.load(disconnect_icon_path): self.label_connected.setPixmap(QPixmap.fromImage(img)) self.btn_connect.setText(i18n[self.lang]['Connect']) self.btn_connect.setStyleSheet( '''color: green;font:bold;''') self.set_disable(True) if item[1]: if img.load(connect_icon_path): self.label_reported.setPixmap(QPixmap.fromImage(img)) else: if img.load(disconnect_icon_path): self.label_reported.setPixmap(QPixmap.fromImage(img)) except Exception as e: print(e) def connect(self, event): try: if str(self.btn_connect.text()) == i18n[self.lang]['Connect']: addr = self.lnt_addr.text().strip() if addr == '192.168.1.': addr = 'localhost' report_type = 'normal' else: tmp = addr.split(':') addr = tmp[0] report_type = tmp[1] if len(tmp) > 1 else 'normal' self.btn_connect.setText('Connecting') self.btn_connect.setStyleSheet('''color: orange;font:bold;''') self.handler.connect(addr, report_type=report_type) # if self.window.connect(addr, report_type=report_type): # self.btn_connect.setText(self.disconnect_label) # self.btn_connect.setStyleSheet('''color: red;font:bold;''') elif str(self.btn_connect.text()) == i18n[self.lang]['Disconnect']: self.handler.disconnect() self.btn_connect.setText(i18n[self.lang]['Connect']) self.btn_connect.setStyleSheet('''color: green;font:bold;''') except Exception as e: print(e) def stop(self, event): try: self.handler.cmd_que.queue.clear() if self.handler.xarm and self.handler.xarm.warn_code != 0: item = { 'cmd': 'clean_warn', } self.handler.put_cmd_que(item) if self.handler.xarm and self.handler.xarm.error_code != 0: item = { 'cmd': 'clean_error', } self.handler.put_cmd_que(item) item = { 'cmd': 'motion_enable', 'kwargs': { 'servo_id': 0, 'enable': True } } self.handler.put_cmd_que(item) item = { 'cmd': 'urgent_stop', } self.handler.put_cmd_que(item) self.reset_flag() except Exception as e: print(e) def clean(self, event): try: self.handler.cmd_que.queue.clear() if self.handler.xarm and self.handler.xarm.warn_code != 0: item = { 'cmd': 'clean_warn', } self.handler.put_cmd_que(item) if self.handler.xarm and self.handler.xarm.error_code != 0: item = { 'cmd': 'clean_error', } self.handler.put_cmd_que(item) except Exception as e: print(e) def reset(self, event): try: self.handler.cmd_que.queue.clear() if self.handler.xarm and self.handler.xarm.warn_code != 0: item = { 'cmd': 'clean_warn', } self.handler.put_cmd_que(item) if self.handler.xarm and self.handler.xarm.error_code != 0: item = { 'cmd': 'clean_error', } self.handler.put_cmd_que(item) item = { 'cmd': 'motion_enable', 'kwargs': { 'servo_id': 0, 'enable': True } } self.handler.put_cmd_que(item) item = {'cmd': 'set_state', 'kwargs': {'state': 0}} self.handler.put_cmd_que(item) item = { 'cmd': 'move_gohome', 'kwargs': { 'speed': 30, 'mvacc': 5000, } } self.handler.put_cmd_que(item) self.reset_flag() except Exception as e: print(e) def get_servo_addr_16(self, event): try: addr = self.lnt_servo_addr.text().strip() try: if addr.lower().startswith('0x'): addr = int(addr, base=16) else: addr = int(addr) except: QMessageBox.warning(self.main_ui.window, '错误', '请输入正确的地址, 地址必须是u16类型') return text = self.combobox_servo.currentText() if text == 'axis-all': QMessageBox.warning(self.main_ui.window, '警告', '请选择其中一个电机,不能选择所有电机') return else: servo_id = int(text.split('-')[-1]) tmp = '你确定要获取电机{}的地址{}的值吗?'.format(servo_id, addr) if QMessageBox.question(self.main_ui.window, '警告', tmp) == QMessageBox.Yes: item = { 'cmd': 'get_servo_addr_16', 'kwargs': { 'servo_id': servo_id, 'addr': addr } } self.handler.put_cmd_que(item) except Exception as e: print(e) def get_servo_addr_32(self, event): try: addr = self.lnt_servo_addr.text().strip() try: if addr.lower().startswith('0x'): addr = int(addr, base=16) else: addr = int(addr) except: QMessageBox.warning(self.main_ui.window, '错误', '请输入正确的地址, 地址必须是u16类型') return text = self.combobox_servo.currentText() if text == 'axis-all': QMessageBox.warning(self.main_ui.window, '警告', '请选择其中一个电机,不能选择所有电机') return else: servo_id = int(text.split('-')[-1]) tmp = '你确定要获取电机{}的地址{}的值吗?'.format(servo_id, addr) if QMessageBox.question(self.main_ui.window, '警告', tmp) == QMessageBox.Yes: item = { 'cmd': 'get_servo_addr_32', 'kwargs': { 'servo_id': servo_id, 'addr': addr } } self.handler.put_cmd_que(item) except Exception as e: print(e) def set_servo_addr_16(self, event): try: addr = self.lnt_servo_addr.text().strip() try: if addr.lower().startswith('0x'): addr = int(addr, base=16) else: addr = int(addr) except: QMessageBox.warning(self.main_ui.window, '错误', '请输入正确的地址, 地址必须是u16类型') return value = self.lnt_servo_addr_value.text().strip() try: value = float(value) except: QMessageBox.warning(self.main_ui.window, '错误', '请输入正确的值, 值必须是float32类型') return text = self.combobox_servo.currentText() if text == 'axis-all': QMessageBox.warning(self.main_ui.window, '警告', '请选择其中一个电机,不能选择所有电机') return else: servo_id = int(text.split('-')[-1]) tmp = '你确定要设置电机{}的地址{}的值为{}吗?'.format(servo_id, addr, value) if QMessageBox.question(self.main_ui.window, '警告', tmp) == QMessageBox.Yes: item = { 'cmd': 'set_servo_addr_16', 'kwargs': { 'servo_id': servo_id, 'addr': addr, 'value': value } } self.handler.put_cmd_que(item) except Exception as e: print(e) def set_servo_addr_32(self, event): try: addr = self.lnt_servo_addr.text().strip() try: if addr.lower().startswith('0x'): addr = int(addr, base=16) else: addr = int(addr) except: QMessageBox.warning(self.main_ui.window, '错误', '请输入正确的地址, 地址必须是u16类型') return value = self.lnt_servo_addr_value.text().strip() try: value = float(value) except: QMessageBox.warning(self.main_ui.window, '错误', '请输入正确的值, 值必须是float32类型') return text = self.combobox_servo.currentText() if text == 'axis-all': QMessageBox.warning(self.main_ui.window, '警告', '请选择其中一个电机,不能选择所有电机') return else: servo_id = int(text.split('-')[-1]) tmp = '你确定要设置电机{}的地址{}的值为{}吗?'.format(servo_id, addr, value) if QMessageBox.question(self.main_ui.window, '警告', tmp) == QMessageBox.Yes: item = { 'cmd': 'set_servo_addr_32', 'kwargs': { 'servo_id': servo_id, 'addr': addr, 'value': value } } self.handler.put_cmd_que(item) except Exception as e: print(e) def set_servo_zero(self, event): try: text = self.combobox_servo.currentText() if text == 'axis-all': servo_id = 8 else: servo_id = int(text.split('-')[-1]) if servo_id == 8: tmp = '你确定要设置所有电机的零点吗?' else: tmp = '你确定要设置电机{}的零点吗?'.format(servo_id) if QMessageBox.question(self.main_ui.window, '警告', tmp) == QMessageBox.Yes: item = { 'cmd': 'set_servo_zero', 'kwargs': { 'servo_id': servo_id } } self.handler.put_cmd_que(item) except Exception as e: print(e) def set_servo_attach(self, event): try: text = self.combobox_servo.currentText() if text == 'axis-all': servo_id = 8 else: servo_id = int(text.split('-')[-1]) item = { 'cmd': 'set_servo_attach', 'kwargs': { 'servo_id': servo_id } } self.handler.put_cmd_que(item) item = { 'cmd': 'motion_enable', 'kwargs': { 'servo_id': servo_id, 'enable': True } } self.handler.put_cmd_que(item) item = {'cmd': 'set_state', 'kwargs': {'state': 0}} self.handler.put_cmd_que(item) except Exception as e: print(e) def set_servo_detach(self, event): try: text = self.combobox_servo.currentText() if text == 'axis-all': servo_id = 8 else: servo_id = int(text.split('-')[-1]) if servo_id == 8: tmp = '你确定要解锁所有电机吗?' else: tmp = '你确定要解锁电机{}吗?'.format(servo_id) if QMessageBox.question(self.main_ui.window, '警告', tmp) == QMessageBox.Yes: item = { 'cmd': 'set_servo_detach', 'kwargs': { 'servo_id': servo_id } } self.handler.put_cmd_que(item) except Exception as e: print(e) def motion_enable(self, event): self._motion_enable(True) def motion_disable(self, event): self._motion_enable(False) def _motion_enable(self, enable=True): try: text = self.combobox_servo.currentText() if text == 'axis-all': servo_id = 8 else: servo_id = int(text.split('-')[-1]) item = { 'cmd': 'motion_enable', 'kwargs': { 'servo_id': servo_id, 'enable': enable } } self.handler.put_cmd_que(item) except Exception as e: print(e) def set_state(self, event): try: text = self.combobox_state.currentText() if text == 'sport': state = 0 elif text == 'pause': state = 3 elif text == 'stop': state = 4 else: return if state in [0, 3, 4]: item = {'cmd': 'set_state', 'kwargs': {'state': state}} self.handler.put_cmd_que(item) except Exception as e: print(e) def set_disable(self, disable): try: self.btn_stop.setDisabled(disable) self.btn_clean.setDisabled(disable) self.btn_reset.setDisabled(disable) self.btn_get_servo_dbmsg.setDisabled(disable) self.combobox_servo.setDisabled(disable) self.combobox_state.setDisabled(disable) self.btn_motion_enable.setDisabled(disable) self.btn_motion_disable.setDisabled(disable) self.btn_servo_attach.setDisabled(disable) self.btn_servo_detach.setDisabled(disable) self.btn_set_state.setDisabled(disable) self.lnt_servo_addr.setDisabled(disable) self.lnt_servo_addr_value.setDisabled(disable) self.btn_get_servo_addr16.setDisabled(disable) self.btn_set_servo_addr16.setDisabled(disable) self.btn_get_servo_addr32.setDisabled(disable) self.btn_set_servo_addr32.setDisabled(disable) self.btn_set_servo_zero.setDisabled(disable) self.slider_speed.setDisabled(disable) self.spinbox_speed.setDisabled(disable) self.slider_acc.setDisabled(disable) self.spinbox_acc.setDisabled(disable) self.axis_ui.set_disable(disable) self.cartesian_ui.set_disable(disable) except Exception as e: print(e)
class SourceSelectorDialog(QDialog): def __init__(self, parent, flags=Qt.WindowFlags()): QDialog.__init__(self, parent, flags) self.setModal(False) self.setWindowTitle("Select sources by...") lo = QVBoxLayout(self) lo.setContentsMargins(10, 10, 10, 10) lo.setSpacing(5) # select by lo1 = QHBoxLayout() lo.addLayout(lo1) lo1.setContentsMargins(0, 0, 0, 0) # lab = QLabel("Select:") # lo1.addWidget(lab) self.wselby = QComboBox(self) lo1.addWidget(self.wselby, 0) self.wselby.activated[str].connect(self._setup_selection_by) # under/over self.wgele = QComboBox(self) lo1.addWidget(self.wgele, 0) self.wgele.addItems([">", ">=", "<=", "<", "sum<=", "sum>", "=="]) self.wgele.activated[str].connect(self._select_threshold) # threshold value self.wthreshold = QLineEdit(self) self.wthreshold.editingFinished.connect(self._select_threshold) lo1.addWidget(self.wthreshold, 1) # min and max label self.wminmax = QLabel(self) lo.addWidget(self.wminmax) # selection slider lo1 = QHBoxLayout() lo.addLayout(lo1) self.wpercent = QSlider(self) self.wpercent.setTracking(False) self.wpercent.valueChanged[int].connect(self._select_percentile) self.wpercent.sliderMoved[int].connect( self._select_percentile_threshold) self.wpercent.setRange(0, 100) self.wpercent.setOrientation(Qt.Horizontal) lo1.addWidget(self.wpercent) self.wpercent_lbl = QLabel("0%", self) self.wpercent_lbl.setMinimumWidth(64) lo1.addWidget(self.wpercent_lbl) # # hide button # lo.addSpacing(10) # lo2 = QHBoxLayout() # lo.addLayout(lo2) # lo2.setContentsMargins(0,0,0,0) # hidebtn = QPushButton("Close",self) # hidebtn.setMinimumWidth(128) # QObject.connect(hidebtn,pyqtSignal("clicked()"),self.hide) # lo2.addStretch(1) # lo2.addWidget(hidebtn) # lo2.addStretch(1) # self.setMinimumWidth(384) self._in_select_threshold = False self._sort_index = None self.qerrmsg = QErrorMessage(self) def resetModel(self): """Resets dialog based on current model.""" if not self.model: return # getset of model tags, and remove the non-sorting tags alltags = set(self.model.tagnames) alltags -= NonSortingTags # make list of tags from StandardTags that are present in model self.sorttags = [ tag for tag in StandardTags if tag in alltags or tag in TagAccessors ] # append model tags that were not in StandardTags self.sorttags += list(alltags - set(self.sorttags)) # set selector self.wselby.clear() self.wselby.addItems(self.sorttags) for tag in "Iapp", "I": if tag in self.sorttags: self.wselby.setCurrentIndex(self.sorttags.index(tag)) break self._setup_selection_by(self.wselby.currentText()) def _reset_percentile(self): self.wthreshold.setText("") self.wpercent.setValue(50) self.wpercent_lbl.setText("--%") def _setup_selection_by(self, tag): tag = str(tag) # may be QString # clear threshold value and percentiles self._reset_percentile() # get min/max values, and sort indices # _sort_index will be an array of (value,src,cumsum) tuples, sorted by tag value (high to low), # where src is the source, and cumsum is the sum of all values in the list from 0 up to and including the current one self._sort_index = [] minval = maxval = value = None for isrc, src in enumerate(self.model.sources): try: if hasattr(src, tag): # test if item can be cast to float try: float(getattr(src, tag)) except: continue else: value = float(getattr(src, tag)) elif tag in TagAccessors: value = float(TagAccessors[tag](src)) else: # not existant for this source (maybe a tag or something??) value = np.nan # skip source if failed to access this tag as a float except: traceback.print_exc() continue if value is not None: self._sort_index.append( [value if not np.isnan(value) else -np.inf, src, 0.]) minval = min(minval, value if not np.isnan(value) else np.inf) if minval is not None else value maxval = max(maxval, value if not np.isnan(value) else -np.inf) if maxval is not None else value # add label if minval is None: self._range = None self.wminmax.setText( "<font color=red>'%s' is not a numeric attribute</font>" % tag) for w in self.wgele, self.wthreshold, self.wpercent, self.wpercent_lbl: w.setEnabled(False) else: self._range = (minval, maxval) self.wminmax.setText("min: %g max: %g" % self._range) for w in self.wgele, self.wthreshold, self.wpercent, self.wpercent_lbl: w.setEnabled(True) # sort index by descending values self._sort_index.sort(reverse=True, key=operator.itemgetter(0)) # generate cumulative sums cumsum = 0. for entry in self._sort_index: if not np.isneginf(entry[0]): cumsum += entry[0] entry[2] = cumsum # Maps comparison operators to callables. Used in _select_threshold. # Each callable takes two arguments: e is a tuple of (value,src,cumsum) (see _sort_index above), and x is a threshold # Second argument is a flag: if False, selection is inverted w.r.t. operator Operators = { "<": ((lambda e, x: e[0] >= x), False), "<=": ((lambda e, x: e[0] > x), False), ">": ((lambda e, x: e[0] > x), True), ">=": ((lambda e, x: e[0] >= x), True), "sum<=": ((lambda e, x: e[2] <= x), True), "sum>": ((lambda e, x: e[2] <= x), False), "==": ((lambda e, x: np.abs(e[0] - x) < 1.0e-8), True), } def _select_threshold(self, *dum): dprint(1, "select_threshold", dum) self._in_select_threshold = True busy = BusyIndicator() try: # get threshold, ignore if not set threshold = str(self.wthreshold.text()) if not threshold: self._reset_percentile() return # try to parse threshold, ignore if invalid try: threshold = float(threshold) except: self._reset_percentile() return # get comparison operator op, select = self.Operators[str(self.wgele.currentText())] # apply to initial segment (that matches operator) for num, entry in enumerate(self._sort_index): if not op(entry, threshold): break entry[1].selected = select else: num = len(self._sort_index) # apply to remaining segment for val, src, cumsum in self._sort_index[num:]: src.selected = not select # set percentile percent = round(float(num * 100) / len(self._sort_index)) if not select: percent = 100 - percent self.wpercent.setValue(percent) self.wpercent_lbl.setText("%3d%%" % percent) # emit signal self.model.emitSelection(self) finally: self._in_select_threshold = False busy.reset_cursor() def _select_percentile(self, percent): self._select_percentile_threshold(percent, do_select=True) def _select_percentile_threshold(self, percent, do_select=False): # ignore if no sort index set up, or if _select_threshold() is being called if self._sort_index is None or self._in_select_threshold: return dprint(1, "select_precentile_threshold", percent) busy = BusyIndicator() # number of objects to select nsrc = len(self._sort_index) nsel = int(math.ceil(nsrc * float(percent) / 100)) # get comparison operator opstr = str(self.wgele.currentText()) op, select = self.Operators[opstr] # select head or tail of list, depending on direction of operator if select: thr = self._sort_index[min(nsel, nsrc - 1)] slc1 = slice(0, nsel) slc2 = slice(nsel, None) else: thr = self._sort_index[-min(nsel + 1, nsrc)] slc1 = slice(nsrc - nsel, None) slc2 = slice(0, nsrc - nsel) if do_select: for val, src, cumsum in self._sort_index[slc1]: src.selected = True for val, src, cumsum in self._sort_index[slc2]: src.selected = False self.model.emitSelection(self) self.wpercent_lbl.setText("%3d%%" % percent) self.wthreshold.setText( "%g" % (thr[2] if opstr.startswith("sum") else thr[0])) busy.reset_cursor() return nsel def setModel(self, model): """Sets the current model. If dialog is visible, applies the changes""" self.model = model if self.isVisible(): self.resetModel() if not model: self.hide() def show(self): """Shows dialog, resetting the model if it was invisible.""" if not self.isVisible(): self.resetModel() QDialog.show(self)
class CreateVirtualLibrary(QDialog): # {{{ def __init__(self, gui, existing_names, editing=None): QDialog.__init__(self, gui) self.gui = gui self.existing_names = existing_names if editing: self.setWindowTitle(_('Edit virtual library')) else: self.setWindowTitle(_('Create virtual library')) self.setWindowIcon(QIcon(I('lt.png'))) gl = QGridLayout() self.setLayout(gl) self.la1 = la1 = QLabel(_('Virtual library &name:')) gl.addWidget(la1, 0, 0) self.vl_name = QComboBox() self.vl_name.setEditable(True) self.vl_name.lineEdit().setMaxLength(MAX_VIRTUAL_LIBRARY_NAME_LENGTH) la1.setBuddy(self.vl_name) gl.addWidget(self.vl_name, 0, 1) self.editing = editing self.saved_searches_label = sl = QTextBrowser(self) sl.viewport().setAutoFillBackground(False) gl.addWidget(sl, 2, 0, 1, 2) self.la2 = la2 = QLabel(_('&Search expression:')) gl.addWidget(la2, 1, 0) self.vl_text = QLineEdit() self.vl_text.textChanged.connect(self.search_text_changed) la2.setBuddy(self.vl_text) gl.addWidget(self.vl_text, 1, 1) self.vl_text.setText(_build_full_search_string(self.gui)) self.sl = sl = QLabel( '<p>' + _('Create a virtual library based on: ') + ('<a href="author.{0}">{0}</a>, ' '<a href="tag.{1}">{1}</a>, ' '<a href="publisher.{2}">{2}</a>, ' '<a href="series.{3}">{3}</a>, ' '<a href="search.{4}">{4}</a>.').format(_('Authors'), _( 'Tags'), _('Publishers'), _('Series'), _('Saved searches'))) sl.setWordWrap(True) sl.setTextInteractionFlags(Qt.LinksAccessibleByMouse) sl.linkActivated.connect(self.link_activated) gl.addWidget(sl, 3, 0, 1, 2) gl.setRowStretch(3, 10) self.hl = hl = QLabel( _(''' <h2>Virtual libraries</h2> <p>Using <i>virtual libraries</i> you can restrict calibre to only show you books that match a search. When a virtual library is in effect, calibre behaves as though the library contains only the matched books. The Tag browser display only the tags/authors/series/etc. that belong to the matched books and any searches you do will only search within the books in the virtual library. This is a good way to partition your large library into smaller and easier to work with subsets.</p> <p>For example you can use a Virtual library to only show you books with the Tag <i>"Unread"</i> or only books by <i>"My favorite author"</i> or only books in a particular series.</p> <p>More information and examples are available in the <a href="%s">User Manual</a>.</p> ''') % localize_user_manual_link( 'https://manual.calibre-ebook.com/virtual_libraries.html')) hl.setWordWrap(True) hl.setOpenExternalLinks(True) hl.setFrameStyle(hl.StyledPanel) gl.addWidget(hl, 0, 3, 4, 1) bb = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) bb.accepted.connect(self.accept) bb.rejected.connect(self.reject) gl.addWidget(bb, 4, 0, 1, 0) if editing: db = self.gui.current_db virt_libs = db.prefs.get('virtual_libraries', {}) for dex, vl in enumerate(sorted(virt_libs.keys(), key=sort_key)): self.vl_name.addItem(vl, virt_libs.get(vl, '')) if vl == editing: self.vl_name.setCurrentIndex(dex) self.original_index = dex self.original_search = virt_libs.get(editing, '') self.vl_text.setText(self.original_search) self.new_name = editing self.vl_name.currentIndexChanged[int].connect( self.name_index_changed) self.vl_name.lineEdit().textEdited.connect(self.name_text_edited) self.resize(self.sizeHint() + QSize(150, 25)) def search_text_changed(self, txt): db = self.gui.current_db searches = [_('Saved searches recognized in the expression:')] txt = unicode(txt) while txt: p = txt.partition('search:') if p[1]: # found 'search:' possible_search = p[2] if possible_search: # something follows the 'search:' if possible_search[0] == '"': # strip any quotes possible_search = possible_search[1:].partition('"') else: # find end of the search name. Is EOL, space, rparen sp = possible_search.find(' ') pp = possible_search.find(')') if pp < 0 or (sp > 0 and sp <= pp): # space in string before rparen, or neither found possible_search = possible_search.partition(' ') else: # rparen in string before space possible_search = possible_search.partition(')') txt = possible_search[2] # grab remainder of the string search_name = possible_search[0] if search_name.startswith('='): search_name = search_name[1:] if search_name in db.saved_search_names(): searches.append(search_name + '=' + db.saved_search_lookup(search_name)) else: txt = '' else: txt = '' if len(searches) > 1: self.saved_searches_label.setPlainText('\n'.join(searches)) else: self.saved_searches_label.setPlainText('') def name_text_edited(self, new_name): self.new_name = unicode(new_name) def name_index_changed(self, dex): if self.editing and (self.vl_text.text() != self.original_search or self.new_name != self.editing): if not question_dialog( self.gui, _('Search text changed'), _('The virtual library name or the search text has changed. ' 'Do you want to discard these changes?'), default_yes=False): self.vl_name.blockSignals(True) self.vl_name.setCurrentIndex(self.original_index) self.vl_name.lineEdit().setText(self.new_name) self.vl_name.blockSignals(False) return self.new_name = self.editing = self.vl_name.currentText() self.original_index = dex self.original_search = unicode(self.vl_name.itemData(dex) or '') self.vl_text.setText(self.original_search) def link_activated(self, url): db = self.gui.current_db f, txt = unicode(url).partition('.')[0::2] if f == 'search': names = db.saved_search_names() else: names = getattr(db, 'all_%s_names' % f)() d = SelectNames(names, txt, parent=self) if d.exec_() == d.Accepted: prefix = f + 's' if f in {'tag', 'author'} else f if f == 'search': search = [ '(%s)' % (db.saved_search_lookup(x)) for x in d.names ] else: search = [ '%s:"=%s"' % (prefix, x.replace('"', '\\"')) for x in d.names ] if search: if not self.editing: self.vl_name.lineEdit().setText(d.names.next()) self.vl_name.lineEdit().setCursorPosition(0) self.vl_text.setText(d.match_type.join(search)) self.vl_text.setCursorPosition(0) def accept(self): n = unicode(self.vl_name.currentText()).strip() if not n: error_dialog( self.gui, _('No name'), _('You must provide a name for the new virtual library'), show=True) return if n.startswith('*'): error_dialog(self.gui, _('Invalid name'), _('A virtual library name cannot begin with "*"'), show=True) return if n in self.existing_names and n != self.editing: if not question_dialog( self.gui, _('Name already in use'), _('That name is already in use. Do you want to replace it ' 'with the new search?'), default_yes=False): return v = unicode(self.vl_text.text()).strip() if not v: error_dialog( self.gui, _('No search string'), _('You must provide a search to define the new virtual library' ), show=True) return try: db = self.gui.library_view.model().db recs = db.data.search_getting_ids('', v, use_virtual_library=False, sort_results=False) except ParseException as e: error_dialog(self.gui, _('Invalid search'), _('The search in the search box is not valid'), det_msg=e.msg, show=True) return if not recs and not question_dialog( self.gui, _('Search found no books'), _('The search found no books, so the virtual library ' 'will be empty. Do you really want to use that search?'), default_yes=False): return self.library_name = n self.library_search = v QDialog.accept(self)
class ConfigWidget(QWidget): # GUI definition def __init__(self): QWidget.__init__(self) self.l = QVBoxLayout() self.setLayout(self.l) self.ll = QHBoxLayout() self.l.addLayout(self.ll) self.label_exe = QLabel(_('&Prince executable:')) self.ll.addWidget(self.label_exe) self.exe = QLineEdit(self) self.exe.setText(prefs['prince_exe']) self.exe.setToolTip(_('<qt>Executable for the Prince program (command-line interface)</qt>')) self.ll.addWidget(self.exe) self.label_exe.setBuddy(self.exe) self.browse = QPushButton(_('&Browse') + '...', self) self.browse.setToolTip(_('<qt>Search the Prince executable in your computer</qt>')) self.browse.clicked.connect(self.select_exe) self.ll.addWidget(self.browse) self.lll = QHBoxLayout() self.l.addLayout(self.lll) self.label_fmts = QLabel(_('Preferred &formats:')) self.lll.addWidget(self.label_fmts) self.fmts = QLineEdit(self) self.fmts.setText(','.join(prefs['formats'])) self.fmts.setToolTip(_('<qt>Comma-separated list of preferred formats to use as source, the first that matches will be used</qt>')) self.lll.addWidget(self.fmts) self.label_fmts.setBuddy(self.fmts) self.add_book = QCheckBox(_('&Add PDF to the book record')) self.add_book.setToolTip(_('<qt>Add the converted PDF to the selected book record</qt>')) self.add_book.setChecked(prefs['add_book']) self.l.addWidget(self.add_book) self.show_css = QCheckBox(_('&Show CSS in the Convert dialog')) self.show_css.setToolTip(_('<qt>Show by default the stylesheets in the Convert dialog</qt>')) self.show_css.setChecked(prefs['show_CSS']) self.l.addWidget(self.show_css) self.css_layout = QVBoxLayout() self.llll = QHBoxLayout() self.css_layout.addLayout(self.llll) self.css_list = QComboBox() self.css_list.setToolTip(_('<qt>List of custom stylesheets defined. Select one to edit</qt>')) self.css_list.setSizeAdjustPolicy(QComboBox.AdjustToContents) self.CSS_list = prefs['custom_CSS_list'].copy() self.default_CSS = prefs['default_CSS'] if 'custom_CSS' in prefs: self.CSS_list[_('old')] = prefs['custom_CSS'] self.default_CSS = _('old') if self.default_CSS not in self.CSS_list: self.default_CSS = sorted(self.CSS_list, key=lambda x: x.lower())[0] for key in sorted(self.CSS_list, key=lambda x: x.lower()): self.css_list.addItem(key, key) self.css_list.setCurrentIndex(self.css_list.findText(self.default_CSS)) self.css_list.currentIndexChanged.connect(self.set_css) self.llll.addWidget(self.css_list) self.css_rename = QPushButton(_('Re&name')) self.css_rename.setToolTip(_('<qt>Rename the current stylesheet to the name on the right</qt>')) self.css_rename.clicked.connect(self.rename_css) self.css_rename.setEnabled(False) self.llll.addWidget(self.css_rename) self.css_name = QLineEdit(self) self.css_name.setToolTip(_('<qt>Name for the new or renamed stylesheet</qt>')) self.css_name.setText(self.css_list.currentText()) self.css_name.textChanged.connect(self.check_names) self.llll.addWidget(self.css_name) self.css_add = QPushButton(_('A&dd')) self.css_add.setToolTip(_('<qt>Add a new empty stylesheet with the name on the left</qt>')) self.css_add.clicked.connect(self.add_css) self.css_add.setEnabled(False) self.llll.addWidget(self.css_add) self.css_remove = QPushButton(_('Re&move')) self.css_remove.setToolTip(_('<qt>Remove the current stylesheet</qt>')) self.css_remove.clicked.connect(self.remove_css) self.llll.addWidget(self.css_remove) self.css = TextEditWithTooltip() self.css.setLineWrapMode(TextEditWithTooltip.NoWrap) self.css.load_text(self.CSS_list[unicode(self.css_list.currentText())],'css') self.css.setToolTip(_('<qt>Custom stylesheet that will be applied, if selected, to all Prince PDF conversions</qt>')) self.css_layout.addWidget(self.css) self.css_templates = QLabel(_('Book metadata can be used in the stylesheet. Anything between %(s1)s and %(s2)s will be processed as a calibre template. For instance, %(s3)s in the stylesheet will be replaced with the book title in the conversion.') % \ {'s1':'<span style="font-family:monospace ; font-weight:bold">@{@</span>', \ 's2':'<span style="font-family:monospace ; font-weight:bold">@}@</span>', \ 's3':'<span style="font-family:monospace ; font-weight:bold">@{@{title}@}@</span>'}) self.css_templates.setWordWrap(True) self.css_layout.addWidget(self.css_templates) self.css_box = QGroupBox(_('&Custom CSS:')) self.css_box.setLayout(self.css_layout) self.l.addWidget(self.css_box) self.lllll = QHBoxLayout() self.lllll.setAlignment(Qt.AlignLeft) self.l.addLayout(self.lllll) self.defaults = QPushButton(_('&Restore defaults')) self.defaults.setToolTip(_('<qt>Restore the default settings</qt>')) self.defaults.clicked.connect(self.restore_defaults) self.lllll.addWidget(self.defaults, alignment=Qt.AlignLeft) self.warning = QLabel(_('<b>Warning</b>: Deletes modified stylesheets')) self.lllll.addWidget(self.warning) self.adjustSize() def select_exe(self): ''' Create a dialog to select the Prince executable ''' dialog = QFileDialog() dialog.setFileMode(QFileDialog.ExistingFile) filename = dialog.getOpenFileName(self, _('Select Prince executable'), '', '') if filename: try: self.exe.setText(filename) except(TypeError): self.exe.setText(filename[0]) def restore_defaults(self): ''' Restore the default settings ''' self.exe.setText(prefs.defaults['prince_exe']) self.fmts.setText(','.join(prefs.defaults['formats']).lower()) self.show_css.setChecked(prefs.defaults['show_CSS']) self.add_book.setChecked(prefs.defaults['add_book']) self.css_list.currentIndexChanged.disconnect() self.css_list.clear() self.CSS_list = prefs.defaults['custom_CSS_list'].copy() self.default_CSS = prefs.defaults['default_CSS'] for key in sorted(self.CSS_list, key=lambda x: x.lower()): self.css_list.addItem(key, key) self.css_list.setCurrentIndex(self.css_list.findText(self.default_CSS)) self.css_name.setText(self.default_CSS) self.css.load_text(self.CSS_list[unicode(self.css_list.currentText())],'css') self.css_list.currentIndexChanged.connect(self.set_css) def save_settings(self): ''' Save the current settings ''' prefs['prince_exe'] = unicode(self.exe.text()) prefs['formats'] = unicode(self.fmts.text().lower()).split(',') prefs['show_CSS'] = self.show_css.isChecked() prefs['add_book'] = self.add_book.isChecked() self.set_css() prefs['default_CSS'] = self.default_CSS prefs['custom_CSS_list'] = self.CSS_list.copy() if 'custom_CSS' in prefs: del prefs['custom_CSS'] def set_css(self): ''' Fill the CSS text box with the selected stylesheet ''' self.CSS_list[self.default_CSS] = unicode(self.css.toPlainText()) self.default_CSS = unicode(self.css_list.currentText()) self.css.load_text(self.CSS_list[self.default_CSS],'css') self.css_name.setText(self.css_list.currentText()) def add_css(self): ''' Add a new stylesheet ''' from calibre.gui2 import error_dialog name = unicode(self.css_name.text()) if name in self.CSS_list: error_dialog(self, _('Cannot add stylesheet'), _('A stylesheet with the name "%s" is already defined, use a different name.') % name, show=True) else: self.CSS_list[name] = '' self.css_list.addItem(name, name) self.css_list.setCurrentIndex(self.css_list.findText(name)) self.css_add.setEnabled(False) self.css_rename.setEnabled(False) def remove_css(self): ''' Remove an existing stylesheet ''' from calibre.gui2 import error_dialog if (self.css_list.count() > 1): self.css_list.currentIndexChanged.disconnect() self.css_list.removeItem(self.css_list.currentIndex()) del self.CSS_list[self.default_CSS] self.default_CSS = unicode(self.css_list.currentText()) self.css.load_text(self.CSS_list[self.default_CSS],'css') self.css_list.currentIndexChanged.connect(self.set_css) self.css_name.setText(self.css_list.currentText()) else: error_dialog(self, _('Cannot delete the last stylesheet'), _('The last stylesheet cannot be removed. You can rename it and/or remove its contents.'), show=True) def rename_css(self): ''' Rename a stylesheet ''' from calibre.gui2 import error_dialog name = unicode(self.css_name.text()) if name in self.CSS_list: error_dialog(self, _('Cannot rename stylesheet'), _('A stylesheet with the name "%s" is already defined, use a different name.') % name, show=True) else: self.CSS_list[name] = self.CSS_list.pop(self.default_CSS) self.css_list.setItemText(self.css_list.currentIndex(),name) self.default_CSS = name def check_names(self, text): name = unicode(text) if name in self.CSS_list: self.css_add.setEnabled(False) self.css_rename.setEnabled(False) else: self.css_add.setEnabled(True) self.css_rename.setEnabled(True)
class OpdsDialog(QDialog): def __init__(self, gui, icon, do_user_config): QDialog.__init__(self, gui) self.gui = gui self.do_user_config = do_user_config self.db = gui.current_db.new_api # The model for the book list self.model = OpdsBooksModel(None, self.dummy_books(), self.db) self.searchproxymodel = QSortFilterProxyModel(self) self.searchproxymodel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.searchproxymodel.setFilterKeyColumn(-1) self.searchproxymodel.setSourceModel(self.model) self.layout = QGridLayout() self.setLayout(self.layout) self.setWindowTitle('OPDS Client') self.setWindowIcon(icon) labelColumnWidths = [] self.opdsUrlLabel = QLabel('OPDS URL: ') self.layout.addWidget(self.opdsUrlLabel, 0, 0) labelColumnWidths.append(self.layout.itemAtPosition(0, 0).sizeHint().width()) config.convertSingleStringOpdsUrlPreferenceToListOfStringsPreference() self.opdsUrlEditor = QComboBox(self) self.opdsUrlEditor.activated.connect(self.opdsUrlEditorActivated) self.opdsUrlEditor.addItems(prefs['opds_url']) self.opdsUrlEditor.setEditable(True) self.opdsUrlEditor.setInsertPolicy(QComboBox.InsertAtTop) self.layout.addWidget(self.opdsUrlEditor, 0, 1, 1, 3) self.opdsUrlLabel.setBuddy(self.opdsUrlEditor) buttonColumnNumber = 7 buttonColumnWidths = [] self.about_button = QPushButton('About', self) self.about_button.setAutoDefault(False) self.about_button.clicked.connect(self.about) self.layout.addWidget(self.about_button, 0, buttonColumnNumber) buttonColumnWidths.append(self.layout.itemAtPosition(0, buttonColumnNumber).sizeHint().width()) # Initially download the catalogs found in the root catalog of the URL # selected at startup. Fail quietly on failing to open the URL catalogsTuple = self.model.downloadOpdsRootCatalog(self.gui, self.opdsUrlEditor.currentText(), False) print catalogsTuple firstCatalogTitle = catalogsTuple[0] self.currentOpdsCatalogs = catalogsTuple[1] # A dictionary of title->feedURL self.opdsCatalogSelectorLabel = QLabel('OPDS Catalog:') self.layout.addWidget(self.opdsCatalogSelectorLabel, 1, 0) labelColumnWidths.append(self.layout.itemAtPosition(1, 0).sizeHint().width()) self.opdsCatalogSelector = QComboBox(self) self.opdsCatalogSelector.setEditable(False) self.opdsCatalogSelectorModel = QStringListModel(self.currentOpdsCatalogs.keys()) self.opdsCatalogSelector.setModel(self.opdsCatalogSelectorModel) self.opdsCatalogSelector.setCurrentText(firstCatalogTitle) self.layout.addWidget(self.opdsCatalogSelector, 1, 1, 1, 3) self.download_opds_button = QPushButton('Download OPDS', self) self.download_opds_button.setAutoDefault(False) self.download_opds_button.clicked.connect(self.download_opds) self.layout.addWidget(self.download_opds_button, 1, buttonColumnNumber) buttonColumnWidths.append(self.layout.itemAtPosition(1, buttonColumnNumber).sizeHint().width()) # Search GUI self.searchEditor = QLineEdit(self) self.searchEditor.returnPressed.connect(self.searchBookList) self.layout.addWidget(self.searchEditor, 2, buttonColumnNumber - 2, 1, 2) self.searchButton = QPushButton('Search', self) self.searchButton.setAutoDefault(False) self.searchButton.clicked.connect(self.searchBookList) self.layout.addWidget(self.searchButton, 2, buttonColumnNumber) buttonColumnWidths.append(self.layout.itemAtPosition(2, buttonColumnNumber).sizeHint().width()) # The main book list self.library_view = QTableView(self) self.library_view.setAlternatingRowColors(True) self.library_view.setModel(self.searchproxymodel) self.library_view.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch) self.library_view.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch) self.library_view.horizontalHeader().setSectionResizeMode(2, QHeaderView.Stretch) self.library_view.setSelectionBehavior(QAbstractItemView.SelectRows) self.resizeAllLibraryViewLinesToHeaderHeight() self.library_view.resizeColumnsToContents() self.layout.addWidget(self.library_view, 3, 0, 3, buttonColumnNumber + 1) self.hideNewsCheckbox = QCheckBox('Hide Newspapers', self) self.hideNewsCheckbox.clicked.connect(self.setHideNewspapers) self.hideNewsCheckbox.setChecked(prefs['hideNewspapers']) self.layout.addWidget(self.hideNewsCheckbox, 6, 0, 1, 3) self.hideBooksAlreadyInLibraryCheckbox = QCheckBox('Hide books already in library', self) self.hideBooksAlreadyInLibraryCheckbox.clicked.connect(self.setHideBooksAlreadyInLibrary) self.hideBooksAlreadyInLibraryCheckbox.setChecked(prefs['hideBooksAlreadyInLibrary']) self.layout.addWidget(self.hideBooksAlreadyInLibraryCheckbox, 7, 0, 1, 3) # Let the checkbox initial state control the filtering self.model.setFilterBooksThatAreNewspapers(self.hideNewsCheckbox.isChecked()) self.model.setFilterBooksThatAreAlreadyInLibrary(self.hideBooksAlreadyInLibraryCheckbox.isChecked()) self.downloadButton = QPushButton('Download selected books', self) self.downloadButton.setAutoDefault(False) self.downloadButton.clicked.connect(self.downloadSelectedBooks) self.layout.addWidget(self.downloadButton, 6, buttonColumnNumber) buttonColumnWidths.append(self.layout.itemAtPosition(6, buttonColumnNumber).sizeHint().width()) self.fixTimestampButton = QPushButton('Fix timestamps of selection', self) self.fixTimestampButton.setAutoDefault(False) self.fixTimestampButton.clicked.connect(self.fixBookTimestamps) self.layout.addWidget(self.fixTimestampButton, 7, buttonColumnNumber) buttonColumnWidths.append(self.layout.itemAtPosition(7, buttonColumnNumber).sizeHint().width()) # Make all columns of the grid layout the same width as the button column buttonColumnWidth = max(buttonColumnWidths) for columnNumber in range(0, buttonColumnNumber): self.layout.setColumnMinimumWidth(columnNumber, buttonColumnWidth) # Make sure the first column isn't wider than the labels it holds labelColumnWidth = max(labelColumnWidths) self.layout.setColumnMinimumWidth(0, labelColumnWidth) self.resize(self.sizeHint()) def opdsUrlEditorActivated(self, text): prefs['opds_url'] = config.saveOpdsUrlCombobox(self.opdsUrlEditor) catalogsTuple = self.model.downloadOpdsRootCatalog(self.gui, self.opdsUrlEditor.currentText(), True) firstCatalogTitle = catalogsTuple[0] self.currentOpdsCatalogs = catalogsTuple[1] # A dictionary of title->feedURL self.opdsCatalogSelectorModel.setStringList(self.currentOpdsCatalogs.keys()) self.opdsCatalogSelector.setCurrentText(firstCatalogTitle) def setHideNewspapers(self, checked): prefs['hideNewspapers'] = checked self.model.setFilterBooksThatAreNewspapers(checked) self.resizeAllLibraryViewLinesToHeaderHeight() def setHideBooksAlreadyInLibrary(self, checked): prefs['hideBooksAlreadyInLibrary'] = checked self.model.setFilterBooksThatAreAlreadyInLibrary(checked) self.resizeAllLibraryViewLinesToHeaderHeight() def searchBookList(self): searchString = self.searchEditor.text() print "starting book list search for: %s" % searchString self.searchproxymodel.setFilterFixedString(searchString) def about(self): text = get_resources('about.txt') QMessageBox.about(self, 'About the OPDS Client plugin', text.decode('utf-8')) def download_opds(self): opdsCatalogUrl = self.currentOpdsCatalogs.get(self.opdsCatalogSelector.currentText(), None) if opdsCatalogUrl is None: # Just give up quietly return self.model.downloadOpdsCatalog(self.gui, opdsCatalogUrl) if self.model.isCalibreOpdsServer(): self.model.downloadMetadataUsingCalibreRestApi(self.opdsUrlEditor.currentText()) self.library_view.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch) self.library_view.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch) self.library_view.horizontalHeader().setSectionResizeMode(2, QHeaderView.Stretch) self.resizeAllLibraryViewLinesToHeaderHeight() self.resize(self.sizeHint()) def config(self): self.do_user_config(parent=self) def downloadSelectedBooks(self): selectionmodel = self.library_view.selectionModel() if selectionmodel.hasSelection(): rows = selectionmodel.selectedRows() for row in reversed(rows): book = row.data(Qt.UserRole) self.downloadBook(book) def downloadBook(self, book): if len(book.links) > 0: self.gui.download_ebook(book.links[0]) def fixBookTimestamps(self): selectionmodel = self.library_view.selectionModel() if selectionmodel.hasSelection(): rows = selectionmodel.selectedRows() for row in reversed(rows): book = row.data(Qt.UserRole) self.fixBookTimestamp(book) def fixBookTimestamp(self, book): bookTimestamp = book.timestamp identicalBookIds = self.findIdenticalBooksForBooksWithMultipleAuthors(book) bookIdToValMap = {} for identicalBookId in identicalBookIds: bookIdToValMap[identicalBookId] = bookTimestamp if len(bookIdToValMap) < 1: print "Failed to set timestamp of book: %s" % book self.db.set_field('timestamp', bookIdToValMap) def findIdenticalBooksForBooksWithMultipleAuthors(self, book): authorsList = book.authors if len(authorsList) < 2: return self.db.find_identical_books(book) # Try matching the authors one by one identicalBookIds = set() for author in authorsList: singleAuthorBook = Metadata(book.title, [author]) singleAuthorIdenticalBookIds = self.db.find_identical_books(singleAuthorBook) identicalBookIds = identicalBookIds.union(singleAuthorIdenticalBookIds) return identicalBookIds def dummy_books(self): dummy_author = ' ' * 40 dummy_title = ' ' * 60 books_list = [] for line in range (1, 10): book = DynamicBook() book.author = dummy_author book.title = dummy_title book.updated = datetime.datetime.now().strftime('%Y-%m-%dT%H:%M:%S+00:00') book.id = '' books_list.append(book) return books_list def resizeAllLibraryViewLinesToHeaderHeight(self): rowHeight = self.library_view.horizontalHeader().height() for rowNumber in range (0, self.library_view.model().rowCount()): self.library_view.setRowHeight(rowNumber, rowHeight)
class OpdsDialog(QDialog): def __init__(self, gui, icon, do_user_config): QDialog.__init__(self, gui) self.gui = gui self.do_user_config = do_user_config self.db = gui.current_db.new_api # The model for the book list self.model = OpdsBooksModel(None, self.dummy_books(), self.db) self.searchproxymodel = QSortFilterProxyModel(self) self.searchproxymodel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.searchproxymodel.setFilterKeyColumn(-1) self.searchproxymodel.setSourceModel(self.model) self.layout = QGridLayout() self.setLayout(self.layout) self.setWindowTitle("OPDS Client") self.setWindowIcon(icon) labelColumnWidths = [] self.opdsUrlLabel = QLabel("OPDS URL: ") self.layout.addWidget(self.opdsUrlLabel, 0, 0) labelColumnWidths.append(self.layout.itemAtPosition(0, 0).sizeHint().width()) config.convertSingleStringOpdsUrlPreferenceToListOfStringsPreference() self.opdsUrlEditor = QComboBox(self) self.opdsUrlEditor.activated.connect(self.opdsUrlEditorActivated) self.opdsUrlEditor.addItems(prefs["opds_url"]) self.opdsUrlEditor.setEditable(True) self.opdsUrlEditor.setInsertPolicy(QComboBox.InsertAtTop) self.layout.addWidget(self.opdsUrlEditor, 0, 1, 1, 3) self.opdsUrlLabel.setBuddy(self.opdsUrlEditor) buttonColumnNumber = 7 buttonColumnWidths = [] self.about_button = QPushButton("About", self) self.about_button.setAutoDefault(False) self.about_button.clicked.connect(self.about) self.layout.addWidget(self.about_button, 0, buttonColumnNumber) buttonColumnWidths.append( self.layout.itemAtPosition(0, buttonColumnNumber).sizeHint().width() ) # Initially download the catalogs found in the root catalog of the URL # selected at startup. Fail quietly on failing to open the URL catalogsTuple = self.model.downloadOpdsRootCatalog( self.gui, self.opdsUrlEditor.currentText(), False ) print(catalogsTuple) firstCatalogTitle = catalogsTuple[0] self.currentOpdsCatalogs = catalogsTuple[1] # A dictionary of title->feedURL self.opdsCatalogSelectorLabel = QLabel("OPDS Catalog:") self.layout.addWidget(self.opdsCatalogSelectorLabel, 1, 0) labelColumnWidths.append(self.layout.itemAtPosition(1, 0).sizeHint().width()) self.opdsCatalogSelector = QComboBox(self) self.opdsCatalogSelector.setEditable(False) self.opdsCatalogSelectorModel = QStringListModel(self.currentOpdsCatalogs.keys()) self.opdsCatalogSelector.setModel(self.opdsCatalogSelectorModel) self.opdsCatalogSelector.setCurrentText(firstCatalogTitle) self.layout.addWidget(self.opdsCatalogSelector, 1, 1, 1, 3) self.download_opds_button = QPushButton("Download OPDS", self) self.download_opds_button.setAutoDefault(False) self.download_opds_button.clicked.connect(self.download_opds) self.layout.addWidget(self.download_opds_button, 1, buttonColumnNumber) buttonColumnWidths.append( self.layout.itemAtPosition(1, buttonColumnNumber).sizeHint().width() ) # Search GUI self.searchEditor = QLineEdit(self) self.searchEditor.returnPressed.connect(self.searchBookList) self.layout.addWidget(self.searchEditor, 2, buttonColumnNumber - 2, 1, 2) self.searchButton = QPushButton("Search", self) self.searchButton.setAutoDefault(False) self.searchButton.clicked.connect(self.searchBookList) self.layout.addWidget(self.searchButton, 2, buttonColumnNumber) buttonColumnWidths.append( self.layout.itemAtPosition(2, buttonColumnNumber).sizeHint().width() ) # The main book list self.library_view = QTableView(self) self.library_view.setAlternatingRowColors(True) self.library_view.setModel(self.searchproxymodel) self.library_view.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch) self.library_view.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch) self.library_view.horizontalHeader().setSectionResizeMode(2, QHeaderView.Stretch) self.library_view.setSelectionBehavior(QAbstractItemView.SelectRows) self.resizeAllLibraryViewLinesToHeaderHeight() self.library_view.resizeColumnsToContents() self.layout.addWidget(self.library_view, 3, 0, 3, buttonColumnNumber + 1) self.hideNewsCheckbox = QCheckBox("Hide Newspapers", self) self.hideNewsCheckbox.clicked.connect(self.setHideNewspapers) self.hideNewsCheckbox.setChecked(prefs["hideNewspapers"]) self.layout.addWidget(self.hideNewsCheckbox, 6, 0, 1, 3) self.hideBooksAlreadyInLibraryCheckbox = QCheckBox("Hide books already in library", self) self.hideBooksAlreadyInLibraryCheckbox.clicked.connect(self.setHideBooksAlreadyInLibrary) self.hideBooksAlreadyInLibraryCheckbox.setChecked(prefs["hideBooksAlreadyInLibrary"]) self.layout.addWidget(self.hideBooksAlreadyInLibraryCheckbox, 7, 0, 1, 3) # Let the checkbox initial state control the filtering self.model.setFilterBooksThatAreNewspapers(self.hideNewsCheckbox.isChecked()) self.model.setFilterBooksThatAreAlreadyInLibrary( self.hideBooksAlreadyInLibraryCheckbox.isChecked() ) self.downloadButton = QPushButton("Download selected books", self) self.downloadButton.setAutoDefault(False) self.downloadButton.clicked.connect(self.downloadSelectedBooks) self.layout.addWidget(self.downloadButton, 6, buttonColumnNumber) buttonColumnWidths.append( self.layout.itemAtPosition(6, buttonColumnNumber).sizeHint().width() ) self.fixTimestampButton = QPushButton("Fix timestamps of selection", self) self.fixTimestampButton.setAutoDefault(False) self.fixTimestampButton.clicked.connect(self.fixBookTimestamps) self.layout.addWidget(self.fixTimestampButton, 7, buttonColumnNumber) buttonColumnWidths.append( self.layout.itemAtPosition(7, buttonColumnNumber).sizeHint().width() ) # Make all columns of the grid layout the same width as the button column buttonColumnWidth = max(buttonColumnWidths) for columnNumber in range(0, buttonColumnNumber): self.layout.setColumnMinimumWidth(columnNumber, buttonColumnWidth) # Make sure the first column isn't wider than the labels it holds labelColumnWidth = max(labelColumnWidths) self.layout.setColumnMinimumWidth(0, labelColumnWidth) self.resize(self.sizeHint()) def opdsUrlEditorActivated(self, text): prefs["opds_url"] = config.saveOpdsUrlCombobox(self.opdsUrlEditor) catalogsTuple = self.model.downloadOpdsRootCatalog( self.gui, self.opdsUrlEditor.currentText(), True ) firstCatalogTitle = catalogsTuple[0] self.currentOpdsCatalogs = catalogsTuple[1] # A dictionary of title->feedURL self.opdsCatalogSelectorModel.setStringList(self.currentOpdsCatalogs.keys()) self.opdsCatalogSelector.setCurrentText(firstCatalogTitle) def setHideNewspapers(self, checked): prefs["hideNewspapers"] = checked self.model.setFilterBooksThatAreNewspapers(checked) self.resizeAllLibraryViewLinesToHeaderHeight() def setHideBooksAlreadyInLibrary(self, checked): prefs["hideBooksAlreadyInLibrary"] = checked self.model.setFilterBooksThatAreAlreadyInLibrary(checked) self.resizeAllLibraryViewLinesToHeaderHeight() def searchBookList(self): searchString = self.searchEditor.text() print("starting book list search for: %s" % searchString) self.searchproxymodel.setFilterFixedString(searchString) def about(self): text = get_resources("about.txt") QMessageBox.about(self, "About the OPDS Client plugin", text.decode("utf-8")) def download_opds(self): opdsCatalogUrl = self.currentOpdsCatalogs.get(self.opdsCatalogSelector.currentText(), None) if opdsCatalogUrl is None: # Just give up quietly return self.model.downloadOpdsCatalog(self.gui, opdsCatalogUrl) if self.model.isCalibreOpdsServer(): self.model.downloadMetadataUsingCalibreRestApi(self.opdsUrlEditor.currentText()) self.library_view.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch) self.library_view.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch) self.library_view.horizontalHeader().setSectionResizeMode(2, QHeaderView.Stretch) self.resizeAllLibraryViewLinesToHeaderHeight() self.resize(self.sizeHint()) def config(self): self.do_user_config(parent=self) def downloadSelectedBooks(self): selectionmodel = self.library_view.selectionModel() if selectionmodel.hasSelection(): rows = selectionmodel.selectedRows() for row in reversed(rows): book = row.data(Qt.UserRole) self.downloadBook(book) def downloadBook(self, book): if len(book.links) > 0: self.gui.download_ebook(book.links[0]) def fixBookTimestamps(self): selectionmodel = self.library_view.selectionModel() if selectionmodel.hasSelection(): rows = selectionmodel.selectedRows() for row in reversed(rows): book = row.data(Qt.UserRole) self.fixBookTimestamp(book) def fixBookTimestamp(self, book): bookTimestamp = book.timestamp identicalBookIds = self.findIdenticalBooksForBooksWithMultipleAuthors(book) bookIdToValMap = {} for identicalBookId in identicalBookIds: bookIdToValMap[identicalBookId] = bookTimestamp if len(bookIdToValMap) < 1: print("Failed to set timestamp of book: %s" % book) self.db.set_field("timestamp", bookIdToValMap) def findIdenticalBooksForBooksWithMultipleAuthors(self, book): authorsList = book.authors if len(authorsList) < 2: return self.db.find_identical_books(book) # Try matching the authors one by one identicalBookIds = set() for author in authorsList: singleAuthorBook = Metadata(book.title, [author]) singleAuthorIdenticalBookIds = self.db.find_identical_books(singleAuthorBook) identicalBookIds = identicalBookIds.union(singleAuthorIdenticalBookIds) return identicalBookIds def dummy_books(self): dummy_author = " " * 40 dummy_title = " " * 60 books_list = [] for line in range(1, 10): book = DynamicBook() book.author = dummy_author book.title = dummy_title book.updated = datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%S+00:00") book.id = "" books_list.append(book) return books_list def resizeAllLibraryViewLinesToHeaderHeight(self): rowHeight = self.library_view.horizontalHeader().height() for rowNumber in range(0, self.library_view.model().rowCount()): self.library_view.setRowHeight(rowNumber, rowHeight)
class Config(QDialog): ''' Configuration dialog for single book conversion. If accepted, has the following important attributes output_format - Output format (without a leading .) input_format - Input format (without a leading .) opf_path - Path to OPF file with user specified metadata cover_path - Path to user specified cover (can be None) recommendations - A pickled list of 3 tuples in the same format as the recommendations member of the Input/Output plugins. ''' def __init__(self, parent, db, book_id, preferred_input_format=None, preferred_output_format=None): QDialog.__init__(self, parent) self.setupUi() self.opt_individual_saved_settings.setVisible(False) self.db, self.book_id = db, book_id self.setup_input_output_formats(self.db, self.book_id, preferred_input_format, preferred_output_format) self.setup_pipeline() self.input_formats.currentIndexChanged[native_string_type].connect( self.setup_pipeline) self.output_formats.currentIndexChanged[native_string_type].connect( self.setup_pipeline) self.groups.setSpacing(5) self.groups.activated[(QModelIndex)].connect(self.show_pane) self.groups.clicked[(QModelIndex)].connect(self.show_pane) self.groups.entered[(QModelIndex)].connect(self.show_group_help) rb = self.buttonBox.button(self.buttonBox.RestoreDefaults) rb.setText(_('Restore &defaults')) rb.clicked.connect(self.restore_defaults) self.groups.setMouseTracking(True) geom = gprefs.get('convert_single_dialog_geom', None) if geom: self.restoreGeometry(geom) else: self.resize(self.sizeHint()) def setupUi(self): self.setObjectName("Dialog") self.resize(1024, 700) self.setWindowIcon(QIcon(I('convert.png'))) self.gridLayout = QGridLayout(self) self.gridLayout.setObjectName("gridLayout") self.horizontalLayout = QHBoxLayout() self.horizontalLayout.setObjectName("horizontalLayout") self.input_label = QLabel(self) self.input_label.setObjectName("input_label") self.horizontalLayout.addWidget(self.input_label) self.input_formats = QComboBox(self) self.input_formats.setSizeAdjustPolicy( QComboBox.AdjustToMinimumContentsLengthWithIcon) self.input_formats.setMinimumContentsLength(5) self.input_formats.setObjectName("input_formats") self.horizontalLayout.addWidget(self.input_formats) self.opt_individual_saved_settings = QCheckBox(self) self.opt_individual_saved_settings.setObjectName( "opt_individual_saved_settings") self.horizontalLayout.addWidget(self.opt_individual_saved_settings) spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.horizontalLayout.addItem(spacerItem) self.label_2 = QLabel(self) self.label_2.setObjectName("label_2") self.horizontalLayout.addWidget(self.label_2) self.output_formats = QComboBox(self) self.output_formats.setSizeAdjustPolicy( QComboBox.AdjustToMinimumContentsLengthWithIcon) self.output_formats.setMinimumContentsLength(5) self.output_formats.setObjectName("output_formats") self.horizontalLayout.addWidget(self.output_formats) self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 2) self.groups = QListView(self) sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(1) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.groups.sizePolicy().hasHeightForWidth()) self.groups.setSizePolicy(sizePolicy) self.groups.setTabKeyNavigation(True) self.groups.setIconSize(QSize(48, 48)) self.groups.setWordWrap(True) self.groups.setObjectName("groups") self.gridLayout.addWidget(self.groups, 1, 0, 3, 1) self.scrollArea = QScrollArea(self) sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(4) sizePolicy.setVerticalStretch(10) sizePolicy.setHeightForWidth( self.scrollArea.sizePolicy().hasHeightForWidth()) self.scrollArea.setSizePolicy(sizePolicy) self.scrollArea.setFrameShape(QFrame.NoFrame) self.scrollArea.setLineWidth(0) self.scrollArea.setWidgetResizable(True) self.scrollArea.setObjectName("scrollArea") self.scrollAreaWidgetContents = QWidget() self.scrollAreaWidgetContents.setGeometry(QRect(0, 0, 810, 494)) self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents") self.verticalLayout_3 = QVBoxLayout(self.scrollAreaWidgetContents) self.verticalLayout_3.setContentsMargins(0, 0, 0, 0) self.verticalLayout_3.setObjectName("verticalLayout_3") self.stack = QStackedWidget(self.scrollAreaWidgetContents) sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.stack.sizePolicy().hasHeightForWidth()) self.stack.setSizePolicy(sizePolicy) self.stack.setObjectName("stack") self.page = QWidget() self.page.setObjectName("page") self.stack.addWidget(self.page) self.page_2 = QWidget() self.page_2.setObjectName("page_2") self.stack.addWidget(self.page_2) self.verticalLayout_3.addWidget(self.stack) self.scrollArea.setWidget(self.scrollAreaWidgetContents) self.gridLayout.addWidget(self.scrollArea, 1, 1, 1, 1) self.buttonBox = QDialogButtonBox(self) self.buttonBox.setOrientation(Qt.Horizontal) self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok | QDialogButtonBox.RestoreDefaults) self.buttonBox.setObjectName("buttonBox") self.gridLayout.addWidget(self.buttonBox, 3, 1, 1, 1) self.help = QTextEdit(self) sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.help.sizePolicy().hasHeightForWidth()) self.help.setSizePolicy(sizePolicy) self.help.setMaximumHeight(80) self.help.setObjectName("help") self.gridLayout.addWidget(self.help, 2, 1, 1, 1) self.input_label.setBuddy(self.input_formats) self.label_2.setBuddy(self.output_formats) self.input_label.setText(_("&Input format:")) self.opt_individual_saved_settings.setText( _("Use &saved conversion settings for individual books")) self.label_2.setText(_("&Output format:")) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) def sizeHint(self): desktop = QCoreApplication.instance().desktop() geom = desktop.availableGeometry(self) nh, nw = max(300, geom.height() - 100), max(400, geom.width() - 70) return QSize(nw, nh) def restore_defaults(self): delete_specifics(self.db, self.book_id) self.setup_pipeline() @property def input_format(self): return unicode_type(self.input_formats.currentText()).lower() @property def output_format(self): return unicode_type(self.output_formats.currentText()).lower() @property def manually_fine_tune_toc(self): for i in range(self.stack.count()): w = self.stack.widget(i) if hasattr(w, 'manually_fine_tune_toc'): return w.manually_fine_tune_toc.isChecked() def setup_pipeline(self, *args): oidx = self.groups.currentIndex().row() input_format = self.input_format output_format = self.output_format self.plumber = create_dummy_plumber(input_format, output_format) def widget_factory(cls): return cls(self.stack, self.plumber.get_option_by_name, self.plumber.get_option_help, self.db, self.book_id) self.mw = widget_factory(MetadataWidget) self.setWindowTitle( _('Convert') + ' ' + unicode_type(self.mw.title.text())) lf = widget_factory(LookAndFeelWidget) hw = widget_factory(HeuristicsWidget) sr = widget_factory(SearchAndReplaceWidget) ps = widget_factory(PageSetupWidget) sd = widget_factory(StructureDetectionWidget) toc = widget_factory(TOCWidget) from calibre.gui2.actions.toc_edit import SUPPORTED toc.manually_fine_tune_toc.setVisible( output_format.upper() in SUPPORTED) debug = widget_factory(DebugWidget) output_widget = self.plumber.output_plugin.gui_configuration_widget( self.stack, self.plumber.get_option_by_name, self.plumber.get_option_help, self.db, self.book_id) input_widget = self.plumber.input_plugin.gui_configuration_widget( self.stack, self.plumber.get_option_by_name, self.plumber.get_option_help, self.db, self.book_id) while True: c = self.stack.currentWidget() if not c: break self.stack.removeWidget(c) widgets = [self.mw, lf, hw, ps, sd, toc, sr] if input_widget is not None: widgets.append(input_widget) if output_widget is not None: widgets.append(output_widget) widgets.append(debug) for w in widgets: self.stack.addWidget(w) w.set_help_signal.connect(self.help.setPlainText) self._groups_model = GroupModel(widgets) self.groups.setModel(self._groups_model) idx = oidx if -1 < oidx < self._groups_model.rowCount() else 0 self.groups.setCurrentIndex(self._groups_model.index(idx)) self.stack.setCurrentIndex(idx) try: shutil.rmtree(self.plumber.archive_input_tdir, ignore_errors=True) except: pass def setup_input_output_formats(self, db, book_id, preferred_input_format, preferred_output_format): if preferred_output_format: preferred_output_format = preferred_output_format.upper() output_formats = get_output_formats(preferred_output_format) input_format, input_formats = get_input_format_for_book( db, book_id, preferred_input_format) preferred_output_format = preferred_output_format if \ preferred_output_format in output_formats else \ sort_formats_by_preference(output_formats, [prefs['output_format']])[0] self.input_formats.addItems( (unicode_type(x.upper()) for x in input_formats)) self.output_formats.addItems( (unicode_type(x.upper()) for x in output_formats)) self.input_formats.setCurrentIndex(input_formats.index(input_format)) self.output_formats.setCurrentIndex( output_formats.index(preferred_output_format)) def show_pane(self, index): self.stack.setCurrentIndex(index.row()) def accept(self): recs = GuiRecommendations() for w in self._groups_model.widgets: if not w.pre_commit_check(): return x = w.commit(save_defaults=False) recs.update(x) self.opf_file, self.cover_file = self.mw.opf_file, self.mw.cover_file self._recommendations = recs if self.db is not None: recs['gui_preferred_input_format'] = self.input_format save_specifics(self.db, self.book_id, recs) self.break_cycles() QDialog.accept(self) def reject(self): self.break_cycles() QDialog.reject(self) def done(self, r): if self.isVisible(): gprefs['convert_single_dialog_geom'] = \ bytearray(self.saveGeometry()) return QDialog.done(self, r) def break_cycles(self): for i in range(self.stack.count()): w = self.stack.widget(i) w.break_cycles() @property def recommendations(self): recs = [(k, v, OptionRecommendation.HIGH) for k, v in self._recommendations.items()] return recs def show_group_help(self, index): widget = self._groups_model.widgets[index.row()] self.help.setPlainText(widget.HELP)
class ConfigWidget(QWidget, Logger): ''' Config dialog for Marvin Manager ''' WIZARD_PROFILES = { 'Annotations': { 'label': 'mm_annotations', 'datatype': 'comments', 'display': {}, 'is_multiple': False }, 'Collections': { 'label': 'mm_collections', 'datatype': 'text', 'display': {u'is_names': False}, 'is_multiple': True }, 'Last read': { 'label': 'mm_date_read', 'datatype': 'datetime', 'display': {}, 'is_multiple': False }, 'Locked': { 'label': 'mm_locked', 'datatype': 'bool', 'display': {}, 'is_multiple': False }, 'Progress': { 'label': 'mm_progress', 'datatype': 'float', 'display': {u'number_format': u'{0:.0f}%'}, 'is_multiple': False }, 'Read': { 'label': 'mm_read', 'datatype': 'bool', 'display': {}, 'is_multiple': False }, 'Reading list': { 'label': 'mm_reading_list', 'datatype': 'bool', 'display': {}, 'is_multiple': False }, 'Word count': { 'label': 'mm_word_count', 'datatype': 'int', 'display': {u'number_format': u'{0:n}'}, 'is_multiple': False } } def __init__(self, plugin_action): QWidget.__init__(self) self.parent = plugin_action self.gui = get_gui() self.icon = plugin_action.icon self.opts = plugin_action.opts self.prefs = plugin_prefs self.resources_path = plugin_action.resources_path self.verbose = plugin_action.verbose self.restart_required = False self._log_location() self.l = QGridLayout() self.setLayout(self.l) self.column1_layout = QVBoxLayout() self.l.addLayout(self.column1_layout, 0, 0) self.column2_layout = QVBoxLayout() self.l.addLayout(self.column2_layout, 0, 1) # ----------------------------- Column 1 ----------------------------- # ~~~~~~~~ Create the Custom fields options group box ~~~~~~~~ self.cfg_custom_fields_gb = QGroupBox(self) self.cfg_custom_fields_gb.setTitle('Custom column assignments') self.column1_layout.addWidget(self.cfg_custom_fields_gb) self.cfg_custom_fields_qgl = QGridLayout(self.cfg_custom_fields_gb) current_row = 0 # ++++++++ Labels + HLine ++++++++ self.marvin_source_label = QLabel("Marvin source") self.cfg_custom_fields_qgl.addWidget(self.marvin_source_label, current_row, 0) self.calibre_destination_label = QLabel("calibre destination") self.cfg_custom_fields_qgl.addWidget(self.calibre_destination_label, current_row, 1) current_row += 1 self.sd_hl = QFrame(self.cfg_custom_fields_gb) self.sd_hl.setFrameShape(QFrame.HLine) self.sd_hl.setFrameShadow(QFrame.Raised) self.cfg_custom_fields_qgl.addWidget(self.sd_hl, current_row, 0, 1, 3) current_row += 1 # ++++++++ Annotations ++++++++ self.cfg_annotations_label = QLabel('Annotations') self.cfg_annotations_label.setAlignment(Qt.AlignLeft) self.cfg_custom_fields_qgl.addWidget(self.cfg_annotations_label, current_row, 0) self.annotations_field_comboBox = QComboBox(self.cfg_custom_fields_gb) self.annotations_field_comboBox.setObjectName('annotations_field_comboBox') self.annotations_field_comboBox.setToolTip('Select a custom column to store Marvin annotations') self.cfg_custom_fields_qgl.addWidget(self.annotations_field_comboBox, current_row, 1) self.cfg_highlights_wizard = QToolButton() self.cfg_highlights_wizard.setIcon(QIcon(I('wizard.png'))) self.cfg_highlights_wizard.setToolTip("Create a custom column to store Marvin annotations") self.cfg_highlights_wizard.clicked.connect(partial(self.launch_cc_wizard, 'Annotations')) self.cfg_custom_fields_qgl.addWidget(self.cfg_highlights_wizard, current_row, 2) current_row += 1 # ++++++++ Collections ++++++++ self.cfg_collections_label = QLabel('Collections') self.cfg_collections_label.setAlignment(Qt.AlignLeft) self.cfg_custom_fields_qgl.addWidget(self.cfg_collections_label, current_row, 0) self.collection_field_comboBox = QComboBox(self.cfg_custom_fields_gb) self.collection_field_comboBox.setObjectName('collection_field_comboBox') self.collection_field_comboBox.setToolTip('Select a custom column to store Marvin collection assignments') self.cfg_custom_fields_qgl.addWidget(self.collection_field_comboBox, current_row, 1) self.cfg_collections_wizard = QToolButton() self.cfg_collections_wizard.setIcon(QIcon(I('wizard.png'))) self.cfg_collections_wizard.setToolTip("Create a custom column for Marvin collection assignments") self.cfg_collections_wizard.clicked.connect(partial(self.launch_cc_wizard, 'Collections')) self.cfg_custom_fields_qgl.addWidget(self.cfg_collections_wizard, current_row, 2) current_row += 1 # ++++++++ Last read ++++++++ self.cfg_date_read_label = QLabel("Last read") self.cfg_date_read_label.setAlignment(Qt.AlignLeft) self.cfg_custom_fields_qgl.addWidget(self.cfg_date_read_label, current_row, 0) self.date_read_field_comboBox = QComboBox(self.cfg_custom_fields_gb) self.date_read_field_comboBox.setObjectName('date_read_field_comboBox') self.date_read_field_comboBox.setToolTip('Select a custom column to store Last read date') self.cfg_custom_fields_qgl.addWidget(self.date_read_field_comboBox, current_row, 1) self.cfg_collections_wizard = QToolButton() self.cfg_collections_wizard.setIcon(QIcon(I('wizard.png'))) self.cfg_collections_wizard.setToolTip("Create a custom column to store Last read date") self.cfg_collections_wizard.clicked.connect(partial(self.launch_cc_wizard, 'Last read')) self.cfg_custom_fields_qgl.addWidget(self.cfg_collections_wizard, current_row, 2) current_row += 1 # ++++++++ Locked ++++++++ self.cfg_locked_label = QLabel("Locked") self.cfg_locked_label.setAlignment(Qt.AlignLeft) self.cfg_custom_fields_qgl.addWidget(self.cfg_locked_label, current_row, 0) self.locked_field_comboBox = QComboBox(self.cfg_custom_fields_gb) self.locked_field_comboBox.setObjectName('locked_field_comboBox') self.locked_field_comboBox.setToolTip('Select a custom column to store Locked status') self.cfg_custom_fields_qgl.addWidget(self.locked_field_comboBox, current_row, 1) self.cfg_locked_wizard = QToolButton() self.cfg_locked_wizard.setIcon(QIcon(I('wizard.png'))) self.cfg_locked_wizard.setToolTip("Create a custom column to store Locked status") self.cfg_locked_wizard.clicked.connect(partial(self.launch_cc_wizard, 'Locked')) self.cfg_custom_fields_qgl.addWidget(self.cfg_locked_wizard, current_row, 2) current_row += 1 # ++++++++ Progress ++++++++ self.cfg_progress_label = QLabel('Progress') self.cfg_progress_label.setAlignment(Qt.AlignLeft) self.cfg_custom_fields_qgl.addWidget(self.cfg_progress_label, current_row, 0) self.progress_field_comboBox = QComboBox(self.cfg_custom_fields_gb) self.progress_field_comboBox.setObjectName('progress_field_comboBox') self.progress_field_comboBox.setToolTip('Select a custom column to store Marvin reading progress') self.cfg_custom_fields_qgl.addWidget(self.progress_field_comboBox, current_row, 1) self.cfg_progress_wizard = QToolButton() self.cfg_progress_wizard.setIcon(QIcon(I('wizard.png'))) self.cfg_progress_wizard.setToolTip("Create a custom column to store Marvin reading progress") self.cfg_progress_wizard.clicked.connect(partial(self.launch_cc_wizard, 'Progress')) self.cfg_custom_fields_qgl.addWidget(self.cfg_progress_wizard, current_row, 2) current_row += 1 # ++++++++ Read flag ++++++++ self.cfg_read_label = QLabel('Read') self.cfg_read_label.setAlignment(Qt.AlignLeft) self.cfg_custom_fields_qgl.addWidget(self.cfg_read_label, current_row, 0) self.read_field_comboBox = QComboBox(self.cfg_custom_fields_gb) self.read_field_comboBox.setObjectName('read_field_comboBox') self.read_field_comboBox.setToolTip('Select a custom column to store Marvin Read status') self.cfg_custom_fields_qgl.addWidget(self.read_field_comboBox, current_row, 1) self.cfg_read_wizard = QToolButton() self.cfg_read_wizard.setIcon(QIcon(I('wizard.png'))) self.cfg_read_wizard.setToolTip("Create a custom column to store Marvin Read status") self.cfg_read_wizard.clicked.connect(partial(self.launch_cc_wizard, 'Read')) self.cfg_custom_fields_qgl.addWidget(self.cfg_read_wizard, current_row, 2) current_row += 1 # ++++++++ Reading list flag ++++++++ self.cfg_reading_list_label = QLabel('Reading list') self.cfg_reading_list_label.setAlignment(Qt.AlignLeft) self.cfg_custom_fields_qgl.addWidget(self.cfg_reading_list_label, current_row, 0) self.reading_list_field_comboBox = QComboBox(self.cfg_custom_fields_gb) self.reading_list_field_comboBox.setObjectName('reading_list_field_comboBox') self.reading_list_field_comboBox.setToolTip('Select a custom column to store Marvin Reading list status') self.cfg_custom_fields_qgl.addWidget(self.reading_list_field_comboBox, current_row, 1) self.cfg_reading_list_wizard = QToolButton() self.cfg_reading_list_wizard.setIcon(QIcon(I('wizard.png'))) self.cfg_reading_list_wizard.setToolTip("Create a custom column to store Marvin Reading list status") self.cfg_reading_list_wizard.clicked.connect(partial(self.launch_cc_wizard, 'Reading list')) self.cfg_custom_fields_qgl.addWidget(self.cfg_reading_list_wizard, current_row, 2) current_row += 1 # ++++++++ Word count ++++++++ self.cfg_word_count_label = QLabel('Word count') self.cfg_word_count_label.setAlignment(Qt.AlignLeft) self.cfg_custom_fields_qgl.addWidget(self.cfg_word_count_label, current_row, 0) self.word_count_field_comboBox = QComboBox(self.cfg_custom_fields_gb) self.word_count_field_comboBox.setObjectName('word_count_field_comboBox') self.word_count_field_comboBox.setToolTip('Select a custom column to store Marvin word counts') self.cfg_custom_fields_qgl.addWidget(self.word_count_field_comboBox, current_row, 1) self.cfg_word_count_wizard = QToolButton() self.cfg_word_count_wizard.setIcon(QIcon(I('wizard.png'))) self.cfg_word_count_wizard.setToolTip("Create a custom column to store Marvin word counts") self.cfg_word_count_wizard.clicked.connect(partial(self.launch_cc_wizard, 'Word count')) self.cfg_custom_fields_qgl.addWidget(self.cfg_word_count_wizard, current_row, 2) current_row += 1 self.spacerItem1 = QSpacerItem(20, 20, QSizePolicy.Minimum, QSizePolicy.Expanding) self.column1_layout.addItem(self.spacerItem1) # ----------------------------- Column 2 ----------------------------- # ~~~~~~~~ Create the CSS group box ~~~~~~~~ self.cfg_css_options_gb = QGroupBox(self) self.cfg_css_options_gb.setTitle('CSS') self.column2_layout.addWidget(self.cfg_css_options_gb) self.cfg_css_options_qgl = QGridLayout(self.cfg_css_options_gb) current_row = 0 # ++++++++ Annotations appearance ++++++++ self.annotations_icon = QIcon(os.path.join(self.resources_path, 'icons', 'annotations_hiliter.png')) self.cfg_annotations_appearance_toolbutton = QToolButton() self.cfg_annotations_appearance_toolbutton.setIcon(self.annotations_icon) self.cfg_annotations_appearance_toolbutton.clicked.connect(self.configure_appearance) self.cfg_css_options_qgl.addWidget(self.cfg_annotations_appearance_toolbutton, current_row, 0) self.cfg_annotations_label = ClickableQLabel("Book notes, Bookmark notes and Annotations") self.cfg_annotations_label.clicked.connect(self.configure_appearance) self.cfg_css_options_qgl.addWidget(self.cfg_annotations_label, current_row, 1) current_row += 1 # ++++++++ Injected CSS ++++++++ self.css_editor_icon = QIcon(I('format-text-heading.png')) self.cfg_css_editor_toolbutton = QToolButton() self.cfg_css_editor_toolbutton.setIcon(self.css_editor_icon) self.cfg_css_editor_toolbutton.clicked.connect(self.edit_css) self.cfg_css_options_qgl.addWidget(self.cfg_css_editor_toolbutton, current_row, 0) self.cfg_css_editor_label = ClickableQLabel("Articles, Vocabulary") self.cfg_css_editor_label.clicked.connect(self.edit_css) self.cfg_css_options_qgl.addWidget(self.cfg_css_editor_label, current_row, 1) """ # ~~~~~~~~ Create the Dropbox syncing group box ~~~~~~~~ self.cfg_dropbox_syncing_gb = QGroupBox(self) self.cfg_dropbox_syncing_gb.setTitle('Dropbox') self.column2_layout.addWidget(self.cfg_dropbox_syncing_gb) self.cfg_dropbox_syncing_qgl = QGridLayout(self.cfg_dropbox_syncing_gb) current_row = 0 # ++++++++ Syncing enabled checkbox ++++++++ self.dropbox_syncing_checkbox = QCheckBox('Enable Dropbox updates') self.dropbox_syncing_checkbox.setObjectName('dropbox_syncing') self.dropbox_syncing_checkbox.setToolTip('Refresh custom column content from Marvin metadata') self.cfg_dropbox_syncing_qgl.addWidget(self.dropbox_syncing_checkbox, current_row, 0, 1, 3) current_row += 1 # ++++++++ Dropbox folder picker ++++++++ self.dropbox_folder_icon = QIcon(os.path.join(self.resources_path, 'icons', 'dropbox.png')) self.cfg_dropbox_folder_toolbutton = QToolButton() self.cfg_dropbox_folder_toolbutton.setIcon(self.dropbox_folder_icon) self.cfg_dropbox_folder_toolbutton.setToolTip("Specify Dropbox folder location on your computer") self.cfg_dropbox_folder_toolbutton.clicked.connect(self.select_dropbox_folder) self.cfg_dropbox_syncing_qgl.addWidget(self.cfg_dropbox_folder_toolbutton, current_row, 1) # ++++++++ Dropbox location lineedit ++++++++ self.dropbox_location_lineedit = QLineEdit() self.dropbox_location_lineedit.setPlaceholderText("Dropbox folder location") self.cfg_dropbox_syncing_qgl.addWidget(self.dropbox_location_lineedit, current_row, 2) """ # ~~~~~~~~ Create the General options group box ~~~~~~~~ self.cfg_runtime_options_gb = QGroupBox(self) self.cfg_runtime_options_gb.setTitle('General options') self.column2_layout.addWidget(self.cfg_runtime_options_gb) self.cfg_runtime_options_qvl = QVBoxLayout(self.cfg_runtime_options_gb) # ++++++++ Temporary markers: Duplicates ++++++++ self.duplicate_markers_checkbox = QCheckBox('Apply temporary markers to duplicate books') self.duplicate_markers_checkbox.setObjectName('apply_markers_to_duplicates') self.duplicate_markers_checkbox.setToolTip('Books with identical content will be flagged in the Library window') self.cfg_runtime_options_qvl.addWidget(self.duplicate_markers_checkbox) # ++++++++ Temporary markers: Updated ++++++++ self.updated_markers_checkbox = QCheckBox('Apply temporary markers to books with updated content') self.updated_markers_checkbox.setObjectName('apply_markers_to_updated') self.updated_markers_checkbox.setToolTip('Books with updated content will be flagged in the Library window') self.cfg_runtime_options_qvl.addWidget(self.updated_markers_checkbox) # ++++++++ Auto refresh checkbox ++++++++ self.auto_refresh_checkbox = QCheckBox('Automatically refresh custom column content') self.auto_refresh_checkbox.setObjectName('auto_refresh_at_startup') self.auto_refresh_checkbox.setToolTip('Update calibre custom column when Marvin XD is opened') self.cfg_runtime_options_qvl.addWidget(self.auto_refresh_checkbox) # ++++++++ Progress as percentage checkbox ++++++++ self.reading_progress_checkbox = QCheckBox('Show reading progress as percentage') self.reading_progress_checkbox.setObjectName('show_progress_as_percentage') self.reading_progress_checkbox.setToolTip('Display percentage in Progress column') self.cfg_runtime_options_qvl.addWidget(self.reading_progress_checkbox) # ~~~~~~~~ Create the Debug options group box ~~~~~~~~ self.cfg_debug_options_gb = QGroupBox(self) self.cfg_debug_options_gb.setTitle('Debug options') self.column2_layout.addWidget(self.cfg_debug_options_gb) self.cfg_debug_options_qvl = QVBoxLayout(self.cfg_debug_options_gb) # ++++++++ Debug logging checkboxes ++++++++ self.debug_plugin_checkbox = QCheckBox('Enable debug logging for Marvin XD') self.debug_plugin_checkbox.setObjectName('debug_plugin_checkbox') self.debug_plugin_checkbox.setToolTip('Print plugin diagnostic messages to console') self.cfg_debug_options_qvl.addWidget(self.debug_plugin_checkbox) self.debug_libimobiledevice_checkbox = QCheckBox('Enable debug logging for libiMobileDevice') self.debug_libimobiledevice_checkbox.setObjectName('debug_libimobiledevice_checkbox') self.debug_libimobiledevice_checkbox.setToolTip('Print libiMobileDevice diagnostic messages to console') self.cfg_debug_options_qvl.addWidget(self.debug_libimobiledevice_checkbox) self.spacerItem2 = QSpacerItem(20, 20, QSizePolicy.Minimum, QSizePolicy.Expanding) self.column2_layout.addItem(self.spacerItem2) # ~~~~~~~~ End of construction zone ~~~~~~~~ self.resize(self.sizeHint()) # ~~~~~~~~ Populate/restore config options ~~~~~~~~ # Annotations comboBox self.populate_annotations() self.populate_collections() self.populate_date_read() self.populate_locked() self.populate_progress() self.populate_read() self.populate_reading_list() self.populate_word_count() """ # Restore Dropbox settings, hook changes dropbox_syncing = self.prefs.get('dropbox_syncing', False) self.dropbox_syncing_checkbox.setChecked(dropbox_syncing) self.set_dropbox_syncing(dropbox_syncing) self.dropbox_syncing_checkbox.clicked.connect(partial(self.set_dropbox_syncing)) self.dropbox_location_lineedit.setText(self.prefs.get('dropbox_folder', '')) """ # Restore general settings self.duplicate_markers_checkbox.setChecked(self.prefs.get('apply_markers_to_duplicates', True)) self.updated_markers_checkbox.setChecked(self.prefs.get('apply_markers_to_updated', True)) self.auto_refresh_checkbox.setChecked(self.prefs.get('auto_refresh_at_startup', False)) self.reading_progress_checkbox.setChecked(self.prefs.get('show_progress_as_percentage', False)) # Restore debug settings, hook changes self.debug_plugin_checkbox.setChecked(self.prefs.get('debug_plugin', False)) self.debug_plugin_checkbox.stateChanged.connect(self.set_restart_required) self.debug_libimobiledevice_checkbox.setChecked(self.prefs.get('debug_libimobiledevice', False)) self.debug_libimobiledevice_checkbox.stateChanged.connect(self.set_restart_required) # Hook changes to Annotations comboBox # self.annotations_field_comboBox.currentIndexChanged.connect( # partial(self.save_combobox_setting, 'annotations_field_comboBox')) # self.connect(self.annotations_field_comboBox, # SIGNAL('currentIndexChanged(const QString &)'), # self.annotations_destination_changed) self.annotations_field_comboBox.currentIndexChanged.connect(self.annotations_destination_changed) # Launch the annotated_books_scanner field = get_cc_mapping('annotations', 'field', None) self.annotated_books_scanner = InventoryAnnotatedBooks(self.gui, field) self.annotated_books_scanner.signal.connect(self.inventory_complete) QTimer.singleShot(1, self.start_inventory) def annotations_destination_changed(self, qs_new_destination_name): ''' If the destination field changes, move all existing annotations from old to new ''' self._log_location(str(qs_new_destination_name)) self._log("self.eligible_annotations_fields: %s" % self.eligible_annotations_fields) old_destination_field = get_cc_mapping('annotations', 'field', None) old_destination_name = get_cc_mapping('annotations', 'combobox', None) self._log("old_destination_field: %s" % old_destination_field) self._log("old_destination_name: %s" % old_destination_name) # new_destination_name = unicode(qs_new_destination_name) # Signnls available have changed. Now receivin an indec rather than a name. Can get the name # from the combobox new_destination_name = unicode(self.annotations_field_comboBox.currentText()) self._log("new_destination_name: %s" % repr(new_destination_name)) if old_destination_name == new_destination_name: self._log_location("old_destination_name = new_destination_name, no changes") return if new_destination_name == '': self._log_location("annotations storage disabled") set_cc_mapping('annotations', field=None, combobox=new_destination_name) return new_destination_field = self.eligible_annotations_fields[new_destination_name] if existing_annotations(self.parent, old_destination_field): command = self.launch_new_destination_dialog(old_destination_name, new_destination_name) if command == 'move': set_cc_mapping('annotations', field=new_destination_field, combobox=new_destination_name) if self.annotated_books_scanner.isRunning(): self.annotated_books_scanner.wait() move_annotations(self, self.annotated_books_scanner.annotation_map, old_destination_field, new_destination_field) elif command == 'change': # Keep the updated destination field, but don't move annotations pass elif command == 'cancel': # Restore previous destination self.annotations_field_comboBox.blockSignals(True) old_index = self.annotations_field_comboBox.findText(old_destination_name) self.annotations_field_comboBox.setCurrentIndex(old_index) self.annotations_field_comboBox.blockSignals(False) else: # No existing annotations, just update prefs self._log("no existing annotations, updating destination to '{0}'".format(new_destination_name)) set_cc_mapping('annotations', field=new_destination_field, combobox=new_destination_name) def configure_appearance(self): ''' ''' self._log_location() appearance_settings = { 'appearance_css': default_elements, 'appearance_hr_checkbox': False, 'appearance_timestamp_format': default_timestamp } # Save, hash the original settings original_settings = {} osh = hashlib.md5() for setting in appearance_settings: original_settings[setting] = plugin_prefs.get(setting, appearance_settings[setting]) osh.update(repr(plugin_prefs.get(setting, appearance_settings[setting]))) # Display the Annotations appearance dialog aa = AnnotationsAppearance(self, self.annotations_icon, plugin_prefs) cancelled = False if aa.exec_(): # appearance_hr_checkbox and appearance_timestamp_format changed live to prefs during previews plugin_prefs.set('appearance_css', aa.elements_table.get_data()) # Generate a new hash nsh = hashlib.md5() for setting in appearance_settings: nsh.update(repr(plugin_prefs.get(setting, appearance_settings[setting]))) else: for setting in appearance_settings: plugin_prefs.set(setting, original_settings[setting]) nsh = osh # If there were changes, and there are existing annotations, # and there is an active Annotations field, offer to re-render field = get_cc_mapping('annotations', 'field', None) if osh.digest() != nsh.digest() and existing_annotations(self.parent, field): title = 'Update annotations?' msg = '<p>Update existing annotations to new appearance settings?</p>' d = MessageBox(MessageBox.QUESTION, title, msg, show_copy_button=False) self._log_location("QUESTION: %s" % msg) if d.exec_(): self._log_location("Updating existing annotations to modified appearance") # Wait for indexing to complete while not self.annotated_books_scanner.isFinished(): Application.processEvents() move_annotations(self, self.annotated_books_scanner.annotation_map, field, field, window_title="Updating appearance") def edit_css(self): ''' ''' self._log_location() from calibre_plugins.marvin_manager.book_status import dialog_resources_path klass = os.path.join(dialog_resources_path, 'css_editor.py') if os.path.exists(klass): sys.path.insert(0, dialog_resources_path) this_dc = importlib.import_module('css_editor') sys.path.remove(dialog_resources_path) dlg = this_dc.CSSEditorDialog(self, 'css_editor') dlg.initialize(self) dlg.exec_() def get_eligible_custom_fields(self, eligible_types=[], is_multiple=None): ''' Discover qualifying custom fields for eligible_types[] ''' #self._log_location(eligible_types) eligible_custom_fields = {} for cf in self.gui.current_db.custom_field_keys(): cft = self.gui.current_db.metadata_for_field(cf)['datatype'] cfn = self.gui.current_db.metadata_for_field(cf)['name'] cfim = self.gui.current_db.metadata_for_field(cf)['is_multiple'] #self._log("cf: %s cft: %s cfn: %s cfim: %s" % (cf, cft, cfn, cfim)) if cft in eligible_types: if is_multiple is not None: if bool(cfim) == is_multiple: eligible_custom_fields[cfn] = cf else: eligible_custom_fields[cfn] = cf return eligible_custom_fields def inventory_complete(self, msg): self._log_location(msg) def launch_cc_wizard(self, column_type): ''' ''' def _update_combo_box(comboBox, destination, previous): ''' ''' cb = getattr(self, comboBox) cb.blockSignals(True) all_items = [str(cb.itemText(i)) for i in range(cb.count())] if previous and previous in all_items: all_items.remove(previous) all_items.append(destination) cb.clear() cb.addItems(sorted(all_items, key=lambda s: s.lower())) # Select the new destination in the comboBox idx = cb.findText(destination) if idx > -1: cb.setCurrentIndex(idx) cb.blockSignals(False) from calibre_plugins.marvin_manager.book_status import dialog_resources_path klass = os.path.join(dialog_resources_path, 'cc_wizard.py') if os.path.exists(klass): #self._log("importing CC Wizard dialog from '%s'" % klass) sys.path.insert(0, dialog_resources_path) this_dc = importlib.import_module('cc_wizard') sys.path.remove(dialog_resources_path) dlg = this_dc.CustomColumnWizard(self, column_type, self.WIZARD_PROFILES[column_type], verbose=True) dlg.exec_() if dlg.modified_column: self._log("modified_column: %s" % dlg.modified_column) self.restart_required = True destination = dlg.modified_column['destination'] label = dlg.modified_column['label'] previous = dlg.modified_column['previous'] source = dlg.modified_column['source'] if source == "Annotations": _update_combo_box("annotations_field_comboBox", destination, previous) # Add/update the new destination so save_settings() can find it self.eligible_annotations_fields[destination] = label # Save manually in case user cancels set_cc_mapping('annotations', combobox=destination, field=label) elif source == 'Collections': _update_combo_box("collection_field_comboBox", destination, previous) # Add/update the new destination so save_settings() can find it self.eligible_collection_fields[destination] = label # Save manually in case user cancels set_cc_mapping('collections', combobox=destination, field=label) elif source == 'Last read': _update_combo_box("date_read_field_comboBox", destination, previous) # Add/update the new destination so save_settings() can find it self.eligible_date_read_fields[destination] = label # Save manually in case user cancels set_cc_mapping('date_read', combobox=destination, field=label) elif source == 'Locked': _update_combo_box("locked_field_comboBox", destination, previous) # Add/update the new destination so save_settings() can find it self.eligible_locked_fields[destination] = label # Save manually in case user cancels set_cc_mapping('locked', combobox=destination, field=label) elif source == "Progress": _update_combo_box("progress_field_comboBox", destination, previous) # Add/update the new destination so save_settings() can find it self.eligible_progress_fields[destination] = label # Save manually in case user cancels set_cc_mapping('progress', combobox=destination, field=label) elif source == "Read": _update_combo_box("read_field_comboBox", destination, previous) # Add/update the new destination so save_settings() can find it self.eligible_read_fields[destination] = label # Save manually in case user cancels set_cc_mapping('read', combobox=destination, field=label) elif source == "Reading list": _update_combo_box("reading_list_field_comboBox", destination, previous) # Add/update the new destination so save_settings() can find it self.eligible_reading_list_fields[destination] = label # Save manually in case user cancels set_cc_mapping('reading_list', combobox=destination, field=label) elif source == "Word count": _update_combo_box("word_count_field_comboBox", destination, previous) # Add/update the new destination so save_settings() can find it self.eligible_word_count_fields[destination] = label # Save manually in case user cancels set_cc_mapping('word_count', combobox=destination, field=label) else: self._log("ERROR: Can't import from '%s'" % klass) def launch_new_destination_dialog(self, old, new): ''' Return 'move', 'change' or 'cancel' ''' from calibre_plugins.marvin_manager.book_status import dialog_resources_path self._log_location() klass = os.path.join(dialog_resources_path, 'new_destination.py') if os.path.exists(klass): self._log("importing new destination dialog from '%s'" % klass) sys.path.insert(0, dialog_resources_path) this_dc = importlib.import_module('new_destination') sys.path.remove(dialog_resources_path) dlg = this_dc.NewDestinationDialog(self, old, new) dlg.exec_() return dlg.command def populate_annotations(self): datatype = self.WIZARD_PROFILES['Annotations']['datatype'] self.eligible_annotations_fields = self.get_eligible_custom_fields([datatype]) self.annotations_field_comboBox.addItems(['']) ecf = sorted(self.eligible_annotations_fields.keys(), key=lambda s: s.lower()) self.annotations_field_comboBox.addItems(ecf) # Retrieve stored value existing = get_cc_mapping('annotations', 'combobox') if existing: ci = self.annotations_field_comboBox.findText(existing) self.annotations_field_comboBox.setCurrentIndex(ci) def populate_collections(self): datatype = self.WIZARD_PROFILES['Collections']['datatype'] self.eligible_collection_fields = self.get_eligible_custom_fields([datatype], is_multiple=True) self.collection_field_comboBox.addItems(['']) ecf = sorted(self.eligible_collection_fields.keys(), key=lambda s: s.lower()) self.collection_field_comboBox.addItems(ecf) # Retrieve stored value existing = get_cc_mapping('collections', 'combobox') if existing: ci = self.collection_field_comboBox.findText(existing) self.collection_field_comboBox.setCurrentIndex(ci) def populate_date_read(self): #self.eligible_date_read_fields = self.get_eligible_custom_fields(['datetime']) datatype = self.WIZARD_PROFILES['Last read']['datatype'] self.eligible_date_read_fields = self.get_eligible_custom_fields([datatype]) self.date_read_field_comboBox.addItems(['']) ecf = sorted(self.eligible_date_read_fields.keys(), key=lambda s: s.lower()) self.date_read_field_comboBox.addItems(ecf) # Retrieve stored value existing = get_cc_mapping('date_read', 'combobox') if existing: ci = self.date_read_field_comboBox.findText(existing) self.date_read_field_comboBox.setCurrentIndex(ci) def populate_locked(self): datatype = self.WIZARD_PROFILES['Locked']['datatype'] self.eligible_locked_fields = self.get_eligible_custom_fields([datatype]) self.locked_field_comboBox.addItems(['']) ecf = sorted(self.eligible_locked_fields.keys(), key=lambda s: s.lower()) self.locked_field_comboBox.addItems(ecf) # Retrieve stored value existing = get_cc_mapping('locked', 'combobox') if existing: ci = self.locked_field_comboBox.findText(existing) self.locked_field_comboBox.setCurrentIndex(ci) def populate_progress(self): #self.eligible_progress_fields = self.get_eligible_custom_fields(['float']) datatype = self.WIZARD_PROFILES['Progress']['datatype'] self.eligible_progress_fields = self.get_eligible_custom_fields([datatype]) self.progress_field_comboBox.addItems(['']) ecf = sorted(self.eligible_progress_fields.keys(), key=lambda s: s.lower()) self.progress_field_comboBox.addItems(ecf) # Retrieve stored value existing = get_cc_mapping('progress', 'combobox') if existing: ci = self.progress_field_comboBox.findText(existing) self.progress_field_comboBox.setCurrentIndex(ci) def populate_read(self): datatype = self.WIZARD_PROFILES['Read']['datatype'] self.eligible_read_fields = self.get_eligible_custom_fields([datatype]) self.read_field_comboBox.addItems(['']) ecf = sorted(self.eligible_read_fields.keys(), key=lambda s: s.lower()) self.read_field_comboBox.addItems(ecf) # Retrieve stored value existing = get_cc_mapping('read', 'combobox') if existing: ci = self.read_field_comboBox.findText(existing) self.read_field_comboBox.setCurrentIndex(ci) def populate_reading_list(self): datatype = self.WIZARD_PROFILES['Reading list']['datatype'] self.eligible_reading_list_fields = self.get_eligible_custom_fields([datatype]) self.reading_list_field_comboBox.addItems(['']) ecf = sorted(self.eligible_reading_list_fields.keys(), key=lambda s: s.lower()) self.reading_list_field_comboBox.addItems(ecf) # Retrieve stored value existing = get_cc_mapping('reading_list', 'combobox') if existing: ci = self.reading_list_field_comboBox.findText(existing) self.reading_list_field_comboBox.setCurrentIndex(ci) def populate_word_count(self): #self.eligible_word_count_fields = self.get_eligible_custom_fields(['int']) datatype = self.WIZARD_PROFILES['Word count']['datatype'] self.eligible_word_count_fields = self.get_eligible_custom_fields([datatype]) self.word_count_field_comboBox.addItems(['']) ecf = sorted(self.eligible_word_count_fields.keys(), key=lambda s: s.lower()) self.word_count_field_comboBox.addItems(ecf) # Retrieve stored value existing = get_cc_mapping('word_count', 'combobox') if existing: ci = self.word_count_field_comboBox.findText(existing) self.word_count_field_comboBox.setCurrentIndex(ci) """ def select_dropbox_folder(self): ''' ''' self._log_location() dropbox_location = QFileDialog.getExistingDirectory( self, "Dropbox folder", os.path.expanduser("~"), QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks) self.dropbox_location_lineedit.setText(unicode(dropbox_location)) def set_dropbox_syncing(self, state): ''' Called when checkbox changes state, or when restoring state Set enabled state of Dropbox folder picker to match ''' self.cfg_dropbox_folder_toolbutton.setEnabled(state) self.dropbox_location_lineedit.setEnabled(state) """ def set_restart_required(self, state): ''' Set restart_required flag to show show dialog when closing dialog ''' self.restart_required = True """ def save_combobox_setting(self, cb, index): ''' Apply changes immediately ''' cf = str(getattr(self, cb).currentText()) self._log_location("%s => %s" % (cb, repr(cf))) if cb == 'annotations_field_comboBox': field = None if cf: field = self.eligible_annotations_fields[cf] set_cc_mapping('annotations', combobox=cf, field=field) """ def save_settings(self): self._log_location() # Annotations field cf = unicode(self.annotations_field_comboBox.currentText()) field = None if cf: field = self.eligible_annotations_fields[cf] set_cc_mapping('annotations', combobox=cf, field=field) # Collections field cf = unicode(self.collection_field_comboBox.currentText()) field = None if cf: field = self.eligible_collection_fields[cf] set_cc_mapping('collections', combobox=cf, field=field) # Date read field cf = unicode(self.date_read_field_comboBox.currentText()) field = None if cf: field = self.eligible_date_read_fields[cf] set_cc_mapping('date_read', combobox=cf, field=field) # Locked field cf = unicode(self.locked_field_comboBox.currentText()) field = None if cf: field = self.eligible_locked_fields[cf] set_cc_mapping('locked', combobox=cf, field=field) # Progress field cf = unicode(self.progress_field_comboBox.currentText()) field = None if cf: field = self.eligible_progress_fields[cf] set_cc_mapping('progress', combobox=cf, field=field) # Read field cf = unicode(self.read_field_comboBox.currentText()) field = None if cf: field = self.eligible_read_fields[cf] set_cc_mapping('read', combobox=cf, field=field) # Reading list field cf = unicode(self.reading_list_field_comboBox.currentText()) field = None if cf: field = self.eligible_reading_list_fields[cf] set_cc_mapping('reading_list', combobox=cf, field=field) # Word count field cf = unicode(self.word_count_field_comboBox.currentText()) field = None if cf: field = self.eligible_word_count_fields[cf] set_cc_mapping('word_count', combobox=cf, field=field) ''' # Save Dropbox settings self.prefs.set('dropbox_syncing', self.dropbox_syncing_checkbox.isChecked()) self.prefs.set('dropbox_folder', unicode(self.dropbox_location_lineedit.text())) ''' # Save general settings self.prefs.set('apply_markers_to_duplicates', self.duplicate_markers_checkbox.isChecked()) self.prefs.set('apply_markers_to_updated', self.updated_markers_checkbox.isChecked()) self.prefs.set('auto_refresh_at_startup', self.auto_refresh_checkbox.isChecked()) self.prefs.set('show_progress_as_percentage', self.reading_progress_checkbox.isChecked()) # Save debug settings self.prefs.set('debug_plugin', self.debug_plugin_checkbox.isChecked()) self.prefs.set('debug_libimobiledevice', self.debug_libimobiledevice_checkbox.isChecked()) # If restart needed, inform user if self.restart_required: do_restart = show_restart_warning('Restart calibre for the changes to be applied.', parent=self.gui) if do_restart: self.gui.quit(restart=True) def start_inventory(self): self._log_location() self.annotated_books_scanner.start()
class FindAnnotationsDialog(SizePersistedDialog, Logger): GENERIC_STYLE = 'Any style' GENERIC_READER = 'Any reader' def __init__(self, opts): self.matched_ids = set() self.opts = opts self.prefs = opts.prefs super(FindAnnotationsDialog, self).__init__(self.opts.gui, 'find_annotations_dialog') self.setWindowTitle(_('Find Annotations')) self.setWindowIcon(self.opts.icon) self.l = QVBoxLayout(self) self.setLayout(self.l) self.search_criteria_gb = QGroupBox(self) self.search_criteria_gb.setTitle(_("Search criteria")) self.scgl = QGridLayout(self.search_criteria_gb) self.l.addWidget(self.search_criteria_gb) # addWidget(widget, row, col, rowspan, colspan) row = 0 # ~~~~~~~~ Create the Readers comboBox ~~~~~~~~ self.reader_label = QLabel(_('Reader')) self.reader_label.setAlignment(Qt.AlignRight|Qt.AlignVCenter) self.scgl.addWidget(self.reader_label, row, 0, 1, 1) self.find_annotations_reader_comboBox = QComboBox() self.find_annotations_reader_comboBox.setObjectName('find_annotations_reader_comboBox') self.find_annotations_reader_comboBox.setToolTip(_('Reader annotations to search for')) self.find_annotations_reader_comboBox.addItem(self.GENERIC_READER) racs = ReaderApp.get_reader_app_classes() for ra in sorted(racs.keys()): self.find_annotations_reader_comboBox.addItem(ra) self.scgl.addWidget(self.find_annotations_reader_comboBox, row, 1, 1, 4) row += 1 # ~~~~~~~~ Create the Styles comboBox ~~~~~~~~ self.style_label = QLabel(_('Style')) self.style_label.setAlignment(Qt.AlignRight|Qt.AlignVCenter) self.scgl.addWidget(self.style_label, row, 0, 1, 1) self.find_annotations_color_comboBox = QComboBox() self.find_annotations_color_comboBox.setObjectName('find_annotations_color_comboBox') self.find_annotations_color_comboBox.setToolTip(_('Annotation style to search for')) self.find_annotations_color_comboBox.addItem(self.GENERIC_STYLE) all_colors = COLOR_MAP.keys() all_colors.remove('Default') for color in sorted(all_colors): self.find_annotations_color_comboBox.addItem(color) self.scgl.addWidget(self.find_annotations_color_comboBox, row, 1, 1, 4) row += 1 # ~~~~~~~~ Create the Text LineEdit control ~~~~~~~~ self.text_label = QLabel(_('Text')) self.text_label.setAlignment(Qt.AlignRight|Qt.AlignVCenter) self.scgl.addWidget(self.text_label, row, 0, 1, 1) self.find_annotations_text_lineEdit = MyLineEdit() self.find_annotations_text_lineEdit.setObjectName('find_annotations_text_lineEdit') self.scgl.addWidget(self.find_annotations_text_lineEdit, row, 1, 1, 3) self.reset_text_tb = QToolButton() self.reset_text_tb.setObjectName('reset_text_tb') self.reset_text_tb.setToolTip(_('Clear search criteria')) self.reset_text_tb.setIcon(QIcon(I('trash.png'))) self.reset_text_tb.clicked.connect(self.clear_text_field) self.scgl.addWidget(self.reset_text_tb, row, 4, 1, 1) row += 1 # ~~~~~~~~ Create the Note LineEdit control ~~~~~~~~ self.note_label = QLabel(_('Note')) self.note_label.setAlignment(Qt.AlignRight|Qt.AlignVCenter) self.scgl.addWidget(self.note_label, row, 0, 1, 1) self.find_annotations_note_lineEdit = MyLineEdit() self.find_annotations_note_lineEdit.setObjectName('find_annotations_note_lineEdit') self.scgl.addWidget(self.find_annotations_note_lineEdit, row, 1, 1, 3) self.reset_note_tb = QToolButton() self.reset_note_tb.setObjectName('reset_note_tb') self.reset_note_tb.setToolTip(_('Clear search criteria')) self.reset_note_tb.setIcon(QIcon(I('trash.png'))) self.reset_note_tb.clicked.connect(self.clear_note_field) self.scgl.addWidget(self.reset_note_tb, row, 4, 1, 1) row += 1 # ~~~~~~~~ Create the Date range controls ~~~~~~~~ self.date_range_label = QLabel(_('Date range')) self.date_range_label.setAlignment(Qt.AlignRight|Qt.AlignVCenter) self.scgl.addWidget(self.date_range_label, row, 0, 1, 1) # Date 'From' self.find_annotations_date_from_dateEdit = MyDateEdit(self, datetime(1970,1,1)) self.find_annotations_date_from_dateEdit.setObjectName('find_annotations_date_from_dateEdit') #self.find_annotations_date_from_dateEdit.current_val = datetime(1970,1,1) self.find_annotations_date_from_dateEdit.clear_button.clicked.connect(self.find_annotations_date_from_dateEdit.reset_from_date) self.scgl.addWidget(self.find_annotations_date_from_dateEdit, row, 1, 1, 1) self.scgl.addWidget(self.find_annotations_date_from_dateEdit.clear_button, row, 2, 1, 1) # Date 'To' self.find_annotations_date_to_dateEdit = MyDateEdit(self, datetime.today()) self.find_annotations_date_to_dateEdit.setObjectName('find_annotations_date_to_dateEdit') #self.find_annotations_date_to_dateEdit.current_val = datetime.today() self.find_annotations_date_to_dateEdit.clear_button.clicked.connect(self.find_annotations_date_to_dateEdit.reset_to_date) self.scgl.addWidget(self.find_annotations_date_to_dateEdit, row, 3, 1, 1) self.scgl.addWidget(self.find_annotations_date_to_dateEdit.clear_button, row, 4, 1, 1) row += 1 # ~~~~~~~~ Create a horizontal line ~~~~~~~~ self.hl = QFrame(self) self.hl.setGeometry(QRect(0, 0, 1, 3)) self.hl.setFrameShape(QFrame.HLine) self.hl.setFrameShadow(QFrame.Raised) self.scgl.addWidget(self.hl, row, 0, 1, 5) row += 1 # ~~~~~~~~ Create the results label field ~~~~~~~~ self.result_label = QLabel('<p style="color:red">{0}</p>'.format(_('scanning…'))) self.result_label.setAlignment(Qt.AlignCenter) self.result_label.setWordWrap(False) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Maximum) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.result_label.sizePolicy().hasHeightForWidth()) self.result_label.setSizePolicy(sizePolicy) self.result_label.setMinimumSize(QtCore.QSize(250, 0)) self.scgl.addWidget(self.result_label, row, 0, 1, 5) row += 1 # ~~~~~~~~ Create the ButtonBox ~~~~~~~~ self.dialogButtonBox = QDialogButtonBox(self) self.dialogButtonBox.setOrientation(Qt.Horizontal) if False: self.update_button = QPushButton(_('Update results')) self.update_button.setDefault(True) self.update_button.setVisible(False) self.dialogButtonBox.addButton(self.update_button, QDialogButtonBox.ActionRole) self.cancel_button = self.dialogButtonBox.addButton(self.dialogButtonBox.Cancel) self.find_button = self.dialogButtonBox.addButton(self.dialogButtonBox.Ok) self.find_button.setText(_('Find Matching Books')) self.l.addWidget(self.dialogButtonBox) self.dialogButtonBox.clicked.connect(self.find_annotations_dialog_clicked) # ~~~~~~~~ Add a spacer ~~~~~~~~ self.spacerItem = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) self.l.addItem(self.spacerItem) # ~~~~~~~~ Restore previously saved settings ~~~~~~~~ self.restore_settings() # ~~~~~~~~ Declare sizing ~~~~~~~~ sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.sizePolicy().hasHeightForWidth()) self.setSizePolicy(sizePolicy) self.resize_dialog() # ~~~~~~~~ Connect all signals ~~~~~~~~ self.find_annotations_reader_comboBox.currentIndexChanged.connect(partial(self.update_results, 'reader')) self.find_annotations_color_comboBox.currentIndexChanged.connect(partial(self.update_results, 'color')) self.find_annotations_text_lineEdit.editingFinished.connect(partial(self.update_results, 'text')) self.find_annotations_note_lineEdit.editingFinished.connect(partial(self.update_results, 'note')) # self.connect(self.find_annotations_text_lineEdit, pyqtSignal("return_pressed"), self.return_pressed) self.find_annotations_text_lineEdit.return_pressed.connect(self.return_pressed) # self.connect(self.find_annotations_note_lineEdit, pyqtSignal("return_pressed"), self.return_pressed) self.find_annotations_note_lineEdit.return_pressed.connect(self.return_pressed) # Date range signals connected in inventory_available() # ~~~~~~~~ Allow dialog to render before doing inventory ~~~~~~~~ #field = self.prefs.get('cfg_annotations_destination_field', None) field = get_cc_mapping('annotations', 'field', None) self.annotated_books_scanner = InventoryAnnotatedBooks(self.opts.gui, field, get_date_range=True) self.annotated_books_scanner.signal.connect(self.inventory_available) QTimer.singleShot(1, self.start_inventory_scan) def clear_note_field(self): if str(self.find_annotations_note_lineEdit.text()) > '': self.find_annotations_note_lineEdit.setText('') self.update_results('clear_note_field') def clear_text_field(self): if str(self.find_annotations_text_lineEdit.text()) > '': self.find_annotations_text_lineEdit.setText('') self.update_results('clear_text_field') def find_annotations_dialog_clicked(self, button): if self.dialogButtonBox.buttonRole(button) == QDialogButtonBox.AcceptRole: self.save_settings() self.accept() elif self.dialogButtonBox.buttonRole(button) == QDialogButtonBox.RejectRole: self.close() def inventory_available(self): ''' Update the Date range widgets with the rounded oldest, newest dates Don't connect date signals until date range available ''' self._log_location() # Reset the date range based on available annotations oldest = QDateTime(datetime.fromtimestamp(self.annotated_books_scanner.oldest_annotation)) oldest_day = QDateTime(datetime.fromtimestamp(self.annotated_books_scanner.oldest_annotation).replace(hour=0, minute=0, second=0)) newest = QDateTime(datetime.fromtimestamp(self.annotated_books_scanner.newest_annotation)) newest_day = QDateTime(datetime.fromtimestamp(self.annotated_books_scanner.newest_annotation).replace(hour=23, minute=59, second=59)) # Set 'From' date limits to inventory values self.find_annotations_date_from_dateEdit.setMinimumDateTime(oldest_day) self.find_annotations_date_from_dateEdit.current_val = oldest self.find_annotations_date_from_dateEdit.setMaximumDateTime(newest_day) # Set 'To' date limits to inventory values self.find_annotations_date_to_dateEdit.setMinimumDateTime(oldest_day) self.find_annotations_date_to_dateEdit.current_val = newest_day self.find_annotations_date_to_dateEdit.setMaximumDateTime(newest_day) # Connect the signals for date range changes self.find_annotations_date_from_dateEdit.dateTimeChanged.connect(partial(self.update_results, 'from_date')) self.find_annotations_date_to_dateEdit.dateTimeChanged.connect(partial(self.update_results, 'to_date')) self.update_results('inventory_available') def restore_settings(self): self.blockSignals(True) ra = self.prefs.get('find_annotations_reader_comboBox', self.GENERIC_READER) ra_index = self.find_annotations_reader_comboBox.findText(ra) self.find_annotations_reader_comboBox.setCurrentIndex(ra_index) color = self.prefs.get('find_annotations_color_comboBox', self.GENERIC_STYLE) color_index = self.find_annotations_color_comboBox.findText(color) self.find_annotations_color_comboBox.setCurrentIndex(color_index) text = self.prefs.get('find_annotations_text_lineEdit', '') self.find_annotations_text_lineEdit.setText(text) note = self.prefs.get('find_annotations_note_lineEdit', '') self.find_annotations_note_lineEdit.setText(note) if False: from_date = self.prefs.get('find_annotations_date_from_dateEdit', datetime(1970,1,1)) self.find_annotations_date_from_dateEdit.current_val = from_date to_date = self.prefs.get('find_annotations_date_to_dateEdit', datetime.today()) self.find_annotations_date_to_dateEdit.current_val = to_date self.blockSignals(False) def return_pressed(self): self.update_results("return_pressed") def save_settings(self): ra = str(self.find_annotations_reader_comboBox.currentText()) self.prefs.set('find_annotations_reader_comboBox', ra) color = str(self.find_annotations_color_comboBox.currentText()) self.prefs.set('find_annotations_color_comboBox', color) text = str(self.find_annotations_text_lineEdit.text()) self.prefs.set('find_annotations_text_lineEdit', text) note = str(self.find_annotations_note_lineEdit.text()) self.prefs.set('find_annotations_note_lineEdit', note) if False: from_date = self.find_annotations_date_from_dateEdit.current_val self.prefs.set('find_annotations_date_from_dateEdit', from_date) to_date = self.find_annotations_date_to_dateEdit.current_val self.prefs.set('find_annotations_date_to_dateEdit', to_date) def start_inventory_scan(self): self._log_location() self.annotated_books_scanner.start() def update_results(self, trigger): #self._log_location(trigger) reader_to_match = str(self.find_annotations_reader_comboBox.currentText()) color_to_match = str(self.find_annotations_color_comboBox.currentText()) text_to_match = str(self.find_annotations_text_lineEdit.text()) note_to_match = str(self.find_annotations_note_lineEdit.text()) from_date = self.find_annotations_date_from_dateEdit.dateTime().toTime_t() to_date = self.find_annotations_date_to_dateEdit.dateTime().toTime_t() annotation_map = self.annotated_books_scanner.annotation_map #field = self.prefs.get("cfg_annotations_destination_field", None) field = get_cc_mapping('annotations', 'field', None) db = self.opts.gui.current_db matched_titles = [] self.matched_ids = set() for cid in annotation_map: mi = db.get_metadata(cid, index_is_id=True) soup = None if field == 'Comments': if mi.comments: soup = BeautifulSoup(mi.comments) else: if mi.get_user_metadata(field, False)['#value#'] is not None: soup = BeautifulSoup(mi.get_user_metadata(field, False)['#value#']) if soup: uas = soup.findAll('div', 'annotation') for ua in uas: # Are we already logged? if cid in self.matched_ids: continue # Check reader if reader_to_match != self.GENERIC_READER: this_reader = ua['reader'] if this_reader != reader_to_match: continue # Check color if color_to_match != self.GENERIC_STYLE: this_color = ua.find('table')['color'] if this_color != color_to_match: continue # Check date range, allow for mangled timestamp try: timestamp = float(ua.find('td', 'timestamp')['uts']) if timestamp < from_date or timestamp > to_date: continue except: continue highlight_text = '' try: pels = ua.findAll('p', 'highlight') highlight_text = '\n'.join([p.string for p in pels]) except: pass if text_to_match > '': if not re.search(text_to_match, highlight_text, flags=re.IGNORECASE): continue note_text = '' try: nels = ua.findAll('p', 'note') note_text = '\n'.join([n.string for n in nels]) except: pass if note_to_match > '': if not re.search(note_to_match, note_text, flags=re.IGNORECASE): continue # If we made it this far, add the id to matched_ids self.matched_ids.add(cid) matched_titles.append(mi.title) # Update the results box matched_titles.sort() if len(annotation_map): if len(matched_titles): first_match = ("<i>%s</i>" % matched_titles[0]) if len(matched_titles) == 1: results = first_match else: results = first_match + (_(" and {0} more.").format(len(matched_titles) - 1)) self.result_label.setText('<p style="color:blue">{0}</p>'.format(results)) else: self.result_label.setText('<p style="color:red">{0}</p>'.format(_('no matches'))) else: self.result_label.setText('<p style="color:red">{0}</p>'.format(_('no annotated books in library'))) self.resize_dialog()
class ConvertDialog(QDialog): hide_text = _('&Hide styles') show_text = _('&Show styles') prince_log = '' prince_file = '' prince_css = '' # GUI definition def __init__(self, mi, fmt, opf, oeb, icon): ''' :param mi: The book metadata :param fmt: The source format used for conversion :param opf: The path to the OPF file :param oeb: An OEB object for the unpacked book :param icon: The window icon ''' self.opf = opf self.oeb = oeb self.mi = mi # The unpacked book needs to be parsed before, to read the contents # of the prince-style file, if it exists self.parse() QDialog.__init__(self) self.setAttribute(Qt.WA_DeleteOnClose) self.setWindowTitle(_('Convert to PDF with Prince')) self.setWindowIcon(icon) self.l = QVBoxLayout() self.setLayout(self.l) self.title_label = QLabel(_('<b>Title:</b> %s') % self.mi.title) self.l.addWidget(self.title_label) self.format_label = QLabel(_('<b>Source format:</b> %s') % fmt) self.l.addWidget(self.format_label) self.add_book = QCheckBox(_('&Add PDF to the book record')) self.add_book.setToolTip( _('<qt>Add the converted PDF to the selected book record</qt>')) self.add_book.setChecked(prefs['add_book']) self.add_book.stateChanged.connect(self.set_add_book) self.l.addWidget(self.add_book) self.ll = QHBoxLayout() self.ll.setAlignment(Qt.AlignLeft) self.l.addLayout(self.ll) self.label_css = QLabel(_('&Custom style:')) self.ll.addWidget(self.label_css) self.css_list = QComboBox() self.css_list.setToolTip( _('<qt>Select one style to use. Additional styles can be created in the plugin configuration</qt>' )) for key in sorted(prefs['custom_CSS_list'], key=lambda x: x.lower()): self.css_list.addItem(key, key) self.css_list.setCurrentIndex( self.css_list.findText(prefs['default_CSS'])) self.css_list.currentIndexChanged.connect(self.set_css) self.ll.addWidget(self.css_list) self.label_css.setBuddy(self.css_list) self.ll_ = QHBoxLayout() self.l.addLayout(self.ll_) self.label_args = QLabel(_('A&dditional command-line arguments:')) self.ll_.addWidget(self.label_args) self.args = QLineEdit(self) self.args.setText(prefs['custom_args_list'][prefs['default_CSS']]) self.args.setToolTip( _('<qt>Specify additional command-line arguments for the conversion</qt>' )) self.ll_.addWidget(self.args) self.label_args.setBuddy(self.args) self.css = QTabWidget() self.l.addWidget(self.css) self.css1 = TextEditWithTooltip(self, expected_geometry=(80, 20)) self.css1.setLineWrapMode(TextEditWithTooltip.NoWrap) self.css1.load_text( self.replace_templates( prefs['custom_CSS_list'][prefs['default_CSS']]), 'css') self.css1.setToolTip( _('<qt>This stylesheet can be modified<br/>The default can be configured</qt>' )) i = self.css.addTab(self.css1, _('C&ustom CSS')) self.css.setTabToolTip( i, _('<qt>Custom CSS stylesheet to be used for this conversion</qt>')) monofont = QFont('') monofont.setStyleHint(QFont.TypeWriter) if (self.prince_css): self.css2 = QPlainTextEdit() self.css2.setStyleSheet('* { font-family: monospace }') self.css2.setLineWrapMode(QPlainTextEdit.NoWrap) self.css2.setPlainText(self.prince_css) self.css2.setReadOnly(True) self.css2.setToolTip( _('<qt>This stylesheet cannot be modified</qt>')) i = self.css.addTab(self.css2, _('&Book CSS')) self.css.setTabToolTip( i, _('<qt>Book-specific CSS stylesheet included in the ebook file</qt>' )) self.ll = QHBoxLayout() self.l.addLayout(self.ll) if (prefs['show_CSS']): self.toggle = QPushButton(self.hide_text, self) else: self.toggle = QPushButton(self.show_text, self) self.toggle.setToolTip( _('<qt>Show/hide the additional styles used for the conversion</qt>' )) self.toggle.clicked.connect(self.toggle_tabs) self.convert = QPushButton(_('Con&vert'), self) self.convert.setToolTip(_('<qt>Run the conversion with Prince</qt>')) self.convert.setDefault(True) self.buttons = QDialogButtonBox(QDialogButtonBox.Cancel) self.buttons.addButton(self.toggle, QDialogButtonBox.ResetRole) self.buttons.addButton(self.convert, QDialogButtonBox.AcceptRole) self.l.addWidget(self.buttons) self.buttons.accepted.connect(self.prince_convert) self.buttons.rejected.connect(self.reject) if (not prefs['show_CSS']): self.css.hide() self.adjustSize() def toggle_tabs(self): ''' Enable/disable the CSS tabs, and store the setting ''' if (self.css.isVisible()): self.css.hide() self.label_args.hide() self.args.hide() self.toggle.setText(self.show_text) self.adjustSize() else: self.css.show() self.label_args.show() self.args.show() self.toggle.setText(self.hide_text) self.adjustSize() prefs['show_CSS'] = self.css.isVisible() def set_add_book(self): ''' Save the status of the add_book checkbox ''' prefs['add_book'] = self.add_book.isChecked() def set_css(self): ''' Fill the custom CSS text box with the selected stylesheet (and command-line arguments) ''' style = unicode(self.css_list.currentText()) self.css1.load_text( self.replace_templates(prefs['custom_CSS_list'][style]), 'css') self.args.setText(prefs['custom_args_list'][style]) prefs['default_CSS'] = style def parse(self): ''' Parse the unpacked OPF file to find and read the prince-style file ''' from calibre.constants import DEBUG from os.path import dirname, join from lxml import etree import codecs if DEBUG: print(_('Parsing book...')) opf_dir = dirname(self.opf) root = etree.parse(self.opf).getroot() metadata = root.find('{*}metadata') for meta in metadata.findall("{*}meta[@name='prince-style']"): prince_id = meta.get('content') for item in self.oeb.manifest: if (item.id == prince_id): self.prince_file = item.href break if (self.prince_file): fl = codecs.open(join(opf_dir, self.prince_file), 'rb', 'utf-8') self.prince_css = fl.read() fl.close() def replace_templates(self, text): ''' Replace templates (enclosed by '@{@', '@}@') in the input text ''' import re import json from calibre.ebooks.metadata.book.formatter import SafeFormat from calibre.constants import DEBUG matches = list(re.finditer('@{@(.+?)@}@', text, re.DOTALL)) results = {} for match in reversed(matches): result = SafeFormat().safe_format(match.group(1), self.mi, ('EXCEPTION: '), self.mi) # Escape quotes, backslashes and newlines result = re.sub(r'''['"\\]''', r'\\\g<0>', result) result = re.sub('\n', r'\\A ', result) results[match.group(1)] = result text = text[:match.start(0)] + result + text[match.end(0):] if DEBUG: print(_('Replacing templates')) for match in matches: print( _('Found: %s (%d-%d)') % (match.group(1), match.start(0), match.end(0))) print(_('Replace with: %s') % results[match.group(1)]) return text def prince_convert(self): ''' Call the actual Prince command to convert to PDF ''' from os import makedirs from os.path import dirname, join, exists from calibre.ptempfile import PersistentTemporaryFile from calibre.constants import DEBUG from shlex import split as shsplit # All files are relative to the OPF location opf_dir = dirname(self.opf) base_dir = dirname(self.pdf_file) base_dir = join(opf_dir, base_dir) try: makedirs(base_dir) except BaseException: if not exists(base_dir): raise # Create a temporary CSS file with the box contents custom_CSS = PersistentTemporaryFile(mode='w+') custom_CSS.write(unicode(self.css1.toPlainText())) custom_CSS.close() # Create a temporary file with the list of input files file_list = PersistentTemporaryFile(mode='w+') for item in self.oeb.spine: file_list.write(item.href + "\n") file_list.close() # Build the command line command = prefs['prince_exe'] args = ['-v'] if self.prince_file: args.append('-s') args.append(self.prince_file) args.append('-s') args.append(custom_CSS.name) args.append('-l') args.append(file_list.name) args.append('-o') args.append(self.pdf_file) # Additional command-line arguments args.extend(shsplit(self.args.text())) # Hide the convert button and show a busy indicator self.convert.setEnabled(False) self.progress_bar = QProgressBar() self.progress_bar.setRange(0, 0) self.progress_bar.setValue(0) self.l.addWidget(self.progress_bar) # Run the command and return the path to the PDF file if DEBUG: print(_('Converting book...')) process = QProcess(self) process.setWorkingDirectory(opf_dir) process.setProcessChannelMode(QProcess.MergedChannels) process.error.connect(self.error) process.finished.connect(self.end) self.process = process if DEBUG: from subprocess import list2cmdline line = list2cmdline([command] + args) print(_('Command line: %s') % line) process.start(command, args) def error(self, rc): ''' Show a message when there is an error in the command :param rc: The error code ''' from calibre.gui2 import error_dialog # Remove the progress bar while the error message is displayed self.progress_bar.hide() self.progress_bar.deleteLater() error_dialog( self, _('Process error'), _('<p>Error code: %s' '<p>make sure Prince (<a href="http://www.princexml.com">www.princexml.com</a>) is installed ' 'and the correct command-line-interface executable is set in the configuration of this plugin, ' 'which is usually:' '<ul><li>In Windows: <code><i>Prince_folder</i>\\Engine\\bin\\prince.exe</code>' ' <li>In Linux: <code>prince</code>' '</ul>') % rc, show=True) self.pdf_file = None self.accept() def end(self, rc): ''' Close and return the filename when the process ends :param rc: The return code (0 if successful) ''' from os.path import join self.prince_log = unicode(self.process.readAllStandardOutput().data()) opf_dir = unicode(self.process.workingDirectory()) if (rc == 0): self.pdf_file = join(opf_dir, self.pdf_file) else: self.pdf_file = None self.accept()
class ConfigWidget(QWidget): def __init__(self, plugin_action): QWidget.__init__(self) self.plugin_action = plugin_action layout = QVBoxLayout(self) self.setLayout(layout) # --- Directory Options --- directory_group_box = QGroupBox(_('Default Unpack Directory:'), self) layout.addWidget(directory_group_box) directory_group_box_layout = QVBoxLayout() directory_group_box.setLayout(directory_group_box_layout) # Directory path Textbox # Load the textbox with the current preference setting self.directory_txtBox = QLineEdit(plugin_prefs['Unpack_Folder'], self) self.directory_txtBox.setToolTip(_('<p>Default directory to extract files to')) directory_group_box_layout.addWidget(self.directory_txtBox) self.directory_txtBox.setReadOnly(True) # Folder select button directory_button = QPushButton(_('Select/Change Unpack Directory'), self) directory_button.setToolTip(_('<p>Select/Change directory to extract files to.')) # Connect button to the getDirectory function directory_button.clicked.connect(self.getDirectory) directory_group_box_layout.addWidget(directory_button) self.default_folder_check = QCheckBox(_('Always use the Default Unpack Directory'), self) self.default_folder_check.setToolTip(_('<p>When unchecked... you will be prompted to select a destination '+ 'directory for the extracted content each time you use Mobiunpack.')) directory_group_box_layout.addWidget(self.default_folder_check) # Load the checkbox with the current preference setting self.default_folder_check.setChecked(plugin_prefs['Always_Use_Unpack_Folder']) misc_group_box = QGroupBox(_('Default settings:'), self) layout.addWidget(misc_group_box) misc_group_box_layout = QVBoxLayout() misc_group_box.setLayout(misc_group_box_layout) self.use_hd_images = QCheckBox(_('Always use HD images if present'), self) self.use_hd_images.setToolTip(_('<p>When checked... any HD images present in the kindlebook '+ 'will be used for creating the ePub.')) misc_group_box_layout.addWidget(self.use_hd_images) # Load the checkbox with the current preference setting self.use_hd_images.setChecked(plugin_prefs['Use_HD_Images']) combo_label = QLabel('Select epub version output:', self) misc_group_box_layout.addWidget(combo_label) self.epub_version_combobox = QComboBox() self.epub_version_combobox.setToolTip(_('<p>Select the type of OPF file to create.')) misc_group_box_layout.addWidget(self.epub_version_combobox) self.epub_version_combobox.addItems(['Auto-detect', 'ePub2', 'ePub3']) if plugin_prefs['Epub_Version'] == 'A': self.epub_version_combobox.setCurrentIndex(0) else: self.epub_version_combobox.setCurrentIndex(int(plugin_prefs['Epub_Version'])-1) def save_settings(self): # Save current dialog sttings back to JSON config file plugin_prefs['Unpack_Folder'] = unicode(self.directory_txtBox.displayText()) plugin_prefs['Always_Use_Unpack_Folder'] = self.default_folder_check.isChecked() plugin_prefs['Use_HD_Images'] = self.use_hd_images.isChecked() if unicode(self.epub_version_combobox.currentText()) == 'Auto-detect': plugin_prefs['Epub_Version'] = 'A' else: plugin_prefs['Epub_Version'] = unicode(self.epub_version_combobox.currentText())[4:] def getDirectory(self): c = choose_dir(self, _(PLUGIN_NAME + 'dir_chooser'), _('Select Default Directory To Unpack Kindle Book/Mobi To')) if c: self.directory_txtBox.setReadOnly(False) self.directory_txtBox.setText(c) self.directory_txtBox.setReadOnly(True) def validate(self): # This is just to catch the situation where somone might # manually enter a non-existent path in the Default path textbox. # Shouldn't be possible at this point. if not os.path.exists(self.directory_txtBox.text()): errmsg = '<p>The path specified for the Default Unpack folder does not exist.</p>' \ '<p>Your latest preference changes will <b>NOT</b> be saved!</p>' + \ '<p>You should configure again and make sure your settings are correct.' error_dialog(None, _(PLUGIN_NAME + ' v' + PLUGIN_VERSION), _(errmsg), show=True) return False return True
class FindAnnotationsDialog(SizePersistedDialog, Logger): GENERIC_STYLE = 'Any style' GENERIC_READER = 'Any reader' def __init__(self, opts): self.matched_ids = set() self.opts = opts self.prefs = opts.prefs super(FindAnnotationsDialog, self).__init__(self.opts.gui, 'find_annotations_dialog') self.setWindowTitle('Find Annotations') self.setWindowIcon(self.opts.icon) self.l = QVBoxLayout(self) self.setLayout(self.l) self.search_criteria_gb = QGroupBox(self) self.search_criteria_gb.setTitle("Search criteria") self.scgl = QGridLayout(self.search_criteria_gb) self.l.addWidget(self.search_criteria_gb) # addWidget(widget, row, col, rowspan, colspan) row = 0 # ~~~~~~~~ Create the Readers comboBox ~~~~~~~~ self.reader_label = QLabel('Reader') self.reader_label.setAlignment(Qt.AlignRight|Qt.AlignVCenter) self.scgl.addWidget(self.reader_label, row, 0, 1, 1) self.find_annotations_reader_comboBox = QComboBox() self.find_annotations_reader_comboBox.setObjectName('find_annotations_reader_comboBox') self.find_annotations_reader_comboBox.setToolTip('Reader annotations to search for') self.find_annotations_reader_comboBox.addItem(self.GENERIC_READER) racs = ReaderApp.get_reader_app_classes() for ra in sorted(racs.keys()): self.find_annotations_reader_comboBox.addItem(ra) self.scgl.addWidget(self.find_annotations_reader_comboBox, row, 1, 1, 4) row += 1 # ~~~~~~~~ Create the Styles comboBox ~~~~~~~~ self.style_label = QLabel('Style') self.style_label.setAlignment(Qt.AlignRight|Qt.AlignVCenter) self.scgl.addWidget(self.style_label, row, 0, 1, 1) self.find_annotations_color_comboBox = QComboBox() self.find_annotations_color_comboBox.setObjectName('find_annotations_color_comboBox') self.find_annotations_color_comboBox.setToolTip('Annotation style to search for') self.find_annotations_color_comboBox.addItem(self.GENERIC_STYLE) all_colors = COLOR_MAP.keys() all_colors.remove('Default') for color in sorted(all_colors): self.find_annotations_color_comboBox.addItem(color) self.scgl.addWidget(self.find_annotations_color_comboBox, row, 1, 1, 4) row += 1 # ~~~~~~~~ Create the Text LineEdit control ~~~~~~~~ self.text_label = QLabel('Text') self.text_label.setAlignment(Qt.AlignRight|Qt.AlignVCenter) self.scgl.addWidget(self.text_label, row, 0, 1, 1) self.find_annotations_text_lineEdit = MyLineEdit() self.find_annotations_text_lineEdit.setObjectName('find_annotations_text_lineEdit') self.scgl.addWidget(self.find_annotations_text_lineEdit, row, 1, 1, 3) self.reset_text_tb = QToolButton() self.reset_text_tb.setObjectName('reset_text_tb') self.reset_text_tb.setToolTip('Clear search criteria') self.reset_text_tb.setIcon(QIcon(I('trash.png'))) self.reset_text_tb.clicked.connect(self.clear_text_field) self.scgl.addWidget(self.reset_text_tb, row, 4, 1, 1) row += 1 # ~~~~~~~~ Create the Note LineEdit control ~~~~~~~~ self.note_label = QLabel('Note') self.note_label.setAlignment(Qt.AlignRight|Qt.AlignVCenter) self.scgl.addWidget(self.note_label, row, 0, 1, 1) self.find_annotations_note_lineEdit = MyLineEdit() self.find_annotations_note_lineEdit.setObjectName('find_annotations_note_lineEdit') self.scgl.addWidget(self.find_annotations_note_lineEdit, row, 1, 1, 3) self.reset_note_tb = QToolButton() self.reset_note_tb.setObjectName('reset_note_tb') self.reset_note_tb.setToolTip('Clear search criteria') self.reset_note_tb.setIcon(QIcon(I('trash.png'))) self.reset_note_tb.clicked.connect(self.clear_note_field) self.scgl.addWidget(self.reset_note_tb, row, 4, 1, 1) row += 1 # ~~~~~~~~ Create the Date range controls ~~~~~~~~ self.date_range_label = QLabel('Date range') self.date_range_label.setAlignment(Qt.AlignRight|Qt.AlignVCenter) self.scgl.addWidget(self.date_range_label, row, 0, 1, 1) # Date 'From' self.find_annotations_date_from_dateEdit = MyDateEdit(self, datetime(1970,1,1)) self.find_annotations_date_from_dateEdit.setObjectName('find_annotations_date_from_dateEdit') #self.find_annotations_date_from_dateEdit.current_val = datetime(1970,1,1) self.find_annotations_date_from_dateEdit.clear_button.clicked.connect(self.find_annotations_date_from_dateEdit.reset_from_date) self.scgl.addWidget(self.find_annotations_date_from_dateEdit, row, 1, 1, 1) self.scgl.addWidget(self.find_annotations_date_from_dateEdit.clear_button, row, 2, 1, 1) # Date 'To' self.find_annotations_date_to_dateEdit = MyDateEdit(self, datetime.today()) self.find_annotations_date_to_dateEdit.setObjectName('find_annotations_date_to_dateEdit') #self.find_annotations_date_to_dateEdit.current_val = datetime.today() self.find_annotations_date_to_dateEdit.clear_button.clicked.connect(self.find_annotations_date_to_dateEdit.reset_to_date) self.scgl.addWidget(self.find_annotations_date_to_dateEdit, row, 3, 1, 1) self.scgl.addWidget(self.find_annotations_date_to_dateEdit.clear_button, row, 4, 1, 1) row += 1 # ~~~~~~~~ Create a horizontal line ~~~~~~~~ self.hl = QFrame(self) self.hl.setGeometry(QRect(0, 0, 1, 3)) self.hl.setFrameShape(QFrame.HLine) self.hl.setFrameShadow(QFrame.Raised) self.scgl.addWidget(self.hl, row, 0, 1, 5) row += 1 # ~~~~~~~~ Create the results label field ~~~~~~~~ self.result_label = QLabel('<p style="color:red">scanning…</p>') self.result_label.setAlignment(Qt.AlignCenter) self.result_label.setWordWrap(False) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Maximum) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.result_label.sizePolicy().hasHeightForWidth()) self.result_label.setSizePolicy(sizePolicy) self.result_label.setMinimumSize(QtCore.QSize(250, 0)) self.scgl.addWidget(self.result_label, row, 0, 1, 5) row += 1 # ~~~~~~~~ Create the ButtonBox ~~~~~~~~ self.dialogButtonBox = QDialogButtonBox(self) self.dialogButtonBox.setOrientation(Qt.Horizontal) if False: self.update_button = QPushButton('Update results') self.update_button.setDefault(True) self.update_button.setVisible(False) self.dialogButtonBox.addButton(self.update_button, QDialogButtonBox.ActionRole) self.cancel_button = self.dialogButtonBox.addButton(self.dialogButtonBox.Cancel) self.find_button = self.dialogButtonBox.addButton(self.dialogButtonBox.Ok) self.find_button.setText('Find Matching Books') self.l.addWidget(self.dialogButtonBox) self.dialogButtonBox.clicked.connect(self.find_annotations_dialog_clicked) # ~~~~~~~~ Add a spacer ~~~~~~~~ self.spacerItem = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) self.l.addItem(self.spacerItem) # ~~~~~~~~ Restore previously saved settings ~~~~~~~~ self.restore_settings() # ~~~~~~~~ Declare sizing ~~~~~~~~ sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.sizePolicy().hasHeightForWidth()) self.setSizePolicy(sizePolicy) self.resize_dialog() # ~~~~~~~~ Connect all signals ~~~~~~~~ self.find_annotations_reader_comboBox.currentIndexChanged.connect(partial(self.update_results, 'reader')) self.find_annotations_color_comboBox.currentIndexChanged.connect(partial(self.update_results, 'color')) self.find_annotations_text_lineEdit.editingFinished.connect(partial(self.update_results, 'text')) self.find_annotations_note_lineEdit.editingFinished.connect(partial(self.update_results, 'note')) # self.connect(self.find_annotations_text_lineEdit, pyqtSignal("return_pressed"), self.return_pressed) self.find_annotations_text_lineEdit.return_pressed.connect(self.return_pressed) # self.connect(self.find_annotations_note_lineEdit, pyqtSignal("return_pressed"), self.return_pressed) self.find_annotations_note_lineEdit.return_pressed.connect(self.return_pressed) # Date range signals connected in inventory_available() # ~~~~~~~~ Allow dialog to render before doing inventory ~~~~~~~~ #field = self.prefs.get('cfg_annotations_destination_field', None) field = get_cc_mapping('annotations', 'field', None) self.annotated_books_scanner = InventoryAnnotatedBooks(self.opts.gui, field, get_date_range=True) self.annotated_books_scanner.signal.connect(self.inventory_available) QTimer.singleShot(1, self.start_inventory_scan) def clear_note_field(self): if str(self.find_annotations_note_lineEdit.text()) > '': self.find_annotations_note_lineEdit.setText('') self.update_results('clear_note_field') def clear_text_field(self): if str(self.find_annotations_text_lineEdit.text()) > '': self.find_annotations_text_lineEdit.setText('') self.update_results('clear_text_field') def find_annotations_dialog_clicked(self, button): if self.dialogButtonBox.buttonRole(button) == QDialogButtonBox.AcceptRole: self.save_settings() self.accept() elif self.dialogButtonBox.buttonRole(button) == QDialogButtonBox.RejectRole: self.close() def inventory_available(self): ''' Update the Date range widgets with the rounded oldest, newest dates Don't connect date signals until date range available ''' self._log_location() # Reset the date range based on available annotations oldest = QDateTime(datetime.fromtimestamp(self.annotated_books_scanner.oldest_annotation)) oldest_day = QDateTime(datetime.fromtimestamp(self.annotated_books_scanner.oldest_annotation).replace(hour=0, minute=0, second=0)) newest = QDateTime(datetime.fromtimestamp(self.annotated_books_scanner.newest_annotation)) newest_day = QDateTime(datetime.fromtimestamp(self.annotated_books_scanner.newest_annotation).replace(hour=23, minute=59, second=59)) # Set 'From' date limits to inventory values self.find_annotations_date_from_dateEdit.setMinimumDateTime(oldest_day) self.find_annotations_date_from_dateEdit.current_val = oldest self.find_annotations_date_from_dateEdit.setMaximumDateTime(newest_day) # Set 'To' date limits to inventory values self.find_annotations_date_to_dateEdit.setMinimumDateTime(oldest_day) self.find_annotations_date_to_dateEdit.current_val = newest_day self.find_annotations_date_to_dateEdit.setMaximumDateTime(newest_day) # Connect the signals for date range changes self.find_annotations_date_from_dateEdit.dateTimeChanged.connect(partial(self.update_results, 'from_date')) self.find_annotations_date_to_dateEdit.dateTimeChanged.connect(partial(self.update_results, 'to_date')) self.update_results('inventory_available') def restore_settings(self): self.blockSignals(True) ra = self.prefs.get('find_annotations_reader_comboBox', self.GENERIC_READER) ra_index = self.find_annotations_reader_comboBox.findText(ra) self.find_annotations_reader_comboBox.setCurrentIndex(ra_index) color = self.prefs.get('find_annotations_color_comboBox', self.GENERIC_STYLE) color_index = self.find_annotations_color_comboBox.findText(color) self.find_annotations_color_comboBox.setCurrentIndex(color_index) text = self.prefs.get('find_annotations_text_lineEdit', '') self.find_annotations_text_lineEdit.setText(text) note = self.prefs.get('find_annotations_note_lineEdit', '') self.find_annotations_note_lineEdit.setText(note) if False: from_date = self.prefs.get('find_annotations_date_from_dateEdit', datetime(1970,1,1)) self.find_annotations_date_from_dateEdit.current_val = from_date to_date = self.prefs.get('find_annotations_date_to_dateEdit', datetime.today()) self.find_annotations_date_to_dateEdit.current_val = to_date self.blockSignals(False) def return_pressed(self): self.update_results("return_pressed") def save_settings(self): ra = str(self.find_annotations_reader_comboBox.currentText()) self.prefs.set('find_annotations_reader_comboBox', ra) color = str(self.find_annotations_color_comboBox.currentText()) self.prefs.set('find_annotations_color_comboBox', color) text = str(self.find_annotations_text_lineEdit.text()) self.prefs.set('find_annotations_text_lineEdit', text) note = str(self.find_annotations_note_lineEdit.text()) self.prefs.set('find_annotations_note_lineEdit', note) if False: from_date = self.find_annotations_date_from_dateEdit.current_val self.prefs.set('find_annotations_date_from_dateEdit', from_date) to_date = self.find_annotations_date_to_dateEdit.current_val self.prefs.set('find_annotations_date_to_dateEdit', to_date) def start_inventory_scan(self): self._log_location() self.annotated_books_scanner.start() def update_results(self, trigger): #self._log_location(trigger) reader_to_match = str(self.find_annotations_reader_comboBox.currentText()) color_to_match = str(self.find_annotations_color_comboBox.currentText()) text_to_match = str(self.find_annotations_text_lineEdit.text()) note_to_match = str(self.find_annotations_note_lineEdit.text()) from_date = self.find_annotations_date_from_dateEdit.dateTime().toTime_t() to_date = self.find_annotations_date_to_dateEdit.dateTime().toTime_t() annotation_map = self.annotated_books_scanner.annotation_map #field = self.prefs.get("cfg_annotations_destination_field", None) field = get_cc_mapping('annotations', 'field', None) db = self.opts.gui.current_db matched_titles = [] self.matched_ids = set() for cid in annotation_map: mi = db.get_metadata(cid, index_is_id=True) soup = None if field == 'Comments': if mi.comments: soup = BeautifulSoup(mi.comments) else: if mi.get_user_metadata(field, False)['#value#'] is not None: soup = BeautifulSoup(mi.get_user_metadata(field, False)['#value#']) if soup: uas = soup.findAll('div', 'annotation') for ua in uas: # Are we already logged? if cid in self.matched_ids: continue # Check reader if reader_to_match != self.GENERIC_READER: this_reader = ua['reader'] if this_reader != reader_to_match: continue # Check color if color_to_match != self.GENERIC_STYLE: this_color = ua.find('table')['color'] if this_color != color_to_match: continue # Check date range, allow for mangled timestamp try: timestamp = float(ua.find('td', 'timestamp')['uts']) if timestamp < from_date or timestamp > to_date: continue except: continue highlight_text = '' try: pels = ua.findAll('p', 'highlight') for pel in pels: highlight_text += pel.string + '\n' except: pass if text_to_match > '': if not re.search(text_to_match, highlight_text, flags=re.IGNORECASE): continue note_text = '' try: nels = ua.findAll('p', 'note') for nel in nels: note_text += nel.string + '\n' except: pass if note_to_match > '': if not re.search(note_to_match, note_text, flags=re.IGNORECASE): continue # If we made it this far, add the id to matched_ids self.matched_ids.add(cid) matched_titles.append(mi.title) # Update the results box matched_titles.sort() if len(annotation_map): if len(matched_titles): first_match = ("<i>%s</i>" % matched_titles[0]) if len(matched_titles) == 1: results = first_match else: results = first_match + (" and %d more." % (len(matched_titles) - 1)) self.result_label.setText('<p style="color:blue">{0}</p>'.format(results)) else: self.result_label.setText('<p style="color:red">no matches</p>') else: self.result_label.setText('<p style="color:red">no annotated books in library</p>') self.resize_dialog()
class ConfigWidget(QWidget): def __init__(self): QWidget.__init__(self) self.l = QVBoxLayout() self.l.setContentsMargins(0, 0, 0, 0) self.setLayout(self.l) self.help_msg = QLabel(''' <h2 style="text-align: center">Get Started</h2> <p> To start syncing your library you will need to create an account to retrieve<br> your API key. </p> ''') self.l.addWidget(self.help_msg) self.form = QFormLayout() self.form.setFieldGrowthPolicy(QFormLayout.ExpandingFieldsGrow) self.l.addLayout(self.form) self.link = QLabel('<a href="{0}">{0}</a>'.format(prefs['api_base'] + '/api-key')) self.link.setOpenExternalLinks(True) self.form.addRow('Visit:', self.link) self.api_key = QLineEdit(self) self.api_key.setText(prefs['api_key']) self.form.addRow('API Key:', self.api_key) self.debug = QCheckBox(self) self.debug.setChecked(prefs['debug']) self.form.addRow('Debug Logging:', self.debug) self.update_metadata_layout = QHBoxLayout() self.update_metadata_layout.setContentsMargins(0, 0, 0, 0) self.update_metadata = QCheckBox(self) self.update_metadata.setChecked(prefs['update_metadata']) self.update_metadata_layout.addWidget(self.update_metadata) self.update_metadata_hint = QLabel('(sync all metadata changes made)') self.update_metadata_layout.addWidget(self.update_metadata_hint) self.form.addRow('Update Metadata:', self.update_metadata_layout) self.threads = QComboBox(self) for n in range(3): self.threads.addItem(str(pow(2, n))) self.threads.setCurrentText(str(prefs['threads'])) self.form.addRow('Sync Threads:', self.threads) self.bookshelves_custom_column = QComboBox(self) self.bookshelves_custom_column.addItem('') for key, meta in get_current_db().new_api.field_metadata.custom_iteritems(): if meta['datatype'] == 'text': self.bookshelves_custom_column.addItem(key) self.bookshelves_custom_column.setCurrentText(prefs['bookshelves_custom_column']) self.form.addRow('Bookshelves Column:', self.bookshelves_custom_column) def save_settings(self): prefs['api_key'] = unicode(self.api_key.text()) prefs['debug'] = self.debug.isChecked() prefs['update_metadata'] = self.update_metadata.isChecked() prefs['threads'] = int(self.threads.currentText()) prefs['bookshelves_custom_column'] = unicode(self.bookshelves_custom_column.currentText())
class HistoChart (QGraphicsView): def __init__(self,parent,size=QSize(400,400)): super(HistoChart,self).__init__(parent) self.my_scene = QGraphicsScene() #self.my_scene.setSceneRect(self.sceneRect()) self.setScene(self.my_scene) #self.setBackgroundBrush(QBrush(QtCore.Qt.black, QtCore.Qt.SolidPattern)); self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self.size_text_width = -1 self.margin = {'top':10,'bottom':10,'left':10,'right':10} self.initialize = False self.max_length_text = 10 self.histo_items = [] def setData (self,list_items): size = min(self.frameSize().width()/2.0,self.frameSize().height()) self.size_pie = QSize(size,size) self.data = list_items if self.initialize == False: self.c_box = QComboBox() self.c_box.addItem("warrior") self.c_box.addItem("rank") self.c_box.addItem("power") self.c_box.currentIndexChanged.connect(self.update) proxy = self.my_scene.addWidget(self.c_box) proxy.setPos(QPointF(0.0,self.margin['top'])) self.margin['top']+=self.c_box.height() self.initialize = True self.update() def update(self): i = 0 #self.my_scene.clear() for item in self.histo_items : self.my_scene.removeItem(item) self.histo_items = [] self.scene().setSceneRect(QRectF(0,0,self.frameSize().width(),self.frameSize().height())) max = -1 for value in self.data.values(): if value[self.c_box.currentText()] > max: max = value[self.c_box.currentText()] size_text_number = QGraphicsTextItem(str(max)).boundingRect().width()+10 interval= self.parent().size().height()-self.margin['top']-self.margin['bottom'] interval = interval /len(self.data) temp = "aaaaaaaaaaaaaaaaa" if len(temp)> self.max_length_text: data = temp[:self.max_length_text]+"." else : data = temp self.size_text_width = QGraphicsTextItem(data).boundingRect().width()+10 #print ('width:',self.size_text_width) horizontal_size = self.parent().size().width()- self.margin['left']- self.margin['right']-self.size_text_width- size_text_number try: ratio = horizontal_size/ max except ZeroDivisionError : ratio = 0 i = 0 for groupe,value in zip(self.data.keys(),self.data.values()) : if self.c_box.currentText() == "warrior": title_str = 'Nombre de Heros' bar_all = QGraphicsRectItem(0,self.margin['top'],value['warrior']*ratio,interval*0.8) bar_all.setPos(self.size_text_width,interval*0.2+(i*interval)) gradient = QLinearGradient(QPointF(bar_all.rect().width()/2,0),QPointF(bar_all.rect().width()/2,bar_all.rect().height()+self.margin['top'])) gradient.setColorAt(0,QColor('white')) gradient.setColorAt(1,QColor('red')) brush = QBrush(gradient) #brush.setTexture(QPixmap(":/textures/"+groupe.attribs['color'])) bar_all.setBrush(brush) self.my_scene.addItem(bar_all) self.histo_items.append(bar_all) bar_alive = QGraphicsRectItem(0,self.margin['top'],value['alive']*ratio,interval*0.8) bar_alive.setPos(self.size_text_width,interval*0.2+(i*interval)) gradient = QLinearGradient(QPointF(bar_alive.rect().width()/2,0),QPointF(bar_alive.rect().width()/2,bar_alive.rect().height()+self.margin['top'])) # gradient.setStart(QPointF(0.5,0)) # gradient.setStop(QPointF(0.5,1)) gradient.setColorAt(0,QColor('white')) gradient.setColorAt(1,QColor('green')) brush = QBrush(gradient) bar_alive.setBrush(brush) self.my_scene.addItem(bar_alive) self.histo_items.append(bar_alive) text_nb_warriors= QGraphicsTextItem(str(value['warrior'])) text_nb_warriors.setDefaultTextColor(QColor('green')) trans = QTransform().translate(bar_all.pos().x()+bar_all.rect().width()+10,interval*0.2+(i*interval)+self.margin['top']) pts = trans.map(QPointF(0,0.0)) text_nb_warriors.setPos(pts.x(),pts.y()) self.my_scene.addItem(text_nb_warriors) self.histo_items.append(text_nb_warriors) #bar ranl elif self.c_box.currentText()== "rank": title_str = "Rank Moyen" bar_rank = QGraphicsRectItem(0,self.margin['top'],value['rank']*ratio,interval*0.8) bar_rank.setPos(self.size_text_width, interval*0.2+(i*interval)) gradient = QLinearGradient(QPointF(bar_rank.rect().width()/2,0),QPointF(bar_rank.rect().width()/2,bar_rank.rect().height()+self.margin['top'])) # gradient.setStart(QPointF(0.5,0)) # gradient.setStop(QPointF(0.5,1)) gradient.setColorAt(0,QColor('white')) gradient.setColorAt(1,QColor('red')) brush = QBrush(gradient) bar_rank.setBrush(brush) self.my_scene.addItem(bar_rank) self.histo_items.append(bar_rank) # value text_rank = QGraphicsTextItem("{0:1.1f}".format(value['rank'])) text_rank.setDefaultTextColor(QColor('red')) trans = QTransform().translate(bar_rank.pos().x()+bar_rank.rect().width()+10,interval*0.2+(i*interval)+self.margin['top']) pts = trans.map(QPointF(0,0.0)) text_rank.setPos(pts.x(),pts.y()) self.my_scene.addItem(text_rank) self.histo_items.append(text_rank) else: title_str = "Puissance" bar_rank = QGraphicsRectItem(0,self.margin['top'],value['power']*ratio,interval*0.8) bar_rank.setPos(self.size_text_width, interval*0.2+(i*interval)) gradient = QLinearGradient(QPointF(bar_rank.rect().width()/2,0),QPointF(bar_rank.rect().width()/2,bar_rank.rect().height()+self.margin['top'])) # gradient.setStart(QPointF(0.5,0)) # gradient.setStop(QPointF(0.5,1)) gradient.setColorAt(0,QColor('white')) gradient.setColorAt(1,QColor('blue')) brush = QBrush(gradient) bar_rank.setBrush(brush) self.my_scene.addItem(bar_rank) self.histo_items.append(bar_rank) # value try : valeur = (value['power']/max)*100 except ZeroDivisionError : valeur = 0 text_rank = QGraphicsTextItem("{0:1.1f}".format(valeur)) text_rank.setDefaultTextColor(QColor('blue')) trans = QTransform().translate(bar_rank.pos().x()+bar_rank.rect().width()+10,interval*0.2+(i*interval)+self.margin['top']) pts = trans.map(QPointF(0,0.0)) text_rank.setPos(pts.x(),pts.y()) self.my_scene.addItem(text_rank) self.histo_items.append(text_rank) #dessin du titre title = QGraphicsTextItem(title_str) title.setPos(self.margin['left']+self.size_text_width+horizontal_size/2.0,self.c_box.pos().y()) self.my_scene.addItem(title) self.histo_items.append(title) #affichage des label colonne de gauche if len(groupe.name)> self.max_length_text: data = groupe.name[:self.max_length_text]+".." else : data = groupe.name text = QGraphicsTextItem(data) #text.setTextWidth(20) trans = QTransform().translate(self.margin['left'],interval*0.2+(i*interval)+self.margin['top']) pts = trans.map(QPointF(0,0.0)) text.setPos(pts.x(),pts.y()) self.my_scene.addItem(text) self.histo_items.append(text) i +=1 # self.fitInView(self.scene.sceneRect())
def currentText(self): if self.currentIndex() == 0: return '' return QComboBox.currentText(self)
class UArmUI(object): def __init__(self, ui, layout): self.main_ui = ui self.layout = layout super(UArmUI, self).__init__() self.handler = UArmHandler(self) self.lang = self.main_ui.lang self.status = 0 self.set_ui() self.set_disable(True) def set_ui(self): self._set_common_top_ui() self._set_tab() self._set_common_down_ui() self.connect_slot() def _set_common_top_ui(self): top_frame = QFrame() top_frame.setMaximumHeight(60) top_layout = QVBoxLayout(top_frame) self.layout.addWidget(top_frame) common_top_frame = QFrame() common_top_frame.setMinimumHeight(50) common_top_frame.setMaximumHeight(50) common_top_layout = QHBoxLayout(common_top_frame) top_layout.addWidget(common_top_frame) common_top_layout.addStretch(0) label = QLabel(i18n[self.lang]['Type'] + ':') self.label_type = QLabel('') self.label_type.setStyleSheet('''color: gray;font:bold;''') common_top_layout.addWidget(label) common_top_layout.addWidget(self.label_type) label = QLabel(i18n[self.lang]['Mode'] + ':') self.label_mode = QLabel('') self.label_mode.setStyleSheet('''color: gray;font:bold;''') common_top_layout.addWidget(label) common_top_layout.addWidget(self.label_mode) label = QLabel(i18n[self.lang]['HardwareVersion'] + ':') self.label_hard_version = QLabel('') self.label_hard_version.setStyleSheet('''color: gray;font:bold;''') common_top_layout.addWidget(label) common_top_layout.addWidget(self.label_hard_version) label = QLabel(i18n[self.lang]['FirmwareVersion'] + ':') self.label_firm_version = QLabel('') self.label_firm_version.setStyleSheet('''color: gray;font:bold;''') common_top_layout.addWidget(label) common_top_layout.addWidget(self.label_firm_version) label_1 = QLabel(i18n[self.lang]['Connected'] + ':') self.label_connected = QLabel() img = QImage() self.label_connected.setMaximumHeight(20) self.label_connected.setMaximumWidth(20) self.label_connected.setScaledContents(True) if img.load(disconnect_icon_path): self.label_connected.setPixmap(QPixmap.fromImage(img)) self.lnt_addr = QLineEdit('COM12') self.lnt_addr.setMaximumWidth(50) self.lnt_addr.setMinimumWidth(30) self.btn_connect = QPushButton(i18n[self.lang]['Connect']) # self.btn_connect.setMaximumWidth(50) # common_top_layout.addStretch(0) common_top_layout.setSpacing(10) common_top_layout.addWidget(label_1) common_top_layout.addWidget(self.label_connected) common_top_layout.addWidget(self.lnt_addr) common_top_layout.addWidget(self.btn_connect) def _set_common_down_ui(self): slider_frame = QFrame() slider_layout = QGridLayout(slider_frame) self.layout.addWidget(slider_frame) label = QLabel(i18n[self.lang]['Speed'] + ':') self.slider_speed = QSlider(Qt.Horizontal) self.spinbox_speed = QSpinBox() self.slider_speed.setMinimum(1) self.slider_speed.setMaximum(100000) self.slider_speed.setValue(10000) self.spinbox_speed.setSingleStep(1) self.spinbox_speed.setMinimum(1) self.spinbox_speed.setMaximum(100000) self.spinbox_speed.setValue(10000) slider_layout.addWidget(label, 0, 0) slider_layout.addWidget(self.slider_speed, 0, 1) slider_layout.addWidget(self.spinbox_speed, 0, 2) # label = QLabel(i18n[self.lang]['Acc'] + ':') # self.slider_acc = QSlider(Qt.Horizontal) # self.spinbox_acc = QSpinBox() # self.slider_acc.setMinimum(1) # self.slider_acc.setMaximum(100000) # self.slider_acc.setValue(5000) # self.spinbox_acc.setSingleStep(1) # self.spinbox_acc.setMinimum(1) # self.spinbox_acc.setMaximum(100000) # self.spinbox_acc.setValue(5000) # slider_layout.addWidget(label, 0, 3) # slider_layout.addWidget(self.slider_acc, 0, 4) # slider_layout.addWidget(self.spinbox_acc, 0, 5) btn_frame = QFrame() btn_layout = QGridLayout(btn_frame) self.layout.addWidget(btn_frame) self.btn_get_device_info = QPushButton(i18n[self.lang]['GetDeviceInfo']) self.btn_get_position = QPushButton(i18n[self.lang]['GetPosition']) self.btn_get_polar = QPushButton(i18n[self.lang]['GetPolar']) self.btn_get_servo_angle = QPushButton(i18n[self.lang]['GetServoAngle']) self.btn_get_mode = QPushButton(i18n[self.lang]['GetMode']) btn_layout.addWidget(self.btn_get_device_info, 0, 0) btn_layout.addWidget(self.btn_get_position, 0, 1) btn_layout.addWidget(self.btn_get_polar, 0, 2) btn_layout.addWidget(self.btn_get_servo_angle, 0, 3) btn_layout.addWidget(self.btn_get_mode, 0, 4) self.combobox_servo = QComboBox() self.combobox_servo.setStyleSheet('''color: blue;''') for item in ['axis-1', 'axis-2', 'axis-3', 'axis-all']: self.combobox_servo.addItem(item) self.combobox_servo.setCurrentIndex(0) btn_layout.addWidget(self.combobox_servo, 0, 0) self.btn_servo_attach = QPushButton(i18n[self.lang]['ServoAttach']) self.btn_servo_detach = QPushButton(i18n[self.lang]['ServoDetach']) self.combobox_mode = QComboBox() self.combobox_mode.setStyleSheet('''color: blue;''') for item in ['normal', 'laser', '3D', 'pen']: self.combobox_mode.addItem(item) self.combobox_mode.setCurrentIndex(0) self.btn_set_mode = QPushButton(i18n[self.lang]['SetMode']) btn_layout.addWidget(self.combobox_servo, 1, 0) btn_layout.addWidget(self.btn_servo_attach, 1, 1) btn_layout.addWidget(self.btn_servo_detach, 1, 2) btn_layout.addWidget(self.combobox_mode, 1, 3) btn_layout.addWidget(self.btn_set_mode, 1, 4) self.btn_reset = QPushButton(i18n[self.lang]['Reset']) self.btn_pump_on = QPushButton(i18n[self.lang]['PumpOn']) self.btn_pump_off = QPushButton(i18n[self.lang]['PumpOff']) self.btn_gripper_catch = QPushButton(i18n[self.lang]['GripperCatch']) self.btn_gripper_release = QPushButton(i18n[self.lang]['GripperRelease']) btn_layout.addWidget(self.btn_reset, 2, 0) btn_layout.addWidget(self.btn_pump_on, 2, 1) btn_layout.addWidget(self.btn_pump_off, 2, 2) btn_layout.addWidget(self.btn_gripper_catch, 2, 3) btn_layout.addWidget(self.btn_gripper_release, 2, 4) @staticmethod def slider_spinbox_related(value, master=None, slave=None, scale=1): try: slave.setValue(value * scale) except Exception as e: print(e) def connect_slot(self): self.btn_connect.clicked.connect(self.connect) self.slider_speed.valueChanged.connect( functools.partial(self.slider_spinbox_related, slave=self.spinbox_speed, scale=1)) self.spinbox_speed.valueChanged.connect( functools.partial(self.slider_spinbox_related, slave=self.slider_speed, scale=1)) # self.slider_acc.valueChanged.connect( # functools.partial(self.slider_spinbox_related, slave=self.spinbox_acc, scale=1)) # self.spinbox_acc.valueChanged.connect( # functools.partial(self.slider_spinbox_related, slave=self.slider_acc, scale=1)) self.btn_get_device_info.clicked.connect(self.get_device_info) self.btn_get_position.clicked.connect(self.get_position) self.btn_get_polar.clicked.connect(self.get_polar) self.btn_get_servo_angle.clicked.connect(self.get_servo_angle) self.btn_get_mode.clicked.connect(self.get_mode) self.btn_servo_attach.clicked.connect(self.set_servo_attach) self.btn_servo_detach.clicked.connect(self.set_servo_detach) self.btn_set_mode.clicked.connect(self.set_mode) self.btn_reset.clicked.connect(self.reset) self.btn_pump_on.clicked.connect(self.pump_on) self.btn_pump_off.clicked.connect(self.pump_off) self.btn_gripper_catch.clicked.connect(self.gripper_catch) self.btn_gripper_release.clicked.connect(self.gripper_release) def reset_flag(self): self.cartesian_ui.reset_flag() self.axis_ui.reset_flag() def update_connect_status(self, item): try: img = QImage() if item and self.status != 1: self.status = 1 logger.info('connect to {} success'.format(self.handler.port)) if img.load(connect_icon_path): self.label_connected.setPixmap(QPixmap.fromImage(img)) self.btn_connect.setText(i18n[self.lang]['Disconnect']) self.btn_connect.setStyleSheet('''color: red;font:bold;''') self.set_disable(False) elif not item and self.status != 0: self.status = 0 logger.info('disconnect from {0} or failed connect {0}'.format(self.handler.port)) self.handler.cmd_que.queue.clear() if img.load(disconnect_icon_path): self.label_connected.setPixmap(QPixmap.fromImage(img)) self.btn_connect.setText(i18n[self.lang]['Connect']) self.btn_connect.setStyleSheet('''color: green;font:bold;''') self.set_disable(True) except Exception as e: print(e) def connect(self): try: if str(self.btn_connect.text()) == i18n[self.lang]['Connect']: addr = self.lnt_addr.text().strip() if addr == 'auto': addr = None self.btn_connect.setText('Connecting') self.status = 2 self.btn_connect.setStyleSheet('''color: orange;font:bold;''') self.handler.connect(addr) # if self.window.connect(addr, report_type=report_type): # self.btn_connect.setText(self.disconnect_label) # self.btn_connect.setStyleSheet('''color: red;font:bold;''') elif str(self.btn_connect.text()) == i18n[self.lang]['Disconnect']: self.handler.disconnect() self.btn_connect.setText(i18n[self.lang]['Connect']) self.btn_connect.setStyleSheet('''color: green;font:bold;''') except Exception as e: print(e) def get_device_info(self, event): try: item = { 'cmd': 'get_device_info', } self.handler.put_cmd_que(item) except Exception as e: print(e) def get_mode(self, event): try: item = { 'cmd': 'get_mode', } self.handler.put_cmd_que(item) except Exception as e: print(e) def get_position(self, event): try: item = { 'cmd': 'get_position', } self.handler.put_cmd_que(item) except Exception as e: print(e) def get_polar(self, event): try: item = { 'cmd': 'get_polar', } self.handler.put_cmd_que(item) except Exception as e: print(e) def get_servo_angle(self, event): try: item = { 'cmd': 'get_servo_angle', } self.handler.put_cmd_que(item) except Exception as e: print(e) def set_servo_attach(self, event): try: text = self.combobox_servo.currentText() if text == 'axis-all': servo_id = None else: servo_id = int(self.combobox_servo.currentIndex()) item = { 'cmd': 'set_servo_attach', 'kwargs': { 'servo_id': servo_id } } self.handler.put_cmd_que(item) except Exception as e: print(e) def set_servo_detach(self, event): try: text = self.combobox_servo.currentText() if text == 'axis-all': servo_id = None else: servo_id = int(self.combobox_servo.currentIndex()) item = { 'cmd': 'set_servo_detach', 'kwargs': { 'servo_id': servo_id } } self.handler.put_cmd_que(item) except Exception as e: print(e) def pump_on(self, event): try: item = { 'cmd': 'set_pump', 'kwargs': { 'on': True } } self.handler.put_cmd_que(item) except Exception as e: print(e) def pump_off(self, event): try: item = { 'cmd': 'set_pump', 'kwargs': { 'on': False } } self.handler.put_cmd_que(item) except Exception as e: print(e) def gripper_catch(self, event): try: item = { 'cmd': 'set_gripper', 'kwargs': { 'catch': True } } self.handler.put_cmd_que(item) except Exception as e: print(e) def gripper_release(self, event): try: item = { 'cmd': 'set_gripper', 'kwargs': { 'catch': False } } self.handler.put_cmd_que(item) except Exception as e: print(e) def set_mode(self, event): try: mode = self.combobox_mode.currentIndex() item = { 'cmd': 'set_mode', 'kwargs': { 'mode': int(mode) } } self.handler.put_cmd_que(item) except Exception as e: print(e) def reset(self, event): try: self.handler.cmd_que.queue.clear() item = { 'cmd': 'reset', 'kwargs': { 'speed': self.spinbox_speed.value(), 'wait': False, } } self.handler.put_cmd_que(item) self.reset_flag() except Exception as e: print(e) @staticmethod def slider_spinbox_related(value, master=None, slave=None, scale=1): try: slave.setValue(value * scale) except Exception as e: print(e) def _set_tab(self): tab_widget = QTabWidget() tab_widget.setMaximumHeight(self.main_ui.window.geometry().height() // 2) self.layout.addWidget(tab_widget) toolBox1 = QToolBox() toolBox2 = QToolBox() groupBox1 = QGroupBox() groupBox2 = QGroupBox() toolBox1.addItem(groupBox1, "") toolBox2.addItem(groupBox2, "") tab_widget.addTab(toolBox1, i18n[self.lang]['Angle']) tab_widget.addTab(toolBox2, i18n[self.lang]['Coordinate']) joint_layout = QVBoxLayout(groupBox1) cartesian_layout = QVBoxLayout(groupBox2) self.cartesian_ui = CoordinateUI(self, cartesian_layout) self.axis_ui = AngleUI(self, joint_layout) def update_device_info(self, device_info): if device_info is not None: self.label_type.setText(str(device_info.get('device_type', None))) self.label_hard_version.setText(str(device_info.get('hardware_version', None))) self.label_firm_version.setText(str(device_info.get('firmware_version', None))) def update_mode(self, mode): if mode is not None: if mode == 0: mode_str = 'Normal' elif mode == 1: mode_str = 'Laser' elif mode == 2: mode_str = '3D' elif mode == 3: mode_str = 'Pen' else: mode_str = self.label_mode.text() self.label_mode.setText(mode_str) def set_disable(self, disable): try: self.btn_get_device_info.setDisabled(disable) self.btn_get_position.setDisabled(disable) self.btn_get_polar.setDisabled(disable) self.btn_get_servo_angle.setDisabled(disable) self.btn_get_mode.setDisabled(disable) self.combobox_servo.setDisabled(disable) self.combobox_mode.setDisabled(disable) self.btn_servo_attach.setDisabled(disable) self.btn_servo_detach.setDisabled(disable) self.btn_reset.setDisabled(disable) self.btn_pump_on.setDisabled(disable) self.btn_pump_off.setDisabled(disable) self.btn_gripper_catch.setDisabled(disable) self.btn_gripper_release.setDisabled(disable) self.slider_speed.setDisabled(disable) self.spinbox_speed.setDisabled(disable) # self.slider_acc.setDisabled(disable) # self.spinbox_acc.setDisabled(disable) self.axis_ui.set_disable(disable) self.cartesian_ui.set_disable(disable) except Exception as e: print(e)
class HotkeyWidget(QWidget): def __init__(self, prefs, configName, title): QWidget.__init__(self) self.l = QVBoxLayout() self.setLayout(self.l) self.configName = configName self.hotkeyLayout = QHBoxLayout() self.l.addLayout(self.hotkeyLayout) enabledLabel = QLabel(title) self.hotkeyLayout.addWidget(enabledLabel) self.enabledBox = QCheckBox() self.hotkeyLayout.addWidget(self.enabledBox) self.enabledBox.setChecked(prefs[configName + '_hotkey_enabled']) hotkeyLayout2 = QHBoxLayout() self.l.addLayout(hotkeyLayout2) ctrlLabel = QLabel("Ctrl") self.ctrlBox = QCheckBox() self.ctrlBox.setChecked(prefs[configName + '_hotkey_ctrl']) ctrlLabel.setBuddy(self.ctrlBox) hotkeyLayout2.addWidget(ctrlLabel) hotkeyLayout2.addWidget(self.ctrlBox) altLabel = QLabel("Alt") self.altBox = QCheckBox() self.altBox.setChecked(prefs[configName + '_hotkey_alt']) altLabel.setBuddy(self.altBox) hotkeyLayout2.addWidget(altLabel) hotkeyLayout2.addWidget(self.altBox) shiftLabel = QLabel("Shift") self.shiftBox = QCheckBox() self.shiftBox.setChecked(prefs[configName + '_hotkey_shift']) shiftLabel.setBuddy(self.shiftBox) hotkeyLayout2.addWidget(shiftLabel) hotkeyLayout2.addWidget(self.shiftBox) self.keycodeBox = QComboBox() for key, value in keycodes.iteritems(): self.keycodeBox.addItem(key, value) index = self.keycodeBox.findData(prefs[configName + '_hotkey_keycode']) if index != -1: self.keycodeBox.setCurrentIndex(index) hotkeyLayout2.addWidget(self.keycodeBox) def save_settings(self, prefs): prefs[self.configName + '_hotkey_enabled'] = self.enabledBox.isChecked() prefs[self.configName + '_hotkey_ctrl'] = self.ctrlBox.isChecked() prefs[self.configName + '_hotkey_alt'] = self.altBox.isChecked() prefs[self.configName + '_hotkey_shift'] = self.shiftBox.isChecked() prefs[self.configName + '_hotkey_keycode'] = keycodes[unicode(self.keycodeBox.currentText())]
class EKWindow(QDialog): """ Class which is responisble for running this entire application """ def __init__(self): """ Constructor for this class """ super(EKWindow, self).__init__() self.engine = Engine("tables/Tamil-bamini.txt.in") # Settings file initialization self.settingsFilePath = os.getenv("APPDATA") + "\\" + qApp.applicationName() + "\eksettings.ini" self.init_settings() # Function to check whether the settings file is or not. self.iniSettings = QSettings(self.settingsFilePath, QSettings.IniFormat) # Variable Initialization self.registrySettings = QSettings("HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run", QSettings.NativeFormat) self.shortcutModifierKey = self.iniSettings.value("shortcut_modifier") self.shortcutKey = self.iniSettings.value("shortcut") self.selectedKeyboard = self.iniSettings.value("selected_keyboard") self.keyboardStatus = False self.fileName = "" # Ui variable Initialization self.iconGroupBox = QGroupBox("Keyboards") self.iconLabel = QLabel("Keyboard:") self.iconComboBox = QComboBox(self) self.shortcutGroupBox = QGroupBox("Shortcut Setting") self.shortcutComboBox1 = QComboBox(self) self.shortcutComboBox2 = QComboBox(self) self.otherSettingsGroupBox = QGroupBox("Other Settings") self.checkboxStartWithWindows = QCheckBox() self.minimizeAction = QAction("Minimize", self) self.maximizeAction = QAction("Maximize", self) self.settingsAction = QAction("Settings", self) self.aboutAction = QAction("About", self) self.quitAction = QAction("Quit", self) self.trayIconMenu = QMenu(self) self.trayIcon = QSystemTrayIcon(self) self.mainLayout = QVBoxLayout() self.mainLayout.addWidget(self.iconGroupBox) self.mainLayout.addWidget(self.shortcutGroupBox) self.mainLayout.addWidget(self.otherSettingsGroupBox) self.setLayout(self.mainLayout) # UI constructor and connectors self.create_settings_group_boxes() self.create_actions() self.create_tray_icon() # Signal connectors self.iconComboBox.currentIndexChanged.connect(self.change_keyboard) self.shortcutComboBox1.currentIndexChanged.connect(self.set_shortcut_modifier) self.shortcutComboBox2.currentIndexChanged.connect(self.set_shortcut_key) self.trayIcon.activated.connect(self.icon_activated) self.checkboxStartWithWindows.stateChanged.connect(self.checkbox_start_with_windows_ticked) if self.keyboardStatus: self.iconComboBox.setCurrentIndex(self.selectedKeyBoard) else: self.change_keyboard(0) self.iconComboBox.setCurrentIndex(0) self.trayIcon.show() self.set_shortcut_key() self.setWindowTitle(qApp.applicationName() + " " + qApp.applicationVersion()) def init_settings(self): """ Function to check whether the settings file is there or not. If there is no file, then it will create with default settings. """ if not os.path.exists(self.settingsFilePath): settings_dir = os.getenv("APPDATA") + "\\" + qApp.applicationName() if not os.path.exists(settings_dir): os.makedirs(settings_dir) setting_path = "" if getattr(sys, 'frozen', False): setting_path = os.path.dirname(sys.executable) elif __file__: setting_path = os.path.dirname(__file__) shutil.copyfile(os.path.join(setting_path, "resources\eksettings.ini"), self.settingsFilePath) return def create_settings_group_boxes(self): """ UI generator function. """ self.iconComboBox.addItem("No Keyboard") self.iconComboBox.addItem("Tamil99") self.iconComboBox.addItem("Phonetic") self.iconComboBox.addItem("Typewriter") self.iconComboBox.addItem("Bamini") self.iconComboBox.addItem("Inscript") icon_layout = QHBoxLayout(self) icon_layout.addWidget(self.iconLabel) icon_layout.addWidget(self.iconComboBox) icon_layout.addStretch() self.iconGroupBox.setLayout(icon_layout) shortcut_label_1 = QLabel("Modifier Key:") shortcut_label_2 = QLabel("Shortcut Key:") self.shortcutComboBox1.addItem("NONE") self.shortcutComboBox1.addItem("CTRL") self.shortcutComboBox1.addItem("ALT") modifier_index = self.shortcutComboBox1.findText(self.shortcutModifierKey) self.shortcutComboBox1.setCurrentIndex(modifier_index) self.shortcutComboBox2.setMinimumContentsLength(3) if modifier_index == 0: self.shortcutComboBox2.addItem("F1") self.shortcutComboBox2.addItem("ESC") self.shortcutComboBox2.addItem("F2") self.shortcutComboBox2.addItem("F3") self.shortcutComboBox2.addItem("F4") self.shortcutComboBox2.addItem("F5") self.shortcutComboBox2.addItem("F6") self.shortcutComboBox2.addItem("F7") self.shortcutComboBox2.addItem("F8") self.shortcutComboBox2.addItem("F9") self.shortcutComboBox2.addItem("F10") else: self.shortcutComboBox2.addItem("1") self.shortcutComboBox2.addItem("2") self.shortcutComboBox2.addItem("3") self.shortcutComboBox2.addItem("4") self.shortcutComboBox2.addItem("5") self.shortcutComboBox2.addItem("6") self.shortcutComboBox2.addItem("7") self.shortcutComboBox2.addItem("8") self.shortcutComboBox2.addItem("9") self.shortcutComboBox2.addItem("0") key_index = self.shortcutComboBox2.findText(self.shortcutKey) self.shortcutComboBox2.setCurrentIndex(key_index) shortcut_layout = QHBoxLayout(self) shortcut_layout.addWidget(shortcut_label_1) shortcut_layout.addWidget(self.shortcutComboBox1) shortcut_layout.addWidget(shortcut_label_2) shortcut_layout.addWidget(self.shortcutComboBox2) shortcut_layout.addStretch() self.shortcutGroupBox.setLayout(shortcut_layout) checkbox_start_with_windows_label = QLabel("Start eKalappai whenever windows starts") # if registry entry for auto start with windows for the current user exists, then check the checkbox if self.registrySettings.contains(qApp.applicationName()): self.checkboxStartWithWindows.setChecked(True) else: self.checkboxStartWithWindows.setChecked(False) other_settings_layout = QHBoxLayout(self) other_settings_layout.addWidget(checkbox_start_with_windows_label) other_settings_layout.addWidget(self.checkboxStartWithWindows) other_settings_layout.addStretch() self.otherSettingsGroupBox.setLayout(other_settings_layout) def set_shortcut_key(self): """ Function to change the shortcut key when its changed. """ self.shortcutKey = self.shortcutComboBox2.currentText() self.iniSettings.setValue("shortcut", self.shortcutKey) self.register_shortcut_listener() if self.shortcutKey == "ESC": self.shortcutKeyHex = 0x1B elif self.shortcutKey == "F1": self.shortcutKeyHex = 0x70 elif self.shortcutKey == "F2": self.shortcutKeyHex = 0x71 elif self.shortcutKey == "F3": self.shortcutKeyHex = 0x72 elif self.shortcutKey == "F4": self.shortcutKeyHex = 0x73 elif self.shortcutKey == "F5": self.shortcutKeyHex = 0x74 elif self.shortcutKey == "F6": self.shortcutKeyHex = 0x75 elif self.shortcutKey == "F7": self.shortcutKeyHex = 0x76 elif self.shortcutKey == "F8": self.shortcutKeyHex = 0x77 elif self.shortcutKey == "F9": self.shortcutKeyHex = 0x78 elif self.shortcutKey == "F10": self.shortcutKeyHex = 0x79 elif self.shortcutKey == "1": self.shortcutKeyHex = 0x31 elif self.shortcutKey == "2": self.shortcutKeyHex = 0x32 elif self.shortcutKey == "3": self.shortcutKeyHex = 0x33 elif self.shortcutKey == "4": self.shortcutKeyHex = 0x34 elif self.shortcutKey == "5": self.shortcutKeyHex = 0x35 elif self.shortcutKey == "6": self.shortcutKeyHex = 0x36 elif self.shortcutKey == "7": self.shortcutKeyHex = 0x37 elif self.shortcutKey == "8": self.shortcutKeyHex = 0x38 elif self.shortcutKey == "9": self.shortcutKeyHex = 0x39 elif self.shortcutKey == "0": self.shortcutKeyHex = 0x30 def create_actions(self): """ Slot connectors for all right clicking and other actions. """ self.minimizeAction.triggered.connect(self.hide) self.maximizeAction.triggered.connect(self.showMaximized) self.settingsAction.triggered.connect(self.showNormal) self.aboutAction.triggered.connect(self.show_about) self.quitAction.triggered.connect(self.quit) def quit(self): self.engine.un_hook() exit(0) def create_tray_icon(self): """ Tray icon creator and corresponding connectors """ self.trayIconMenu.addAction(self.settingsAction) self.trayIconMenu.addSeparator() self.trayIconMenu.addAction(self.aboutAction) self.trayIconMenu.addSeparator() self.trayIconMenu.addAction(self.quitAction) self.trayIcon.setContextMenu(self.trayIconMenu) def setVisible(self, visible): self.settingsAction.setEnabled(self.isMaximized() or not visible) super(EKWindow, self).setVisible(visible) def closeEvent(self, event): if self.trayIcon.isVisible(): self.hide() event.ignore() def load_keyboard(self): """ Mapping file loading function """ if self.selectedKeyboard == 1: self.fileName = "tables/Tamil-tamil99.txt.in" elif self.selectedKeyboard == 2: self.fileName = "tables/Tamil-phonetic.txt.in" elif self.selectedKeyboard == 3: self.fileName = "tables/Tamil-typewriter.txt.in" elif self.selectedKeyboard == 4: self.fileName = "tables/Tamil-bamini.txt.in" elif self.selectedKeyboard == 5: self.fileName = "tables/Tamil-inscript.txt.in" else: pass def getPath(self, index): if index == 1: self.path = "tables/Tamil-tamil99.txt.in" elif index == 2: self.path = "tables/Tamil-phonetic.txt.in" elif index == 3: self.path = "tables/Tamil-typewriter.txt.in" elif index == 4: self.path = "tables/Tamil-bamini.txt.in" elif index == 5: self.path = "tables/Tamil-inscript.txt.in" else: pass def change_keyboard(self, index): """ Function to change the keyboard based on the index which was sent as a param """ if int(index) != 0: self.iniSettings.setValue("selected_keyboard", index) self.selectedKeyboard = index self.iconComboBox.setCurrentIndex(int(index)) icon = self.iconComboBox.itemIcon(int(index)) self.trayIcon.setIcon(icon) self.setWindowIcon(icon) self.trayIcon.setToolTip(self.iconComboBox.itemText(int(index))) self.show_tray_message(index) self.load_keyboard() if int(index) != 0: self.getPath(int(index)) self.engine.file_name = self.path self.engine.initialize() self.engine.conv_state = True else: try: self.engine.conv_state = False except: pass def icon_activated(self, reason): """ Function to toggle the state when the icon is clicked or shortcut key is pressed """ if reason == QSystemTrayIcon.DoubleClick: pass elif reason == QSystemTrayIcon.Trigger: if self.keyboardStatus: self.keyboardStatus = False else: self.keyboardStatus = True if self.keyboardStatus: self.change_keyboard(self.selectedKeyboard) else: self.change_keyboard(0) elif reason == QSystemTrayIcon.MiddleClick: pass else: pass def show_tray_message(self, index): """ Tray message generator when there is change in keyboard state """ icon = QSystemTrayIcon.MessageIcon(0) message = self.iconComboBox.itemText(int(index)) + " set" self.trayIcon.showMessage(qApp.applicationName() + " " + qApp.applicationVersion(), message, icon, 100) def checkbox_start_with_windows_ticked(self): """ Function to add or disable registry entry to auto start ekalappai with windows for the current users """ if self.checkboxStartWithWindows.isChecked(): self.registrySettings.setValue(qApp.applicationName(), qApp.applicationFilePath()) else: self.registrySettings.remove(qApp.applicationName()) def show_about(self): pass def set_shortcut_modifier(self, index): """ Function to set the shortcut modifier when its changed. """ self.iniSettings.setValue("shortcut_modifier", self.shortcutComboBox1.currentText()) self.shortcutModifierKey = self.iniSettings.value("shortcut_modifier") # if none is selected, the allowed single key shortcuts should change if index == 0: self.shortcutComboBox2.clear() self.shortcutComboBox2.addItem("ESC") self.shortcutComboBox2.addItem("F1") self.shortcutComboBox2.addItem("F2") self.shortcutComboBox2.addItem("F3") self.shortcutComboBox2.addItem("F4") self.shortcutComboBox2.addItem("F5") self.shortcutComboBox2.addItem("F6") self.shortcutComboBox2.addItem("F7") self.shortcutComboBox2.addItem("F8") self.shortcutComboBox2.addItem("F9") self.shortcutComboBox2.addItem("F10") else: self.shortcutComboBox2.clear() self.shortcutComboBox2.addItem("1") self.shortcutComboBox2.addItem("2") self.shortcutComboBox2.addItem("3") self.shortcutComboBox2.addItem("4") self.shortcutComboBox2.addItem("5") self.shortcutComboBox2.addItem("6") self.shortcutComboBox2.addItem("7") self.shortcutComboBox2.addItem("8") self.shortcutComboBox2.addItem("9") self.shortcutComboBox2.addItem("0") self.register_shortcut_listener() def register_shortcut_listener(self): self.engine.event_queue.remove_all() if self.iniSettings.value("shortcut_modifier") == "NONE": self.engine.event_queue.register_event([[self.shortcutKey], self.icon_activated, QSystemTrayIcon.Trigger]) elif self.iniSettings.value("shortcut_modifier") == "CTRL": self.engine.event_queue.register_event([['Lcontrol', self.shortcutKey], self.icon_activated, QSystemTrayIcon.Trigger]) self.engine.event_queue.register_event([['Rcontrol', self.shortcutKey], self.icon_activated, QSystemTrayIcon.Trigger]) elif self.iniSettings.value("shortcut_modifier") == "ALT": self.engine.event_queue.register_event([['LMenu', self.shortcutKey], self.icon_activated, QSystemTrayIcon.Trigger]) self.engine.event_queue.register_event([['RMenu', self.shortcutKey], self.icon_activated, QSystemTrayIcon.Trigger]) return True
class CreateVirtualLibrary(QDialog): # {{{ def __init__(self, gui, existing_names, editing=None): QDialog.__init__(self, gui) self.gui = gui self.existing_names = existing_names if editing: self.setWindowTitle(_('Edit virtual library')) else: self.setWindowTitle(_('Create virtual library')) self.setWindowIcon(QIcon(I('lt.png'))) gl = QGridLayout() self.setLayout(gl) self.la1 = la1 = QLabel(_('Virtual library &name:')) gl.addWidget(la1, 0, 0) self.vl_name = QComboBox() self.vl_name.setEditable(True) self.vl_name.lineEdit().setMaxLength(MAX_VIRTUAL_LIBRARY_NAME_LENGTH) la1.setBuddy(self.vl_name) gl.addWidget(self.vl_name, 0, 1) self.editing = editing self.saved_searches_label = QLabel('') self.saved_searches_label.setTextInteractionFlags(Qt.TextSelectableByMouse) gl.addWidget(self.saved_searches_label, 2, 0, 1, 2) self.la2 = la2 = QLabel(_('&Search expression:')) gl.addWidget(la2, 1, 0) self.vl_text = QLineEdit() self.vl_text.textChanged.connect(self.search_text_changed) la2.setBuddy(self.vl_text) gl.addWidget(self.vl_text, 1, 1) self.vl_text.setText(_build_full_search_string(self.gui)) self.sl = sl = QLabel('<p>'+_('Create a virtual library based on: ')+ ('<a href="author.{0}">{0}</a>, ' '<a href="tag.{1}">{1}</a>, ' '<a href="publisher.{2}">{2}</a>, ' '<a href="series.{3}">{3}</a>, ' '<a href="search.{4}">{4}</a>.').format(_('Authors'), _('Tags'), _('Publishers'), _('Series'), _('Saved Searches'))) sl.setWordWrap(True) sl.setTextInteractionFlags(Qt.LinksAccessibleByMouse) sl.linkActivated.connect(self.link_activated) gl.addWidget(sl, 3, 0, 1, 2) gl.setRowStretch(3,10) self.hl = hl = QLabel(_(''' <h2>Virtual Libraries</h2> <p>Using <i>virtual libraries</i> you can restrict calibre to only show you books that match a search. When a virtual library is in effect, calibre behaves as though the library contains only the matched books. The Tag Browser display only the tags/authors/series/etc. that belong to the matched books and any searches you do will only search within the books in the virtual library. This is a good way to partition your large library into smaller and easier to work with subsets.</p> <p>For example you can use a Virtual Library to only show you books with the Tag <i>"Unread"</i> or only books by <i>"My Favorite Author"</i> or only books in a particular series.</p> <p>More information and examples are available in the <a href="%s">User Manual</a>.</p> ''') % localize_user_manual_link('http://manual.calibre-ebook.com/virtual_libraries.html')) hl.setWordWrap(True) hl.setOpenExternalLinks(True) hl.setFrameStyle(hl.StyledPanel) gl.addWidget(hl, 0, 3, 4, 1) bb = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) bb.accepted.connect(self.accept) bb.rejected.connect(self.reject) gl.addWidget(bb, 4, 0, 1, 0) if editing: db = self.gui.current_db virt_libs = db.prefs.get('virtual_libraries', {}) for dex,vl in enumerate(sorted(virt_libs.keys(), key=sort_key)): self.vl_name.addItem(vl, virt_libs.get(vl, '')) if vl == editing: self.vl_name.setCurrentIndex(dex) self.original_index = dex self.original_search = virt_libs.get(editing, '') self.vl_text.setText(self.original_search) self.new_name = editing self.vl_name.currentIndexChanged[int].connect(self.name_index_changed) self.vl_name.lineEdit().textEdited.connect(self.name_text_edited) self.resize(self.sizeHint()+QSize(150, 25)) def search_text_changed(self, txt): db = self.gui.current_db searches = [_('Saved searches recognized in the expression:')] txt = unicode(txt) while txt: p = txt.partition('search:') if p[1]: # found 'search:' possible_search = p[2] if possible_search: # something follows the 'search:' if possible_search[0] == '"': # strip any quotes possible_search = possible_search[1:].partition('"') else: # find end of the search name. Is EOL, space, rparen sp = possible_search.find(' ') pp = possible_search.find(')') if pp < 0 or (sp > 0 and sp <= pp): # space in string before rparen, or neither found possible_search = possible_search.partition(' ') else: # rparen in string before space possible_search = possible_search.partition(')') txt = possible_search[2] # grab remainder of the string search_name = possible_search[0] if search_name.startswith('='): search_name = search_name[1:] if search_name in db.saved_search_names(): searches.append(search_name + '=' + db.saved_search_lookup(search_name)) else: txt = '' else: txt = '' if len(searches) > 1: self.saved_searches_label.setText('\n'.join(searches)) else: self.saved_searches_label.setText('') def name_text_edited(self, new_name): self.new_name = unicode(new_name) def name_index_changed(self, dex): if self.editing and (self.vl_text.text() != self.original_search or self.new_name != self.editing): if not question_dialog(self.gui, _('Search text changed'), _('The virtual library name or the search text has changed. ' 'Do you want to discard these changes?'), default_yes=False): self.vl_name.blockSignals(True) self.vl_name.setCurrentIndex(self.original_index) self.vl_name.lineEdit().setText(self.new_name) self.vl_name.blockSignals(False) return self.new_name = self.editing = self.vl_name.currentText() self.original_index = dex self.original_search = unicode(self.vl_name.itemData(dex) or '') self.vl_text.setText(self.original_search) def link_activated(self, url): db = self.gui.current_db f, txt = unicode(url).partition('.')[0::2] if f == 'search': names = db.saved_search_names() else: names = getattr(db, 'all_%s_names'%f)() d = SelectNames(names, txt, parent=self) if d.exec_() == d.Accepted: prefix = f+'s' if f in {'tag', 'author'} else f if f == 'search': search = ['(%s)'%(db.saved_search_lookup(x)) for x in d.names] else: search = ['%s:"=%s"'%(prefix, x.replace('"', '\\"')) for x in d.names] if search: if not self.editing: self.vl_name.lineEdit().setText(d.names.next()) self.vl_name.lineEdit().setCursorPosition(0) self.vl_text.setText(d.match_type.join(search)) self.vl_text.setCursorPosition(0) def accept(self): n = unicode(self.vl_name.currentText()).strip() if not n: error_dialog(self.gui, _('No name'), _('You must provide a name for the new virtual library'), show=True) return if n.startswith('*'): error_dialog(self.gui, _('Invalid name'), _('A virtual library name cannot begin with "*"'), show=True) return if n in self.existing_names and n != self.editing: if not question_dialog(self.gui, _('Name already in use'), _('That name is already in use. Do you want to replace it ' 'with the new search?'), default_yes=False): return v = unicode(self.vl_text.text()).strip() if not v: error_dialog(self.gui, _('No search string'), _('You must provide a search to define the new virtual library'), show=True) return try: db = self.gui.library_view.model().db recs = db.data.search_getting_ids('', v, use_virtual_library=False, sort_results=False) except ParseException as e: error_dialog(self.gui, _('Invalid search'), _('The search in the search box is not valid'), det_msg=e.msg, show=True) return if not recs and not question_dialog( self.gui, _('Search found no books'), _('The search found no books, so the virtual library ' 'will be empty. Do you really want to use that search?'), default_yes=False): return self.library_name = n self.library_search = v QDialog.accept(self)
class _BuilderTab(QWidget): """Handles the creation of launchers.""" def __init__(self): super().__init__() self._layout = QVBoxLayout() host_label = QLabel("Server host (where EvilOSX will connect to):") self._host_field = QLineEdit() self._layout.addWidget(host_label) self._layout.addWidget(self._host_field) port_label = QLabel("Server port:") self._port_field = QLineEdit() self._layout.addWidget(port_label) self._layout.addWidget(self._port_field) live_label = QLabel( "Where should EvilOSX live? (Leave empty for ~/Library/Containers/.<RANDOM>): " ) self._live_field = QLineEdit() self._layout.addWidget(live_label) self._layout.addWidget(self._live_field) launcher_label = QLabel("Launcher name:") self._launcher_combobox = QComboBox() for launcher_name in launchers.get_names(): self._launcher_combobox.addItem(launcher_name) self._layout.addWidget(launcher_label) self._layout.addWidget(self._launcher_combobox) loader_label = QLabel("Loader name:") loader_combobox = QComboBox() self._loader_layout = QVBoxLayout() for loader_name in loaders.get_names(): loader_combobox.addItem(loader_name) self._layout.addWidget(loader_label) self._layout.addWidget(loader_combobox) loader_combobox.currentTextChanged.connect(self._set_on_loader_change) # Dynamically loaded loader layout self._layout.addLayout(self._loader_layout) self._set_on_loader_change(loader_combobox.currentText()) self._layout.setContentsMargins(10, 10, 10, 0) self._layout.setAlignment(Qt.AlignTop) self.setLayout(self._layout) def _set_on_loader_change(self, new_text: str): """Handles the loader combobox change event.""" while self._loader_layout.count(): child = self._loader_layout.takeAt(0) if child.widget(): child.widget().deleteLater() input_fields = [] for message in loaders.get_option_messages(new_text): input_field = QLineEdit() self._loader_layout.addWidget(QLabel(message)) self._loader_layout.addWidget(input_field) input_fields.append(input_field) create_button = QPushButton("Create launcher") create_button.setMaximumWidth(250) create_button.setMinimumHeight(30) create_button.pressed.connect(lambda: self._on_create_launcher( self._host_field.text(), self._port_field.text(), self._live_field.text(), new_text, self._launcher_combobox.currentText(), input_fields)) self._loader_layout.addWidget(QLabel("")) self._loader_layout.addWidget(create_button) @staticmethod def display_error(text: str): """Displays an error message to the user.""" message = QMessageBox() message.setIcon(QMessageBox.Critical) message.setWindowTitle("Error") message.setText(text) message.setStandardButtons(QMessageBox.Ok) message.exec_() @staticmethod def display_info(text: str): message = QMessageBox() message.setIcon(QMessageBox.Information) message.setWindowTitle("Information") message.setText(text) message.setStandardButtons(QMessageBox.Ok) message.exec_() def _on_create_launcher(self, server_host, server_port, program_directory, loader_name: str, launcher_name: str, input_fields: list): """Creates the launcher and outputs it to the builds directory.""" if not self._host_field.text(): self.display_error("Invalid host specified.") elif not str(self._port_field.text()).isdigit(): self.display_error("Invalid port specified.") else: set_options = [] for field in input_fields: set_options.append(field.text()) loader_options = loaders.get_options(loader_name, set_options) loader_options["program_directory"] = program_directory stager = launchers.create_stager(server_host, server_port, loader_options) launcher_extension, launcher = launchers.generate( launcher_name, stager) launcher_path = path.realpath( path.join( path.dirname(__file__), path.pardir, path.pardir, "data", "builds", "Launcher-{}.{}".format( str(uuid4())[:6], launcher_extension))) with open(launcher_path, "w") as output_file: output_file.write(launcher) self.display_info( "Launcher written to: \n{}".format(launcher_path))
class App(QWidget): USs = [] # { F# , name , description , Acceptance Criteria } USNames = [] # user stories names TCs = [] # { TC name , TC description } def __init__(self): super().__init__() self.title = 'NLP project.' self.left = 50 self.top = 50 self.width = 1500 self.height = 800 self.initUI() def initUI(self): self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, self.width, self.height) app.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling, True) # Create Label label = QLabel('Select User Story by name:', self) label.move(50, 0) # Create Label label2 = QLabel('US Description + Acceptance Criteria:', self) label2.move(50, 125) # Create Label label3 = QLabel('Choose Algorithm:', self) label3.move(50, 600) # Create Label label4 = QLabel('Results:', self) label4.move(1000, 50) # Create textbox user story self.textbox = QPlainTextEdit(self) self.textbox.move(50, 175) self.textbox.resize(500, 300) """ # Create textbox results self.textbox3 = QLineEdit(self) self.textbox3.move(1000, 75) self.textbox3.resize(400,600) self.textbox3.setDisabled(True) """ self.tableWidget = QTableWidget() #self.tableWidget.resize(400,600) # Create textbox tokenize self.textbox4 = QLineEdit(self) self.textbox4.move(50, 700) self.textbox4.resize(280, 40) #self.textbox4.setDisabled(True) """ self.scrollArea = QScrollArea(self) self.scrollArea.setWidgetResizable(True) self.scrollArea.move(50,400) self.scrollArea.resize(280,40) self.scrollArea.setWidget(self.textbox4) """ button = QPushButton('Start', self) button.move(150, 750) button.clicked.connect(self.on_click_start) button2 = QPushButton('Tokenize', self) button2.move(50, 625) button2.clicked.connect(self.on_click_tokenize) button3 = QPushButton('Load excel', self) button3.move(50, 750) button3.clicked.connect(self.on_click_load) button4 = QPushButton('Choose US', self) button4.move(550, 29) button4.clicked.connect(self.on_click_choose_US) button5 = QPushButton('Choose AC', self) button5.move(550, 149) button5.clicked.connect(self.on_click_choose_AC) button6 = QPushButton('Clear', self) button6.move(250, 750) button6.clicked.connect(self.on_click_clear) # algorithm combobox self.comboBox = QComboBox(self) algorithms = ['cosine', 'other'] self.comboBox.addItems(algorithms) self.comboBox.move(150, 625) # US combobox self.comboBox2 = QComboBox(self) self.comboBox2.move(50, 25) self.comboBox2.resize(500, 20) #action_relect = lambda: self.on_click_reselect_us(self) self.comboBox2.currentIndexChanged.connect(self.on_click_reselect_us) # AC combobox self.comboBox3 = QComboBox(self) self.comboBox3.move(50, 150) self.comboBox3.resize(500, 20) self.show() @pyqtSlot() def on_click_reselect_us(self): self.textbox.clear() self.comboBox3.clear() @pyqtSlot() def on_click_clear(self): self.textbox.clear() self.textbox3.clear() self.textbox4.clear() self.comboBox.clear() self.comboBox2.clear() self.comboBox3.clear() @pyqtSlot() def on_click_start(self): us = self.textbox2.text() results = [] for tc in self.TCs: tmp = tc[0] + tc[1] results.append({ 'match_sentence': tmp, 'score': compare_sens(us, tmp) }) i = 0 self.tableWidget.setSortingEnabled(True) self.tableWidget.setRowCount(100) self.tableWidget.setColumnCount(2) results = sorted(results, key=itemgetter('score'), reverse=True) for res in results: #self.tableWidget.insertRow(str(res)+str1) #self.tableWidget.setItem(i,0, QTableWidgetItem(str(res[1]))) self.tableWidget.setItem(i, 0, QTableWidgetItem(str(res))) i += 1 #self.textbox3.insert(str(res)+str1 self.tableWidget.move(1000, 75) self.tableWidget.show() @pyqtSlot() def on_click_choose_US(self): # set User Story over textbox so we can edit if needed text = self.comboBox2.currentText() self.textbox.setPlainText(text) self.textbox.show() # fill AC combobox descriptions = [] ACs = [] tmp = [] us = self.comboBox2.currentText() for a, b, c, d in self.USs: if b == us: descriptions.append(c) ACs.append(d) tmp.append(c + d) self.comboBox3.addItems(tmp) @pyqtSlot() def on_click_choose_AC(self): # set AC over textbox so we can edit if needed text = self.comboBox3.currentText() self.textbox.paste() self.textbox.show() @pyqtSlot() def on_click_tokenize(self): text = self.textbox.text() tok = str(tokenize(text)) self.textbox4.setText(tok) @pyqtSlot() def on_click_load(self): # Assign spreadsheet filename to `file` file = self.openFileNameDialog() # Load in the workbook wb = load_workbook(file) # Get sheet names data_sheets_names = wb.sheetnames print(wb.sheetnames) # Get all the sheet names that hold User stories and Test Cases USs_sheets_names = [] TCs_Sheets_names = [] for sheet_name in data_sheets_names: if sheet_name.find("TCs") != -1: TCs_Sheets_names.append(sheet_name) if sheet_name.find(" F") != -1: USs_sheets_names.append(sheet_name) # get NLP data sheets for name in USs_sheets_names: sheet = wb[name] for i in range(2, sheet.max_row + 1): self.USs.append( ((sheet.cell(i, 1).value), (sheet.cell(i, 2).value), (sheet.cell(i, 3).value), (sheet.cell(i, 4).value))) self.USNames.append((sheet.cell(2, 2).value)) for name in TCs_Sheets_names: sheet = wb[name] for i in range(2, sheet.max_row + 1): self.TCs.append( (name, (sheet.cell(i, 1).value), (sheet.cell(i, 2).value))) #remove doubles self.USNames = list(set(self.USNames)) self.comboBox2.addItems(self.USNames) print(self.USNames) def openFileNameDialog(self): options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog fileName, _ = QFileDialog.getOpenFileName( self, "QFileDialog.getOpenFileName()", "", "All Files (*);;Excel Files (*.xlsx)", options=options) if fileName: print(fileName) return fileName
class Window(QWidget): ext_list = ['mkv', 'mp4', 'avi'] def __init__(self): QWidget.__init__(self) self.v_box = QVBoxLayout() self.v_dir = QVBoxLayout() self.v_ext = QVBoxLayout() self.v_av = QVBoxLayout() self.H_default_save_dir = QHBoxLayout() self.H_default_ext = QHBoxLayout() self.H_av = QHBoxLayout() self.H_save = QHBoxLayout() self.settings = xml.ParseXMLSettings() self.txt_default_dir = QLineEdit(self) self.drpdwn_av = QComboBox(self) self.drpdwn_ext = QComboBox(self) self.shortcut = QShortcut(QKeySequence('Ctrl+W'), self) self.shortcut.activated.connect(self.close) self.initUI() self.v_box.addSpacing(12) self.H_av.addSpacing(12) self.H_default_ext.addSpacing(12) self.H_default_save_dir.addSpacing(12) self.H_save.addSpacing(12) self.setLayout(self.v_box) self.setGeometry(200, 200, 250, 150) self.setWindowTitle('Global Settings') def initUI(self): self.txt_default_dir.setText(self.settings.settings_list['save_dir']) self.txt_default_dir.setEnabled(False) lbl_default_dir = QLabel(self) lbl_default_dir.setText('Default Directory: ') btn_default_dir = QPushButton(self) btn_default_dir.setText('Change...') self.drpdwn_av.addItem(self.settings.settings_list['av']) lbl_av = QLabel(self) lbl_av.setText('Record Audio and Video? ') self.drpdwn_ext.addItem(self.settings.settings_list['ext']) lbl_ext = QLabel(self) lbl_ext.setText('Video Extension: ') btn_save = QPushButton(self) btn_save.setText('Save changes...') btn_default_dir.clicked.connect(lambda: self.change_dir()) btn_save.clicked.connect(self.save) if self.settings.settings_list['av'] == 'True': self.drpdwn_av.addItem('False') else: self.drpdwn_av.addItem('True') for ext in self.ext_list: if ext != self.settings.settings_list['ext']: self.drpdwn_ext.addItem(ext) self.v_dir.addWidget(lbl_default_dir) self.H_default_save_dir.addWidget(self.txt_default_dir) self.H_default_save_dir.addWidget(btn_default_dir) self.v_dir.addLayout(self.H_default_save_dir) self.v_av.addWidget(lbl_av) self.H_av.addWidget(self.drpdwn_av) self.v_av.addLayout(self.H_av) self.v_ext.addWidget(lbl_ext) self.H_default_ext.addWidget(self.drpdwn_ext) self.v_ext.addLayout(self.H_default_ext) self.H_save.addWidget(btn_save) self.v_ext.addStretch(1) self.v_av.addStretch(1) self.v_dir.addStretch(1) self.v_box.addLayout(self.v_dir) self.v_box.addLayout(self.v_av) self.v_box.addLayout(self.v_ext) self.v_box.addLayout(self.H_save) self.v_box.addStretch(1) def save(self): if self.drpdwn_ext.currentText( ) != self.settings.settings_list['ext'] or self.drpdwn_av.currentText( ) != self.settings.settings_list['av']: dlg = dialog.QuestionDialog('Are you ready to save changes?') if dlg.clickedButton() == dlg.yes: if self.drpdwn_ext.currentText( ) != self.settings.settings_list['ext']: self.settings.edit('ext', self.drpdwn_ext.currentText()) SETTINGS.settings_list[ 'ext'] = self.drpdwn_ext.currentText() if self.drpdwn_av.currentText( ) != self.settings.settings_list['av']: self.settings.edit('av', self.drpdwn_av.currentText()) SETTINGS.settings_list['av'] = self.drpdwn_av.currentText() else: pass def change_dir(self): dlg = str(QFileDialog.getExistingDirectory(self, "Select Directory")) if dlg != "": self.settings.edit('save_dir', dlg) self.txt_default_dir.setText(dlg) SETTINGS.settings_list['save_dir'] = dlg
class SpotlightLibreDialog(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 > class LibraryDatabase2 self.db = gui.current_db self.l = QVBoxLayout() self.setLayout(self.l) # Label self.labelText = QLabel('Use "and" and "or" for the search.') self.l.addWidget(self.labelText) # Title self.setWindowTitle('SpotlightLibre Full Text Search') self.setWindowIcon(icon) # Search window self.searchTextWindow = QComboBox() self.searchTextWindow.setEditable(True) self.l.addWidget(self.searchTextWindow) self.searchTextWindow.setFocus() self.searchTextWindow.setInsertPolicy(QComboBox.NoInsert) self.searchTextWindow.setDuplicatesEnabled(False) #Completer for the seach window self.completer = QCompleter() self.completer.setCompletionMode( QCompleter.UnfilteredPopupCompletion ) self.searchTextWindow.setCompleter(self.completer) # output window self.outputWindow = QLabel() self.l.addWidget(self.outputWindow) # search button 1 self.doSearchButton = QPushButton('Search and replace the filter', self) self.doSearchButton.clicked.connect(self.spotlightSearchNew) self.l.addWidget(self.doSearchButton) self.doSearchButton.setDefault(True) # search button 2 self.doSearchButton = QPushButton('Search and add to filter', self) self.doSearchButton.clicked.connect(self.spotlightSearchAdd) self.l.addWidget(self.doSearchButton) # about button self.aboutButton = QPushButton('About', self) self.aboutButton.clicked.connect(self.about) self.l.addWidget(self.aboutButton) self.resize(self.sizeHint()) #self.resize(500, self.height()) def about(self): # Get the about text from a file inside the plugin zip file # The get_resources function is a builtin function defined for all your # plugin code. It loads files from the plugin zip file. It returns # the bytes from the specified file. text = get_resources('about.txt') #box = QMessageBox() #box.about(self, 'About the SpotlightLibre Full Text Search \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t',text.decode('utf-8')) #self.resize(600, self.height()) self.box = AboutWindow() self.box.setWindowTitle("About the SpotlightLibre Full Text Search Plugin") self.box.textWindow.setText(text) self.box.textWindow.setReadOnly(True) self.box.resize(600, 500) self.box.show() def spotlightSearchNew(self): self.searchAdd = False self.spotlightSearch() def spotlightSearchAdd(self): self.searchAdd = True self.spotlightSearch() def spotlightSearch(self): self.searchText = str(self.searchTextWindow.currentText())# search text from the plugin gui self.searchTextWindow.insertItem(0, self.searchText) # self.cmd = 'mdfind -onlyin '+ prefs['pathToLibrary']+' ' # saved to allow for checkbox option later? print(self.db.library_path) self.cmd = 'mdfind -onlyin "'+self.db.library_path+'" ' self.cmdString = self.cmd + '"'+self.searchText+'"' print(self.cmdString) self.p = Popen(self.cmdString, shell=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True) self.output = self.p.stdout.read()# output from the spotlight search print(self.output) self.found = '' self.found = re.findall(r" \((\d+)\)\/", self.output)# regex to find the calibre ids in the folder names self.wholeString = '' if len(self.found) == 0 : self.outputWindow.setText('no books found' + ' for ' + self.searchText) else : self.wholeString = '#cid:' for elem in self.found: self.wholeString += '=' + elem + ' or ' self.wholeString = self.wholeString[:-4] self.outputWindow.setText(str(len(self.found)) + ' books found' + ' for ' + self.searchText) if self.searchAdd == True : self.oldFilter = self.gui.search.text() self.wholeString = self.oldFilter + ' and (' + self.wholeString + ')' self.searchTextWindow.clearEditText() self.gui.search.setEditText(self.wholeString) # set calibre search to the string found by spotlight self.gui.search.do_search() def config(self): self.do_user_config(parent=self)
class RecollFulltextSearchDialog(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 # db is an instance of the class LibraryDatabase2 from database.py # This class has many, many methods that allow you to do a lot of # things. self.db = gui.current_db self.l = QVBoxLayout() self.setLayout(self.l) # Label self.labelText = QLabel('Use "and" and "or" for the search.') self.l.addWidget(self.labelText) # Title self.setWindowTitle('Recoll Full Text Search') self.setWindowIcon(icon) # Search window self.searchTextWindow = QComboBox() self.searchTextWindow.setEditable(True) self.l.addWidget(self.searchTextWindow) self.searchTextWindow.setFocus() self.searchTextWindow.setInsertPolicy(QComboBox.NoInsert) self.searchTextWindow.setDuplicatesEnabled(False) #Completer for the seach window self.completer = QCompleter() self.completer.setCompletionMode( QCompleter.UnfilteredPopupCompletion ) self.searchTextWindow.setCompleter(self.completer) # output window self.outputWindow = QLabel() self.l.addWidget(self.outputWindow) # search button 1 self.doSearchButton = QPushButton('Search and replace the filter', self) self.doSearchButton.clicked.connect(self.recollSearchNew) self.l.addWidget(self.doSearchButton) self.doSearchButton.setDefault(True) # search button 2 self.doSearchButton = QPushButton('Search and add to filter', self) self.doSearchButton.clicked.connect(self.recollSearchAdd) self.l.addWidget(self.doSearchButton) # update database button 1 self.updateDatabaseButton = QPushButton('Update recoll database', self) self.updateDatabaseButton.clicked.connect(self.updateDatabase) self.l.addWidget(self.updateDatabaseButton) # update database button 2 self.newDatabaseButton = QPushButton('Make new recoll database', self) self.newDatabaseButton.clicked.connect(self.newDatabase) self.l.addWidget(self.newDatabaseButton) # config button self.configButton = QPushButton('Configure this plugin', self) self.configButton.clicked.connect(self.config) self.l.addWidget(self.configButton) # about button self.aboutButton = QPushButton('About', self) self.aboutButton.clicked.connect(self.about) self.l.addWidget(self.aboutButton) self.resize(self.sizeHint()) #self.resize(500, self.height()) def about(self): # Get the about text from a file inside the plugin zip file # The get_resources function is a builtin function defined for all your # plugin code. It loads files from the plugin zip file. It returns # the bytes from the specified file. text = get_resources('about.txt') #box = QMessageBox() #box.about(self, 'About the Recoll Full Text Search \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t',text.decode('utf-8')) #self.resize(600, self.height()) self.box = AboutWindow() self.box.setWindowTitle("About the Recoll Full Text Search Plugin") self.box.textWindow.setText(text) self.box.textWindow.setReadOnly(True) self.box.resize(600, 500) self.box.show() def updateDatabase(self): self.replaceDatabase =False self.makeDatabase() def newDatabase(self): self.replaceDatabase = True self.makeDatabase() def recollSearchNew(self): self.searchAdd = False self.recollSearch() def recollSearchAdd(self): self.searchAdd = True self.recollSearch() def makeDatabase(self): '''Runs recollindex outside calibre like in a terminal. Look for recollindex for more information about the flags and options''' self.cmd = [prefs['pathToRecoll'] + '/recollindex', '-c', prefs['pathToCofig'] + '/plugins/recollFullTextSearchPlugin'] #TODO: Fix for Linux #self.cmd = 'LD_LIBRARY_PATH="" ' + prefs['pathToRecoll'] + '/recollindex -c ' + prefs['pathToCofig'] + '/plugins/recollFullTextSearchPlugin' if self.replaceDatabase == True : self.cmd += [' -z'] self.p = Popen(self.cmd, shell=False) # TODO: Was close_fds nessesary? check it on linux #self.p = Popen(self.cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True) box = QMessageBox() box.about(self, 'Please read! \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t','Depending on you library size this operation can take a lot of time.\nThe process runs outside calibre so you can use or close it, but do not use this plugin.\nFor now there is no information about when recoll finishs,\nso look up, whether a recoll of recollindex process is running on you system.') def recollSearch(self): '''Runs recoll outside calibre like in a terminal. Look for recollindex for more information about the flags and options''' self.searchText = str(self.searchTextWindow.currentText())# search text from the plugin gui self.searchTextWindow.insertItem(0, self.searchText) #TODO: Fix Linux #self.cmd = 'LD_LIBRARY_PATH="" ' + prefs['pathToRecoll'] + '/recoll -c ' + prefs['pathToCofig'] + '/plugins/recollFullTextSearchPlugin -b -t ' self.cmd = [prefs['pathToRecoll'] + '/recoll', '-c', prefs['pathToCofig'] + '/plugins/recollFullTextSearchPlugin', '-b', '-t'] self.cmdString = self.cmd + [self.searchText] # TODO: Was close_fds nessesary? check it on linux #self.p = Popen(self.cmdString, shell=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True) self.p = Popen(self.cmdString, shell=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT) self.output = self.p.stdout.read()# output from the recoll search self.found = list(set(re.findall(r" \((\d+)\)\/[^/]*", self.output)))# regex to find the calibre ids in the folder names self.wholeString = '' if len(self.found) == 0 : self.outputWindow.setText('no books found' + ' for ' + self.searchText) else : for elem in self.found[:400]: self.wholeString += 'id:=' + elem + ' or ' self.wholeString = self.wholeString[:-4] if len(self.found) > 400 : self.outputWindow.setText(str(len(self.found)) + ' books found' + ' for ' + self.searchText+ '. Only the first 400 books are shown') else : self.outputWindow.setText(str(len(self.found)) + ' books found' + ' for ' + self.searchText) if self.searchAdd == True : self.oldFilter = self.gui.search.text() self.wholeString = self.oldFilter + ' and (' + self.wholeString + ')' self.searchTextWindow.clearEditText() self.gui.search.setEditText(self.wholeString) # set calibre search to the string found by recoll self.gui.search.do_search() def config(self): self.do_user_config(parent=self)
class PieChart (QGraphicsView): def __init__(self,parent,total_value): super(PieChart,self).__init__(parent) self.my_scene = QGraphicsScene() #self.my_scene.setSceneRect(self.sceneRect()) self.setScene(self.my_scene) #self.setBackgroundBrush(QBrush(QtCore.Qt.black, QtCore.Qt.SolidPattern)); self.total_value = total_value self.initialize = False self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self.margin = {'top':10,'bottom':10,'left':10,'right':10} self.space = 30 self.pie_items = [] def setTotal (self, total): self.total_value = total # def setData (self,list_item): # # rect = QGraphicsRectItem(self.sceneRect()) # rect.setBrush(QColor("red")) # self.scene().addItem(rect) def setData (self,list_items): size = min(self.frameSize().width()/2.0,self.frameSize().height()) self.size_pie = QSize(size,size) #print ('size pie ',self.size_pie) if self.initialize == False: self.c_box = QComboBox() self.c_box.addItem("all") self.c_box.addItem("alive") self.c_box.addItem("dead") self.c_box.currentIndexChanged.connect(self.update) proxy = self.my_scene.addWidget(self.c_box) x = self.size_pie.width()+ self.space proxy.setPos(QPointF(x,self.margin['top'])) self.initialize = True self.data = list_items self.update() def update(self): restant = 360 i = 0 #self.my_scene.clear() for item in self.pie_items : self.my_scene.removeItem(item) self.pie_items = [] self.scene().setSceneRect(QRectF(0,0,self.frameSize().width(),self.frameSize().height())) #print ('size',self.scene().sceneRect()) #proxy = QGraphicsProxyWidget () for item in self.data : if (i==len(self.data)-1 ): angle = restant else: try : angle = int(360*item[self.c_box.currentText()]/self.total_value[self.c_box.currentText()]) except ZeroDivisionError: angle = 0 ellipse = Section(0,0,self.size_pie.width(),self.size_pie.height()) y = (self.parent().size().height()-self.size_pie.height())/2.0 x_pie = ((self.parent().size().width()/2.0)-self.size_pie.height())/2.0 ellipse.setPos(x_pie,y) ellipse.setStartAngle(16*(360-restant)) ellipse.setSpanAngle(angle*16) ellipse.setBrush(item['color']) self.my_scene.addItem(ellipse) self.pie_items.append(ellipse) # text pourcentage a afficher dans les portions de disque try : v = (item[self.c_box.currentText()]/self.total_value[self.c_box.currentText()])*100 except ZeroDivisionError : v = 0 text = QGraphicsSimpleTextItem("{0:5.2f}".format(v)+"%") trans = QTransform().translate(x_pie+self.size_pie.width()/2.0,y+self.size_pie.height()/2.0).rotate(((360-restant)+angle/2.0)*-1) pts = trans.map(QPointF(self.size_pie.width()/3.0,0)) text.setPos(pts.x(),pts.y()) self.my_scene.addItem(text) self.pie_items.append(text) #libelle rect = QGraphicsRectItem(0,0,10,10) x = x_pie + self.size_pie.width()+ self.space interval_height = (self.parent().size().height()-self.margin['top']-self.margin['bottom'])/(len(self.data)+1) rect.setPos(QPointF(x,self.margin['top']+((i+1)*interval_height))) rect.setBrush(item['color']) self.my_scene.addItem(rect) self.pie_items.append(rect) text = QGraphicsSimpleTextItem(item['label']+ " ("+str(int(item[self.c_box.currentText()]))+")") pts = rect.pos() transform = QTransform().translate(30, 0) pts = transform.map(pts) text.setPos(pts) self.my_scene.addItem(text) self.pie_items.append(text) restant = restant - angle i +=1 # self.fitInView(self.scene.sceneRect())
class RemoveDialog(Dialog): def __init__(self, parent): from calibre_plugins.diaps_toolbag.span_div_config import plugin_prefs as prefs self.criteria = None self.prefs = prefs self.parent = parent self.help_file_name = '{0}_span_div_help.html'.format(PLUGIN_SAFE_NAME) self.taglist = TAGLIST Dialog.__init__(self, _('Edit Spans & Divs'), 'toolbag_spans_divs_dialog', parent) def setup_ui(self): DELETE_STR = _('Delete') MODIFY_STR = _('Modify') NO_ATTRIB_STR = _('No attributes ("naked" tag)') self.NO_CHANGE_STR = _('No change') layout = QVBoxLayout(self) self.setLayout(layout) help_layout = QHBoxLayout() layout.addLayout(help_layout) # Add hyperlink to a help file at the right. We will replace the correct name when it is clicked. help_label = QLabel('<a href="http://www.foo.com/">Plugin Help</a>', self) help_label.setTextInteractionFlags(Qt.LinksAccessibleByMouse | Qt.LinksAccessibleByKeyboard) help_label.setAlignment(Qt.AlignRight) help_label.linkActivated.connect(self.help_link_activated) help_layout.addWidget(help_label) action_layout = QHBoxLayout() layout.addLayout(action_layout) label = QLabel(_('Action type:'), self) action_layout.addWidget(label) self.action_combo = QComboBox() action_layout.addWidget(self.action_combo) self.action_combo.addItems([DELETE_STR, MODIFY_STR]) self.action_combo.currentIndexChanged.connect(self.update_gui) tag_layout = QHBoxLayout() layout.addLayout(tag_layout) label = QLabel(_('Tag name:'), self) tag_layout.addWidget(label) self.tag_combo = QComboBox() tag_layout.addWidget(self.tag_combo) self.tag_combo.addItems(self.taglist) self.tag_combo.currentIndexChanged.connect(self.update_gui) attr_layout = QHBoxLayout() layout.addLayout(attr_layout) label = QLabel(_('Having the attribute:'), self) attr_layout.addWidget(label) self.attr_combo = QComboBox() attr_layout.addWidget(self.attr_combo) self.attr_combo.addItems(self.prefs['attrs']) self.attr_combo.addItem(NO_ATTRIB_STR) self.attr_combo.currentIndexChanged.connect(self.update_gui) srch_layout = QHBoxLayout() layout.addLayout(srch_layout) label = QLabel(_("Whose value is (no quotes):"), self) srch_layout.addWidget(label) self.srch_txt = QLineEdit('', self) srch_layout.addWidget(self.srch_txt) self.srch_method = QCheckBox(_('Regex'), self) srch_layout.addWidget(self.srch_method) newtag_layout = QHBoxLayout() layout.addLayout(newtag_layout) label = QLabel(_('Change tag to:'), self) newtag_layout.addWidget(label) self.newtag_combo = QComboBox() newtag_layout.addWidget(self.newtag_combo) self.newtag_combo.addItem(self.NO_CHANGE_STR) self.newtag_combo.addItems(self.prefs['{}_changes'.format(unicode(self.tag_combo.currentText()))]) if self.action_combo.currentIndex() == 0: self.newtag_combo.setDisabled(True) newattr_layout = QVBoxLayout() layout.addLayout(newattr_layout) label = QLabel(_('New attribute string to insert (entire):'), self) newattr_layout.addWidget(label) self.newattr_txt = QLineEdit('', self) newattr_layout.addWidget(self.newattr_txt) self.copy_attr = QCheckBox(_('Copy existing attribute string'), self) self.copy_attr.stateChanged.connect(self.update_txt_box) newattr_layout.addWidget(self.copy_attr) if self.action_combo.currentIndex() == 0: self.copy_attr.setDisabled(True) self.newattr_txt.setDisabled(True) layout.addSpacing(10) button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) button_box.accepted.connect(self._ok_clicked) button_box.rejected.connect(self.reject) layout.addWidget(button_box) def update_gui(self): if self.attr_combo.currentIndex() == self.attr_combo.count()-1: self.srch_txt.clear() self.srch_txt.setDisabled(True) self.srch_method.setChecked(False) self.srch_method.setDisabled(True) else: self.srch_txt.setDisabled(False) self.srch_method.setDisabled(False) self.newtag_combo.clear() self.newtag_combo.addItem(self.NO_CHANGE_STR) self.newtag_combo.addItems(self.prefs['{}_changes'.format(unicode(self.tag_combo.currentText()))]) if self.action_combo.currentIndex() == 0: self.newtag_combo.setCurrentIndex(0) self.newtag_combo.setDisabled(True) self.newattr_txt.clear() self.newattr_txt.setDisabled(True) self.copy_attr.setChecked(False) self.copy_attr.setDisabled(True) else: self.newtag_combo.setDisabled(False) self.newattr_txt.setDisabled(False) self.copy_attr.setDisabled(False) def update_txt_box(self): if self.copy_attr.isChecked(): self.newattr_txt.clear() self.newattr_txt.setDisabled(True) else: self.newattr_txt.setDisabled(False) def _ok_clicked(self): if self.action_combo.currentIndex() == 0: action = 'delete' else: action = 'modify' if self.attr_combo.currentIndex() == self.attr_combo.count()-1: attribute = None else: attribute = unicode(self.attr_combo.currentText()) srch_str = unicode(self.srch_txt.displayText()) if not len(srch_str): srch_str = None if srch_str is None and attribute is not None: return error_dialog(self.parent, _('Error'), '<p>{0}'.format( _('Must enter a value for the attribute selected')), det_msg='', show=True) srch_method = 'normal' if self.srch_method.isChecked(): srch_method = 'regex' if self.newtag_combo.currentIndex() == 0: newtag = None else: newtag = unicode(self.newtag_combo.currentText()) if action == 'modify' and newtag is None and self.copy_attr.isChecked(): return error_dialog(self.parent, _('Error'), '<p>{0}'.format( _('What--exactly--would that achieve?')), det_msg='', show=True) new_str = unicode(self.newattr_txt.displayText()) copy_attr = False if self.copy_attr.isChecked(): copy_attr = True if not len(new_str): new_str = '' self.criteria = (srch_str, srch_method, unicode(self.tag_combo.currentText()), attribute, action, newtag, new_str, copy_attr) self.accept() def getCriteria(self): return self.criteria def help_link_activated(self, url): def get_help_file_resource(): # Copy the HTML helpfile to the plugin directory each time the # link is clicked in case the helpfile is updated in newer plugins. file_path = os.path.join(config_dir, 'plugins', self.help_file_name) with open(file_path,'w') as f: f.write(load_resource('resources/{}'.format(self.help_file_name))) return file_path url = 'file:///' + get_help_file_resource() open_url(QUrl(url))
class MainWin(QMainWindow): def __init__(self, fileName=None, logName=None, parent=None): super(MainWin, self).__init__(parent) #self.setWindowIcon(QIcon(':/images/logo.png')) self.setToolButtonStyle(Qt.ToolButtonFollowStyle) self.setupFileActions() self.setupEditActions() self.setupTextActions() self.setupRunActions() self.initializeSettings() self.populateRunSettings() # FIXME put in initializeSettings()? settingsMenu = QMenu('Settings', self) self.menuBar().addMenu(settingsMenu) settingsMenu.addAction('Configure...', self.configure) helpMenu = QMenu("Help", self) self.menuBar().addMenu(helpMenu) helpMenu.addAction("About", self.about) helpMenu.addAction("About &Qt", QApplication.instance().aboutQt) self.splitter = QSplitter(self) self.splitter.setOrientation(Qt.Vertical) self.textPane = TextPane() self.logPane = LogPane() self.logBox = QGroupBox() self.logBox.setFlat(True) vbox = QVBoxLayout() vbox.addWidget(self.logPane) self.logBox.setLayout(vbox) self.splitter.addWidget(self.textPane) self.splitter.addWidget(QLabel()) # spacer self.splitter.addWidget(self.logBox) self.setCentralWidget(self.splitter) self.loadSrc(fileName) self.loadLog(logName) #if logName and (-1 == self.comboLogFile.findText(logName)): #self.comboLogFile.addItem(logName) self.logPane.setFocus() self.fontChanged(self.textPane.font()) self.textPane.document().modificationChanged.connect(self.actionSave.setEnabled) self.textPane.document().modificationChanged.connect(self.setWindowModified) self.textPane.document().undoAvailable.connect(self.actionUndo.setEnabled) self.textPane.document().redoAvailable.connect( self.actionRedo.setEnabled) self.setWindowModified(self.textPane.document().isModified()) self.actionSave.setEnabled(self.textPane.document().isModified()) self.actionUndo.setEnabled(self.textPane.document().isUndoAvailable()) self.actionRedo.setEnabled(self.textPane.document().isRedoAvailable()) self.actionUndo.triggered.connect(self.textPane.undo) self.actionRedo.triggered.connect(self.textPane.redo) self.actionCut.setEnabled(False) self.actionCopy.setEnabled(False) self.actionCut.triggered.connect(self.textPane.cut) self.actionCopy.triggered.connect(self.textPane.copy) self.actionPaste.triggered.connect(self.textPane.paste) self.textPane.copyAvailable.connect(self.actionCut.setEnabled) self.textPane.copyAvailable.connect(self.actionCopy.setEnabled) QApplication.clipboard().dataChanged.connect(self.clipboardDataChanged) self.actionRun.triggered.connect(self.scannoCheck) self.logPane.lineMatchChanged.connect(self.logLineMatchChanged) def closeEvent(self, e): if self.maybeSave(): e.accept() else: e.ignore() def setupFileActions(self): tb = QToolBar(self) tb.setWindowTitle("File Actions") self.addToolBar(tb) menu = QMenu("&File", self) self.menuBar().addMenu(menu) self.actionNew = QAction("&New", self, priority=QAction.LowPriority, shortcut=QKeySequence.New, triggered=self.fileNew) tb.addAction(self.actionNew) menu.addAction(self.actionNew) self.actionOpen = QAction("&Open...", self, shortcut=QKeySequence.Open, triggered=self.fileOpen) tb.addAction(self.actionOpen) menu.addAction(self.actionOpen) menu.addSeparator() self.actionSave = QAction("&Save", self, shortcut=QKeySequence.Save, triggered=self.fileSave, enabled=False) tb.addAction(self.actionSave) menu.addAction(self.actionSave) self.actionSaveAs = QAction("Save &As...", self, priority=QAction.LowPriority, shortcut=Qt.CTRL + Qt.SHIFT + Qt.Key_S, triggered=self.fileSaveAs) menu.addAction(self.actionSaveAs) menu.addSeparator() self.actionQuit = QAction("&Quit", self, shortcut=QKeySequence.Quit, triggered=self.close) menu.addAction(self.actionQuit) def setupEditActions(self): tb = QToolBar(self) tb.setWindowTitle("Edit Actions") self.addToolBar(tb) menu = QMenu("&Edit", self) self.menuBar().addMenu(menu) self.actionUndo = QAction("&Undo", self, shortcut=QKeySequence.Undo) tb.addAction(self.actionUndo) menu.addAction(self.actionUndo) self.actionRedo = QAction("&Redo", self, priority=QAction.LowPriority, shortcut=QKeySequence.Redo) tb.addAction(self.actionRedo) menu.addAction(self.actionRedo) menu.addSeparator() self.actionCut = QAction("Cu&t", self, priority=QAction.LowPriority, shortcut=QKeySequence.Cut) tb.addAction(self.actionCut) menu.addAction(self.actionCut) self.actionCopy = QAction("&Copy", self, priority=QAction.LowPriority, shortcut=QKeySequence.Copy) tb.addAction(self.actionCopy) menu.addAction(self.actionCopy) self.actionPaste = QAction("&Paste", self, priority=QAction.LowPriority, shortcut=QKeySequence.Paste, enabled=(len(QApplication.clipboard().text()) != 0)) tb.addAction(self.actionPaste) menu.addAction(self.actionPaste) def setupTextActions(self): tb = QToolBar(self) tb.setWindowTitle("Format Actions") self.addToolBar(tb) tb = QToolBar(self) tb.setAllowedAreas(Qt.TopToolBarArea | Qt.BottomToolBarArea) tb.setWindowTitle("Format Actions") self.addToolBarBreak(Qt.TopToolBarArea) self.addToolBar(tb) self.comboFont = QFontComboBox(tb) tb.addWidget(self.comboFont) self.comboFont.activated[str].connect(self.textFamily) self.comboSize = QComboBox(tb) self.comboSize.setObjectName("comboSize") tb.addWidget(self.comboSize) self.comboSize.setEditable(True) db = QFontDatabase() for size in db.standardSizes(): self.comboSize.addItem('{}'.format(size)) self.comboSize.activated[str].connect(self.textSize) self.comboSize.setCurrentIndex(self.comboSize.findText('{}'.format(QApplication.font().pointSize()))) def setupRunActions(self): tb = QToolBar(self) tb.setWindowTitle("Run Actions") self.addToolBar(tb) menu = QMenu("Run", self) self.menuBar().addMenu(menu) self.actionRun = QAction( "&Run", self, shortcut=Qt.CTRL + Qt.Key_R) tb.addAction(self.actionRun) menu.addAction(self.actionRun) self.comboScannoFile = QComboBox(tb) self.comboScannoFile.setObjectName("comboScannoFile") tb.addWidget(self.comboScannoFile) self.comboScannoFile.setEditable(True) self.comboLogFile= QComboBox(tb) self.comboLogFile.setObjectName("comboLogFile") self.comboLogFile.setEditable(True) tb.addWidget(self.comboLogFile) def populateRunSettings(self): for f in self.scannoFiles(): self.comboScannoFile.addItem(f) if self.defaultScannoFile: idx = self.comboScannoFile.findText(self.defaultScannoFile) self.comboScannoFile.setCurrentIndex(idx) self.comboLogFile.addItem('plog.txt') def loadSrc(self, src): if src: if not self.textPane.load(src): return False self.setCurrentFileName(src) return True def loadLog(self, log): if log: if not self.logPane.load(log): return False self.comboLogFile.clear() self.comboLogFile.addItem(log) else: self.logPane.clear() self.logPane.setEnabled(False) self.logBox.setTitle(self.tr('No log file loaded.')) return True def maybeSave(self): if not self.textPane.document().isModified(): return True ret = QMessageBox.warning(self, 'GuiScannos', 'The document has been modified.\n' 'Do you want to save your changes?', QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel) if ret == QMessageBox.Save: return self.fileSave() if ret == QMessageBox.Cancel: return False return True def setCurrentFileName(self, fileName=''): self.fileName = fileName self.textPane.document().setModified(False) if not fileName: shownName = 'untitled.txt' self.actionRun.setEnabled(False) else: shownName = QFileInfo(fileName).fileName() self.actionRun.setEnabled(True) self.setWindowTitle(self.tr('{}[*] - {}'.format(shownName, 'GUI Scannos'))) self.setWindowModified(False) def fileNew(self): if self.maybeSave(): self.textPane.clear() self.loadLog(None) # clears logPane, logBox title, etc self.setCurrentFileName() def fileOpen(self): fn, _ = QFileDialog.getOpenFileName(self, 'Open File...', None, 'Text Files (*.txt);;All Files (*)') if fn: self.loadSrc(fn) self.loadLog(None) # clears logPane, logBox title, etc def fileSave(self): if not self.fileName: return self.fileSaveAs() return self.textpane.save(self.fileName) def fileSaveAs(self): fn, _ = QFileDialog.getSaveFileName(self, "Save as...", None, "text files (*.txt);;All Files (*)") if not fn: return False self.setCurrentFileName(fn) return self.fileSave() def logLineMatchChanged(self): linenum = self.logPane.srcLineNum() col = self.logPane.srcColNum() s = self.logPane.srcScanno() self.textPane.setSelection(linenum, col, len(s)) def textFamily(self, family): """Set font family for text and log panes.""" self.textPane.setFontFamily(family) self.logPane.setFontFamily(family) def textSize(self, pointSize): """Set font size for text and log panes.""" self.textPane.setFontPointSize(pointSize) self.logPane.setFontPointSize(pointSize) def clipboardDataChanged(self): self.actionPaste.setEnabled(len(QApplication.clipboard().text()) != 0) def about(self): QMessageBox.about(self, 'About', 'GUI for ppscannos.') def fontChanged(self, font): self.comboFont.setCurrentIndex(self.comboFont.findText(QFontInfo(font).family())) self.comboSize.setCurrentIndex(self.comboSize.findText('{}'.format(font.pointSize()))) def scannoCheck(self): """Run ppscannos.""" scannodir = os.path.dirname(self.ppscannos) cmd = sys.executable assert(cmd) scannoFile = self.comboScannoFile.currentText() if not scannoFile: scannoFile = self.defaultScannoFile scannoFile = scannodir + '/' + scannoFile src = self.fileName log = self.comboLogFile.currentText() if not log: log = './plog.txt' subprocess.call([cmd, self.ppscannos, '-s' + scannoFile, '-o' + log, '-i' + src]) self.loadLog(log) self.logPane.setEnabled(True) def configure(self): """Configure application settings by way of a dialog.""" dlg = ConfigDialog() if dlg.exec(): self.setPPScannos(dlg.lineEditPPScannos.text()) self.setDefaultScannoFile(dlg.comboScannoFiles.currentText()) settings = QSettings(QApplication.organizationName(), QApplication.applicationName()) settings.setValue('ppscannos', self.ppscannos) settings.setValue('defaultScannoFile', self.defaultScannoFile) def setPPScannos(self, s): self.ppscannos = s self.actionRun.setEnabled(self.ppscannos and os.path.exists(self.ppscannos)) def scannoFiles(self): """Return list of .rc filenames (without path) that are in ppscannos directory.""" if not self.ppscannos: return [] return getRCFilesForDir(os.path.dirname(self.ppscannos)) def setDefaultScannoFile(self, s): self.defaultScannoFile = s valid = False if self.defaultScannoFile and self.ppscannos and os.path.exists(self.ppscannos): if os.path.exists(os.path.dirname(self.ppscannos) + '/' + self.defaultScannoFile): valid = True self.actionRun.setEnabled(valid) def initializeSettings(self): """Load persistent config settings.""" settings = QSettings() s = settings.value('ppscannos', type=str) if not s: # try the default s = os.path.expanduser('~') + '/ppscannos1/ppscannos1.py' #s = os.environ['HOME'] + '/ppscannos1/ppscannos1.py' self.setPPScannos(s) s = settings.value('defaultScannoFile', type=str) if (not s) and self.ppscannos: # try the default lst = getRCFilesForDir(os.path.dirname(self.ppscannos)) if len(lst): # prefer 'regex.rc'; otherwise use the first one s = lst[0] for f in lst: if f == 'regex.rc': s = f break self.setDefaultScannoFile(s)