def loadCompletions(self): """ Loads the completion data from the config. """ conf = config("scorewiz completion") for w in self.completableWidgets: compObj, name = w.completionObject(), str(w.name()) compObj.setOrder(KCompletion.Sorted) compObj.setItems(py2qstringlist(conf.get(name, '').splitlines()))
def install(): """ Main install functions, called always if this file is imported """ conf = config() if "version" not in conf: install_katefiletyperc()
def loadSettings(self): """ Load the settings """ conf = config("rumor") if 'oss:1' in self.ilist: idefault = odefault = 'oss:1' else: idefault = 'kbd' odefault = self.olist[max(1, len(self.olist)-1)] i = conf.get("midi in", idefault) o = conf.get("midi out", odefault) if i in self.ilist: self.ibut.setCurrentItem(self.ilist.index(i)) if o in self.olist: self.obut.setCurrentItem(self.olist.index(o)) self.lang.setCurrentText(unautofy(conf.get("language", "auto"))) self.absPitches.setChecked(conf["absolute pitches"] == "1") self.explDur.setChecked(conf["explicit durations"] == "1") self.noBar.setChecked(conf["no barlines"] == "1") self.noDots.setChecked(conf["no dots"] == "1") self.legato.setChecked(conf["legato"] == "1") self.stripRests.setChecked(conf["strip rests"] == "1") # Guile scripts self.scripts.clear() scripts = conf.get("scripts", "").splitlines() for name, path in getRumorFiles().iteritems(): try: d = open(path).readline().strip() c = QCheckListItem(self.scripts, name, QCheckListItem.CheckBox) c.setOn(name in scripts) if d.startswith(';'): c.setText(1, d.strip(";")) except IOError: pass
def actionsConfig(): """ Returns a dictionary with action names mapping to a value 0 or 1, (whether they are enabled by the user or by default). """ conf = config("actions") return dict((name, int(conf[name] or default)) for name, default, title, tooltip in actions)
def saveCompletions(self): """ Saves completion items for all lineedits. """ conf = config("scorewiz completion") for w in self.completableWidgets: name, text = str(w.name()), unicode(w.text()) items = qstringlist2py(w.completionObject().items()) if len(text) > 1 and text not in items: items.append(text) conf[name] = '\n'.join(items)
def init(): global version lilypond = config("commands").get("lilypond", "lilypond") try: match = re.search(r"(\d+)\.(\d+)(?:\.(\d+))?", Popen((lilypond, "-v"), stdout=PIPE).communicate()[0].splitlines()[0]) except OSError, e: match = None error(_("Could not start LilyPond: %s") % e)
def _run(self, args, mode=None): if config("preferences")["delete intermediate files"] == "1": args.insert(0, "-ddelete-intermediate-files") args = [a.encode(sys.getfilesystemencoding() or "utf-8") for a in args] self.setArguments(args) a = dict(filename=self.f.ly, mode=mode) if mode: self.log.msg(_("LilyPond [$filename] starting ($mode)...").args(a)) else: self.log.msg(_("LilyPond [$filename] starting...").args(a)) self.start() or self.log.fail(_("Could not start LilyPond."))
def __init__(self, f, log): super(LyJob, self).__init__() self.f = f self.setExecutable(config("commands").get("lilypond", "lilypond")) self.setWorkingDirectory(f.directory) self.log = log self.stdout = Outputter(log, f) self.stderr = Outputter(log, f) QObject.connect(self, SIGNAL("receivedStdout(KProcess*, char*, int)"), self.stdout.receive) QObject.connect(self, SIGNAL("receivedStderr(KProcess*, char*, int)"), self.stderr.receive)
def saveSettings(self): """ Saves the settings to lilykderc """ conf = config("rumor") conf["tempo"] = self.tempo.tempo() conf["quantize"] = self.quantize.currentText() conf["step"] = int(self.step.isChecked()) conf["mono"] = int(self.mono.isChecked()) conf["meter"] = autofy(self.meter.currentText()) conf["keysig"] = autofy(self.keysig.currentText()) conf["timidity"] = int(self.timidity.isRunning()) self.status.message(_("Settings have been saved."), 2000)
def loadSettings(self): """ Loads the settings from lilykderc """ conf = config("rumor") self.tempo.setTempo(int(conf.get("tempo", 100))) self.quantize.setCurrentText(conf.get("quantize", "16")) self.step.setChecked(conf["step"] == "1") self.mono.setChecked(conf["mono"] == "1") self.meter.setCurrentText(unautofy(conf.get("meter", "auto"))) self.keysig.setCurrentText(unautofy(conf.get("meter", "auto"))) if conf["timidity"] == "1": self.timidity.start()
def findDicts(): conf = config("hyphenation") def paths(): """ build a list of paths based on config """ # in which prefixes to look for relative paths if 'KDEDIRS' in os.environ: prefixes = os.environ['KDEDIRS'].split(':') else: prefixes = ['/usr', '/usr/local'] if 'KDEDIR' in os.environ: prefixes.append(os.environ['KDEDIR']) # if the path is not absolute, add it to all prefixes. for path in conf["paths"].splitlines() or defaultpaths: if os.path.isabs(path): yield path else: for pref in prefixes: yield os.path.join(pref, path) # now find the hyph_xx_XX.dic files dicfiles = (f for p in paths() if os.path.isdir(p) for f in glob(os.path.join(p, 'hyph_*.dic')) if os.path.isfile(f)) # present the user with human readable language names all_languages = kconfig("all_languages", True, False, "locale") # default to the users current locale if not used before defaultlang = None global hyphdicts # empty it, because we might be called again when the user changes # the settings. hyphdicts = {} for dic in dicfiles: lang = os.path.basename(dic)[5:-4] # find a human readable name belonging to the language code for i in lang, lang.split('_')[0]: name = all_languages.group(i).get("Name") if name: name = '%s (%s)' % (name, lang) hyphdicts[name] = dic # set current locale as default if lang == language: defaultlang = name break else: hyphdicts[lang] = dic # if not used before, write the current locale (if existing) as default if defaultlang and conf["lastused"] not in hyphdicts: conf["lastused"] = defaultlang
def runLilyPond(doc, preview=False): """ Run Lilypond on the specified kate.document() and produce a PDF in the same directory if that is writable. If param `preview` is True, the PDF will contain clickable notes and other objects (point-and-click). If false, the PDF will not contain those textedit:// links. """ if doc.url == "": sorry(_("Your document currently has no filename, please save first.")) return if doc.modified: if config("preferences")["save on run"] == "1": doc.save() else: sorry(_("Your document has been modified, please save first.")) return f = LyFile(doc) if not f.isLocalFile(): # TODO: implement remote file support sorry( _("Sorry, support for remote files is not yet implemented.\n" "Please save your document to a local file.") ) return # the document may contain specially formatted variables to specify e.g. # another lilypond file to build instead of the current document. lvars = variables(doc) ly = lvars.get(preview and "master-preview" or "master-publish") or lvars.get("master") if ly: f.setPath(os.path.join(f.directory, ly)) from lilykde import log if config("preferences")["clear log"] == "1": log.logWidget().clear() Ly2PDF(f, log.logWidget()).run(preview)
def completed(self, success): if success and self.f.pdf: if self.f.hasUpdatedPDF(): self.f.previewPDF() # should we embed the LilyPond source files in the PDF? from lilykde import pdftk if not self.preview and config("preferences")["embed source files"] == "1" and pdftk.installed(): pdftk.attach_files(self.f.path, 3) else: self.log.msg(_("LilyPond did not write a PDF. " "You probably forgot <b>\layout</b>?")) from lilykde import actions self.log.actions(actions.listActions(self.f, self.preview))
def parseAconnect(channel): """ Returns a list of tuples ('0:0', 'Port name') of the available MIDI ports for either reading (channel = 'i') or writing (channel = 'o') """ option = channel == 'i' and '--input' or '--output' cmd = config("commands").get("aconnect", "aconnect") res = [] for line in Popen([cmd, option], stdout=PIPE).communicate()[0].splitlines(): m = re.match(r"client\s*(\d+)|\s*(\d+)\s+'([^']+)'", line) if m.group(1): client = m.group(1) elif client != "0": port, name = m.group(2,3) res.append(("%s:%s" % (client, port), name.strip())) return res
def openFile(pdf): """ Open the specified PDF document """ global _file c = KApplication.dcopClient() kpdf = DCOPApp(c.appId(), c).kpdf # When LilyPond writes a PDF, it first deletes the old one. # So the new PDF gets a different inode number, which causes # KPDF to sometimes loose the 'watch' on the file. # So we call KPDF to open the file, and remember the page number # displayed ourselves, because KPDF also seems to forget the scroll # position due to LilyPond deleting the old PDF first. # It would be best that either KPDF is fixed to just look for a # named file, even if it has a different inode number, or that # LilyPond is fixed to not delete the old PDF first, but just # truncate it and write the new data into it. # Update June 17, 2008: LilyPond >= 2.11.49 does not delete the PDF # anymore on unix platforms! # Document already shown? if _file == pdf: # Yes this is the same PDF, see if we need to force KPDF to # reload the document. If not, we trust that KPDF automatically # updates its view. from lilykde.version import version if ( # User can force reload of PDF with config option config('preferences')['force reload pdf'] == '1' # LilyPond >= 2.11.49 does not delete the PDF anymore on unix or version < (2, 11, 49) or os.name not in ('posix', 'mac') # read KPDF's 'watch file' option (default is true) or kconfig('kpdfpartrc', True, False).group('General')['WatchFile'] in ('false', '0', 'off', 'no')): # Reopen same document, remembering page number page = kpdf.currentPage()[1] kpdf.openDocument(KURL(pdf)) QTimer.singleShot(100, lambda: kpdf.goToPage(page)) else: # This is another PDF, just open it. kpdf.openDocument(KURL(pdf)) _file = pdf
def askLanguage(): """ Ask the user which language to use. Returns None if the user cancels the dialog. """ conf = config("hyphenation") lang = conf["lastused"] or "" langs = list(sorted(hyphdicts.keys())) index = lang in langs and langs.index(lang) or 0 lang, ok = KInputDialog.getItem( _("Language selection"), _("Please select a language:"), py2qstringlist(langs), index, False, editor.topLevelWidget() ) if ok: lang = unicode(lang) conf["lastused"] = lang return lang
def saveConfig(self): """ Save some preferences. """ conf = config("scorewiz") conf['language'] = self.getLanguage() or 'default' conf['typographical'] = self.typq.isChecked() and '1' or '0' conf['remove tagline'] = self.tagl.isChecked() and '1' or '0' conf['remove barnumbers'] = self.barnum.isChecked() and '1' or '0' conf['midi'] = self.midi.isChecked() and '1' or '0' conf['metronome mark'] = self.metro.isChecked() and '1' or '0' conf['paper size'] = self.paper.currentItem() and \ str(self.paper.currentText()) or 'default' conf['paper landscape'] = self.paperLandscape.isChecked() and '1' or '0' conf['instrument names'] = self.instr.isChecked() and '1' or '0' conf['instrument names italian'] = self.instrIt.isChecked() and '1' or '0' conf['instrument names first system'] = \ ('short', 'long')[self.instrFirst.currentItem()] conf['instrument names other systems'] = \ ('none', 'short', 'long')[self.instrOther.currentItem()]
def saveSettings(self): """ Save the settings """ conf = config("rumor") conf["midi in"] = self.ilist[self.ibut.currentItem()] conf["midi out"] = self.olist[self.obut.currentItem()] conf["language"] = autofy(self.lang.currentText()) conf["absolute pitches"] = int(self.absPitches.isChecked()) conf["explicit durations"] = int(self.explDur.isChecked()) conf["no barlines"] = int(self.noBar.isChecked()) conf["no dots"] = int(self.noDots.isChecked()) conf["legato"] = int(self.legato.isChecked()) conf["strip rests"] = int(self.stripRests.isChecked()) # Read script listview c = self.scripts.firstChild() names = [] while c is not None: if c.isOn(): names.append(unicode(c.text())) c = c.nextSibling() conf["scripts"] = '\n'.join(names)
def undock(self): """ Undocks the widget and place it in a QDialog """ if not self.dialog: parent = None if config("preferences")["keep undocked on top"] != '0': parent = kate.mainWidget() d = DockDialog(self, parent) d.setCaption("%s - %s" % (self.title, PROGRAMNAME)) if self.dialogSize: d.resize(*self.dialogSize) if self.dialogPos: d.move(*self.dialogPos) QVBoxLayout(d).setAutoAdd(True) self.widget.reparent(d, QPoint(0, 0)) if not self.focus: self.widget.setFocusPolicy(QWidget.WheelFocus) self.dialog = d if self.tool: sip.delete(self.tool.widget) self.tool = None self.show()
def runAction(url): """ Runs an URL with KRun. If url starts with "email=" or "emailpreview=", it is converted to a mailto: link with the url attached, and opened in the default KDE mailer. If url starts with "print=", the file is directly printed with lpr. If url starts with "embed=", a subroutine in pdftk is called to embed LilyPond documents in the output PDF. """ # hack: prevent QTextView recognizing mailto: urls cos it can't handle # query string url = unicode(url) # url could be a QString m = re.match("([a-z]+)=(.*)", url) if not m: return krun(url) command, url = m.groups() if command == 'print': path = unicode(KURL(url).path()) cmd = splitcommandline(config("commands").get("lpr", "lpr")) cmd.append(path) p = Popen(cmd, stderr=PIPE) if p.wait() != 0: error(_("Printing failed: %s") % p.stderr.read()) else: info(_("The document has been sent to the printer.")) elif command in ('email', 'emailpreview'): if command == "email" or warncontinue(_( "This PDF has been created with point-and-click urls (preview " "mode), which increases the file size dramatically. It's better " "to email documents without point-and-click urls (publish mode), " "because they are much smaller. Continue anyway?")): KApplication.kApplication().invokeMailer( KURL(u"mailto:?attach=%s" % url), "", True) elif command == 'embed': ly = unicode(KURL(url).path()) from lilykde import pdftk pdftk.attach_files(ly)
def listActions(lyfile, preview): """ returns a actions list that LogWidget (see widgets.py) can display, based on the given LyFile object (see runlily.py) and the preview mode (True or False). """ act = actionsConfig() actions = [] if act["open_folder"]: actions.append(("file://%s" % lyfile.directory, _("Open folder"))) if lyfile.hasUpdatedPDF(): if act["open_pdf"]: actions.append(("file://%s" % lyfile.pdf, _("Open PDF"))) if act["print_pdf"]: actions.append(("print=file://%s" % lyfile.pdf, _("Print"))) if act["email_pdf"]: # hack: prevent QTextView from recognizing mailto urls, as # it then uses the mailClick signal, which does not give us # the query string. Later on, we prepend the "mailto:?" :) if preview: actions.append(("emailpreview=file://%s" % lyfile.pdf, _("Email PDF (preview)"))) else: actions.append(("email=file://%s" % lyfile.pdf, _("Email PDF"))) # should we embed the LilyPond source files in the PDF? from lilykde import pdftk if act["embed_source"] and pdftk.installed() and (preview or config("preferences")['embed source files'] != '1'): actions.append(("embed=file://%s" % lyfile.path, _("Embed source"))) midis = lyfile.getUpdated(".midi") if act["play_midi"] and midis: actions.append(("file://%s" % midis[0], _("Play MIDI"))) actions.extend([("file://%s" % m, str(n+1)) for n, m in enumerate(midis[1:])]) return actions
def convertLy(): """ Run convert-ly on the current document """ global version docVersion = getVersion() if not docVersion: sorry(_("Can't determine the LilyPond version of the current document." " Please add a \\version statement with the correct version.")) elif not version: sorry(_("Can't determine the version of LilyPond. " "Please check your LilyPond installation.")) elif docVersion >= version: sorry(_("This LilyPond document is already up-to-date.")) else: # Ok, let's run convert-ly. # We add the from-version. Only in that case convert-ly wants to # read from stdin. convert_ly = config("commands").get("convert-ly", "convert-ly") try: out, err = Popen((convert_ly, "-f", "%d.%d.%d" % docVersion, "-"), stdin=PIPE, stdout=PIPE, stderr=PIPE ).communicate(editor.text().encode('utf8')) if out: editor.setText(u"%s\n\n%%{\n%s\n%%}\n" % (out.decode('utf8'), err.decode('utf8'))) info(_( "The document has been processed with convert-ly. You'll find " "the messages of convert-ly in a comment block at the end. " "You still may have to edit some parts manually."), timeout=10) else: msg = err.decode('utf8').replace('\n', '<br>') info(_( "The document has been processed with convert-ly, but " "remained unchanged. This is the message given by " "convert-ly: %s") % "<br><br>%s" % msg, timeout=10) except OSError, e: error(_("Could not start convert-ly: %s") % e)
def load(self): conf = config("preferences") for w, c, d in self.checks: w.setChecked(bool(int(conf.get(c, d))))
def save(self): conf = config("actions") for a, w, d in self.actions: conf[a] = int(w.isChecked())
def _saveTab(index): config('lqi')['current tab'] = index
def applyRhythm(self): """ Adds the entered rhythm to the selected music.""" durs = [m.group() for m in Res.finddurs.finditer( unicode(self.rhythm.text()))] def durgen(): old = '' while True: for i in durs: yield i != old and i or '' old = i nextdur = durgen().next def repl(m): if m.group('chord'): return m.group('chord') + nextdur() Res.edit(repl) Articulations() Rhythm() # Remember the currently selected tab. def _saveTab(index): config('lqi')['current tab'] = index toolbox.setCurrentIndex(int(config('lqi')['current tab'] or '0')) QObject.connect(toolbox, SIGNAL("currentChanged(int)"), _saveTab)
def save(self): conf = config("preferences") for w, c, d in self.checks: conf[c] = int(w.isChecked())
def onStart(self): self.command = config("commands").get("timidity", 'timidity -iA -B2,8 -Os -EFreverb=0')
def onStart(self): """ Here we construct the command etc. """ p = self.parent() conf = config("rumor") # indent of current line self.indent = re.match(r'\s*', editor.currentLine()[:editor.pos()[1]]).group() # text from start to cursor text = editor.fragment((0, 0), editor.pos()) cmd = [config("commands").get("rumor", "rumor")] # Language lang = conf.get("language", "auto") if lang not in ( 'ne', 'en', 'en-short', 'de', 'no', 'sv', 'it', 'ca', 'es'): # determine lily language from document m = re.compile(r'.*\\include\s*"(' "nederlands|english|deutsch|norsk|svenska|suomi|" "italiano|catalan|espanol|portuges|vlaams" r')\.ly"', re.DOTALL).match(text) if m: lang = m.group(1)[:2] if lang == "po": lang = "es" elif lang == "su": lang = "de" elif lang == "en" and not re.match( r'\b[a-g](flat|sharp)\b', text): lang == "en-short" elif lang == "vl": # "vlaams" is not supported by Rumor # TODO: rewrite the pitches output by Rumor :-) lang == "it" else: lang = "ne" # the default cmd.append("--lang=%s" % lang) # Step recording? if p.step.isChecked(): cmd.append("--flat") else: # No, set tempo, quantization and meter cmd.append("--tempo=%d" % p.tempo.tempo()) cmd.append("--grain=%s" % p.quantize.currentText()) meter = autofy(p.meter.currentText()) if meter == "auto": # determine from document - find the latest \time command: m = re.compile(r'.*\\time\s*(\d+/(1|2|4|8|16|32|64|128))(?!\d)', re.DOTALL).match(text) if m: meter = m.group(1) else: meter = '4/4' cmd.append("--meter=%s" % meter) # Key signature acc = autofy(p.keysig.currentText()) if acc == "auto": # Determine key signature from document. m = re.compile( r'.*\\key\s+(' + '|'.join(pitches[lang].keys()) + r')\s*\\' r'(major|minor|(ion|dor|phryg|(mixo)?lyd|aeol|locr)ian)\b', re.DOTALL).match(text) if m: pitch, mode = m.group(1,2) acc = pitches[lang][pitch] + modes[mode] else: acc = 0 else: acc == int(acc) acc += 2 # use sharps for half tones leading to second, fifth, sixth cmd.append("--key=%s" % revpitches[lang][bound(acc, -8, 12)]) # Monophonic input? if p.mono.isChecked(): cmd.append("--no-chords") # Absolute pitches? if int(conf.get("absolute pitches", "0")): cmd.append("--absolute-pitches") # Explicit durations? if int(conf.get("explicit durations", "0")): cmd.append("--explicit-durations") # No barlines? self.noBarlines = int(conf.get("no barlines", "0")) # No dots? if int(conf.get("no dots", "0")): cmd.append("--no-dots") # Legato? if int(conf.get("legato", "0")): cmd.append("--legato") # Strip rests? if int(conf.get("strip rests", "0")): cmd.append("--strip") # Guile scripts? scripts = conf.get("scripts", "").splitlines() if scripts: paths = getRumorFiles() for s in scripts: if s in paths: cmd.append("--script=%s" % paths[s]) # input/output i = conf.get("midi in", "oss:1") o = conf.get("midi out", "oss:1") if o.startswith('oss:'): cmd.append("--oss=%s" % o.split(":")[1]) elif re.match(r"\d", o) and re.match(r"\d", i): cmd.append("--alsa=%s,%s" % (i, o)) elif re.match(r"\d", o): cmd.append("--alsa=%s" % o) self.keyboardEmu = i == "keyboard" if self.keyboardEmu: cmd.append("--kbd") p.setFocus() self.comm = KProcess.Communication(KProcess.Stdin | KProcess.Stdout) self.pty = True else: self.comm = KProcess.Stdout self.pty = False self.command = cmd
def __init__(self, *args): QFrame.__init__(self, *args) # Accept keyboard focus self.setFocusPolicy(QWidget.ClickFocus) layout = QGridLayout(self, 4, 5, 4) layout.setColStretch(4, 1) # Big Start/stop toggle button self.r = RumorButton(self) layout.addMultiCellWidget(self.r, 0, 3, 0, 0) # labels for other controls: layout.addWidget(QLabel(_("Tempo:"), self), 0, 1) layout.addWidget(QLabel(_("Meter:"), self), 1, 1) layout.addWidget(QLabel(_("Key:"), self), 2, 1) # Status line self.status = QStatusBar(self) self.status.setSizeGripEnabled(False) layout.addMultiCellWidget(self.status, 3, 3, 1, 4) # Tempo adjustment (spinbox + long slider) self.tempo = TempoControl(self) layout.addWidget(self.tempo.spinbox, 0, 2) hb = QHBoxLayout() layout.addLayout(hb, 0, 3) hb.addWidget(self.tempo.tapButton) hb.addWidget(self.tempo.slider) # Meter select (editable qcombobox defaulting to document) self.meter = QComboBox(self) self.meter.setEditable(True) self.meter.insertStringList(py2qstringlist([ AUTO, '1/4', '2/4', '3/4', '4/4', '5/4', '6/4', '2/2', '3/2', '3/8', '6/8', '9/8', '12/8', '3/16', ])) self.meter.setValidator(QRegExpValidator(QRegExp( re.escape(AUTO) + "|[1-9][0-9]*/(1|2|4|8|16|32|64|128)"), self.meter)) QToolTip.add(self.meter, _( "The meter to use. Leave 'Auto' to let LilyKDE determine " "the meter from the LilyPond document.")) layout.addWidget(self.meter, 1, 2) # Quantize (1,2,4,8,16,32,64 or 128, default to 16) hb = QHBoxLayout() layout.addLayout(hb, 1, 3) hb.addWidget(QLabel(_("Quantize:"), self)) self.quantize = QComboBox(self) self.quantize.insertStringList(py2qstringlist( str(2**i) for i in range(8))) QToolTip.add(self.quantize, _( "The shortest note duration to use.")) hb.addWidget(self.quantize) # Step recording: (checkbox, disables the three controls above) self.step = QCheckBox(_("Step"), self) QToolTip.add(self.step, _( "Record LilyPond input note by note, without durations.")) hb.addWidget(self.step) # Monophonic input (no chords) self.mono = QCheckBox(_("Mono"), self) QToolTip.add(self.mono, _( "Record monophonic input, without chords.")) hb.addWidget(self.mono) # Key signature select (any lilypond pitch, defaulting to document) self.keysig = QComboBox(self) self.keysig.insertItem(AUTO) self.keysig.insertStringList(py2qstringlist( "%d" % i for i in range(-7, 1))) self.keysig.insertStringList(py2qstringlist( "%+d" % i for i in range(1, 8))) QToolTip.add(self.keysig, _( "The number of accidentals. A negative number designates flats. " "Leave 'Auto' to let LilyKDE determine the key signature from " "the LilyPond document.")) layout.addWidget(self.keysig, 2, 2) hb = QHBoxLayout() layout.addLayout(hb, 2, 3) # Timidity button self.timidity = TimidityButton(self) hb.addWidget(self.timidity) # Button 'More Settings' sb = QPushButton(_("Configure..."), self) QToolTip.add(sb, _("Adjust more settings, like MIDI input and output.")) QObject.connect(sb, SIGNAL("clicked()"), lambda: RumorSettings(self)) hb.addWidget(sb) # Save Button sb = QPushButton(_("Save"), self) QToolTip.add(sb, _("Set these settings as default.")) QObject.connect(sb, SIGNAL("clicked()"), self.saveSettings) hb.addWidget(sb) self.loadSettings() # display Rumor version on first start. cmd = config("commands").get("rumor", "rumor") try: v = Popen([cmd, '--version'], stdout=PIPE).communicate()[0].strip() self.status.message(_("Found rumor version $version.").args( version = v), 5000) except OSError, e: self.status.message(_("Could not find Rumor: %s") % e, 10000)