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)
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()
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() 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, 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()
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() 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, 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() p.parse_file(lessonfile.uri_expand(filename)) 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. """ 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) 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="utf-8") 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)