Exemple #1
0
def get_front_pages_list(debug):
    files = []
    def is_frontpage(fn):
            # no subdirs
            if not os.path.isfile(fn):
                return False
            # filter out bzr revert backups
            if fn.endswith("~"):
                return False
            if os.path.split(fn)[1] == u"Makefile":
                return False
            return True
    def add_subdir(subdir):
        if os.path.isdir(subdir):
            v = [os.path.join(subdir, fn) for fn in os.listdir(subdir)]
            return [x for x in v if is_frontpage(x)]
        return []
    files = [fn for fn in glob.glob(os.path.join(u"exercises", "*", "*"))
             if is_frontpage(fn)]
    # The next line is for pre 3.15 compat. We used to place learning
    # trees there.
    files.extend(add_subdir(os.path.join(filesystem.app_data(), u"learningtrees")))
    # This is the recommended place to save front page files
    files.extend([
        fn for fn in glob.glob(os.path.join(filesystem.user_data(), u"exercises", "*", "*"))
        if is_frontpage(fn)])
    if not debug:
        try:
            files.remove(os.path.join(lessonfile.exercises_dir, 'debugtree.txt'))
        except ValueError:
            # The debugtree.txtfile is for some reason missing
            pass
    return files
Exemple #2
0
    def import_module(self, modulename):
        """
        If prefixed with "solfege:"

          user:collection/modulename

        collection is the directory name in
        ~/.solfege/exercises/collection/modulename
        and "user:"******"""
        if modulename.startswith("user:"******"user:"******"exercises",
                                      collection, "modules")
            sys.path.insert(0, module_dir)
            m = __import__(modulename.split("/")[1])
            reload(m)
            del sys.path[0]
        else:
            m = __import__("solfege.exercises.%s" % modulename,
                           fromlist=("solfege.exercises.%s" % modulename, ),
                           level=0)
        return m
Exemple #3
0
    def import_module(self, modulename):
        """
        If prefixed with "solfege:"

          user:collection/modulename

        collection is the directory name in
        ~/.solfege/exercises/collection/modulename
        and "user:"******"""
        if modulename.startswith("user:"******"user:"******"exercises", collection, "modules")
            sys.path.insert(0, module_dir)
            m = __import__(modulename.split("/")[1])
            importlib.reload(m)
            del sys.path[0]
        else:
            try:
                m = __import__("solfege.exercises.%s" % modulename, fromlist=("solfege.exercises.%s" % modulename,), level=0)
            except ImportError as e:
                raise abstract.ExerciseModuleImportError(e)
        return m
Exemple #4
0
def presetup(app_defaults_filename, system_filename, user_filename):
    if not os.path.exists(filesystem.app_data()):
        os.makedirs(filesystem.app_data())
    if not os.path.exists(filesystem.user_data()):
        os.makedirs(filesystem.user_data())
    try:
        cfg.initialise(app_defaults_filename, system_filename, user_filename)
    except UnicodeDecodeError:
        traceback.print_exc()
        print(file=sys.stderr)
        print("\n".join(
            textwrap.wrap(
                "Your %s file is not properly utf8 encoded. Most likely"
                " it is the path to some external program that contain non-ascii"
                " characters. Please edit or delete the file. Or email it to"
                " [email protected], and he will tell you what the problem is." %
                filesystem.rcfile().encode("ascii", "backslashreplace"))),
              file=sys.stderr)
        print(file=sys.stderr)
        sys.exit("I give up (solfege.py)")
    except cfg.CfgParseException as e:
        i18n.setup(".")
        a, b = os.path.split(user_filename)
        renamed_fn = os.path.join(a, "BAD-" + b)
        m = Gtk.MessageDialog(None, Gtk.DialogFlags.MODAL,
                              Gtk.MessageType.ERROR, Gtk.ButtonsType.NONE,
                              _("Parsing %s failed") % filesystem.rcfile())
        m.format_secondary_text(
            str(e) + "\n\n" +
            _("We cannot recover from this, we can rename the corrupt file to %s and then start the program."
              % renamed_fn))
        m.add_buttons("Rename", 10)
        m.add_buttons(Gtk.STOCK_QUIT, 11)
        m.set_default_response(11)
        ret = m.run()
        if ret == 10:
            os.rename(user_filename, renamed_fn)
            m.destroy()
            cfg.initialise(app_defaults_filename, system_filename,
                           user_filename)
        else:
            sys.exit(1)
    # MIGRATION from 2.9.2 to 2.9.3
    if cfg.get_string("app/lc_messages") == 'C (english)':
        cfg.set_string("app/lc_messages", "C")
Exemple #5
0
def presetup(app_defaults_filename, system_filename, user_filename):
    if not os.path.exists(filesystem.app_data()):
        os.makedirs(filesystem.app_data())

    if not os.path.exists(filesystem.user_data()):
        os.makedirs(filesystem.user_data())
    try:
        cfg.initialise(app_defaults_filename, system_filename, user_filename)
    except UnicodeDecodeError, e:
        traceback.print_exc()
        print >> sys.stderr
        print >> sys.stderr, "\n".join(textwrap.wrap(
              "Your %s file is not properly utf8 encoded. Most likely"
              " it is the path to some external program that contain non-ascii"
              " characters. Please edit or delete the file. Or email it to"
              " [email protected], and he will tell you what the problem is." % filesystem.rcfile().encode("ascii", "backslashreplace")))
        print >> sys.stderr
        sys.exit("I give up (solfege.py)")
Exemple #6
0
def presetup(app_defaults_filename, system_filename, user_filename):
    if not os.path.exists(filesystem.app_data()):
        os.makedirs(filesystem.app_data())
    if not os.path.exists(filesystem.user_data()):
        os.makedirs(filesystem.user_data())
    try:
        cfg.initialise(app_defaults_filename, system_filename, user_filename)
    except UnicodeDecodeError, e:
        traceback.print_exc()
        print >> sys.stderr
        print >> sys.stderr, "\n".join(
            textwrap.wrap(
                "Your %s file is not properly utf8 encoded. Most likely"
                " it is the path to some external program that contain non-ascii"
                " characters. Please edit or delete the file. Or email it to"
                " [email protected], and he will tell you what the problem is." %
                filesystem.rcfile().encode("ascii", "backslashreplace")))
        print >> sys.stderr
        sys.exit("I give up (solfege.py)")
Exemple #7
0
def presetup(app_defaults_filename, system_filename, user_filename):
    if not os.path.exists(filesystem.app_data()):
        os.makedirs(filesystem.app_data())
    if not os.path.exists(filesystem.user_data()):
        os.makedirs(filesystem.user_data())
    try:
        cfg.initialise(app_defaults_filename, system_filename, user_filename)
    except UnicodeDecodeError:
        traceback.print_exc()
        print(file=sys.stderr)
        print("\n".join(textwrap.wrap(
              "Your %s file is not properly utf8 encoded. Most likely"
              " it is the path to some external program that contain non-ascii"
              " characters. Please edit or delete the file. Or email it to"
              " [email protected], and he will tell you what the problem is." % filesystem.rcfile().encode("ascii", "backslashreplace"))), file=sys.stderr)
        print(file=sys.stderr)
        sys.exit("I give up (solfege.py)")
    except cfg.CfgParseException as e:
        i18n.setup(".")
        a, b = os.path.split(user_filename)
        renamed_fn = os.path.join(a, "BAD-" + b)
        m = Gtk.MessageDialog(None, Gtk.DialogFlags.MODAL, Gtk.MessageType.ERROR,
                Gtk.ButtonsType.NONE,
                _("Parsing %s failed") % filesystem.rcfile())
        m.format_secondary_text(str(e) + "\n\n" + _("We cannot recover from this, we can rename the corrupt file to %s and then start the program." % renamed_fn))
        m.add_buttons("Rename", 10)
        m.add_buttons(Gtk.STOCK_QUIT, 11)
        m.set_default_response(11)
        ret = m.run()
        if ret == 10:
            os.rename(user_filename, renamed_fn)
            m.destroy()
            cfg.initialise(app_defaults_filename, system_filename, user_filename)
        else:
            sys.exit(1)
    # MIGRATION from 2.9.2 to 2.9.3
    if cfg.get_string("app/lc_messages") == 'C (english)':
        cfg.set_string("app/lc_messages", "C")
Exemple #8
0
 def on_add_link(self, btn):
     if editor_of(self).m_filename:
         open_dir = os.path.split(editor_of(self).m_filename)[0]
     else:
         open_dir = filesystem.user_data()
     dlg = SelectLessonFileDialog(editor_of(self))
     dlg.set_current_folder(open_dir)
     while 1:
         ret = dlg.run()
         if ret in (gtk.RESPONSE_REJECT, gtk.RESPONSE_DELETE_EVENT, gtk.RESPONSE_CANCEL):
             break
         else:
             assert ret == gtk.RESPONSE_OK
             self._add_filenames(dlg.get_filenames())
             break
     dlg.destroy()
Exemple #9
0
 def on_add_link(self, btn):
     if editor_of(self).m_filename:
         open_dir = os.path.split(editor_of(self).m_filename)[0]
     else:
         open_dir = filesystem.user_data()
     dlg = SelectLessonFileDialog(editor_of(self))
     dlg.set_current_folder(open_dir)
     while 1:
         ret = dlg.run()
         if ret in (gtk.RESPONSE_REJECT, gtk.RESPONSE_DELETE_EVENT, gtk.RESPONSE_CANCEL):
             break
         else:
             assert ret == gtk.RESPONSE_OK
             self._add_filenames(dlg.get_filenames())
             break
     dlg.destroy()
Exemple #10
0
 def display_user_exercises(self, w):
     self.set_title("GNU Solfege - " + _("User Exercises"))
     if not self.show_view('userview'):
         self.add_view(UserView(""), 'userview')
         self.get_view().g_searchentry.grab_focus()
         self.get_view().on_search()
     else:
         self.show_view('userview')
     col = frontpage.Column()
     page = frontpage.Page(_('User Exercises'), col)
     curdir = None
     linklist = None
     d = os.path.join(filesystem.user_data(), "exercises/user/lesson-files")
     linklist = frontpage.LinkList(d)
     col.append(linklist)
     for filename in lessonfile.infocache.iter_user_files(only_user_collection=True):
         linklist.append(filename)
     self.get_view().display_data(page)
Exemple #11
0
 def display_user_exercises(self, w):
     self.set_title("GNU Solfege - " + _("User Exercises"))
     if not self.show_view('userview'):
         self.add_view(UserView(""), 'userview')
         self.get_view().g_searchentry.grab_focus()
         self.get_view().on_search()
     else:
         self.show_view('userview')
     col = frontpage.Column()
     page = frontpage.Page(_('User Exercises'), col)
     curdir = None
     linklist = None
     d = os.path.join(filesystem.user_data(), "exercises/user/lesson-files")
     linklist = frontpage.LinkList(d)
     col.append(linklist)
     for filename in lessonfile.infocache.iter_user_files(
             only_user_collection=True):
         linklist.append(filename)
     self.get_view().display_data(page)
Exemple #12
0
def get_front_pages_list(debug):
    files = []

    def is_frontpage(fn):
        # no subdirs
        if not os.path.isfile(fn):
            return False
        # filter out bzr revert backups
        if fn.endswith("~"):
            return False
        if os.path.split(fn)[1] == "Makefile":
            return False
        return True

    def add_subdir(subdir):
        if os.path.isdir(subdir):
            v = [os.path.join(subdir, fn) for fn in os.listdir(subdir)]
            return [x for x in v if is_frontpage(x)]
        return []

    files = [
        fn for fn in glob.glob(os.path.join("exercises", "*", "*"))
        if is_frontpage(fn)
    ]
    # The next line is for pre 3.15 compat. We used to place learning
    # trees there.
    files.extend(
        add_subdir(os.path.join(filesystem.app_data(), "learningtrees")))
    # This is the recommended place to save front page files
    files.extend([
        fn for fn in glob.glob(
            os.path.join(filesystem.user_data(), "exercises", "*", "*"))
        if is_frontpage(fn)
    ])
    if not debug:
        try:
            files.remove(
                os.path.join(lessonfile.exercises_dir, 'debugtree.txt'))
        except ValueError:
            # The debugtree.txtfile is for some reason missing
            pass
    return files
class PractiseSheetDialog(Gtk.Window, gu.EditorDialogBase,
                          lessonfilegui.ExercisesMenuAddIn):
    """
    The definition of which lesson files to create questions from are
    stored in the list PractiseSheetDialog.m_sections. Each item is a
    dict filled with data. See PractiseSheet._add_common for details.

    When an exercise is selected from the menu, on_select_exercise(...)
    is called, and submethods from this will create a set of questions,
    and store the generated questions in m_sections[-1]['questions]
    and the definition (filename, number of questions to generate etc) in
    other items in the dict in PractiseSheetDialog.m_sections.

    When you change the configuration of the exercises, Solfege will only
    generate new random questions if the config changes require new
    questions, for example when question type changes.

    on_create_sheet is called to create the .tex or .html files. Calling
    is several times in a row will generate the same test.

    The file we save from "Ear Training Test Printout" is a combination
    of two things:
        1. The definitons of which exericses to create questions from and
           how many many questions to generate.
        2. The generated questions. So loading the file will let you
           click "Create Sheet" and have the program create the files to
           print out, and you will get the same test as last time you
           did so.

    When you add an exercise, the translated exercise title is used. The
    translated title is stored in the saved file, so it will not change
    language if you restart Solfege in a different locale.
    """
    current_fileformat_version = "2.0"
    STORE_TITLE = 0
    STORE_FILENAME = 1
    ok_music_types = (lessonfile.Chord, lessonfile.Voice, lessonfile.Rvoice)
    ok_modules = ('idbyname', 'harmonicinterval', 'melodicinterval')
    savedir = os.path.join(filesystem.user_data(), "eartrainingtests")

    def __init__(self, filename=None):
        logging.debug("PractiseSheetDialog.__init__")
        Gtk.Window.__init__(self)
        gu.EditorDialogBase.__init__(self, filename)
        self.m_changed = False
        self.set_title(self._get_a_filename())
        self.m_exported_to = None
        self.set_default_size(800, 300)
        # self.vbox contains the toolbar and the vbox with the rest of the
        # window contents
        self.vbox = Gtk.VBox()
        self.add(self.vbox)
        self.setup_toolbar()
        vbox = Gtk.VBox()
        vbox.set_spacing(6)
        self.vbox.pack_start(vbox, True, True, 0)
        vbox.set_border_width(8)
        hbox = Gtk.HBox()
        vbox.pack_start(hbox, False, False, 0)
        hbox.pack_start(Gtk.Label(_("Output format:")), False, False, 0)
        self.g_latex_radio = Gtk.RadioButton.new_with_label(None, "LaTeX")
        hbox.pack_start(self.g_latex_radio, False, False, 0)
        self.g_html_radio = Gtk.RadioButton.new_with_label_from_widget(
            self.g_latex_radio, "HTML")
        hbox.pack_start(self.g_html_radio, False, False, 0)
        #
        hbox = Gtk.HBox()
        hbox.set_spacing(6)
        vbox.pack_start(hbox, False, False, 0)
        hbox.pack_start(Gtk.Label(_("Title:")), False, False, 0)
        self.g_title = Gtk.Entry()
        hbox.pack_start(self.g_title, False, False, 0)
        #
        self.m_sections = []
        self.g_liststore = Gtk.ListStore(
            GObject.TYPE_STRING,  # lesson-file title
            GObject.TYPE_STRING,  # lessonfile filename, hidden column
        )
        self.g_treeview = Gtk.TreeView(self.g_liststore)
        self.g_treeview.set_size_request(400, 100)
        self.g_treeview.set_headers_visible(False)
        self.g_treeview.connect('cursor-changed', self.on_tv_cursor_changed)
        self.g_treeview.connect('unselect-all', self.on_tv_unselect_all)
        scrolled_window = Gtk.ScrolledWindow()
        scrolled_window.add(self.g_treeview)
        scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC,
                                   Gtk.PolicyType.AUTOMATIC)
        vbox.pack_start(scrolled_window, True, True, 0)
        #
        renderer = Gtk.CellRendererText()

        def mark_invalid(column,
                         cell_renderer,
                         liststore,
                         iter,
                         user_data=None):
            filename = liststore.get(iter, self.STORE_FILENAME)[0]
            if not filename:
                cell_renderer.props.markup = '<span background="red">%s</span>' % liststore.get_value(
                    iter, self.STORE_TITLE)

        column = Gtk.TreeViewColumn(None, renderer, text=self.STORE_TITLE)
        column.set_cell_data_func(renderer, mark_invalid)
        self.g_treeview.append_column(column)
        column = Gtk.TreeViewColumn(None, renderer, text=self.STORE_FILENAME)
        self.g_treeview.append_column(column)
        #
        sizegroup = Gtk.SizeGroup(Gtk.SizeGroupMode.HORIZONTAL)
        self.g_lbox = Gtk.VBox()
        self.g_lbox.set_sensitive(False)
        vbox.pack_start(self.g_lbox, True, True, 0)
        self.g_lesson_title = Gtk.Entry()
        self.g_lbox.pack_start(
            gu.hig_label_widget(_("Section title:"), self.g_lesson_title,
                                sizegroup), False, False, 0)
        self.g_lesson_title_event_handle =\
            self.g_lesson_title.connect('changed', self.on_lesson_title_changed)
        #
        self.g_qtype = Gtk.ComboBoxText()
        # We should not change the order of music types, as the index
        # of the different types are used in SolfegeApp.on_create_sheet
        self.g_qtype.append_text(_("Name the music"))
        self.g_qtype.append_text(_("Write the music, first tone given"))
        self.g_qtype_event_handler = \
            self.g_qtype.connect('changed', self.on_qtype_changed)
        self.g_lbox.pack_start(
            gu.hig_label_widget(_("Type of question:"), self.g_qtype,
                                sizegroup), False, False, 0)
        #
        self.g_intervals = IntervalCheckBox()
        self.g_intervals_box = gu.hig_label_widget(_("Intervals:"),
                                                   self.g_intervals, sizegroup)
        self.g_intervals_event_handler = \
            self.g_intervals.connect('value-changed', self.on_intervals_changed)
        self.g_lbox.pack_start(self.g_intervals_box, False, False, 0)
        #
        self.g_line_len = Gtk.SpinButton(
            adjustment=Gtk.Adjustment(0, 1, 10, 1, 10))
        self.g_line_len_event_handler = \
            self.g_line_len.connect('value-changed', self.on_spin_changed, 'line_len')
        self.g_lbox.pack_start(
            gu.hig_label_widget(_("Questions per line:"), self.g_line_len,
                                sizegroup), False, False, 0)
        #
        self.g_count = Gtk.SpinButton(
            adjustment=Gtk.Adjustment(0, 1, 1000, 1, 10))
        self.g_count_event_handler = \
            self.g_count.connect('value-changed', self.on_spin_changed, 'count')
        self.g_lbox.pack_start(
            gu.hig_label_widget(_("Number of questions:"), self.g_count,
                                sizegroup), False, False, 0)
        if filename:
            self.load_file(filename)
        self.add_to_instance_dict()

        # The popupmenu shown to add exercises
        self.g_emenu = self.create_learning_tree_menu()

    def view_lesson(self, idx):
        self.g_qtype.handler_block(self.g_qtype_event_handler)
        self.g_intervals.handler_block(self.g_intervals_event_handler)
        self.g_line_len.handler_block(self.g_line_len_event_handler)
        self.g_count.handler_block(self.g_count_event_handler)
        self.g_lesson_title.handler_block(self.g_lesson_title_event_handle)

        self.g_lbox.set_sensitive(idx is not None)
        if idx is None:
            self.g_lesson_title.set_text("")
            self.g_count.set_value(1)
            self.g_line_len.set_value(1)
        else:
            try:
                module = lessonfile.infocache.get(
                    self.m_sections[idx]['filename'], 'module')
            except lessonfile.infocache.InfoCacheException:
                module = None

            if module == 'harmonicinterval':
                self.g_intervals_box.show()
                self.g_intervals.set_value(self.m_sections[idx]['intervals'])
            else:
                self.g_intervals_box.hide()
            self.g_lesson_title.set_text(_tr(self.m_sections[idx]['title']))
            self.g_count.set_value(self.m_sections[idx]['count'])
            self.g_line_len.set_value(self.m_sections[idx]['line_len'])
            self.g_qtype.set_active(self.m_sections[idx]['qtype'])
        self.g_qtype.handler_unblock(self.g_qtype_event_handler)
        self.g_intervals.handler_unblock(self.g_intervals_event_handler)
        self.g_line_len.handler_unblock(self.g_line_len_event_handler)
        self.g_count.handler_unblock(self.g_count_event_handler)
        self.g_lesson_title.handler_unblock(self.g_lesson_title_event_handle)

    def on_add_lesson_clicked(self, button):
        self.g_emenu.popup(None, None, None, None, 1, 0)

    def on_remove_lesson_clicked(self, button):
        path, column = self.g_treeview.get_cursor()
        if path is not None:
            iter = self.g_liststore.get_iter(path)
            self.g_liststore.remove(iter)
            del self.m_sections[path[0]]

    def on_randomize(self, widget):
        for section in self.m_sections:
            section['questions'] = []
        self.generate_questions()

    def on_create_sheet(self, widget):
        self.generate_questions()
        if not self.m_exported_to:
            self.m_exported_to = self.select_empty_directory(
                _("Select where to export the files"))
        if not self.m_exported_to:
            return
        if self.g_html_radio.get_active():
            writer = HtmlSheetWriter(self.g_title.get_text())
        else:
            writer = LatexSheetWriter(self.g_title.get_text())
        for idx, sect in enumerate(self.m_sections):
            new_section = writer.new_section(_(sect['title']),
                                             sect['line_len'])
            for question_dict in sect['questions']:
                new_section.append(question_dict)
        ww = gu.LogWindow(self)
        try:
            writer.write_to(self.m_exported_to, ww)
        except osutils.BinaryForProgramException as e:
            solfege.win.display_error_message2(e.msg1, e.msg2)
        ww.run_finished()

    def on_intervals_changed(self, widget, value):
        self.m_changed = True
        idx = self.g_treeview.get_cursor()[0][0]
        self._delete_questions(idx)
        self.m_sections[idx]['intervals'] = value

    def on_lesson_title_changed(self, widget):
        self.m_changed = True
        path = self.g_treeview.get_cursor()[0]
        iter = self.g_liststore.get_iter(path)
        self.m_sections[path[0]]['title'] = widget.get_text()
        self.g_liststore.set(iter, self.STORE_TITLE, widget.get_text())

    def on_spin_changed(self, widget, varname):
        self.m_changed = True
        idx = self.g_treeview.get_cursor()[0][0]
        if varname == 'count':
            if len(self.m_sections[idx]
                   ['questions']) > widget.get_value_as_int():
                self.m_sections[idx]['questions'] = \
                    self.m_sections[idx]['questions'][:widget.get_value_as_int()]
        self.m_sections[idx][varname] = widget.get_value_as_int()

    def on_select_exercise(self, menuitem, filename):
        module = lessonfile.infocache.get(filename, 'module')
        if module not in self.ok_modules:
            print("only some modules work:", self.ok_modules)
            return
        p = lessonfile.LessonfileCommon()
        parsetree.Identifier.check_ns = False
        p.parse_file(lessonfile.uri_expand(filename))
        parsetree.Identifier.check_ns = True
        if module == 'idbyname':
            self._add_idbyname_lesson(p, filename)
        elif module == 'harmonicinterval':
            self._add_harmonicinterval_lesson(p, filename)
        elif module == 'melodicinterval':
            self._add_melodicinterval_lesson(p, filename)
        self.g_treeview.set_cursor((len(self.m_sections) - 1, ))

    def _add_idbyname_lesson(self, p, filename):
        """
        p is a lessonfile.LessonfileCommon parser that has parsed the file.
        """
        not_ok = len([
            q.music for q in p.m_questions
            if not isinstance(q.music, self.ok_music_types)
        ])
        ok = len([
            q.music for q in p.m_questions
            if isinstance(q.music, self.ok_music_types)
        ])
        if not_ok > 0:
            if ok > 0:
                do_add = gu.dialog_yesno(
                    _("Not all music types are supported. This file contain %(ok)i supported questions and %(not_ok)i that are not supported. The unsupported questions will be ignored. Add any way?"
                      % locals()), self)
            else:
                gu.dialog_ok(
                    _("Could not add the lesson file. It has no questions with a music object with supported music type."
                      ), self)
                do_add = False
        else:
            do_add = True
        if do_add:
            self.m_changed = True
            self._add_common(filename)

    def _add_melodicinterval_lesson(self, p, filename):
        self._add_common(filename)
        self.m_sections[-1]['intervals'] = p.header.ask_for_intervals_0

    def _add_harmonicinterval_lesson(self, p, filename):
        self._add_common(filename)
        self.m_sections[-1]['intervals'] = p.header.intervals

    def _add_common(self, filename):
        self.m_changed = True
        self.m_sections.append({
            'filename':
            filename,
            'title':
            lessonfile.infocache.get(filename, 'title'),
            'count':
            6,
            'qtype':
            0,
            'line_len':
            3,
            'questions': [],
        })
        self.g_liststore.append((_(self.m_sections[-1]['title']), filename))

    def generate_questions(self):
        """
        Delete any extra questions and then add new random questions
        if some sections have too few questions generated.
        """
        parsetree.Identifier.check_ns = False
        for section in self.m_sections:
            # Remove trailing questions if we have too many questions
            del self.m_sections[section['count']:]
            # Then add questions if we have too few
            count = section['count'] - len(section['questions'])
            if count:
                for question_dict in solfege.app.sheet_gen_questions(
                        count, section):
                    section['questions'].append(question_dict)
        parsetree.Identifier.check_ns = True

    def _delete_questions(self, idx):
        """
        Delete the generated questions for exercise idx in self.m_sections.
        """
        self.m_sections[idx]['questions'] = []

    def on_show_help(self, widget):
        solfege.app.handle_href("ear-training-test-printout-editor.html")

    def on_tv_cursor_changed(self, treeview, *v):
        if self.g_treeview.get_cursor()[0] is None:
            return
        self.view_lesson(self.g_treeview.get_cursor()[0][0])

    def on_tv_unselect_all(self, treeview, *v):
        self.view_lesson(None)

    def on_qtype_changed(self, widget):
        self.m_changed = True
        idx = self.g_treeview.get_cursor()[0][0]
        self._delete_questions(idx)
        self.m_sections[idx]['qtype'] = widget.get_active()

    def load_file(self, filename):
        tree = et.ElementTree()
        tree.parse(filename)
        if tree.getroot().get("fileformat_version") != "2.0":
            e = Exception("PractiseSheet")
            e.msg1 = _("Cannot read old file format")
            e.msg2 = _(
                "To convert the file to the new file format, you must open and save it in an older version of Solfege. Versions from 3.16.0 to 3.20.4 should do the job."
            )
            raise e
        # section.find('text').text will be none if it was not set
        # when saved, and we need a string.
        if tree.find("title").text:
            self.g_title.set_text(tree.find("title").text)
        else:
            self.g_title.set_text("")
        if tree.find('output_format').text == 'latex':
            self.g_latex_radio.set_active(True)
        else:
            self.g_latex_radio.set_active(False)
        self.g_liststore.clear()
        self.m_sections = []
        for section in tree.findall("section"):
            d = {}
            lessonfilename = section.find('filename').text
            # It seems that the elementtree parser will return str if
            # there are no non-ascii chars in the filename. So lets
            # make unicode of it if it is str
            if isinstance(lessonfilename, str):
                lessonfilename = str(lessonfilename)
            d['filename'] = lessonfilename
            # section.find('text').text will be none if it was not set
            # when saved, and d['title'] need to be a string
            if section.find('title').text:
                d['title'] = section.find('title').text
            else:
                d['title'] = ""
            if section.find('intervals') is not None:
                d['intervals'] = eval(section.find('intervals').text, {}, {})
            else:
                if lessonfile.infocache.get(lessonfilename,
                                            'module') == 'harmonicinterval':
                    d['intervals'] = []
                    gu.dialog_ok(
                        "FIXME: «%s» was saved with a buggy version of solfege, so you must set the intervals by selecting the file in the dialog and clicking the intervals to be asked. Sorry!"
                        % lessonfilename, self)
            d['count'] = int(section.find('count').text)
            d['line_len'] = int(section.find('line_len').text)
            d['qtype'] = int(section.find('qtype').text)
            d['questions'] = []
            for question in section.findall("question"):
                q = {'question': {}, 'answer': {}}
                q['question']['music'] = question.find("students").find(
                    "music").text
                q['question']['name'] = question.find("students").find(
                    "name").text
                q['answer']['music'] = question.find("teachers").find(
                    "music").text
                q['answer']['name'] = question.find("teachers").find(
                    "name").text
                d['questions'].append(q)
            self.m_sections.append(d)
            try:
                # Check that the filename is valid
                lessonfile.infocache.get(lessonfilename, 'title')
                self.g_liststore.append((d['title'], lessonfilename))
            except lessonfile.infocache.InfoCacheException as e:
                self.g_liststore.append((_("«%s» not found") % str(e), None))
            self.g_treeview.set_cursor((0, ))
        self.m_filename = filename

    def save(self):
        assert self.m_filename
        if self.g_latex_radio.get_active():
            format = "latex"
        else:
            format = "html"
        doc = et.Element("sheet",
                         fileformat_version=self.current_fileformat_version,
                         creator="GNU Solfege",
                         app_version=buildinfo.VERSION_STRING)
        doc.append(et.Comment(""))
        et.SubElement(doc, "title").text = self.g_title.get_text()
        et.SubElement(doc, "output_format").text = format
        for sect in self.m_sections:
            s = et.Element("section")
            et.SubElement(s, "title").text = sect['title']
            et.SubElement(s, "filename").text = sect['filename']
            # only the harmonic and melodic intervals have this variable:
            if 'intervals' in sect:
                et.SubElement(s, 'intervals').text = "%s" % sect['intervals']
            # We do save 'count' because this variable say how many questions
            # we want to generate, and len(questions) will just say how many
            # are generated now.
            et.SubElement(s, "count").text = "%i" % sect['count']
            et.SubElement(s, "line_len").text = "%i" % sect['line_len']
            et.SubElement(s, "qtype").text = "%i" % sect['qtype']
            for qdict in sect['questions']:
                q = et.SubElement(s, "question")
                t = et.SubElement(q, "teachers")
                et.SubElement(t, "name").text = qdict['answer']['name']
                et.SubElement(t, "music").text = qdict['answer']['music']
                t = et.SubElement(q, "students")
                et.SubElement(t, "name").text = qdict['question']['name']
                et.SubElement(t, "music").text = qdict['question']['music']
            doc.append(s)
        tree = et.ElementTree(doc)
        f = open(self.m_filename, 'w')
        print('<?xml version="1.0" encoding="utf-8"?>', file=f)
        tree.write(f, encoding="unicode")
        f.close()
        self.m_changed = False

    def setup_toolbar(self):
        self.g_toolbar = Gtk.Toolbar()
        self.g_actiongroup.add_actions([
            ('Add', Gtk.STOCK_ADD, None, None, None,
             self.on_add_lesson_clicked),
            ('Remove', Gtk.STOCK_REMOVE, None, None, None,
             self.on_remove_lesson_clicked),
            ('Create', Gtk.STOCK_EXECUTE, _("Create Sheet"), None, None,
             self.on_create_sheet),
            ('Randomize', None, _("Randomize"), None, None, self.on_randomize),
        ])
        self.g_ui_manager.insert_action_group(self.g_actiongroup, 0)
        uixml = """
        <ui>
         <toolbar name='ExportToolbar'>
          <toolitem action='Add'/>
          <toolitem action='Remove'/>
          <toolitem action='New'/>
          <toolitem action='Open'/>
          <toolitem action='Save'/>
          <toolitem action='SaveAs'/>
          <toolitem action='Create'/>
          <toolitem action='Randomize'/>
          <toolitem action='Close'/>
          <toolitem action='Help'/>
         </toolbar>
         <accelerator action='Close'/>
         <accelerator action='New'/>
         <accelerator action='Open'/>
         <accelerator action='Save'/>
        </ui>
        """
        self.g_ui_manager.add_ui_from_string(uixml)
        self.vbox.pack_start(self.g_ui_manager.get_widget("/ExportToolbar"),
                             False, False, 0)
        self.g_ui_manager.get_widget("/ExportToolbar").set_style(
            Gtk.ToolbarStyle.BOTH)
Exemple #14
0
class TrainingSetDialog(gtk.Window, gu.EditorDialogBase,
                        lessonfilegui.ExercisesMenuAddIn):
    fileformat_version = 2
    STORE_FILENAME = 0
    STORE_TITLE = 1
    STORE_COUNT = 2
    STORE_REPEAT = 3
    STORE_DELAY = 4
    savedir = os.path.join(filesystem.user_data(), "trainingsets")

    def __init__(self, filename=None):
        gtk.Window.__init__(self)
        gu.EditorDialogBase.__init__(self, filename)
        self.set_default_size(800, 300)
        # This VBox will have 2 parts.
        # 1: the tool bar
        # 2: another container widget that has the content of a file
        self.g_vbox = gtk.VBox()
        self.add(self.g_vbox)
        self.setup_toolbar()
        #
        self.g_settings_box = gtk.VBox()
        self.g_settings_box.set_border_width(6)
        self.g_vbox.pack_start(self.g_settings_box, False)
        self.g_output = {}
        self.g_output['midi'] = gtk.RadioButton(None, _("MIDI"))
        self.g_output['wav'] = gtk.RadioButton(self.g_output['midi'], _("WAV"))
        self.g_output['mp3'] = gtk.RadioButton(self.g_output['midi'], _("MP3"))
        self.g_output['ogg'] = gtk.RadioButton(self.g_output['midi'], _("OGG"))
        hbox = gtk.HBox()
        hbox.set_spacing(6)
        self.g_settings_box.pack_start(hbox)
        hbox.pack_start(gtk.Label(_("Preferred output format:")), False)
        for s in ('midi', 'wav', 'mp3', 'ogg'):
            hbox.pack_start(self.g_output[s], False)
        ####
        hbox.pack_start(gtk.VSeparator(), False)
        self.g_named_tracks = gtk.CheckButton(_("Name files by questions"))
        hbox.pack_start(self.g_named_tracks, False)
        self.g_liststore = gtk.ListStore(
            gobject.TYPE_STRING,  # filename
            gobject.TYPE_STRING,  # visible exercise name
            gobject.TYPE_INT,  # count
            gobject.TYPE_INT,  # repeat
            gobject.TYPE_INT)  # delay
        self.g_treeview = gtk.TreeView(self.g_liststore)
        self.g_treeview.set_size_request(400, 100)
        self.g_treeview.connect('cursor-changed',
                                self.on_treeview_cursor_changed)

        renderer = gtk.CellRendererText()
        column = gtk.TreeViewColumn(_("Title"),
                                    renderer,
                                    text=self.STORE_TITLE,
                                    markup=1)

        def mark_invalid(column,
                         cell_renderer,
                         tree_model,
                         iter,
                         user_data=None):
            fn = tree_model.get_value(iter, self.STORE_FILENAME)
            if not fn:
                cell_renderer.props.markup = '<span background="red">%s</span>' % tree_model.get_value(
                    iter, self.STORE_TITLE)

        column.set_cell_data_func(renderer, mark_invalid)
        self.g_treeview.append_column(column)
        renderer = gtk.CellRendererText()
        renderer.set_property('editable', True)
        renderer.connect('edited', self.on_count_edited)
        column = gtk.TreeViewColumn(_("Count"),
                                    renderer,
                                    text=self.STORE_COUNT)
        self.g_treeview.append_column(column)
        renderer = gtk.CellRendererText()
        renderer.set_property('editable', True)
        renderer.connect('edited', self.on_repeat_edited)
        column = gtk.TreeViewColumn(_("_Repeat").replace("_", ""),
                                    renderer,
                                    text=self.STORE_REPEAT)
        self.g_treeview.append_column(column)
        renderer = gtk.CellRendererText()
        renderer.set_property('editable', True)
        renderer.connect('edited', self.on_delay_edited)
        column = gtk.TreeViewColumn(_("Delay"),
                                    renderer,
                                    text=self.STORE_DELAY)
        self.g_treeview.append_column(column)
        self.g_vbox.pack_start(self.g_treeview)
        if filename:
            self.load_file(filename)
        else:
            self.init_empty_file()
        self.show_all()
        self.add_to_instance_dict()

    def on_treeview_cursor_changed(self, treeview):
        self.g_ui_manager.get_widget("/ExportToolbar/Remove").set_sensitive(
            True)

    def on_count_edited(self, renderer, path, text):
        self._edit_col(2, path, text)

    def on_repeat_edited(self, renderer, path, text):
        self._edit_col(3, path, text)

    def on_delay_edited(self, renderer, path, text):
        self._edit_col(4, path, text)

    def _edit_col(self, col_num, path, text):
        """
        This method does the real work when on_XXXX_edited is called.
        """
        try:
            i = int(text)
        except ValueError:
            i = None
        if i is not None:
            iter = self.g_liststore.get_iter_from_string(path)
            self.g_liststore.set_value(iter, col_num, i)

    def on_remove_lesson_clicked(self, *w):
        path, column = self.g_treeview.get_cursor()
        assert path
        iter = self.g_liststore.get_iter(path)
        if iter:
            if self.g_liststore.remove(iter):
                self.g_treeview.set_cursor(self.g_liststore.get_path(iter))
            elif path[0] > 0:
                self.g_treeview.set_cursor((path[0] - 1, ))
            else:
                self.g_ui_manager.get_widget(
                    "/ExportToolbar/Remove").set_sensitive(False)

    def on_add_lesson_clicked(self, button):
        try:
            self.menu
        except AttributeError:
            self.menu = self.create_learning_tree_menu()
        self.menu.popup(None, None, None, 1, 0)

    def on_select_exercise(self, item, filename):
        """
        This method is called when the user has selected an exercise to
        add.
        """
        module = lessonfile.infocache.get(filename, 'module')
        if module not in ('harmonicinterval', 'melodicinterval', 'idbyname'):
            print "Only harmonicinterval, melodicinterval and idbyname module exercises are working now. Ignoring..."
            return
        if module == 'idbyname':
            p = lessonfile.LessonfileCommon()
            p.parse_file(filename)
            if not [
                    q for q in p.m_questions
                    if isinstance(q.music, lessonfile.MpdParsable)
            ]:
                gu.dialog_ok(
                    _("This lesson file cannot be exported because some of the music in the file are not parsable by the mpd module."
                      ))
                return
        self.m_changed = True
        self.g_liststore.append(
            (filename, self.get_lessonfile_title(filename), 3, 3, 4))

    def get_lessonfile_title(self, filename):
        """
        Return a string we use the name the lesson file in the GUI.
        """
        return "%s (%s)" % (_(lessonfile.infocache.get(filename,
                                                       'title')), filename)

    def init_empty_file(self):
        self.m_changed = False
        self.set_title(self._get_a_filename())
        self.g_ui_manager.get_widget("/ExportToolbar/Remove").set_sensitive(
            False)

    def setup_toolbar(self):
        self.g_actiongroup.add_actions([
            ('Export', gtk.STOCK_EXECUTE, _("Export"), None, None,
             self.on_export),
            ('Add', gtk.STOCK_ADD, None, None, None,
             self.on_add_lesson_clicked),
            ('Remove', gtk.STOCK_REMOVE, None, None, None,
             self.on_remove_lesson_clicked),
        ])
        self.g_ui_manager.insert_action_group(self.g_actiongroup, 0)
        uixml = """
        <ui>
         <toolbar name='ExportToolbar'>
          <toolitem action='Add'/>
          <toolitem action='Remove'/>
          <toolitem action='New'/>
          <toolitem action='Open'/>
          <toolitem action='Save'/>
          <toolitem action='SaveAs'/>
          <toolitem action='Export'/>
          <toolitem action='Close'/>
          <toolitem action='Help'/>
         </toolbar>
         <accelerator action='Close'/>
         <accelerator action='New'/>
         <accelerator action='Open'/>
         <accelerator action='Save'/>
        </ui>
        """
        self.g_ui_manager.add_ui_from_string(uixml)
        self.g_vbox.pack_start(self.g_ui_manager.get_widget("/ExportToolbar"),
                               False)
        self.g_ui_manager.get_widget("/ExportToolbar").set_style(
            gtk.TOOLBAR_BOTH)

    def on_show_help(self, widget):
        solfege.app.handle_href("trainingset-editor.html")

    def load_file(self, filename):
        p = Dataparser()
        p.parse_file(filename)
        mod = lfmod.parse_tree_interpreter(p.tree, {'yes': True, 'no': False})
        s = mod.m_globals.setdefault('output_format', 'midi')
        s = s.lower()
        if s in self.g_output:
            self.g_output[s].set_active(True)
        else:
            # MIDI is the default format
            self.g_output['midi'].set_active(True)
        self.m_filename = filename
        if mod.m_globals['fileformat_version'] == 1:
            e = Exception("TrainingSetDlg")
            e.msg1 = _("Cannot read old file format")
            e.msg2 = _(
                "To convert the file to the new file format, you must open and save it in an older version of Solfege. Versions from 3.16.0 to 3.20.4 should do the job."
            )
            raise e
        self.g_named_tracks.set_active(mod.m_globals.get(
            'named_tracks', False))
        for lesson in mod.m_blocklists.setdefault('lesson', []):
            # In this loop we will set the filename column of the liststore
            # to None if there is any problem, and set a title mentioning
            # the unknown filename.
            filename = lesson['filename']
            if filename:
                if os.path.exists(lessonfile.uri_expand(filename)):
                    fn = filename
                    title = self.get_lessonfile_title(filename)
                else:
                    fn = None
                    title = _(u"«<b>%s</b>» was not found") % filename
                self.g_liststore.append((fn, title, lesson['count'],
                                         lesson['repeat'], lesson['delay']))
            else:
                self.g_liststore.append(
                    (filename, _("«%s» not found") % filename, lesson['count'],
                     lesson['repeat'], lesson['delay']))
        self.m_changed = False
        self.set_title(self.m_filename)

    def save(self):
        """
        Save the file to a file named by self.m_filename
        """
        assert self.m_filename
        f = open(self.m_filename, 'w')
        print >> f, "# Training set definition file for GNU Solfege %s" % buildinfo.VERSION_STRING
        print >> f, "\nfileformat_version = %i" % self.fileformat_version
        print >> f, "output_format = \"%s\"" % [
            k for k in self.g_output if self.g_output[k].get_active()
        ][0]
        print >> f, "named_tracks = %s" % (
            u"yes" if self.g_named_tracks.get_active() else u"no")
        iter = self.g_liststore.get_iter_first()
        while iter:
            print >> f, "lesson {"
            filename = self.g_liststore.get_value(iter, self.STORE_FILENAME)
            print >> f, '  filename = "%s"' % filename
            print >> f, '  count = %i' \
                % self.g_liststore.get_value(iter, self.STORE_COUNT)
            print >> f, '  repeat = %i' \
                % self.g_liststore.get_value(iter, self.STORE_REPEAT)
            print >> f, '  delay = %i' \
                % self.g_liststore.get_value(iter, self.STORE_DELAY)
            print >> f, "}\n"
            iter = self.g_liststore.iter_next(iter)
        f.close()
        self.m_changed = False

    def on_export(self, widget):
        iter = self.g_liststore.get_iter_first()
        all_files_ok = True
        while iter:
            if self.g_liststore.get(iter, self.STORE_FILENAME) == (None, ):
                all_files_ok = False
            iter = self.g_liststore.iter_next(iter)
        if not all_files_ok:
            gu.dialog_ok("Can not run because some exercises are not found.")
            return
        export_to = \
            self.select_empty_directory(_("Select where to export the files"))
        if not export_to:
            return
        progress_dialog = gtk.Dialog(_("Exporting training set"), self, 0,
                                     (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
        progress_dialog.show()
        label = gtk.Label()
        label.set_markup('<span weight="bold">%s</span>' %
                         gu.escape(_("Export training set")))
        label.show()
        progress_dialog.vbox.pack_start(label, False)

        def _cancel(widget, response):
            solfege.app.m_abort_export = True

        progress_dialog.connect('response', _cancel)
        progress_bar = gtk.ProgressBar()
        progress_bar.show()
        progress_dialog.vbox.pack_start(progress_bar)
        # We have to make a version of the data without gtk widgets
        v = []
        iter = self.g_liststore.get_iter_first()
        while iter:
            v.append({
                'filename': \
                        unicode(self.g_liststore.get_value(iter, self.STORE_FILENAME)),
                'count': self.g_liststore.get_value(iter, self.STORE_COUNT),
                'repeat': self.g_liststore.get_value(iter, self.STORE_REPEAT),
                'delay': self.g_liststore.get_value(iter, self.STORE_DELAY),
                        })
            iter = self.g_liststore.iter_next(iter)
        output_format = [
            k for k in self.g_output if self.g_output[k].get_active()
        ][0]
        progress_dialog.queue_draw()
        while gtk.events_pending():
            gtk.main_iteration(0)
        time.sleep(0.1)
        while gtk.events_pending():
            gtk.main_iteration(0)
        try:
            for prog in solfege.app.export_training_set(
                    v, export_to, output_format,
                    self.g_named_tracks.get_active()):
                progress_bar.set_fraction(prog)
                while gtk.events_pending():
                    gtk.main_iteration(0)
            progress_dialog.destroy()
        except osutils.BinaryBaseException, e:
            progress_dialog.destroy()
            solfege.win.display_error_message2(e.msg1, e.msg2)
Exemple #15
0
    def show_path_info(self, w):
        if not self.g_path_info_dlg:
            self.g_path_info_dlg = Gtk.Dialog(
                _("_File locations").replace("_", ""),
                self,
                buttons=(Gtk.STOCK_OK, Gtk.ResponseType.ACCEPT))
            sc = Gtk.ScrolledWindow()
            sc.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.NEVER)
            self.g_path_info_dlg.vbox.pack_start(sc, True, True, 0)
            #
            vbox = gu.hig_dlg_vbox()
            sc.add_with_viewport(vbox)

            box1, box2 = gu.hig_category_vbox(
                _("_File locations").replace("_", ""))
            vbox.pack_start(box1, True, True, 0)
            sizegroup = Gtk.SizeGroup(Gtk.SizeGroupMode.HORIZONTAL)
            # statistics.sqlite
            # win32 solfegerc
            # win32 langenviron.txt
            box2.pack_start(
                gu.hig_label_widget(_("Solfege application data:"),
                                    Gtk.Label(label=filesystem.app_data()),
                                    sizegroup), False, False, 0)
            box2.pack_start(
                gu.hig_label_widget(_("Solfege user data:"),
                                    Gtk.Label(label=filesystem.user_data()),
                                    sizegroup), False, False, 0)
            box2.pack_start(
                gu.hig_label_widget(_("Solfege config file:"),
                                    Gtk.Label(label=filesystem.rcfile()),
                                    sizegroup), False, False, 0)
            box2.pack_start(
                gu.hig_label_widget(_("Solfege installation directory:"),
                                    Gtk.Label(label=os.getcwd()), sizegroup),
                False, False, 0)
            box2.pack_start(
                gu.hig_label_widget(
                    _("User manual in HTML format:"),
                    Gtk.Label(label=os.path.join(os.getcwd(), "help")),
                    sizegroup), False, False, 0)
            box2.pack_start(
                gu.hig_label_widget("gtk:", Gtk.Label(label=str(Gtk)),
                                    sizegroup), False, False, 0)
            box2.pack_start(
                gu.hig_label_widget("pyalsa:", Gtk.Label(label=str(alsaseq)),
                                    sizegroup), False, False, 0)
            box2.pack_start(
                gu.hig_label_widget(
                    "PYTHONHOME",
                    Gtk.Label(os.environ.get('PYTHONHOME', 'Not defined')),
                    sizegroup), False, False, 0)
            self.g_path_info_dlg.show_all()

            def f(*w):
                self.g_path_info_dlg.hide()
                return True

            self.g_path_info_dlg.connect('response', f)
            self.g_path_info_dlg.connect('delete-event', f)
            sc.set_size_request(
                min(vbox.size_request().width + gu.hig.SPACE_LARGE * 2,
                    Gdk.Screen.width() * 0.9),
                vbox.size_request().height)
Exemple #16
0
class PractiseSheetDialog(gtk.Window, gu.EditorDialogBase,
                          lessonfilegui.ExercisesMenuAddIn):
    """
    The definition of which lesson files to create questions from are
    stored in the list PractiseSheetDialog.m_sections. Each item is a
    dict filled with data. See PractiseSheet._add_common for details.

    When an exercise is selected from the menu, on_select_exercise(...)
    is called, and submethods from this will create a set of questions,
    and store the generated questions in m_sections[-1]['questions]
    and the definition (filename, number of questions to generate etc) in
    other items in the dict in PractiseSheetDialog.m_sections.

    When you change the configuration of the exercises, Solfege will only
    generate new random questions if the config changes require new
    questions, for example when question type changes.

    on_create_sheet is called to create the .tex or .html files. Calling
    is several times in a row will generate the same test.

    The file we save from "Ear Training Test Printout" is a combination
    of two things:
        1. The definitons of which exericses to create questions from and
           how many many questions to generate.
        2. The generated questions. So loading the file will let you
           click "Create Sheet" and have the program create the files to
           print out, and you will get the same test as last time you
           did so.

    When you add an exercise, the translated exercise title is used. The
    translated title is stored in the saved file, so it will not change
    language if you restart Solfege in a different locale.
    """
    current_fileformat_version = "2.0"
    STORE_TITLE = 0
    STORE_FILENAME = 1
    ok_music_types = (lessonfile.Chord, lessonfile.Voice, lessonfile.Rvoice)
    ok_modules = ('idbyname', 'harmonicinterval', 'melodicinterval')
    savedir = os.path.join(filesystem.user_data(), "eartrainingtests")

    def __init__(self, filename=None):
        logging.debug("PractiseSheetDialog.__init__")
        gtk.Window.__init__(self)
        gu.EditorDialogBase.__init__(self, filename)
        self.m_changed = False
        self.set_title(self._get_a_filename())
        self.m_exported_to = None
        self.set_default_size(800, 300)
        # self.vbox contains the toolbar and the vbox with the rest of the
        # window contents
        self.vbox = gtk.VBox()
        self.add(self.vbox)
        self.setup_toolbar()
        vbox = gtk.VBox()
        vbox.set_spacing(6)
        self.vbox.pack_start(vbox)
        vbox.set_border_width(8)
        hbox = gtk.HBox()
        vbox.pack_start(hbox, False)
        hbox.pack_start(gtk.Label(_("Output format:")), False)
        self.g_latex_radio = gtk.RadioButton(None, "LaTeX")
        hbox.pack_start(self.g_latex_radio, False)
        self.g_html_radio = gtk.RadioButton(self.g_latex_radio, "HTML")
        hbox.pack_start(self.g_html_radio, False)
        #
        hbox = gtk.HBox()
        hbox.set_spacing(6)
        vbox.pack_start(hbox, False)
        hbox.pack_start(gtk.Label(_("Title:")), False)
        self.g_title = gtk.Entry()
        hbox.pack_start(self.g_title, False)
        #
        self.m_sections = []
        self.g_liststore = gtk.ListStore(
            gobject.TYPE_STRING,  # lesson-file title
            gobject.TYPE_STRING,  # lessonfile filename, hidden column
        )
        self.g_treeview = gtk.TreeView(self.g_liststore)
        self.g_treeview.set_size_request(400, 100)
        self.g_treeview.set_headers_visible(False)
        self.g_treeview.connect('cursor-changed', self.on_tv_cursor_changed)
        self.g_treeview.connect('unselect-all', self.on_tv_unselect_all)
        scrolled_window = gtk.ScrolledWindow()
        scrolled_window.add(self.g_treeview)
        scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        vbox.pack_start(scrolled_window)
        #
        renderer = gtk.CellRendererText()

        def mark_invalid(column,
                         cell_renderer,
                         liststore,
                         iter,
                         user_data=None):
            filename = liststore.get(iter, self.STORE_FILENAME)[0]
            if not filename:
                cell_renderer.props.markup = '<span background="red">%s</span>' % liststore.get_value(
                    iter, self.STORE_TITLE)

        column = gtk.TreeViewColumn(None, renderer, text=self.STORE_TITLE)
        column.set_cell_data_func(renderer, mark_invalid)
        self.g_treeview.append_column(column)
        column = gtk.TreeViewColumn(None, renderer, text=self.STORE_FILENAME)
        self.g_treeview.append_column(column)
        #
        sizegroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
        self.g_lbox = gtk.VBox()
        self.g_lbox.set_sensitive(False)
        vbox.pack_start(self.g_lbox)
        self.g_lesson_title = gtk.Entry()
        self.g_lbox.pack_start(
            gu.hig_label_widget(_("Section title:"), self.g_lesson_title,
                                sizegroup), False)
        self.g_lesson_title_event_handle =\
            self.g_lesson_title.connect('changed', self.on_lesson_title_changed)
        #
        self.g_qtype = gtk.combo_box_new_text()
        # We should not change the order of music types, as the index
        # of the different types are used in SolfegeApp.on_create_sheet
        self.g_qtype.append_text(_("Name the music"))
        self.g_qtype.append_text(_("Write the music, first tone given"))
        self.g_qtype_event_handler = \
            self.g_qtype.connect('changed', self.on_qtype_changed)
        self.g_lbox.pack_start(
            gu.hig_label_widget(_("Type of question:"), self.g_qtype,
                                sizegroup), False)
        #
        self.g_intervals = IntervalCheckBox()
        self.g_intervals_box = gu.hig_label_widget(_("Intervals:"),
                                                   self.g_intervals, sizegroup)
        self.g_intervals_event_handler = \
            self.g_intervals.connect('value-changed', self.on_intervals_changed)
        self.g_lbox.pack_start(self.g_intervals_box, False)
        #
        self.g_line_len = gtk.SpinButton(gtk.Adjustment(0, 1, 10, 1, 10))
        self.g_line_len_event_handler = \
            self.g_line_len.connect('value-changed', self.on_spin_changed, 'line_len')
        self.g_lbox.pack_start(
            gu.hig_label_widget(_("Questions per line:"), self.g_line_len,
                                sizegroup), False)
        #
        self.g_count = gtk.SpinButton(gtk.Adjustment(0, 1, 1000, 1, 10))
        self.g_count_event_handler = \
            self.g_count.connect('value-changed', self.on_spin_changed, 'count')
        self.g_lbox.pack_start(
            gu.hig_label_widget(_("Number of questions:"), self.g_count,
                                sizegroup), False)
        if filename:
            self.load_file(filename)
        self.add_to_instance_dict()

    def view_lesson(self, idx):
        self.g_qtype.handler_block(self.g_qtype_event_handler)
        self.g_intervals.handler_block(self.g_intervals_event_handler)
        self.g_line_len.handler_block(self.g_line_len_event_handler)
        self.g_count.handler_block(self.g_count_event_handler)
        self.g_lesson_title.handler_block(self.g_lesson_title_event_handle)

        self.g_lbox.set_sensitive(idx is not None)
        if idx is None:
            self.g_lesson_title.set_text("")
            self.g_count.set_value(1)
            self.g_line_len.set_value(1)
        else:
            try:
                module = lessonfile.infocache.get(
                    self.m_sections[idx]['filename'], 'module')
            except lessonfile.infocache.InfoCacheException:
                module = None

            if module == 'harmonicinterval':
                self.g_intervals_box.show()
                self.g_intervals.set_value(self.m_sections[idx]['intervals'])
            else:
                self.g_intervals_box.hide()
            self.g_lesson_title.set_text(_tr(self.m_sections[idx]['title']))
            self.g_count.set_value(self.m_sections[idx]['count'])
            self.g_line_len.set_value(self.m_sections[idx]['line_len'])
            self.g_qtype.set_active(self.m_sections[idx]['qtype'])
        self.g_qtype.handler_unblock(self.g_qtype_event_handler)
        self.g_intervals.handler_unblock(self.g_intervals_event_handler)
        self.g_line_len.handler_unblock(self.g_line_len_event_handler)
        self.g_count.handler_unblock(self.g_count_event_handler)
        self.g_lesson_title.handler_unblock(self.g_lesson_title_event_handle)

    def on_add_lesson_clicked(self, button):
        menu = self.create_learning_tree_menu()
        menu.popup(None, None, None, 1, 0)

    def on_remove_lesson_clicked(self, button):
        path, column = self.g_treeview.get_cursor()
        if path is not None:
            iter = self.g_liststore.get_iter(path)
            self.g_liststore.remove(iter)
            del self.m_sections[path[0]]

    def on_randomize(self, widget):
        for section in self.m_sections:
            section['questions'] = []
        self.generate_questions()

    def on_create_sheet(self, widget):
        self.generate_questions()
        if not self.m_exported_to:
            self.m_exported_to = self.select_empty_directory(
                _("Select where to export the files"))
        if not self.m_exported_to:
            return
        if self.g_html_radio.get_active():
            writer = HtmlSheetWriter(self.g_title.get_text())
        else:
            writer = LatexSheetWriter(self.g_title.get_text())
        iter = self.g_liststore.get_iter_first()
        for idx, sect in enumerate(self.m_sections):
            new_section = writer.new_section(_(sect['title']),
                                             sect['line_len'])
            for question_dict in sect['questions']:
                new_section.append(question_dict)
        ww = gu.LogWindow(self)
        try:
            writer.write_to(self.m_exported_to, ww)
        except osutils.BinaryForProgramException, e:
            solfege.win.display_error_message2(e.msg1, e.msg2)
        ww.run_finished()
Exemple #17
0
class Editor(Gtk.Window, gu.EditorDialogBase):
    savedir = os.path.join(filesystem.user_data(), 'exercises', 'user')
    # The clipboard will be shared between all Editor instances
    clipboard = Clipboard()

    def __init__(self, filename=None):
        Gtk.Window.__init__(self)
        logging.debug("fpeditor.Editor.__init__(%s)", filename)
        gu.EditorDialogBase.__init__(self, filename)
        self.set_default_size(800, 600)
        self.g_main_box = Gtk.VBox()
        self.add(self.g_main_box)
        self.g_actiongroup.add_actions([
            ('GoBack', Gtk.STOCK_GO_BACK, None, None, None, self.go_back),
        ])
        self.setup_toolbar()
        self.g_title_hbox = Gtk.HBox()
        self.g_title_hbox.set_spacing(gu.hig.SPACE_SMALL)
        self.g_title_hbox.set_border_width(gu.hig.SPACE_SMALL)
        label = Gtk.Label()
        label.set_markup("<b>%s</b>" % _("Front page title:"))
        self.g_title_hbox.pack_start(label, False, False, 0)
        self.g_fptitle = Gtk.Entry()
        self.g_title_hbox.pack_start(self.g_fptitle, True, True, 0)
        self.g_main_box.pack_start(self.g_title_hbox, False, False, 0)
        # This dict maps the windows created for all pages belonging to
        # the file.
        self.m_page_mapping = {}
        self.m_model = None
        if filename:
            self.load_file(filename)
        else:
            self.m_model = pd.Page(
                _("Untitled%s") % self.m_instance_number, pd.Column())
            self.set_not_modified()
        self.add_page(Page(self.m_model, self))
        self.clipboard.update_buttons()
        self.show_all()
        self.add_to_instance_dict()
        self.g_fptitle.set_text(self.m_model.m_name)
        self.g_fptitle.connect('changed', self.on_frontpage_title_changed)

    def __del__(self):
        logging.debug("fpeditor.Editor.__del__, filename=%s", self.m_filename)

    def add_page(self, page):
        """
        Add and show the page.
        """
        editor_of(self).m_page_mapping[id(page.m_model)] = page
        self.g_main_box.pack_start(page, True, True, 0)
        self.show_page(page)

    def show_page_id(self, page_id):
        self.show_page(self.m_page_mapping[page_id])

    def show_page(self, page):
        """
        Hide the currently visible page, and show PAGE instead.
        """
        try:
            self.g_visible_page.hide()
        except AttributeError:
            pass
        self.g_visible_page = page
        page.show()
        if isinstance(page.m_parent, Page):
            self.g_title_hbox.hide()
        else:
            self.g_title_hbox.show()
        self.g_ui_manager.get_widget("/Toolbar/GoBack").set_sensitive(
            not isinstance(self.g_visible_page.m_parent, Editor))

    def go_back(self, *action):
        self.show_page(self.g_visible_page.m_parent)

    def on_frontpage_title_changed(self, widget):
        self.m_model.m_name = widget.get_text()

    def setup_toolbar(self):
        self.g_ui_manager.insert_action_group(self.g_actiongroup, 0)
        uixml = """
        <ui>
         <toolbar name='Toolbar'>
          <toolitem action='GoBack'/>
          <toolitem action='New'/>
          <toolitem action='Open'/>
          <toolitem action='Save'/>
          <toolitem action='SaveAs'/>
          <toolitem action='Close'/>
          <toolitem action='Help'/>
         </toolbar>
         <accelerator action='Close'/>
         <accelerator action='New'/>
         <accelerator action='Open'/>
         <accelerator action='Save'/>
        </ui>
        """
        self.g_ui_manager.add_ui_from_string(uixml)
        toolbar = self.g_ui_manager.get_widget("/Toolbar")
        self.g_main_box.pack_start(toolbar, False, False, 0)
        self.g_main_box.reorder_child(toolbar, 0)
        self.g_ui_manager.get_widget("/Toolbar").set_style(
            Gtk.ToolbarStyle.BOTH)

    def destroy_window(self, window_id):
        """
        Destroy the window with the id 'windowid' and all subwindows.
        """
        def do_del(wid):
            for key in self.m_page_mapping:
                parent = parent_page(self.m_page_mapping[key])
                if id(parent) == wid:
                    do_del(key)
            editor_of(self).m_page_mapping[wid].destroy()
            del editor_of(self).m_page_mapping[wid]

        do_del(window_id)

    @staticmethod
    def edit_file(fn):
        if fn in Editor.instance_dict:
            Editor.instance_dict[fn].present()
        else:
            try:
                win = Editor(fn)
                win.show()
            except IOError as e:
                gu.dialog_ok(
                    _("Loading file '%(filename)s' failed: %(msg)s") % {
                        'filename': fn,
                        'msg': str(e)
                    }, solfege.win)

    def load_file(self, filename):
        """
        Load a file into a empty, newly created Editor object.
        """
        assert self.m_model is None
        self.m_model = pd.load_tree(filename, C_locale=True)
        self.m_filename = filename
        #
        if not os.path.isabs(filename):
            if not os.access(filename, os.W_OK):
                m = Gtk.MessageDialog(
                    self, Gtk.DialogFlags.MODAL, Gtk.MessageType.INFO,
                    Gtk.ButtonsType.CLOSE,
                    _("The front page file is write protected in your install. This is normal. If you want to edit a front page file, you have to select one of the files stored in .solfege/exercises/*/ in your home directory."
                      ))
                m.run()
                m.destroy()
        self.set_not_modified()
        self.set_title(self.m_filename)

    def set_not_modified(self):
        """
        Store the current state of the data in self.m_orig_dump so that
        is_modified() will return False until we make new changes.
        """
        s = io.StringIO()
        self.m_model.dump(s)
        self.m_orig_dump = s.getvalue()

    def is_modified(self):
        """
        Return True if the data has changed since the last call to
        set_not_modified()
        """
        s = io.StringIO()
        self.m_model.dump(s)
        s = s.getvalue()
        return s != self.m_orig_dump

    @property
    def m_changed(self):
        return self.is_modified()

    def save(self, w=None):
        assert self.m_filename
        fh = pd.FileHeader(1, self.m_model)
        fh.save_file(self.m_filename)
        self.set_not_modified()
        # We do test for solfege.win since it is not available during testing
        if hasattr(solfege, 'win'):
            solfege.win.load_frontpage()

    def on_show_help(self, *w):
        return

    def get_save_as_dialog(self):
        dialog = gu.EditorDialogBase.get_save_as_dialog(self)
        ev2 = Gtk.EventBox()
        ev2.set_name("DIALOGWARNING2")
        ev = Gtk.EventBox()
        ev.set_border_width(gu.hig.SPACE_SMALL)
        ev2.add(ev)
        ev.set_name("DIALOGWARNING")
        label = Gtk.Label()
        label.set_padding(gu.hig.SPACE_MEDIUM, gu.hig.SPACE_MEDIUM)
        ev.add(label)
        label.set_markup(
            _("<b>IMPORTANT:</b> Your front page file <b>must</b> be saved in a subdirectory below the directory named exercises. See the user manual for details."
              ))
        dialog.set_extra_widget(ev2)
        ev2.show_all()
        return dialog
Exemple #18
0
class Editor(gtk.Window, gu.EditorDialogBase):
    savedir = os.path.join(filesystem.user_data(), u'exercises', u'user')
    # The clipboard will be shared between all Editor instances
    clipboard = Clipboard()
    def __init__(self, filename=None):
        gtk.Window.__init__(self)
        logging.debug("fpeditor.Editor.__init__(%s)", filename)
        gu.EditorDialogBase.__init__(self, filename)
        self.set_default_size(800, 600)
        self.g_main_box = gtk.VBox()
        self.add(self.g_main_box)
        self.g_actiongroup.add_actions([
            ('GoBack', gtk.STOCK_GO_BACK, None, None, None, self.go_back),
        ])
        self.setup_toolbar()
        self.g_title_hbox = gtk.HBox()
        self.g_title_hbox.set_spacing(gu.hig.SPACE_SMALL)
        self.g_title_hbox.set_border_width(gu.hig.SPACE_SMALL)
        label = gtk.Label()
        label.set_markup(u"<b>%s</b>" % _("Front page title:"))
        self.g_title_hbox.pack_start(label, False)
        self.g_fptitle = gtk.Entry()
        self.g_title_hbox.pack_start(self.g_fptitle)
        self.g_main_box.pack_start(self.g_title_hbox, False)
        # This dict maps the windows created for all pages belonging to
        # the file.
        self.m_page_mapping = {}
        self.m_model = None
        if filename:
            self.load_file(filename)
        else:
            self.m_model = pd.Page(_("Untitled%s") % self.m_instance_number,
                    pd.Column())
            self.set_not_modified()
        self.add_page(Page(self.m_model, self))
        self.clipboard.update_buttons()
        self.show_all()
        self.add_to_instance_dict()
        self.g_fptitle.set_text(self.m_model.m_name)
        self.g_fptitle.connect('changed', self.on_frontpage_title_changed)
    def __del__(self):
        logging.debug("fpeditor.Editor.__del__, filename=%s", self.m_filename)
    def add_page(self, page):
        """
        Add and show the page.
        """
        editor_of(self).m_page_mapping[id(page.m_model)] = page
        self.g_main_box.pack_start(page)
        self.show_page(page)
    def show_page_id(self, page_id):
        self.show_page(self.m_page_mapping[page_id])
    def show_page(self, page):
        """
        Hide the currently visible page, and show PAGE instead.
        """
        try:
            self.g_visible_page.hide()
        except AttributeError:
            pass
        self.g_visible_page = page
        page.show()
        if isinstance(page.m_parent, Page):
            self.g_title_hbox.hide()
        else:
            self.g_title_hbox.show()
        self.g_ui_manager.get_widget("/Toolbar/GoBack").set_sensitive(
            not isinstance(self.g_visible_page.m_parent, Editor))
    def go_back(self, *action):
        self.show_page(self.g_visible_page.m_parent)
    def on_frontpage_title_changed(self, widget):
        self.m_model.m_name = widget.get_text()
    def setup_toolbar(self):
        self.g_ui_manager.insert_action_group(self.g_actiongroup, 0)
        uixml = """
        <ui>
         <toolbar name='Toolbar'>
          <toolitem action='GoBack'/>
          <toolitem action='New'/>
          <toolitem action='Open'/>
          <toolitem action='Save'/>
          <toolitem action='SaveAs'/>
          <toolitem action='Close'/>
          <toolitem action='Help'/>
         </toolbar>
         <accelerator action='Close'/>
         <accelerator action='New'/>
         <accelerator action='Open'/>
         <accelerator action='Save'/>
        </ui>
        """
        self.g_ui_manager.add_ui_from_string(uixml)
        toolbar = self.g_ui_manager.get_widget("/Toolbar")
        self.g_main_box.pack_start(toolbar, False)
        self.g_main_box.reorder_child(toolbar, 0)
        self.g_ui_manager.get_widget("/Toolbar").set_style(gtk.TOOLBAR_BOTH)
    def destroy_window(self, window_id):
        """
        Destroy the window with the id 'windowid' and all subwindows.
        """
        def do_del(wid):
            for key in self.m_page_mapping:
                parent = parent_page(self.m_page_mapping[key])
                if id(parent) == wid:
                    do_del(key)
            editor_of(self).m_page_mapping[wid].destroy()
            del editor_of(self).m_page_mapping[wid]
        do_del(window_id)
    @staticmethod
    def edit_file(fn):
        if fn in Editor.instance_dict:
            Editor.instance_dict[fn].present()
        else:
            try:
                win = Editor(fn)
                win.show()
            except IOError, e:
                gu.dialog_ok(_("Loading file '%(filename)s' failed: %(msg)s") %
                        {'filename': fn, 'msg': str(e).decode('utf8', 'replace')})
Exemple #19
0
        if not os.path.exists(filesystem.app_data()):
            if os.path.exists(os.path.join(filesystem.get_home_dir(), ".solfege")):
                shutil.copytree(os.path.join(filesystem.get_home_dir(), ".solfege"),
                                filesystem.app_data())
            else:
                os.mkdir(filesystem.app_data())
        if not os.path.exists(filesystem.rcfile()):
            if os.path.exists(os.path.join(filesystem.get_home_dir(), ".solfegerc")):
                shutil.copy(os.path.join(filesystem.get_home_dir(), ".solfegerc"),
                            filesystem.rcfile())
    except (IOError, os.error), e:
        print "Migration failed:", e

if not os.path.exists(filesystem.app_data()):
    os.makedirs(filesystem.app_data())
if not os.path.exists(filesystem.user_data()):
    os.makedirs(filesystem.user_data())

try:
    cfg.initialise("default.config", None, filesystem.rcfile())
except UnicodeDecodeError, e:
    traceback.print_exc()
    print >> sys.stderr
    print >> sys.stderr, "\n".join(textwrap.wrap(
          "Your %s file is not properly utf8 encoded. Most likely"
          " it is the path to some external program that contain non-ascii"
          " characters. Please edit or delete the file. Or email it to"
          " [email protected], and he will tell you what the problem is." % filesystem.rcfile().encode("ascii", "backslashreplace")))
    print >> sys.stderr
    sys.exit("I give up (solfege.py)")
Exemple #20
0
    def show_path_info(self, w):
        if not self.g_path_info_dlg:
            self.g_path_info_dlg = Gtk.Dialog(_("_File locations").replace("_", ""), self,
                buttons=(Gtk.STOCK_OK, Gtk.ResponseType.ACCEPT))
            sc = Gtk.ScrolledWindow()
            sc.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.NEVER)
            self.g_path_info_dlg.vbox.pack_start(sc, True, True, 0)
            #
            vbox = gu.hig_dlg_vbox()
            sc.add_with_viewport(vbox)

            box1, box2 = gu.hig_category_vbox(_("_File locations").replace("_", ""))
            vbox.pack_start(box1, True, True, 0)
            sizegroup = Gtk.SizeGroup(Gtk.SizeGroupMode.HORIZONTAL)
            # statistics.sqlite
            # win32 solfegerc
            # win32 langenviron.txt
            box2.pack_start(gu.hig_label_widget(_("Solfege application data:"), Gtk.Label(label=filesystem.app_data()), sizegroup), False, False, 0)
            box2.pack_start(gu.hig_label_widget(_("Solfege user data:"), Gtk.Label(label=filesystem.user_data()), sizegroup), False, False, 0)
            box2.pack_start(gu.hig_label_widget(_("Solfege config file:"), Gtk.Label(label=filesystem.rcfile()), sizegroup), False, False, 0)
            box2.pack_start(gu.hig_label_widget(_("Solfege installation directory:"), Gtk.Label(label=os.getcwd()), sizegroup), False, False, 0)
            box2.pack_start(gu.hig_label_widget(_("User manual in HTML format:"), Gtk.Label(label=os.path.join(os.getcwd(), "help")), sizegroup), False, False, 0)
            box2.pack_start(gu.hig_label_widget("gtk:", Gtk.Label(label=str(Gtk)), sizegroup), False, False, 0)
            box2.pack_start(gu.hig_label_widget("pyalsa:", Gtk.Label(label=str(alsaseq)), sizegroup), False, False, 0)
            box2.pack_start(gu.hig_label_widget("PYTHONHOME", Gtk.Label(os.environ.get('PYTHONHOME', 'Not defined')), sizegroup), False, False, 0)
            self.g_path_info_dlg.show_all()

            def f(*w):
                self.g_path_info_dlg.hide()
                return True
            self.g_path_info_dlg.connect('response', f)
            self.g_path_info_dlg.connect('delete-event', f)
            sc.set_size_request(min(vbox.size_request().width + gu.hig.SPACE_LARGE * 2,
                                    Gdk.Screen.width() * 0.9),
                                vbox.size_request().height)