def closeEvent(self, closeEvent): # refuse to close if the bibolamazi thread is busy for us if self.run_bibolamazi.isBusy(): logger.debug("Bibolamazi is running, can't close window") closeEvent.ignore() return logger.debug("closeEvent(): cleaning up.") if self._modified: ans = QMessageBox.question(self, 'Save Changes', "Save changes to file `%s'?" %(self.bibolamaziFileName,), QMessageBox.Save|QMessageBox.Discard|QMessageBox.Cancel, QMessageBox.Save) if ans == QMessageBox.Save: # save first (no need for updating), then proceed with close self.saveToFile(noupdate=True) if ans == QMessageBox.Cancel: # ignore the event closeEvent.ignore() return if self.bibolamaziFileName: self.fwatcher.removePath(self.bibolamaziFileName) self.bibolamaziFileName = None if self.bibolamaziFile: self.bibolamaziFile = None QMetaObject.invokeMethod(self, 'fileClosed', Qt.QueuedConnection) return super(OpenBibFile, self).closeEvent(closeEvent)
def reloadFile(self): logger.debug("reloading file") self._flag_modified_externally = False # we may be sensitive again to external changes again if self.fwatcher.signalsBlocked(): self.fwatcher.blockSignals(False) if not self.bibolamaziFileName: with ContextAttributeSetter( (self.ui.btnGo.isEnabled, self.ui.btnGo.setEnabled, False) ): self.ui.txtInfo.setText("<h3 style=\"color: rgb(127,0,0)\">no file loaded.</h3>") self.ui.txtConfig.setPlainText("") return False if not self.bibolamaziFile: self.bibolamaziFile = bibolamazifile.BibolamaziFile() try: self.bibolamaziFile.load(self.bibolamaziFileName, to_state=bibolamazifile.BIBOLAMAZIFILE_READ) except butils.BibolamaziError as e: self.bibolamaziFile = None QMessageBox.critical(self, "Load Error", u"Error loading file: %s" %(str(e))) self._set_win_state(False, "<h3 style=\"color: rgb(127,0,0)\">Error reading file.</h3>\n" + bibolamazi_error_html(str(e))) return False self._set_win_state(True) # all set, clear any read-only flags on the txtConfig self.ui.txtConfig.setReadOnly(False) has_cache = os.path.exists(os.path.join(self.bibolamaziFile.fdir(), self.bibolamaziFileName + '.bibolamazicache')) # if there is a cache file, then offer to delete it self._set_ui_delete_cache_btn(has_cache) with ContextAttributeSetter( (self.ui.txtConfig.signalsBlocked, self.ui.txtConfig.blockSignals, True) ): cursorpos = self.ui.txtConfig.textCursor().position() self.ui.txtConfig.setPlainText(self.bibolamaziFile.configData()) cur = self.ui.txtConfig.textCursor() cur.setPosition(cursorpos) self.ui.txtConfig.setTextCursor(cur) # now, try to further parse the config self._bibolamazifile_reparse() self._set_modified(False) self._needs_update_txtbibentries = True if self.bibolamaziFile.getLoadState() == bibolamazifile.BIBOLAMAZIFILE_PARSED: self._display_header() logger.debug("file contents updated!") return True
def _open_anchor(self, url): logger.debug("Link clicked: %r", url.toString()) if url.scheme() == "helptopic": self.requestHelpTopic.emit(url.path()) return if url.scheme() == "action": m = re.match(r'/goto-config-line/(?P<lineno>\d+)', url.path()) if m: self.gotoConfigLineNo(int(m.group('lineno'))) return m = re.match(r'/goto-bibolamazi-file-line/(?P<lineno>\d+)', url.path()) if m: if (self.bibolamaziFile is None or self.bibolamaziFile.getLoadState() < bibolamazifile.BIBOLAMAZIFILE_READ): logger.warning("No bibolamazifile open!") return configlineno = self.bibolamaziFile.configLineNo(int(m.group('lineno'))) self.gotoConfigLineNo(configlineno) return logger.warning("Unknown action: %s", str(url.toString())) return logger.debug("Opening URL %r", str(url.toString())) QDesktopServices.openUrl(url)
def canonpath_check_fn(canonpath): urlcanon = 'help:'+canonpath logger.debug("requested page has canonical url %r", urlcanon) # check to see if the tab is already open for tab in self.openTabs: if (str(tab.property('helpurl')) == urlcanon): # just raise this tab. raise TabAlreadyOpen(tab)
def on_filterInstanceEditor_filterInstanceDefinitionChanged(self): filtername = self.ui.filterInstanceEditor.filterName() optionstring = self.ui.filterInstanceEditor.optionString() logger.debug('on_filterInstanceEditor_filterInstanceDefinitionChanged() ' 'filtername=%r, optionstring=%r', filtername, optionstring) cmdtext = "filter: " + filtername + ' ' + optionstring + "\n" self._replace_current_cmd(cmdtext, 'filter')
def runBibolamazi(self, bibolamazifilename, verbosity_level, reload_filter_path): # reload_filter_path is a list of 2-tuples (fpname, fpdir) if self.threadparent.busy: logger.warning("Can't run bibolamazi twice simultaneously") return logger.debug("RunBibolamazi.runBibolamazi(): bibolamazifilename=%r, verbosity_level=%r, " "reload_filter_path=%r. thread id=%r", bibolamazifilename, verbosity_level, reload_filter_path, QThread.currentThread().objectName()) self._busy = True self.threadparent.busy = True self.busy.emit(True) try: rootlogger = logging.getLogger() loggerleveltoset = bibolamazimain.verbosity_logger_level(verbosity_level) with ContextAttributeSetter( (lambda : rootlogger.level, rootlogger.setLevel, loggerleveltoset), ): with LogToGuiContextManager(logqtsig=self.logqtsig, bibolamazi_fname=bibolamazifilename) as log2sig: try: # TODO: we should reload all relevant packages here, not # in the other method below self._reload_filterpkg_modules(reload_filter_path) bibolamazimain.run_bibolamazi(bibolamazifile=bibolamazifilename) self.logqtsig.dolog(" --> Finished successfully. <--") self.bibolamaziDone.emit() except butils.BibolamaziError as e: logger.error(str(e)) self.logqtsig.dolog(" --> Finished with errors. <--") self.bibolamaziDoneError.emit(str(e)) except KeyboardInterrupt as e: logger.error("*** interrupted ***") self.logqtsig.dolog(" --> Stop: Interrupted. <--") self.bibolamaziDoneError.emit("Bibolamazi interrupted") except Exception as e: stre = str(e) logger.error(stre) import traceback logger.error(traceback.format_exc()) self.logqtsig.dolog(" --> EXCEPTION <--") self.bibolamaziDoneError.emit(stre) finally: self.busy.emit(False) self._busy = False self.threadparent.busy = False
def on_btnDeleteCache_clicked(self): cachefile = os.path.join(self.bibolamaziFile.fdir(), self.bibolamaziFileName + '.bibolamazicache') if not os.path.exists(cachefile): QMessageBox.warning(self, "No cache file", "There is no cache file to remove (%s)"%(cachefile)) return logger.debug("removing cache file %r", cachefile) os.remove(cachefile) self._set_ui_delete_cache_btn(False)
def on_btnAddFilter_clicked(self): logger.debug('add filter: clicked') filter_list = filterinstanceeditor.get_filter_list() (filtname, ok) = QInputDialog.getItem(self, "Select Filter", "Please select which filter you wish to add", filter_list); if (not ok): # cancelled return # insert new filter command self._insert_new_cmd('filter: %s' %(filtname))
def reloadFile(self): logger.debug("reloading file") self._flag_modified_externally = False self._set_modified(False) # we may be sensitive again to external changes again if (self.fwatcher.signalsBlocked()): self.fwatcher.blockSignals(False) if (not self.bibolamaziFileName): with ContextAttributeSetter( (self.ui.btnGo.isEnabled, self.ui.btnGo.setEnabled, False) ): self.ui.txtInfo.setText("<h3 style=\"color: rgb(127,0,0)\">no file loaded.</h3>"); self.ui.txtConfig.setPlainText("") return if (not self.bibolamaziFile): self.bibolamaziFile = bibolamazifile.BibolamaziFile() try: self.bibolamaziFile.load(self.bibolamaziFileName, to_state=bibolamazifile.BIBOLAMAZIFILE_READ) except butils.BibolamaziError as e: self.bibolamaziFile = None QMessageBox.critical(self, "Load Error", u"Error loading file: %s" %(unicode(e))) self._set_win_state(False, "<h3 style=\"color: rgb(127,0,0)\">Error reading file.</h3>\n" + bibolamazi_error_html(unicode(e), bibolamaziFile=self.bibolamaziFile)) return self._set_win_state(True) with ContextAttributeSetter( (self.ui.txtConfig.signalsBlocked, self.ui.txtConfig.blockSignals, True) ): cursorpos = self.ui.txtConfig.textCursor().position() self.ui.txtConfig.setPlainText(self.bibolamaziFile.configData()) cur = self.ui.txtConfig.textCursor() cur.setPosition(cursorpos) self.ui.txtConfig.setTextCursor(cur) self._needs_update_txtbibentries = True # now, try to further parse the config self._bibolamazifile_reparse() if self.bibolamaziFile.getLoadState() == bibolamazifile.BIBOLAMAZIFILE_PARSED: self._display_info() logger.debug("file contents updated!")
def _do_update_edittools(self): if self._ignore_change_for_edittools: return cmdlist = self._get_current_bibolamazi_cmdlist() if not cmdlist: # None, or empty list self.ui.stackEditTools.setCurrentWidget(self.ui.toolspageBase) return if len(cmdlist) > 1: # several commands selected self.ui.stackEditTools.setCurrentWidget(self.ui.toolspageSelectedMultiple) return # single command selected cmd = cmdlist[0] if cmd.cmd == "src": try: thesrcs = shlex.split(cmd.text) except ValueError as e: logger.debug("Invalid source list specification, syntax error: %s", str(e)) self.ui.sourceListEditor.dispSourceListError(str(e)) return self.ui.sourceListEditor.setSourceList(thesrcs, noemit=True) self.ui.sourceListEditor.setRefDir(self.bibolamaziFile.fdir()) self.ui.stackEditTools.setCurrentWidget(self.ui.toolspageSource) return if cmd.cmd == "package": try: self.ui.filterPackagePathEditor.setFilterPackageInfo(cmd.text.strip()) self.ui.filterPackagePathEditor.setRefDir(self.bibolamaziFile.fdir()) except BibolamaziError as e: self.ui.filterPackagePathEditor.setFilterPackageError(str(e)) self.ui.stackEditTools.setCurrentWidget(self.ui.toolspagePackage) return if cmd.cmd == "filter": filtername = cmd.info['filtername'] text = cmd.text if text and text[-1] == '\n': text = text[:-1] self.ui.filterInstanceEditor.setFilterPath(self.bibolamaziFile.fullFilterPath()) self.ui.filterInstanceEditor.setFilterInstanceDefinition(filtername, text, noemit=True) self.ui.stackEditTools.setCurrentWidget(self.ui.toolspageFilter) return logger.warning("Unknown command: %r", cmd) return
def on_txtConfig_textChanged(self): logger.debug("text changed! fwatcher signals blocked=%d", self.fwatcher.signalsBlocked()) if self._flag_modified_externally: logger.debug("Modified externally!") revertbtn = None box = None if not self._modified: # file was modified externally, but we have no local changes. box = QMessageBox(QMessageBox.Warning, "File Modified Externally", "The File was modified externally. Revert and reload it?") revertbtn = box.addButton("Revert", QMessageBox.AcceptRole) box.addButton(QMessageBox.Cancel) box.setDefaultButton(revertbtn) else: # file was modified externally, but we have LOCAL UNSAVED CHANGES box = QMessageBox(QMessageBox.Warning, "File Modified Externally", "The File was modified externally, but you have unsaved changes. " "Keep your changes and ignore the external modification, or " "revert it to version on disk?") revertbtn = box.addButton("Revert", QMessageBox.AcceptRole) box.addButton("Keep changes", QMessageBox.RejectRole) box.setDefaultButton(revertbtn) box.exec_() clickedbtn = box.clickedButton() if clickedbtn == revertbtn: logger.debug("Reverting!!") # reload the file self.reloadFile() return self._flag_modified_externally = False if not self.bibolamaziFile: logger.debug("No file loaded.") return self._set_modified(True) logger.debug('modified!!') self.bibolamaziFile.setConfigData(str(self.ui.txtConfig.toPlainText())) # set a short time-out to reparse the file. This is to avoid reparsing # too often, especially when doing # computationally/transactionally-intensive changes such as changing the # argument of a 'package:' directive with a remote location. self._delayed_reparse_textedit()
def on_filterInstanceEditor_filterInstanceDefinitionChanged(self): filtername = self.ui.filterInstanceEditor.filterName() optionstring = self.ui.filterInstanceEditor.optionString() errorstring = self.ui.filterInstanceEditor.errorString() logger.debug('on_filterInstanceEditor_filterInstanceDefinitionChanged() ' 'filtername=%r, optionstring=%r, errorstring=%r', filtername, optionstring, errorstring) if errorstring is not None: return cmdtext = "filter: " + filtername + ' ' + optionstring + "\n" self._replace_current_cmd(cmdtext, "filter")
def add_favorite_cmd(self): cmd = self._get_current_bibolamazi_cmd() if (cmd is None): logger.warning("No command to add to favorites!") return logger.debug("Adding command %s on lines %d--%d to favorites: %r", cmd.cmd, cmd.lineno, cmd.linenoend, cmd) cmdtext = [] doc = self.ui.txtConfig.document(); for n in xrange(self.bibolamaziFile.configLineNo(cmd.lineno), self.bibolamaziFile.configLineNo(cmd.linenoend)+1): cmdtext.append(unicode(doc.findBlockByNumber(n-1).text())) cmdtext = "\n".join(cmdtext) self.add_favorite_cmdtext(cmdtext)
def on_txtConfig_textChanged(self): logger.debug("text changed! fwatcher signals blocked=%d", self.fwatcher.signalsBlocked()) if self._flag_modified_externally: logger.debug("Modified externally!") revertbtn = None box = None if (not self._modified): # file was modified externally, but we have no local changes. box = QMessageBox(QMessageBox.Warning, "File Modified Externally", "The File was modified externally. Revert and reload it?") revertbtn = box.addButton("Revert", QMessageBox.AcceptRole) box.addButton(QMessageBox.Cancel) box.setDefaultButton(revertbtn) else: # file was modified externally, but we have LOCAL UNSAVED CHANGES box = QMessageBox(QMessageBox.Warning, "File Modified Externally", "The File was modified externally, but you have unsaved changes. " "Keep your changes and ignore the external modification, or " "revert it to version on disk?") revertbtn = box.addButton("Revert", QMessageBox.AcceptRole) box.addButton("Keep changes", QMessageBox.RejectRole) box.setDefaultButton(revertbtn) box.exec_() clickedbtn = box.clickedButton() if (clickedbtn == revertbtn): logger.debug("Reverting!!") # reload the file self.reloadFile() return self._flag_modified_externally = False if not self.bibolamaziFile: logger.debug("No file loaded.") return self._set_modified(True) logger.debug('modified!!') self.bibolamaziFile.setConfigData(unicode(self.ui.txtConfig.toPlainText())) self._bibolamazifile_reparse(); self._do_update_edittools()
def _open_anchor(self, url): if (url.scheme() == "helptopic"): self.requestHelpTopic.emit(url.path()); return if (url.scheme() == "action"): m = re.match(r'/goto-config-line/(?P<lineno>\d+)', url.path()) if m: self.ui.tabs.setCurrentWidget(self.ui.pageConfig) cur = QTextCursor(self.ui.txtConfig.document().findBlockByNumber(int(m.group('lineno'))-1)) self.ui.txtConfig.setTextCursor(cur) return logger.warning("Unknown action: %s", str(url.toString())) return logger.debug("Opening URL %r", str(url.toString())) QDesktopServices.openUrl(url)
def on_btnAddFilter_clicked(self): logger.debug('add filter: clicked') filterpath = filterfactory.filterpath if self.bibolamaziFile.getLoadState() == bibolamazifile.BIBOLAMAZIFILE_PARSED: filterpath = self.bibolamaziFile.fullFilterPath() filter_list = filterinstanceeditor.get_filter_list(filterpath) (filtname, ok) = QInputDialog.getItem(self, "Select Filter", "Please select which filter you wish to add", filter_list) if not ok: # cancelled return # insert new filter command self._insert_new_cmd('filter: %s' %(filtname))
def __init__(self, fix_swedish_a=False, encode_utf8_to_latex=False, encode_latex_to_utf8=False, remove_type_from_phd=False, remove_full_braces=False, protect_names=None, remove_file_field=False): """ Constructor method for a useless filter. """ BibFilter.__init__(self); self.fix_swedish_a = butils.getbool(fix_swedish_a); self.encode_utf8_to_latex = butils.getbool(encode_utf8_to_latex); self.encode_latex_to_utf8 = butils.getbool(encode_latex_to_utf8); if (self.encode_utf8_to_latex and self.encode_latex_to_utf8): raise FilterError("Conflicting options: `encode_utf8_to_latex' and `encode_latex_to_utf8'."); self.remove_type_from_phd = butils.getbool(remove_type_from_phd); try: self.remove_full_braces = butils.getbool(remove_full_braces); self.remove_full_braces_fieldlist = None; # all fields except ValueError: # not boolean, we have provided a field list. self.remove_full_braces = True; self.remove_full_braces_fieldlist = [ x.strip().lower() for x in remove_full_braces.split(',') ]; if protect_names is not None: self.protect_names = dict([ (x.strip(), re.compile(r'\b'+x.strip()+r'\b', re.IGNORECASE)) for x in protect_names.split(',') ]); else: self.protect_names = None; self.remove_file_field = butils.getbool(remove_file_field); logger.debug('useless test filter: fix_swedish_a=%r; encode_utf8_to_latex=%r; encode_latex_to_utf8=%r; ' 'remove_type_from_phd=%r; ' 'remove_full_braces=%r [fieldlist=%r], protect_names=%r, remove_file_field=%r' % (self.fix_swedish_a, self.encode_utf8_to_latex, self.encode_latex_to_utf8, self.remove_type_from_phd, self.remove_full_braces, self.remove_full_braces_fieldlist, self.protect_names, self.remove_file_field));
def _reload_filterpkg_modules(self, reload_filter_path): # # reload all relevant local filter packages, in case the filter packages # have changed. # # reload_filter_path is a list of 2-tuples (fpname, fpdir) # allmodules = sorted(sys.modules.keys(), reverse=True) # so that "pkg.submodule" appears before "pkg" for pkgname, pkgpath in reload_filter_path: logger.debug("Inspecting user filter package `%s` to reload modules ...", pkgname) for modname in allmodules: if modname.startswith(pkgname): mod = sys.modules[modname] logger.debug("Reloading module `%s` (%r) in user filter package `%s`", modname, mod, pkgname) origpath = sys.path try: sys.path = [pkgpath] + origpath reload(mod) finally: sys.path = origpath
def _replace_current_cmd(self, repltext, forcecheckcmd): logger.debug('_replace_current_cmd(%r,%r)', repltext, forcecheckcmd) cmd = self._get_current_bibolamazi_cmd() if (cmd is None or cmd.cmd != forcecheckcmd): logger.debug("Expected to currently be in cmd %s!!", forcecheckcmd) return logger.debug('About to change cmd %s on lines=%d--%d', cmd.cmd, cmd.lineno, cmd.linenoend) configlineno = self.bibolamaziFile.configLineNo(cmd.lineno) configlinenoend = self.bibolamaziFile.configLineNo(cmd.linenoend) self._ignore_change_for_edittools = True doc = self.ui.txtConfig.document() cursor = QTextCursor(doc.findBlockByNumber(configlineno-1)) endblock = (configlinenoend+1)-1 if (endblock >= doc.blockCount()): cursor.movePosition(QTextCursor.End, QTextCursor.KeepAnchor) else: cursorend = QTextCursor(doc.findBlockByNumber(endblock)) cursor.setPosition(cursorend.position(), QTextCursor.KeepAnchor) cursor.insertText(repltext) tcursor = QTextCursor(doc.findBlockByNumber(configlineno-1)) self.ui.txtConfig.setTextCursor(tcursor) self._ignore_change_for_edittools = False # now, reparse the config self.bibolamaziFile.setConfigData(unicode(self.ui.txtConfig.toPlainText())) self._bibolamazifile_reparse()
def openHelpTopic(self, url): logger.debug("Help: open topic at URL %s", url) # split off scheme and fragment, if any (scheme, netloc, path, query, fragment) = urlsplit(url) logger.debug("url parsed into (scheme,netloc,path,query,fragment) = %r", (scheme, netloc, path, query, fragment)) if scheme in ['http', 'https']: QDesktopServices.openUrl(QUrl(url)) return if scheme and scheme != 'help': raise ValueError("Invalid URL scheme: {} [url={}]".format(scheme, url)) # combine path with query string pathqs = path if query: pathqs += '?' + query def canonpath_check_fn(canonpath): urlcanon = 'help:'+canonpath logger.debug("requested page has canonical url %r", urlcanon) # check to see if the tab is already open for tab in self.openTabs: if (str(tab.property('helpurl')) == urlcanon): # just raise this tab. raise TabAlreadyOpen(tab) widget = None try: page = helppages.get_help_page( pathqs, html_table_width_px=TABLE_WIDTH, canonpath_check_fn=canonpath_check_fn, # raise tab if page already open ) widget = HelpTopicPageWidget(page, self, self.ui.tabs) widget.setProperty("helpurl", 'help:'+page.canonpath()) tabindex = self.ui.tabs.addTab(widget, widget.property('HelpTabTitle')) self.ui.tabs.setTabToolTip(tabindex, widget.property('HelpTabToolTip')) #self.ui.tabs.setCurrentIndex(tabindex) self.openTabs.append(widget) except TabAlreadyOpen as t: widget = t.widget if widget is None: logger.debug("Couldn't open help topic widget for %r", url) return self.ui.tabs.setCurrentWidget(widget) if fragment: # we were asked to scroll to a specific anchor widget.scrollToAnchor(fragment)
def on_btnAddSourceList_clicked(self): logger.debug('add source list: clicked') self._insert_new_cmd('src: ""') # directly set the focus on the file name field self.ui.sourceListEditor.selectSourceAltLoc(0)
def add_favorite_selection(self): selected_text = self.ui.txtConfig.textCursor().selectedText() logger.debug("Selected text: %r", selected_text) self.add_favorite_cmdtext(selected_text)
def on_txtConfig_cursorPositionChanged(self): logger.debug("cursor position changed!") self._do_update_edittools()
def fileModifiedExternally(self): logger.debug("File modified externally!!") self._flag_modified_externally = True
def on_btnInfoPageEntries_toggled(self, on): logger.debug("on_btnInfoPageEntries_toggled(%r)", on) if on: # self.ui.stkInfo.widget(index) is self.ui.pageInfoEntries: logger.debug("redisplaying bib entries") if self._needs_update_txtbibentries: self.ui.txtBibEntries.setPlainText(self.bibolamaziFile.rawRest())
def on_btnPageFileInfo_toggled(self, on): logger.debug("on_btnPageFileInfo_toggled(%r)", on) if on and self.ui.btnInfoPageEntries.isChecked(): logger.debug("redisplaying bib entries") if self._needs_update_txtbibentries: self.ui.txtBibEntries.setPlainText(self.bibolamaziFile.rawRest())