示例#1
0
def changeLanguage(cursor, language):
    """Changes the language of the pitch names."""
    c = lydocument.cursor(cursor, select_all=True)
    try:
        with qutil.busyCursor():
            changed = ly.pitch.translate.translate(c, language)
    except ly.pitch.PitchNameNotAvailable:
        QMessageBox.critical(None, app.caption(_("Pitch Name Language")), _(
            "Can't perform the requested translation.\n\n"
            "The music contains quarter-tone alterations, but "
            "those are not available in the pitch language \"{name}\"."
            ).format(name=language))
        return
    if changed:
        return
    if not cursor.hasSelection():
        # there was no selection and no language command, so insert one
        version = (documentinfo.docinfo(cursor.document()).version()
                   or lilypondinfo.preferred().version())
        ly.pitch.translate.insert_language(c.document, language, version)
        return
    # there was a selection but no command, user must insert manually.
    QMessageBox.information(None, app.caption(_("Pitch Name Language")),
        '<p>{0}</p>'
        '<p><code>\\include "{1}.ly"</code> {2}</p>'
        '<p><code>\\language "{1}"</code> {3}</p>'.format(
            _("The pitch language of the selected text has been "
                "updated, but you need to manually add the following "
                "command to your document:"),
            language,
            _("(for LilyPond below 2.14), or"),
            _("(for LilyPond 2.14 and higher.)")))
示例#2
0
    def importMusicXML(self):
        """ Opens a MusicXML file. Converts it to ly by using musicxml2ly """
        filetypes = "{0} (*.xml);;{1} (*)".format(_("XML Files"), _("All Files"))
        caption = app.caption(_("dialog title", "Import a MusicXML file"))
        directory = os.path.dirname(self.mainwindow().currentDocument().url().toLocalFile()) or app.basedir()
        importfile = QFileDialog.getOpenFileName(self.mainwindow(), caption, directory, filetypes)
        if not importfile:
            return  # the dialog was cancelled by user

        try:
            dlg = self._importDialog
        except AttributeError:
            from . import musicxml

            dlg = self._importDialog = musicxml.Dialog(self.mainwindow())
            dlg.addAction(self.mainwindow().actionCollection.help_whatsthis)
            dlg.setWindowModality(Qt.WindowModal)

        dlg.setDocument(importfile)
        if dlg.exec_():
            with qutil.busyCursor():
                stdout, stderr = dlg.run_command()
            if stdout:  # success
                dlg.saveSettings()
                lyfile = os.path.splitext(importfile)[0] + ".ly"
                doc = self.createDocument(lyfile, stdout.decode("utf-8"))
                self.postImport(dlg.getPostSettings(), doc)
                self.mainwindow().saveDocument(doc)
            else:  # failure to convert
                QMessageBox.critical(
                    None, _("Error"), _("The file couldn't be converted. Error message:\n") + stderr.decode("utf-8")
                )
示例#3
0
def changeLanguage(cursor, language):
    """Changes the language of the pitch names."""
    selection = cursor.hasSelection()
    if selection:
        start = cursor.selectionStart()
        cursor.setPosition(cursor.selectionEnd())
        cursor.setPosition(0, QTextCursor.KeepAnchor)
        source = tokeniter.Source.selection(cursor)
    else:
        source = tokeniter.Source.document(cursor)
    
    pitches = PitchIterator(source)
    tokens = pitches.tokens()
    writer = ly.pitch.pitchWriter(language)
    
    if selection:
        # consume tokens before the selection, following the language
        source.consume(tokens, start)
    
    changed = False # track change of \language or \include language command
    with cursortools.compress_undo(cursor):
        try:
            with qutil.busyCursor():
                with cursortools.Editor() as e:
                    for t in tokens:
                        if isinstance(t, ly.lex.lilypond.Note):
                            # translate the pitch name
                            p = pitches.read(t)
                            if p:
                                n = writer(*p)
                                if n != t:
                                    e.insertText(source.cursor(t), n)
                        elif isinstance(t, LanguageName) and t != language:
                            # change the language name in a command
                            e.insertText(source.cursor(t), language)
                            changed = True
        except ly.pitch.PitchNameNotAvailable:
            QMessageBox.critical(None, app.caption(_("Pitch Name Language")), _(
                "Can't perform the requested translation.\n\n"
                "The music contains quarter-tone alterations, but "
                "those are not available in the pitch language \"{name}\"."
                ).format(name=language))
            return
        if changed:
            return
        if not selection:
            # there was no selection and no language command, so insert one
            insertLanguage(cursor.document(), language)
            return
    # there was a selection but no command, user must insert manually.
    QMessageBox.information(None, app.caption(_("Pitch Name Language")),
        '<p>{0}</p>'
        '<p><code>\\include "{1}.ly"</code> {2}</p>'
        '<p><code>\\language "{1}"</code> {3}</p>'.format(
            _("The pitch language of the selected text has been "
                "updated, but you need to manually add the following "
                "command to your document:"),
            language,
            _("(for LilyPond below 2.14), or"),
            _("(for LilyPond 2.14 and higher.)")))
示例#4
0
    def importMusicXML(self):
        """ Opens a MusicXML file. Converts it to ly by using musicxml2ly """
        filetypes = '{0} (*.xml);;{1} (*)'.format(_("XML Files"),
                                                  _("All Files"))
        caption = app.caption(_("dialog title", "Import a MusicXML file"))
        directory = os.path.dirname(self.mainwindow().currentDocument().url().
                                    toLocalFile()) or app.basedir()
        importfile = QFileDialog.getOpenFileName(self.mainwindow(), caption,
                                                 directory, filetypes)
        if not importfile:
            return  # the dialog was cancelled by user

        try:
            dlg = self._importDialog
        except AttributeError:
            from . import musicxml
            dlg = self._importDialog = musicxml.Dialog(self.mainwindow())
            dlg.addAction(self.mainwindow().actionCollection.help_whatsthis)
            dlg.setWindowModality(Qt.WindowModal)

        dlg.setDocument(importfile)
        if dlg.exec_():
            with qutil.busyCursor():
                stdout, stderr = dlg.run_command()
            if stdout:  #success
                dlg.saveSettings()
                lyfile = os.path.splitext(importfile)[0] + ".ly"
                doc = self.createDocument(lyfile, stdout.decode('utf-8'))
                self.postImport(dlg.getPostSettings(), doc)
                self.mainwindow().saveDocument(doc)
            else:  #failure to convert
                QMessageBox.critical(
                    None, _("Error"),
                    _("The file couldn't be converted. Error message:\n") +
                    stderr.decode('utf-8'))
示例#5
0
def abs2rel(cursor, startpitch, first_pitch_absolute):
    """Converts pitches from absolute to relative."""
    with qutil.busyCursor():
        c = lydocument.cursor(cursor, select_all=True)
        ly.pitch.abs2rel.abs2rel(c,
                                 startpitch=startpitch,
                                 first_pitch_absolute=first_pitch_absolute)
示例#6
0
def changeLanguage(cursor, language):
    """Changes the language of the pitch names."""
    c = lydocument.cursor(cursor, select_all=True)
    try:
        with qutil.busyCursor():
            changed = ly.pitch.translate.translate(c, language)
    except ly.pitch.PitchNameNotAvailable:
        QMessageBox.critical(
            None, app.caption(_("Pitch Name Language")),
            _("Can't perform the requested translation.\n\n"
              "The music contains quarter-tone alterations, but "
              "those are not available in the pitch language \"{name}\".").
            format(name=language))
        return
    if changed:
        return
    if not cursor.hasSelection():
        # there was no selection and no language command, so insert one
        version = (documentinfo.docinfo(cursor.document()).version()
                   or lilypondinfo.preferred().version())
        ly.pitch.translate.insert_language(c.document, language, version)
        return
    # there was a selection but no command, user must insert manually.
    QMessageBox.information(
        None, app.caption(_("Pitch Name Language")), '<p>{0}</p>'
        '<p><code>\\include "{1}.ly"</code> {2}</p>'
        '<p><code>\\language "{1}"</code> {3}</p>'.format(
            _("The pitch language of the selected text has been "
              "updated, but you need to manually add the following "
              "command to your document:"), language,
            _("(for LilyPond below 2.14), or"),
            _("(for LilyPond 2.14 and higher.)")))
示例#7
0
    def run(self):
        """Runs convert-ly (again)."""
        fromVersion = self.fromVersion.text()
        toVersion = self.toVersion.text()
        if not fromVersion or not toVersion:
            self.messages.setPlainText(
                _("Both 'from' and 'to' versions need to be set."))
            return
        info = self._info
        command = info.toolcommand(info.ly_tool('convert-ly'))
        command += ['-f', fromVersion, '-t', toVersion, '-']

        # if the user wants english messages, do it also here: LANGUAGE=C
        env = None
        if os.name == "nt":
            # Python 2.7 subprocess on Windows chokes on unicode in env
            env = util.bytes_environ()
        else:
            env = dict(os.environ)
        if sys.platform.startswith('darwin'):
            try:
                del env['PYTHONHOME']
            except KeyError:
                pass
            try:
                del env['PYTHONPATH']
            except KeyError:
                pass
        if QSettings().value("lilypond_settings/no_translation", False, bool):
            if os.name == "nt":
                # Python 2.7 subprocess on Windows chokes on unicode in env
                env[b'LANGUAGE'] = b'C'
            else:
                env['LANGUAGE'] = 'C'

        with qutil.busyCursor():
            try:
                proc = subprocess.Popen(command,
                                        env=env,
                                        stdin=subprocess.PIPE,
                                        stdout=subprocess.PIPE,
                                        stderr=subprocess.PIPE)
                out, err = proc.communicate(
                    util.platform_newlines(self._text).encode(self._encoding))
            except OSError as e:
                self.messages.setPlainText(
                    _("Could not start {convert_ly}:\n\n"
                      "{message}\n").format(convert_ly=command[0], message=e))
                return
            out = util.universal_newlines(out.decode('UTF-8'))
            err = util.universal_newlines(err.decode('UTF-8'))
            self.messages.setPlainText(err)
            self.setConvertedText(out)
            self.setDiffText(out)
            if not out or self._convertedtext == self._text:
                self.messages.append('\n' +
                                     _("The document has not been changed."))
示例#8
0
 def run(self):
     """Runs convert-ly (again)."""
     fromVersion = self.fromVersion.text()
     toVersion = self.toVersion.text()
     if not fromVersion or not toVersion:
         self.messages.setPlainText(_(
             "Both 'from' and 'to' versions need to be set."))
         return
     info = self._info
     command = info.toolcommand(info.ly_tool('convert-ly'))
     command += ['-f', fromVersion, '-t', toVersion, '-']
     
     # if the user wants english messages, do it also here: LANGUAGE=C
     env = None
     if os.name == "nt":
         # Python 2.7 subprocess on Windows chokes on unicode in env
         env = util.bytes_environ()
     else:
         env = dict(os.environ)
     if sys.platform.startswith('darwin'):
         try:
             del env['PYTHONHOME']
         except KeyError:
             pass
         try:
             del env['PYTHONPATH']
         except KeyError:
             pass
     if QSettings().value("lilypond_settings/no_translation", False, bool):
         if os.name == "nt":
             # Python 2.7 subprocess on Windows chokes on unicode in env
             env[b'LANGUAGE'] = b'C'
         else:
             env['LANGUAGE'] = 'C'
     
     with qutil.busyCursor():
         try:
             proc = subprocess.Popen(command,
                 env = env,
                 stdin = subprocess.PIPE,
                 stdout = subprocess.PIPE,
                 stderr = subprocess.PIPE)
             out, err = proc.communicate(util.platform_newlines(self._text).encode(self._encoding))
         except OSError as e:
             self.messages.setPlainText(_(
                 "Could not start {convert_ly}:\n\n"
                 "{message}\n").format(convert_ly = command[0], message = e))
             return
         out = util.universal_newlines(out.decode('UTF-8'))
         err = util.universal_newlines(err.decode('UTF-8'))
         self.messages.setPlainText(err)
         self.setConvertedText(out)
         self.setDiffText(out)
         if not out or self._convertedtext == self._text:
             self.messages.append('\n' + _("The document has not been changed."))
示例#9
0
def transpose(cursor, transposer, mainwindow=None):
    """Transpose pitches using the specified transposer."""
    c = lydocument.cursor(cursor, select_all=True)
    try:
        with qutil.busyCursor():
            ly.pitch.transpose.transpose(c, transposer)
    except ly.pitch.PitchNameNotAvailable as e:
        QMessageBox.critical(mainwindow, app.caption(_("Transpose")), _(
            "Can't perform the requested transposition.\n\n"
            "The transposed music would contain quarter-tone alterations "
            "that are not available in the pitch language \"{language}\"."
            ).format(language = e.language))
示例#10
0
def transpose(cursor, transposer, mainwindow=None):
    """Transpose pitches using the specified transposer."""
    c = lydocument.cursor(cursor, select_all=True)
    try:
        with qutil.busyCursor():
            ly.pitch.transpose.transpose(c, transposer)
    except ly.pitch.PitchNameNotAvailable as e:
        QMessageBox.critical(
            mainwindow, app.caption(_("Transpose")),
            _("Can't perform the requested transposition.\n\n"
              "The transposed music would contain quarter-tone alterations "
              "that are not available in the pitch language \"{language}\".").
            format(language=e.language))
示例#11
0
 def run(self):
     """Runs convert-ly (again)."""
     fromVersion = self.fromVersion.text()
     toVersion = self.toVersion.text()
     if not fromVersion or not toVersion:
         self.messages.setPlainText(_(
             "Both 'from' and 'to' versions need to be set."))
         return
     info = self._info
     convert_ly = os.path.join(info.bindir(), info.convert_ly)
     
     # on Windows the convert-ly command is not directly executable, but
     # must be started using the LilyPond-provided Python interpreter
     if os.name == "nt":
         if not os.access(convert_ly, os.R_OK) and not convert_ly.endswith('.py'):
             convert_ly += '.py'
         command = [info.python(), convert_ly]
     else:
         command = [convert_ly]
     command += ['-f', fromVersion, '-t', toVersion, '-']
     
     # if the user wants english messages, do it also here: LANGUAGE=C
     env = None
     if QSettings().value("lilypond_settings/no_translation", False) in (True, "true"):
         if os.name == "nt":
             # Python 2.7 subprocess on Windows chokes on unicode in env
             env = util.bytes_environ()
             env[b'LANGUAGE'] = b'C'
         else:
             env = dict(os.environ)
             env['LANGUAGE'] = 'C'
     
     with qutil.busyCursor():
         try:
             proc = subprocess.Popen(command,
                 universal_newlines = True,
                 env = env,
                 stdin = subprocess.PIPE,
                 stdout = subprocess.PIPE,
                 stderr = subprocess.PIPE)
             out, err = proc.communicate(self._text.encode(self._encoding))
         except OSError as e:
             self.messages.setPlainText(_(
                 "Could not start {convert_ly}:\n\n"
                 "{message}\n").format(convert_ly = convert_ly, message = e))
             return
         self.messages.setPlainText(err.decode('UTF-8'))
         self.setConvertedText(out.decode('UTF-8'))
         if not out or self._convertedtext == self._text:
             self.messages.append('\n' + _("The document has not been changed."))
示例#12
0
 def runImport(self):
     """Generic execution of all import dialogs."""
     dlg = self._importDialog
     dlg.setDocument(self.importfile)
     if dlg.exec_():
         with qutil.busyCursor():
             stdout, stderr = dlg.run_command()
         if stdout: #success
             dlg.saveSettings()
             lyfile = os.path.splitext(self.importfile)[0] + ".ly"
             doc = self.createDocument(lyfile, stdout.decode('utf-8'))
             self.postImport(dlg.getPostSettings(), doc)
             self.mainwindow().saveDocument(doc)
         else: #failure to convert
             QMessageBox.critical(None, _("Error"),
                 _("The file couldn't be converted. Error message:\n") + stderr.decode('utf-8'))
示例#13
0
    def importMusicXML(self):
        """ Opens a MusicXML file. Converts it to ly by using musicxml2ly """
        filetypes = app.filetypes('*.xml')
        caption = app.caption(_("dialog title", "Import a MusicXML file"))
        directory = os.path.dirname(self.mainwindow().currentDocument().url().toLocalFile()) or app.basedir()
        importfile = QFileDialog.getOpenFileName(self.mainwindow(), caption, directory, filetypes)
        if not importfile:
            return # the dialog was cancelled by user

        try:
            dlg = self._importDialog
        except AttributeError:
            from . import musicxml
            dlg = self._importDialog = musicxml.Dialog(self.mainwindow())
            dlg.addAction(self.mainwindow().actionCollection.help_whatsthis)
            dlg.setWindowModality(Qt.WindowModal)
        
        dlg.setDocument(importfile)
        if dlg.exec_():
            with qutil.busyCursor():
                stdout, stderr = dlg.run_command()
                print stderr #put this in log window instead
                lyfile = os.path.splitext(importfile)[0] + ".ly"
                self.createDocument(lyfile, stdout.decode('utf-8'))
示例#14
0
def transpose(cursor, transposer, mainwindow=None):
    """Transpose pitches using the specified transposer."""
    
    selection = cursor.hasSelection()
    if selection:
        start = cursor.selectionStart()
        cursor.setPosition(cursor.selectionEnd())
        cursor.setPosition(0, QTextCursor.KeepAnchor)
        source = tokeniter.Source.selection(cursor, True)
    else:
        source = tokeniter.Source.document(cursor, True)
    
    pitches = PitchIterator(source)
    psource = pitches.pitches()
    
    class gen(object):
        def __init__(self):
            self.inSelection = not selection
        
        def __iter__(self):
            return self
        
        def __next__(self):
            while True:
                t = next(psource)
                if isinstance(t, (ly.lex.Space, ly.lex.Comment)):
                    continue
                elif not self.inSelection and pitches.position(t) >= start:
                    self.inSelection = True
                # Handle stuff that's the same in relative and absolute here
                if t == "\\relative":
                    relative()
                elif isinstance(t, ly.lex.lilypond.MarkupScore):
                    absolute(context())
                elif isinstance(t, ly.lex.lilypond.ChordMode):
                    chordmode()
                elif isinstance(t, ly.lex.lilypond.PitchCommand):
                    if t == "\\transposition":
                        next(psource) # skip pitch
                    elif t == "\\transpose":
                        for p in getpitches(context()):
                            transpose(p)
                    elif t == "\\key":
                        for p in getpitches(context()):
                            transpose(p, 0)
                    else:
                        return t
                else:
                    return t
        
        next = __next__
    
    tsource = gen()
    
    def context():
        """Consume tokens till the level drops (we exit a construct)."""
        depth = source.state.depth()
        for t in tsource:
            yield t
            if source.state.depth() < depth:
                return
    
    def consume():
        """Consume tokens from context() returning the last token, if any."""
        t = None
        for t in context():
            pass
        return t
        
    def transpose(p, resetOctave = None):
        """Transpose absolute pitch, using octave if given."""
        transposer.transpose(p)
        if resetOctave is not None:
            p.octave = resetOctave
        if tsource.inSelection:
            pitches.write(p, editor)

    def chordmode():
        """Called inside \\chordmode or \\chords."""
        for p in getpitches(context()):
            transpose(p, 0)
            
    def absolute(tokens):
        """Called when outside a possible \\relative environment."""
        for p in getpitches(tokens):
            transpose(p)
    
    def relative():
        """Called when \\relative is encountered."""
        def transposeRelative(p, lastPitch):
            """Transposes a relative pitch; returns the pitch in absolute form."""
            # absolute pitch determined from untransposed pitch of lastPitch
            p.makeAbsolute(lastPitch)
            if not tsource.inSelection:
                return p
            # we may change this pitch. Make it relative against the
            # transposed lastPitch.
            try:
                last = lastPitch.transposed
            except AttributeError:
                last = lastPitch
            # transpose a copy and store that in the transposed
            # attribute of lastPitch. Next time that is used for
            # making the next pitch relative correctly.
            newLastPitch = p.copy()
            transposer.transpose(p)
            newLastPitch.transposed = p.copy()
            if p.octaveCheck is not None:
                p.octaveCheck = p.octave
            p.makeRelative(last)
            if relPitch:
                # we are allowed to change the pitch after the
                # \relative command. lastPitch contains this pitch.
                lastPitch.octave += p.octave
                p.octave = 0
                pitches.write(lastPitch, editor)
                del relPitch[:]
            pitches.write(p, editor)
            return newLastPitch

        lastPitch = None
        relPitch = [] # we use a list so it can be changed from inside functions
        
        # find the pitch after the \relative command
        t = next(tsource)
        if isinstance(t, Pitch):
            lastPitch = t
            if tsource.inSelection:
                relPitch.append(lastPitch)
            t = next(tsource)
        else:
            lastPitch = Pitch.c1()
        
        while True:
            # eat stuff like \new Staff == "bla" \new Voice \notes etc.
            if isinstance(source.state.parser(), ly.lex.lilypond.ParseTranslator):
                t = consume()
            elif isinstance(t, ly.lex.lilypond.NoteMode):
                t = next(tsource)
            else:
                break
        
        # now transpose the relative expression
        if t in ('{', '<<'):
            # Handle full music expression { ... } or << ... >>
            for t in context():
                if t == '\\octaveCheck':
                    for p in getpitches(context()):
                        lastPitch = p.copy()
                        del relPitch[:]
                        if tsource.inSelection:
                            transposer.transpose(p)
                            lastPitch.transposed = p
                            pitches.write(p, editor)
                elif isinstance(t, ly.lex.lilypond.ChordStart):
                    chord = [lastPitch]
                    for p in getpitches(context()):
                        chord.append(transposeRelative(p, chord[-1]))
                    lastPitch = chord[:2][-1] # same or first
                elif isinstance(t, Pitch):
                    lastPitch = transposeRelative(t, lastPitch)
        elif isinstance(t, ly.lex.lilypond.ChordStart):
            # Handle just one chord
            for p in getpitches(context()):
                lastPitch = transposeRelative(p, lastPitch)
        elif isinstance(t, Pitch):
            # Handle just one pitch
            transposeRelative(token, lastPitch)

    # Do it!
    try:
        with qutil.busyCursor():
            with cursortools.Editor() as editor:
                absolute(tsource)
    except ly.pitch.PitchNameNotAvailable:
        QMessageBox.critical(mainwindow, app.caption(_("Transpose")), _(
            "Can't perform the requested transposition.\n\n"
            "The transposed music would contain quarter-tone alterations "
            "that are not available in the pitch language \"{language}\"."
            ).format(language = pitches.language))
示例#15
0
def load(filename, widget):
    """Loads snippets from a file, displaying them in a list.
    
    The user can then choose:
    - overwrite builtin snippets or not
    - overwrite own snippets with same title or not
    - select and view snippets contents.
    
    """
    try:
        d = ET.parse(filename)
        elements = list(d.findall('snippet'))
        if not elements:
            raise ValueError(_("No snippets found."))
    except Exception as e:
        QMessageBox.critical(
            widget, app.caption(_("Error")),
            _("Can't read from source:\n\n{url}\n\n{error}").format(
                url=filename, error=e))
        return

    dlg = widgets.dialog.Dialog(widget)
    dlg.setWindowModality(Qt.WindowModal)
    dlg.setWindowTitle(app.caption(_("dialog title", "Import Snippets")))
    tree = QTreeWidget(headerHidden=True, rootIsDecorated=False)
    dlg.setMainWidget(tree)
    userguide.addButton(dlg.buttonBox(), "snippet_import_export")

    allnames = frozenset(snippets.names())
    builtins = frozenset(builtin.builtin_snippets)
    titles = dict(
        (snippets.title(n), n) for n in allnames if n not in builtins)

    new = QTreeWidgetItem(tree, [_("New Snippets")])
    updated = QTreeWidgetItem(tree, [_("Updated Snippets")])
    unchanged = QTreeWidgetItem(tree, [_("Unchanged Snippets")])

    new.setFlags(Qt.ItemIsEnabled)
    updated.setFlags(Qt.ItemIsEnabled)
    unchanged.setFlags(Qt.ItemIsEnabled)

    new.setExpanded(True)
    updated.setExpanded(True)

    items = []
    for snip in elements:
        item = QTreeWidgetItem()

        item.body = snip.find('body').text
        item.title = snip.find('title').text
        item.shortcuts = list(e.text
                              for e in snip.findall('shortcuts/shortcut'))

        title = item.title or snippets.maketitle(
            snippets.parse(item.body).text)
        item.setText(0, title)

        name = snip.get('id')
        name = name if name in builtins else None

        # determine if new, updated or unchanged
        if not name:
            name = titles.get(title)
        item.name = name

        if not name or name not in allnames:
            new.addChild(item)
            items.append(item)
            item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsUserCheckable)
            item.setCheckState(0, Qt.Checked)
        elif name:
            if (item.body != snippets.text(name)
                    or title != snippets.title(name)
                    or (item.shortcuts and item.shortcuts !=
                        [s.toString() for s in model.shortcuts(name) or ()])):
                updated.addChild(item)
                items.append(item)
                item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsUserCheckable)
                item.setCheckState(0, Qt.Checked)
            else:
                unchanged.addChild(item)
                item.setFlags(Qt.ItemIsEnabled)
    # count:
    for i in new, updated, unchanged:
        i.setText(0, i.text(0) + " ({0})".format(i.childCount()))
    for i in new, updated:
        if i.childCount():
            i.setFlags(Qt.ItemIsEnabled | Qt.ItemIsUserCheckable)
            i.setCheckState(0, Qt.Checked)

    def changed(item):
        if item in (new, updated):
            for i in range(item.childCount()):
                c = item.child(i)
                c.setCheckState(0, item.checkState(0))

    tree.itemChanged.connect(changed)

    importShortcuts = QTreeWidgetItem([_("Import Keyboard Shortcuts")])
    if items:
        tree.addTopLevelItem(importShortcuts)
        importShortcuts.setFlags(Qt.ItemIsEnabled | Qt.ItemIsUserCheckable)
        importShortcuts.setCheckState(0, Qt.Checked)
        dlg.setMessage(_("Choose which snippets you want to import:"))
    else:
        dlg.setMessage(_("There are no new or updated snippets in the file."))
        unchanged.setExpanded(True)

    tree.setWhatsThis(
        _("<p>Here the snippets from {filename} are displayed.</p>\n"
          "<p>If there are new or updated snippets, you can select or deselect "
          "them one by one, or all at once, using the checkbox of the group. "
          "Then click OK to import all the selected snippets.</p>\n"
          "<p>Existing, unchanged snippets can't be imported.</p>\n").format(
              filename=os.path.basename(filename)))

    qutil.saveDialogSize(dlg, "snippettool/import/size", QSize(400, 300))
    if not dlg.exec_() or not items:
        return
    ac = model.collection()
    m = model.model()
    with qutil.busyCursor():
        for i in items:
            if i.checkState(0) == Qt.Checked:
                index = m.saveSnippet(i.name, i.body, i.title)
                if i.shortcuts and importShortcuts.checkState(0):
                    shortcuts = list(map(QKeySequence.fromString, i.shortcuts))
                    ac.setShortcuts(m.name(index), shortcuts)
        widget.updateColumnSizes()
示例#16
0
def abs2rel(cursor):
    """Converts pitches from absolute to relative."""
    selection = cursor.hasSelection()
    if selection:
        start = cursor.selectionStart()
        cursor.setPosition(cursor.selectionEnd())
        cursor.setPosition(0, QTextCursor.KeepAnchor)
        source = tokeniter.Source.selection(cursor, True)
    else:
        source = tokeniter.Source.document(cursor, True)
    
    pitches = PitchIterator(source)
    psource = pitches.pitches()
    if selection:
        # consume tokens before the selection, following the language
        t = source.consume(pitches.tokens(), start)
        if t:
            psource = itertools.chain((t,), psource)
    
    # this class dispatches the tokens. we can't use a generator function
    # as that doesn't like to be called again while there is already a body
    # running.
    class gen(object):
        def __iter__(self):
            return self
        
        def __next__(self):
            t = next(psource)
            while isinstance(t, (ly.lex.Space, ly.lex.Comment)):
                t = next(psource)
            if t == '\\relative' and isinstance(t, ly.lex.lilypond.Command):
                relative()
                t = next(psource)
            elif isinstance(t, ly.lex.lilypond.ChordMode):
                consume() # do not change chords
                t = next(psource)
            elif isinstance(t, ly.lex.lilypond.MarkupScore):
                consume()
                t = next(psource)
            return t
        
        next = __next__
            
    tsource = gen()

    def context():
        """Consume tokens till the level drops (we exit a construct)."""
        depth = source.state.depth()
        for t in tsource:
            yield t
            if source.state.depth() < depth:
                return
    
    def consume():
        """Consume tokens from context() returning the last token, if any."""
        t = None
        for t in context():
            pass
        return t
    
    def relative():
        """Consume the whole \relative expression without doing anything. """
        # skip pitch argument
        t = next(tsource)
        if isinstance(t, Pitch):
            t = next(tsource)
        
        while True:
            # eat stuff like \new Staff == "bla" \new Voice \notes etc.
            if isinstance(source.state.parser(), ly.lex.lilypond.ParseTranslator):
                t = consume()
            elif isinstance(t, ly.lex.lilypond.NoteMode):
                t = next(tsource)
            else:
                break
        
        if t in ('{', '<<', '<'):
            consume()
    
    # Do it!
    with qutil.busyCursor():
        with cursortools.Editor() as editor:
            for t in tsource:
                if t in ('{', '<<'):
                    # Ok, parse current expression.
                    c = source.cursor(t, end=0) # insert the \relative command
                    lastPitch = None
                    chord = None
                    for t in context():
                        # skip commands with pitches that do not count
                        if isinstance(t, ly.lex.lilypond.PitchCommand):
                            consume()
                        elif isinstance(t, ly.lex.lilypond.ChordStart):
                            # Handle chord
                            chord = []
                        elif isinstance(t, ly.lex.lilypond.ChordEnd):
                            if chord:
                                lastPitch = chord[0]
                            chord = None
                        elif isinstance(t, Pitch):
                            # Handle pitch
                            if lastPitch is None:
                                lastPitch = Pitch.c1()
                                lastPitch.octave = t.octave
                                if t.note > 3:
                                    lastPitch.octave += 1
                                editor.insertText(c,
                                    "\\relative {0} ".format(
                                        lastPitch.output(pitches.language)))
                            p = t.copy()
                            t.makeRelative(lastPitch)
                            pitches.write(t, editor)
                            lastPitch = p
                            # remember the first pitch of a chord
                            if chord == []:
                                chord.append(p)
示例#17
0
def rel2abs(cursor):
    """Converts pitches from relative to absolute."""
    selection = cursor.hasSelection()
    if selection:
        start = cursor.selectionStart()
        cursor.setPosition(cursor.selectionEnd())
        cursor.setPosition(0, QTextCursor.KeepAnchor)
        source = tokeniter.Source.selection(cursor, True)
    else:
        source = tokeniter.Source.document(cursor, True)
    
    pitches = PitchIterator(source)
    psource = pitches.pitches()
    if selection:
        # consume tokens before the selection, following the language
        t = source.consume(pitches.tokens(), start)
        if t:
            psource = itertools.chain((t,), psource)
    
    # this class dispatches the tokens. we can't use a generator function
    # as that doesn't like to be called again while there is already a body
    # running.
    class gen(object):
        def __iter__(self):
            return self
        
        def __next__(self):
            t = next(psource)
            while isinstance(t, (ly.lex.Space, ly.lex.Comment)):
                t = next(psource)
            if t == '\\relative' and isinstance(t, ly.lex.lilypond.Command):
                relative(t)
                t = next(psource)
            elif isinstance(t, ly.lex.lilypond.MarkupScore):
                consume()
                t = next(psource)
            return t
        
        next = __next__
            
    tsource = gen()
    
    def makeAbsolute(p, lastPitch):
        """Makes pitch absolute (honoring and removing possible octaveCheck)."""
        if p.octaveCheck is not None:
            p.octave = p.octaveCheck
            p.octaveCheck = None
        else:
            p.makeAbsolute(lastPitch)
        pitches.write(p, editor)
    
    def context():
        """Consume tokens till the level drops (we exit a construct)."""
        depth = source.state.depth()
        for t in tsource:
            yield t
            if source.state.depth() < depth:
                return
    
    def consume():
        """Consume tokens from context() returning the last token, if any."""
        t = None
        for t in context():
            pass
        return t
    
    def relative(t):
        c = source.cursor(t)
        lastPitch = None
        
        t = next(tsource)
        if isinstance(t, Pitch):
            lastPitch = t
            t = next(tsource)
        else:
            lastPitch = Pitch.c1()
        
        # remove the \relative <pitch> tokens
        c.setPosition(source.position(t), c.KeepAnchor)
        editor.removeSelectedText(c)
        
        while True:
            # eat stuff like \new Staff == "bla" \new Voice \notes etc.
            if isinstance(source.state.parser(), ly.lex.lilypond.ParseTranslator):
                t = consume()
            elif isinstance(t, (ly.lex.lilypond.ChordMode, ly.lex.lilypond.NoteMode)):
                t = next(tsource)
            else:
                break
        
        # now convert the relative expression to absolute
        if t in ('{', '<<'):
            # Handle full music expression { ... } or << ... >>
            for t in context():
                # skip commands with pitches that do not count
                if isinstance(t, ly.lex.lilypond.PitchCommand):
                    if t == '\\octaveCheck':
                        c = source.cursor(t)
                        for p in getpitches(context()):
                            # remove the \octaveCheck
                            lastPitch = p
                            c.setPosition((p.octaveCursor or p.noteCursor).selectionEnd(), c.KeepAnchor)
                            editor.removeSelectedText(c)
                            break
                    else:
                        consume()
                elif isinstance(t, ly.lex.lilypond.ChordStart):
                    # handle chord
                    chord = [lastPitch]
                    for p in getpitches(context()):
                        makeAbsolute(p, chord[-1])
                        chord.append(p)
                    lastPitch = chord[:2][-1] # same or first
                elif isinstance(t, Pitch):
                    makeAbsolute(t, lastPitch)
                    lastPitch = t
        elif isinstance(t, ly.lex.lilypond.ChordStart):
            # Handle just one chord
            for p in getpitches(context()):
                makeAbsolute(p, lastPitch)
                lastPitch = p
        elif isinstance(t, Pitch):
            # Handle just one pitch
            makeAbsolute(t, lastPitch)
    
    # Do it!
    with qutil.busyCursor():
        with cursortools.Editor() as editor:
            for t in tsource:
                pass
示例#18
0
def abs2rel(cursor, startpitch, first_pitch_absolute):
    """Converts pitches from absolute to relative."""
    with qutil.busyCursor():
        c = lydocument.cursor(cursor, select_all=True)
        ly.pitch.abs2rel.abs2rel(c, startpitch=startpitch, first_pitch_absolute=first_pitch_absolute)
示例#19
0
def rel2abs(cursor, first_pitch_absolute):
    """Converts pitches from relative to absolute."""
    with qutil.busyCursor():
        c = lydocument.cursor(cursor, select_all=True)
        ly.pitch.rel2abs.rel2abs(c, first_pitch_absolute=first_pitch_absolute)
示例#20
0
def load(filename, widget):
    """Loads snippets from a file, displaying them in a list.
    
    The user can then choose:
    - overwrite builtin snippets or not
    - overwrite own snippets with same title or not
    - select and view snippets contents.
    
    """
    try:
        d = ET.parse(filename)
        elements = list(d.findall('snippet'))
        if not elements:
            raise ValueError(_("No snippets found."))
    except Exception as e:
        QMessageBox.critical(widget, app.caption(_("Error")),
        _("Can't read from source:\n\n{url}\n\n{error}").format(
            url=filename, error=e))
        return


    dlg = widgets.dialog.Dialog(widget)
    dlg.setWindowModality(Qt.WindowModal)
    dlg.setWindowTitle(app.caption(_("dialog title", "Import Snippets")))
    tree = QTreeWidget(headerHidden=True, rootIsDecorated=False)
    dlg.setMainWidget(tree)
    userguide.addButton(dlg.buttonBox(), "snippet_import_export")
    
    allnames = frozenset(snippets.names())
    builtins = frozenset(builtin.builtin_snippets)
    titles = dict((snippets.title(n), n) for n in allnames if n not in builtins)
    
    new = QTreeWidgetItem(tree, [_("New Snippets")])
    updated = QTreeWidgetItem(tree, [_("Updated Snippets")])
    unchanged = QTreeWidgetItem(tree, [_("Unchanged Snippets")])
    
    new.setFlags(Qt.ItemIsEnabled)
    updated.setFlags(Qt.ItemIsEnabled)
    unchanged.setFlags(Qt.ItemIsEnabled)
    
    new.setExpanded(True)
    updated.setExpanded(True)
    
    items = []
    for snip in elements:
        item = QTreeWidgetItem()
        
        item.body = snip.find('body').text
        item.title = snip.find('title').text
        item.shortcuts = list(e.text for e in snip.findall('shortcuts/shortcut'))
        
        title = item.title or snippets.maketitle(snippets.parse(item.body).text)
        item.setText(0, title)
        
        name = snip.get('id')
        name = name if name in builtins else None
        
        
        # determine if new, updated or unchanged
        if not name:
            name = titles.get(title)
        item.name = name
        
        if not name or name not in allnames:
            new.addChild(item)
            items.append(item)
            item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsUserCheckable)
            item.setCheckState(0, Qt.Checked)
        elif name:
            if (item.body != snippets.text(name)
                or title != snippets.title(name)
                or (item.shortcuts and item.shortcuts != 
                    [s.toString() for s in model.shortcuts(name) or ()])):
                updated.addChild(item)
                items.append(item)
                item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsUserCheckable)
                item.setCheckState(0, Qt.Checked)
            else:
                unchanged.addChild(item)
                item.setFlags(Qt.ItemIsEnabled)
    # count:
    for i in new, updated, unchanged:
        i.setText(0, i.text(0) + " ({0})".format(i.childCount()))
    for i in new, updated:
        if i.childCount():
            i.setFlags(Qt.ItemIsEnabled | Qt.ItemIsUserCheckable)
            i.setCheckState(0, Qt.Checked)
    
    def changed(item):
        if item in (new, updated):
            for i in range(item.childCount()):
                c = item.child(i)
                c.setCheckState(0, item.checkState(0))
            
    tree.itemChanged.connect(changed)
    
    importShortcuts = QTreeWidgetItem([_("Import Keyboard Shortcuts")])
    if items:
        tree.addTopLevelItem(importShortcuts)
        importShortcuts.setFlags(Qt.ItemIsEnabled | Qt.ItemIsUserCheckable)
        importShortcuts.setCheckState(0, Qt.Checked)
        dlg.setMessage(_("Choose which snippets you want to import:"))
    else:
        dlg.setMessage(_("There are no new or updated snippets in the file."))
        unchanged.setExpanded(True)
    
    tree.setWhatsThis(_(
        "<p>Here the snippets from {filename} are displayed.</p>\n"
        "<p>If there are new or updated snippets, you can select or deselect "
        "them one by one, or all at once, using the checkbox of the group. "
        "Then click OK to import all the selected snippets.</p>\n"
        "<p>Existing, unchanged snippets can't be imported.</p>\n"
        ).format(filename=os.path.basename(filename)))
        
    qutil.saveDialogSize(dlg, "snippettool/import/size", QSize(400, 300))
    if not dlg.exec_() or not items:
        return
    ac = model.collection()
    m = model.model()
    with qutil.busyCursor():
        for i in items:
            if i.checkState(0) == Qt.Checked:
                index = m.saveSnippet(i.name, i.body, i.title)
                if i.shortcuts and importShortcuts.checkState(0):
                    shortcuts = list(map(QKeySequence.fromString, i.shortcuts))
                    ac.setShortcuts(m.name(index), shortcuts)
        widget.updateColumnSizes()
示例#21
0
def abs2rel(cursor):
    """Converts pitches from absolute to relative."""
    with qutil.busyCursor():
        c = lydocument.cursor(cursor, select_all=True)
        ly.pitch.abs2rel.abs2rel(c)
示例#22
0
def abs2rel(cursor):
    """Converts pitches from absolute to relative."""
    with qutil.busyCursor():
        c = lydocument.cursor(cursor, select_all=True)
        ly.pitch.abs2rel.abs2rel(c)
示例#23
0
def rel2abs(cursor):
    """Converts pitches from relative to absolute."""
    with qutil.busyCursor():
        c = lydocument.cursor(cursor, select_all=True)
        ly.pitch.rel2abs.rel2abs(c)