def __init__(self, teacher): abstract.LessonbasedGui.__init__(self, teacher) ################ # practise_box # ################ self.g_music_displayer = mpd.MusicDisplayer() self.practise_box.pack_start(self.g_music_displayer, False, False, 0) self.g_bb = QuestionNameButtonTable(self.m_t.m_exname) self.practise_box.pack_start(self.g_bb, False, False, 0) self.g_flashbar = gu.FlashBar() self.g_flashbar.show() self.practise_box.pack_start(self.g_flashbar, False, False, 0) self.practise_box.set_spacing(gu.PAD) self.std_buttons_add( ('new', self.new_question), ('play_music', lambda w: self.run_exception_handled(self.m_t.m_P.play_question)), ('repeat', lambda w: self.run_exception_handled(self.m_t.m_P.play_question)), ('repeat_arpeggio', lambda w: self.run_exception_handled(self.m_t.m_P.play_question_arpeggio)), ('repeat_slowly', lambda w: self.run_exception_handled(self.m_t.m_P.play_question_slowly)), ('play_tonic', lambda w: self.run_exception_handled(self.m_t.play_tonic)), ('display_music', self.show_answer), ('show', self.show_answer), ('give_up', self.give_up), ) self.practise_box.show_all() ############## # config_box # ############## self.config_box.set_border_width(12) self.config_box.set_spacing(18) self.add_random_transpose_gui() # ----------------------------------------- self.g_select_questions_category_box, category_box= gu.hig_category_vbox( _("Questions to ask")) self.config_box.pack_start(self.g_select_questions_category_box, False, False, 0) self.g_select_questions = QuestionNameCheckButtonTable(self.m_t) self.g_select_questions.initialize(4, 0) category_box.pack_start(self.g_select_questions, False, False, 0) self.g_select_questions.show() # ------------------------------------------ self._add_auto_new_question_gui(self.config_box) # ---------------------------------------------- ############## # statistics # ############## self.setup_statisticsviewer(statisticsviewer.StatisticsViewer, _("Identify by name")) def _f(varname): self.m_t.q_status = self.QSTATUS_NO self.setup_action_area_buttons() self.add_watch('ask_for_names', _f)
def __init__(self, teacher): abstract.LessonbasedGui.__init__(self, teacher) ################ # practise_box # ################ self.add_module_is_deprecated_label() self.g_hbox = hbox = gu.bHBox(self.practise_box) hbox.set_spacing(gu.PAD) spacebox = Gtk.HBox() hbox.pack_start(spacebox, True, True, 0) self.g_music_displayer = mpd.MusicDisplayer() self.g_music_displayer.set_size_request(100, -1) hbox.pack_start(self.g_music_displayer, False, False, 0) spacebox = Gtk.HBox() hbox.pack_start(spacebox, True, True, 0) self.g_flashbar = gu.FlashBar() self.practise_box.pack_start(self.g_flashbar, False, False, 0) self.g_new = gu.bButton(self.action_area, _("_New chord"), self.new_question) self.g_repeat = gu.bButton( self.action_area, _("_Repeat"), lambda w: self.run_exception_handled(self.m_t.m_P.play_question)) self.g_repeat_arpeggio = gu.bButton( self.action_area, _("Repeat _arpeggio"), lambda w: self. run_exception_handled(self.m_t.m_P.play_question_arpeggio)) self.g_give_up = gu.bButton(self.action_area, _("_Give up"), self.give_up) self.practise_box.show_all() ############## # config_box # ############## self.config_box.set_spacing(gu.PAD_SMALL) self.add_random_transpose_gui() # ----------------------------------------- self.g_select_questions_category_box, category_box = gu.hig_category_vbox( _("Chord types to ask")) self.config_box.pack_start(self.g_select_questions_category_box, True, True, 0) self.g_select_questions = QuestionNameCheckButtonTable(self.m_t) self.g_select_questions.initialize(4, 0) category_box.pack_start(self.g_select_questions, False, False, 0) self.g_select_questions.show()
def __init__(self, teacher): abstract.LessonbasedGui.__init__(self, teacher) ################ # practise_box # ################ self.g_contents = Gtk.Table() hbox = gu.bHBox(self.practise_box, True, False) hbox.pack_start(Gtk.HBox(), True, True, 0) hbox.pack_start(self.g_contents, True, True, 0) hbox.pack_start(Gtk.HBox(), True, True, 0) self.g_contents.set_col_spacings(gu.PAD) self.g_contents.set_row_spacings(gu.PAD) self.g_music_displayer = mpd.MusicDisplayer() self.g_music_displayer.set_size_request(100, -1) self.g_contents.attach(self.g_music_displayer, 1, 2, 1, 2) self.g_flashbar = gu.FlashBar() self.practise_box.pack_start(self.g_flashbar, False, False, 0) self.std_buttons_add( ('new', self.new_question), ('repeat', lambda w: self.run_exception_handled(self.m_t.m_P.play_question)), ('repeat_arpeggio', lambda w: self.run_exception_handled( self.m_t.m_P.play_question_arpeggio)), ('give_up', self.give_up)) self.practise_box.show_all() ############## # config_box # ############## self.config_box.set_spacing(gu.PAD_SMALL) self.add_random_transpose_gui() # ----------------------------------------- self.g_select_questions_category_box, category_box = gu.hig_category_vbox( _("Chord types to ask")) self.config_box.pack_start(self.g_select_questions_category_box, True, True, 0) self.g_select_questions = QuestionNameCheckButtonTable(self.m_t) self.g_select_questions.initialize(4, 0) category_box.pack_start(self.g_select_questions, False, False, 0) self.g_select_questions.show()
def __init__(self, teacher): abstract.LessonbasedGui.__init__(self, teacher) ################ # practise_box # ################ self.add_module_is_deprecated_label() self.g_hbox = hbox = gu.bHBox(self.practise_box) hbox.set_spacing(gu.PAD) spacebox = Gtk.HBox() hbox.pack_start(spacebox, True, True, 0) self.g_music_displayer = mpd.MusicDisplayer() self.g_music_displayer.set_size_request(100, -1) hbox.pack_start(self.g_music_displayer, False, False, 0) spacebox = Gtk.HBox() hbox.pack_start(spacebox, True, True, 0) self.g_flashbar = gu.FlashBar() self.practise_box.pack_start(self.g_flashbar, False, False, 0) self.g_new = gu.bButton(self.action_area, _("_New chord"), self.new_question) self.g_repeat = gu.bButton(self.action_area, _("_Repeat"), lambda w: self.run_exception_handled(self.m_t.m_P.play_question)) self.g_repeat_arpeggio = gu.bButton(self.action_area, _("Repeat _arpeggio"), lambda w: self.run_exception_handled(self.m_t.m_P.play_question_arpeggio)) self.g_give_up = gu.bButton(self.action_area, _("_Give up"), self.give_up) self.practise_box.show_all() ############## # config_box # ############## self.config_box.set_spacing(gu.PAD_SMALL) self.add_random_transpose_gui() # ----------------------------------------- self.g_select_questions_category_box, category_box= gu.hig_category_vbox( _("Chord types to ask")) self.config_box.pack_start(self.g_select_questions_category_box, True, True, 0) self.g_select_questions = QuestionNameCheckButtonTable(self.m_t) self.g_select_questions.initialize(4, 0) category_box.pack_start(self.g_select_questions, False, False, 0) self.g_select_questions.show()
def __init__(self, teacher): abstract.LessonbasedGui.__init__(self, teacher) ################ # practise_box # ################ self.g_contents = gtk.Table() hbox = gu.bHBox(self.practise_box, True, False) hbox.pack_start(gtk.HBox()) hbox.pack_start(self.g_contents) hbox.pack_start(gtk.HBox()) self.g_contents.set_col_spacings(gu.PAD) self.g_contents.set_row_spacings(gu.PAD) self.g_music_displayer = mpd.musicdisplayer.MusicDisplayer() self.g_music_displayer.set_size_request(100, -1) self.g_contents.attach(self.g_music_displayer, 1, 2, 1, 2) self.g_flashbar = gu.FlashBar() self.practise_box.pack_start(self.g_flashbar, False) self.std_buttons_add( ('new', self.new_question), ('repeat', lambda w: self.run_exception_handled(self.m_t.m_P.play_question)), ('repeat_arpeggio', lambda w: self.run_exception_handled(self.m_t.m_P.play_question_arpeggio)), ('give_up', self.give_up)) self.practise_box.show_all() ############## # config_box # ############## self.config_box.set_spacing(gu.PAD_SMALL) self.add_random_transpose_gui() # ----------------------------------------- self.g_select_questions_category_box, category_box= gu.hig_category_vbox( _("Chord types to ask")) self.config_box.pack_start(self.g_select_questions_category_box, True) self.g_select_questions = QuestionNameCheckButtonTable(self.m_t) self.g_select_questions.initialize(4, 0) category_box.pack_start(self.g_select_questions, False) self.g_select_questions.show()
class Gui(abstract.LessonbasedGui): def __init__(self, teacher): abstract.LessonbasedGui.__init__(self, teacher) ################ # practise_box # ################ self.add_module_is_deprecated_label() self.g_hbox = hbox = gu.bHBox(self.practise_box) hbox.set_spacing(gu.PAD) spacebox = Gtk.HBox() hbox.pack_start(spacebox, True, True, 0) self.g_music_displayer = mpd.MusicDisplayer() self.g_music_displayer.set_size_request(100, -1) hbox.pack_start(self.g_music_displayer, False, False, 0) spacebox = Gtk.HBox() hbox.pack_start(spacebox, True, True, 0) self.g_flashbar = gu.FlashBar() self.practise_box.pack_start(self.g_flashbar, False, False, 0) self.g_new = gu.bButton(self.action_area, _("_New chord"), self.new_question) self.g_repeat = gu.bButton( self.action_area, _("_Repeat"), lambda w: self.run_exception_handled(self.m_t.m_P.play_question)) self.g_repeat_arpeggio = gu.bButton( self.action_area, _("Repeat _arpeggio"), lambda w: self. run_exception_handled(self.m_t.m_P.play_question_arpeggio)) self.g_give_up = gu.bButton(self.action_area, _("_Give up"), self.give_up) self.practise_box.show_all() ############## # config_box # ############## self.config_box.set_spacing(gu.PAD_SMALL) self.add_random_transpose_gui() # ----------------------------------------- self.g_select_questions_category_box, category_box = gu.hig_category_vbox( _("Chord types to ask")) self.config_box.pack_start(self.g_select_questions_category_box, True, True, 0) self.g_select_questions = QuestionNameCheckButtonTable(self.m_t) self.g_select_questions.initialize(4, 0) category_box.pack_start(self.g_select_questions, False, False, 0) self.g_select_questions.show() def update_select_question_buttons(self): """ The g_select_questions widget is used in m_custom_mode to select which questions to ask. This method will show and update the widget to the current lesson file if we are in m_custom_mode. If not, it will hide the widget. """ if self.m_t.m_custom_mode: self.g_select_questions_category_box.show() self.g_select_questions.initialize(self.m_t.m_P.header.fillnum, self.m_t.m_P.header.filldir) self.m_t.check_askfor() for question in self.m_t.m_P.iterate_questions_with_unique_names(): self.g_select_questions.add(question) else: self.g_select_questions_category_box.hide() self.g_select_questions.initialize(0, 0) def update_answer_buttons(self, obj=None): """ Only columns with question properties that are actually used in the lesson file will be displayed. This way, we can make a default configuration: qprops = "name", "toptone", "inversion" qprop_labels = _("Name"), _("Toptone"), _("Inversion") and only lesson files that require other properties have to define these two variables. """ # This part will create the table with the buttons used to answer. try: self.g_atable.destroy() except AttributeError: pass self.g_atable = Gtk.Table() self.g_atable.show() self.g_hbox.pack_start(self.g_atable, False, False, 0) self.g_hbox.reorder_child(self.g_atable, 1) # pprops say how many properties are we going to display. # We will not display a property if no questions use it. num_used_props = len([ x for x in self.m_t.m_P.m_props.keys() if self.m_t.m_P.m_props[x] ]) # tcols say how many columns we need. We need a column for each # column separator tcols = num_used_props * 2 - 1 trows = max([len(x) for x in self.m_t.m_P.m_props.values()]) + 2 # The column headings for idx, label in enumerate(self.m_t.m_P.header.qprop_labels): self.g_atable.attach(Gtk.Label(label=label), idx * 2, idx * 2 + 1, 0, 1, xoptions=Gtk.AttachOptions.FILL, yoptions=Gtk.AttachOptions.SHRINK, xpadding=gu.PAD_SMALL) # Then we create the buttons used to answer. for x, prop in enumerate(self.m_t.m_P.header.qprops): for y, proplabel in enumerate(self.m_t.m_P.m_props[prop]): button = Gtk.Button(unicode(proplabel)) button.m_property_name = prop button.m_property_value = proplabel.cval button.connect('clicked', self.on_prop_button_clicked) button.connect('button_release_event', self.on_prop_button_right_clicked) self.g_atable.attach(button, x * 2, x * 2 + 1, y + 2, y + 3, xpadding=gu.PAD_SMALL, yoptions=Gtk.AttachOptions.SHRINK) # The separator below the column headings self.g_atable.attach(Gtk.HSeparator(), 0, tcols, 1, 2, xoptions=Gtk.AttachOptions.FILL, yoptions=Gtk.AttachOptions.FILL, xpadding=0, ypadding=gu.PAD_SMALL) # The vertical separator between columns for idx in range(len(self.m_t.m_P.header.qprops) - 1): self.g_atable.attach(Gtk.VSeparator(), idx * 2 + 1, idx * 2 + 2, 0, trows, xoptions=Gtk.AttachOptions.FILL, yoptions=Gtk.AttachOptions.FILL, xpadding=0, ypadding=gu.PAD_SMALL) self.g_atable.show_all() # self.g_random_transpose.set_text( str(self.m_t.m_P.header.random_transpose)) self.g_repeat.set_sensitive(False) self.g_repeat_arpeggio.set_sensitive(False) self.g_give_up.set_sensitive(False) def update_gui_after_lessonfile_change(self): self.g_music_displayer.clear() self.update_select_question_buttons() if self.m_t.m_P.header.lesson_heading: self.set_lesson_heading(self.m_t.m_P.header.lesson_heading) else: self.set_lesson_heading(_("Identify the chord")) self.g_new.set_sensitive(True) self.update_answer_buttons() def on_prop_button_clicked(self, button): if self.m_t.q_status != self.QSTATUS_NEW: return g = self.m_t.guess_property(button.m_property_name, button.m_property_value) if g: self.g_flashbar.flash(_("Correct")) for btn in self.g_atable.get_children(): if isinstance(btn, Gtk.Button): if btn.m_property_name == button.m_property_name \ and btn.m_property_value == button.m_property_value: btn.get_children()[0].set_name("BoldText") break if g == self.m_t.ALL_CORRECT: self.all_guessed_correct() else: self.g_flashbar.flash(_("Wrong")) def on_prop_button_right_clicked(self, button, event): """ Search for a question in the lesson file with the same properties as the question being asked, but with the one property changed to be the property right-clicked on. Do nothing if no matching question is found. """ if event.button != 3: return if not self.m_t.m_P.has_question(): return if not self.m_t.m_P.header.enable_right_click: self.g_flashbar.flash( _("Right click is not allowed for this lesson file.")) return d = {} for k in self.m_t.m_P.header.qprops: d[k] = self.m_t.m_P.get_question()[k].cval # replace one property with the one we right clicked d[button.m_property_name] = button.m_property_value for idx, question in enumerate(self.m_t.m_P.m_questions): match = True for k in d: if d[k] != question[k].cval: match = False if match: try: self.m_t.m_P.play_question(question) except Exception, e: if not self.standard_exception_handler(e): raise return
class Gui(abstract.LessonbasedGui): def __init__(self, teacher): abstract.LessonbasedGui.__init__(self, teacher) ################ # practise_box # ################ self.g_music_displayer = mpd.MusicDisplayer() self.practise_box.pack_start(self.g_music_displayer, False, False, 0) self.g_bb = QuestionNameButtonTable(self.m_t.m_exname) self.practise_box.pack_start(self.g_bb, False, False, 0) self.g_flashbar = gu.FlashBar() self.g_flashbar.show() self.practise_box.pack_start(self.g_flashbar, False, False, 0) self.practise_box.set_spacing(gu.PAD) self.std_buttons_add( ('new', self.new_question), ('play_music', lambda w: self.run_exception_handled(self.m_t.m_P.play_question)), ('repeat', lambda w: self.run_exception_handled(self.m_t.m_P.play_question)), ('repeat_arpeggio', lambda w: self.run_exception_handled(self.m_t.m_P.play_question_arpeggio)), ('repeat_slowly', lambda w: self.run_exception_handled(self.m_t.m_P.play_question_slowly)), ('play_tonic', lambda w: self.run_exception_handled(self.m_t.play_tonic)), ('display_music', self.show_answer), ('show', self.show_answer), ('give_up', self.give_up), ) self.practise_box.show_all() ############## # config_box # ############## self.config_box.set_border_width(12) self.config_box.set_spacing(18) self.add_random_transpose_gui() # ----------------------------------------- self.g_select_questions_category_box, category_box= gu.hig_category_vbox( _("Questions to ask")) self.config_box.pack_start(self.g_select_questions_category_box, False, False, 0) self.g_select_questions = QuestionNameCheckButtonTable(self.m_t) self.g_select_questions.initialize(4, 0) category_box.pack_start(self.g_select_questions, False, False, 0) self.g_select_questions.show() # ------------------------------------------ self._add_auto_new_question_gui(self.config_box) # ---------------------------------------------- ############## # statistics # ############## self.setup_statisticsviewer(statisticsviewer.StatisticsViewer, _("Identify by name")) def _f(varname): self.m_t.q_status = self.QSTATUS_NO self.setup_action_area_buttons() self.add_watch('ask_for_names', _f) def update_answer_buttons(self): self.g_bb.initialize(self.m_t.m_P.header.fillnum, self.m_t.m_P.header.filldir) for question in self.m_t.m_P.iterate_questions_with_unique_names(): self.g_bb.add(question, self.on_click) if self.m_t.m_custom_mode: self.g_bb.ask_for_names_changed() def update_select_question_buttons(self): #FIXME duplicate code in src/chord.py if self.m_t.m_custom_mode: self.g_select_questions_category_box.show() self.g_select_questions.initialize(self.m_t.m_P.header.fillnum, self.m_t.m_P.header.filldir) self.m_t.check_askfor() for question in self.m_t.m_P.iterate_questions_with_unique_names(): self.g_select_questions.add(question) else: self.g_select_questions_category_box.hide() self.g_select_questions.initialize(0, 0) def setup_action_area_buttons(self): """ Make the buttons visible or invisible depending on the lesson file, and make the right set of buttons sensitive for the first question. """ self.std_buttons_start_practise() if [q for q in self.m_t.m_P.m_questions if isinstance(q.music, lessonfile.MpdTransposable)]: self.g_random_transpose_box.show() else: self.g_random_transpose_box.hide() self.show_hide_at_question_start_buttons() if self.m_t.m_P.header.have_music_displayer: self.g_music_displayer.show() self.g_music_displayer.clear(self.m_t.m_P.header.music_displayer_stafflines) else: self.g_music_displayer.hide() def give_up(self, _o=None): if self.m_t.q_status == self.QSTATUS_WRONG: self.g_flashbar.push(_("The answer is: {answer}"), answer=self.m_t.m_P.get_name()) self.m_t.give_up() self.std_buttons_give_up() if self.m_t.m_P.header.have_music_displayer: self.run_exception_handled(self.show_answer) def on_click(self, button, event=None): if not event: self.on_left_click(button) elif event.button == 3: if self.m_t.m_P and self.m_t.m_P.header.enable_right_click: self.on_right_click(button) else: self.g_flashbar.flash(_("Right click is not allowed for this lesson file.")) def on_right_click(self, button): if solfege.app.m_test_mode: return if self.m_t.q_status == self.QSTATUS_NO: self.g_flashbar.flash(_("Click 'New' to begin.")) return if self.m_t.q_status == self.QSTATUS_NEW: self.g_flashbar.flash(_("You should try to guess before right-clicking.")) return if self.m_t.q_status not in (self.QSTATUS_GIVE_UP, self.QSTATUS_WRONG, self.QSTATUS_SOLVED): self.g_flashbar.flash(_("You should try to guess before right-clicking.")) return try: if 'set' in self.m_t.m_P.get_question(): for idx, question in enumerate(self.m_t.m_P.m_questions): if question.set == self.m_t.m_P.get_question().set \ and question.name.cval == button.m_cname: self.m_t.m_P.play_question(question) return for idx, question in enumerate(self.m_t.m_P.m_questions): if question.name.cval == button.m_cname: self.m_t.m_P.play_question(question) return except Exception, e: if not self.standard_exception_handler(e): raise
class Gui(abstract.LessonbasedGui): def __init__(self, teacher): abstract.LessonbasedGui.__init__(self, teacher) ################ # practise_box # ################ self.g_music_displayer = mpd.MusicDisplayer() self.practise_box.pack_start(self.g_music_displayer, False, False, 0) self.g_bb = QuestionNameButtonTable(self.m_t.m_exname) self.practise_box.pack_start(self.g_bb, False, False, 0) self.g_flashbar = gu.FlashBar() self.g_flashbar.show() self.practise_box.pack_start(self.g_flashbar, False, False, 0) self.practise_box.set_spacing(gu.PAD) self.std_buttons_add( ('new', self.new_question), ('play_music', lambda w: self.run_exception_handled(self.m_t.m_P.play_question)), ('repeat', lambda w: self.run_exception_handled(self.m_t.m_P.play_question)), ('repeat_arpeggio', lambda w: self.run_exception_handled(self.m_t.m_P.play_question_arpeggio)), ('repeat_slowly', lambda w: self.run_exception_handled(self.m_t.m_P.play_question_slowly)), ('play_tonic', lambda w: self.run_exception_handled(self.m_t.play_tonic)), ('display_music', self.show_answer), ('show', self.show_answer), ('give_up', self.give_up), ) self.practise_box.show_all() ############### # config_grid # ############### self.add_random_transpose_gui(0) # ----------------------------------------- self.g_select_questions_category_box, category_box = gu.hig_category_vbox( _("Questions to ask")) self.g_config_grid.attach(self.g_select_questions_category_box, 0, 1, 3, 1) self.g_select_questions = QuestionNameCheckButtonTable(self.m_t) self.g_select_questions.initialize(4, 0) category_box.pack_start(self.g_select_questions, False, False, 0) self.g_select_questions.show() # ------------------------------------------ self._add_auto_new_question_gui(2) # ---------------------------------------------- ############## # statistics # ############## self.setup_statisticsviewer(statisticsviewer.StatisticsViewer, _("Identify by name")) def _f(varname): self.m_t.q_status = self.QSTATUS_NO self.setup_action_area_buttons() self.add_watch('ask_for_names', _f) def update_answer_buttons(self): self.g_bb.initialize(self.m_t.m_P.header.fillnum, self.m_t.m_P.header.filldir) for question in self.m_t.m_P.iterate_questions_with_unique_names(): self.g_bb.add(question, self.on_click) if self.m_t.m_custom_mode: self.g_bb.ask_for_names_changed() def update_select_question_buttons(self): # FIXME duplicate code in src/chord.py if self.m_t.m_custom_mode: self.g_select_questions_category_box.show() self.g_select_questions.initialize(self.m_t.m_P.header.fillnum, self.m_t.m_P.header.filldir) self.m_t.check_askfor() for question in self.m_t.m_P.iterate_questions_with_unique_names(): self.g_select_questions.add(question) else: self.g_select_questions_category_box.hide() self.g_select_questions.initialize(0, 0) def setup_action_area_buttons(self): """ Make the buttons visible or invisible depending on the lesson file, and make the right set of buttons sensitive for the first question. """ self.std_buttons_start_practise() if [q for q in self.m_t.m_P.m_questions if isinstance(q.music, lessonfile.MpdTransposable)]: self.g_random_transpose_label.show() self.g_random_transpose.show() self.g_random_transpose_button.show() else: self.g_random_transpose_label.hide() self.g_random_transpose.hide() self.g_random_transpose_button.hide() self.show_hide_at_question_start_buttons() if self.m_t.m_P.header.have_music_displayer: self.g_music_displayer.show() self.g_music_displayer.clear(self.m_t.m_P.header.music_displayer_stafflines) else: self.g_music_displayer.hide() def give_up(self, _o=None): if self.m_t.q_status == self.QSTATUS_WRONG: self.g_flashbar.push(_("The answer is: {answer}"), answer=self.m_t.m_P.get_name()) self.m_t.give_up() self.std_buttons_give_up() if self.m_t.m_P.header.have_music_displayer: self.run_exception_handled(self.show_answer) def on_click(self, button, event=None): if not event: self.on_left_click(button) elif event.button == 3: if self.m_t.m_P and self.m_t.m_P.header.enable_right_click: self.on_right_click(button) else: self.g_flashbar.flash(_("Right click is not allowed for this lesson file.")) def on_right_click(self, button): if solfege.app.m_test_mode: return if self.m_t.q_status == self.QSTATUS_NO: self.g_flashbar.flash(_("Click 'New' to begin.")) return if self.m_t.q_status == self.QSTATUS_NEW: self.g_flashbar.flash(_("You should try to guess before right-clicking.")) return if self.m_t.q_status not in (self.QSTATUS_GIVE_UP, self.QSTATUS_WRONG, self.QSTATUS_SOLVED): self.g_flashbar.flash(_("You should try to guess before right-clicking.")) return try: if 'set' in self.m_t.m_P.get_question(): for idx, question in enumerate(self.m_t.m_P.m_questions): if question.set == self.m_t.m_P.get_question().set \ and question.name.cval == button.m_cname: self.m_t.m_P.play_question(question) return for idx, question in enumerate(self.m_t.m_P.m_questions): if question.name.cval == button.m_cname: self.m_t.m_P.play_question(question) return except Exception as e: if not self.standard_exception_handler(e): raise def on_left_click(self, button): if self.m_t.q_status == self.QSTATUS_NO: if solfege.app.m_test_mode: self.g_flashbar.flash(_("Click 'Start test' to begin.")) else: self.g_flashbar.flash(_("Click 'New' to begin.")) return try: if self.m_t.q_status == self.QSTATUS_SOLVED: if self.m_t.guess_answer(button.m_cname): self.g_flashbar.flash(_("Correct, but you have already solved this question")) else: self.g_flashbar.flash(_("Wrong, but you have already solved this question")) elif self.m_t.q_status in (self.QSTATUS_NEW, self.QSTATUS_WRONG): if self.m_t.guess_answer(button.m_cname): self.g_flashbar.flash(_("Correct")) self.std_buttons_answer_correct() if self.m_t.m_P.header.have_music_displayer: self.show_answer() else: self.g_flashbar.flash(_("Wrong")) if self.get_bool("config/auto_repeat_question_if_wrong_answer"): self.m_t.m_P.play_question() self.std_buttons_answer_wrong() except Exception as e: if not self.standard_exception_handler(e): raise def new_question(self, widget=None): """ The new button should be insensitive if we have no lesson file. """ if solfege.app.m_test_mode and self.m_t.m_P.is_test_complete(): self.do_test_complete() return if solfege.app.m_test_mode: self.g_new.hide() def exception_cleanup(): self.m_t.q_status = self.QSTATUS_NO soundcard.synth.stop() self.std_buttons_exception_cleanup() if self.m_t.m_P.header.have_music_displayer: self.g_music_displayer.clear(self.m_t.m_P.header.music_displayer_stafflines) try: g = self.m_t.new_question() if g == self.m_t.OK: self.do_at_question_start_show_play() self.std_buttons_new_question() self.g_bb.grab_focus_first_button() self.g_flashbar.clear() except Exception as e: if isinstance(e, mpd.MpdException): if 'm_mpd_badcode' not in dir(e): e.m_mpd_badcode = self.m_t.m_P.get_question()['music'].get_err_context(e, self.m_t.m_P) if not self.standard_exception_handler(e, exception_cleanup): raise def on_start_practise(self): self.m_t.m_custom_mode = self.get_bool('gui/expert_mode') super(Gui, self).on_start_practise() for question in self.m_t.m_P.m_questions: question.active = 1 self.setup_action_area_buttons() self.update_answer_buttons() self.update_select_question_buttons() self.g_random_transpose.set_text(str(self.m_t.m_P.header.random_transpose)) self.g_flashbar.require_size([ _("Right click is not allowed for this lesson file."), _("You should try to guess before right-clicking."), _("You should try to guess before right-clicking."), _("Correct, but you have already solved this question"), _("Wrong, but you have already solved this question"), _("You have to select some questions to practise."), ]) if (not self.m_t.m_custom_mode) or solfege.app.m_test_mode: self.m_t.m_statistics.reset_session() self.g_statview.g_heading.set_text(self.m_t.m_P.header.title) self.g_music_displayer.clear(self.m_t.m_P.header.music_displayer_stafflines) if solfege.app.m_test_mode: self.g_flashbar.delayed_flash(self.short_delay, _("Click 'Start test' to begin.")) else: self.g_flashbar.delayed_flash(self.short_delay, _("Click 'New' to begin.")) def on_end_practise(self): self.m_t.end_practise() self.std_buttons_end_practise() if self.m_t.m_P and self.m_t.m_P.header.have_music_displayer: self.g_music_displayer.clear(self.m_t.m_P.header.music_displayer_stafflines) self.g_flashbar.clear() def enter_test_mode(self): self.m_saved_q_auto = self.get_bool('new_question_automatically') self.m_saved_s_new = self.get_float('seconds_before_new_question') self.set_bool('new_question_automatically', True) self.set_float('seconds_before_new_question', 0.5) self.m_t.enter_test_mode() self.g_give_up.hide() self.g_new.set_label(_("_Start test")) self.g_show.hide() self.g_repeat_arpeggio.hide() self.g_repeat_slowly.hide() self.g_cancel_test.show() def exit_test_mode(self): self.set_bool('new_question_automatically', self.m_saved_q_auto) self.set_float('seconds_before_new_question', self.m_saved_s_new) self.m_t.exit_test_mode() self.g_new.set_label(_("_New")) self.g_new.show() self.g_repeat_slowly.show()
class Gui(abstract.LessonbasedGui): def __init__(self, teacher): abstract.LessonbasedGui.__init__(self, teacher) ################ # practise_box # ################ self.add_module_is_deprecated_label() self.g_hbox = hbox = gu.bHBox(self.practise_box) hbox.set_spacing(gu.PAD) spacebox = Gtk.HBox() hbox.pack_start(spacebox, True, True, 0) self.g_music_displayer = mpd.MusicDisplayer() self.g_music_displayer.set_size_request(100, -1) hbox.pack_start(self.g_music_displayer, False, False, 0) spacebox = Gtk.HBox() hbox.pack_start(spacebox, True, True, 0) self.g_flashbar = gu.FlashBar() self.practise_box.pack_start(self.g_flashbar, False, False, 0) self.g_new = gu.bButton(self.action_area, _("_New chord"), self.new_question) self.g_repeat = gu.bButton(self.action_area, _("_Repeat"), lambda w: self.run_exception_handled(self.m_t.m_P.play_question)) self.g_repeat_arpeggio = gu.bButton(self.action_area, _("Repeat _arpeggio"), lambda w: self.run_exception_handled(self.m_t.m_P.play_question_arpeggio)) self.g_give_up = gu.bButton(self.action_area, _("_Give up"), self.give_up) self.practise_box.show_all() ############## # config_box # ############## self.config_box.set_spacing(gu.PAD_SMALL) self.add_random_transpose_gui() # ----------------------------------------- self.g_select_questions_category_box, category_box= gu.hig_category_vbox( _("Chord types to ask")) self.config_box.pack_start(self.g_select_questions_category_box, True, True, 0) self.g_select_questions = QuestionNameCheckButtonTable(self.m_t) self.g_select_questions.initialize(4, 0) category_box.pack_start(self.g_select_questions, False, False, 0) self.g_select_questions.show() def update_select_question_buttons(self): """ The g_select_questions widget is used in m_custom_mode to select which questions to ask. This method will show and update the widget to the current lesson file if we are in m_custom_mode. If not, it will hide the widget. """ if self.m_t.m_custom_mode: self.g_select_questions_category_box.show() self.g_select_questions.initialize(self.m_t.m_P.header.fillnum, self.m_t.m_P.header.filldir) self.m_t.check_askfor() for question in self.m_t.m_P.iterate_questions_with_unique_names(): self.g_select_questions.add(question) else: self.g_select_questions_category_box.hide() self.g_select_questions.initialize(0, 0) def update_answer_buttons(self, obj=None): """ Only columns with question properties that are actually used in the lesson file will be displayed. This way, we can make a default configuration: qprops = "name", "toptone", "inversion" qprop_labels = _("Name"), _("Toptone"), _("Inversion") and only lesson files that require other properties have to define these two variables. """ # This part will create the table with the buttons used to answer. try: self.g_atable.destroy() except AttributeError: pass self.g_atable = Gtk.Table() self.g_atable.show() self.g_hbox.pack_start(self.g_atable, False, False, 0) self.g_hbox.reorder_child(self.g_atable, 1) # pprops say how many properties are we going to display. # We will not display a property if no questions use it. num_used_props = len( [x for x in self.m_t.m_P.m_props.keys() if self.m_t.m_P.m_props[x]]) # tcols say how many columns we need. We need a column for each # column separator tcols = num_used_props * 2 - 1 trows = max([len(x) for x in self.m_t.m_P.m_props.values()]) + 2 # The column headings for idx, label in enumerate(self.m_t.m_P.header.qprop_labels): self.g_atable.attach(Gtk.Label(label=label), idx * 2, idx * 2 + 1, 0, 1, xoptions=Gtk.AttachOptions.FILL, yoptions=Gtk.AttachOptions.SHRINK, xpadding=gu.PAD_SMALL) # Then we create the buttons used to answer. for x, prop in enumerate(self.m_t.m_P.header.qprops): for y, proplabel in enumerate(self.m_t.m_P.m_props[prop]): button = Gtk.Button(unicode(proplabel)) button.m_property_name = prop button.m_property_value = proplabel.cval button.connect('clicked', self.on_prop_button_clicked) button.connect('button_release_event', self.on_prop_button_right_clicked) self.g_atable.attach(button, x * 2, x * 2 + 1, y + 2, y + 3, xpadding=gu.PAD_SMALL, yoptions=Gtk.AttachOptions.SHRINK) # The separator below the column headings self.g_atable.attach(Gtk.HSeparator(), 0, tcols, 1, 2, xoptions=Gtk.AttachOptions.FILL, yoptions=Gtk.AttachOptions.FILL, xpadding=0, ypadding=gu.PAD_SMALL) # The vertical separator between columns for idx in range(len(self.m_t.m_P.header.qprops)-1): self.g_atable.attach(Gtk.VSeparator(), idx * 2 + 1, idx * 2 + 2, 0, trows, xoptions=Gtk.AttachOptions.FILL, yoptions=Gtk.AttachOptions.FILL, xpadding=0, ypadding=gu.PAD_SMALL) self.g_atable.show_all() # self.g_random_transpose.set_text(str(self.m_t.m_P.header.random_transpose)) self.g_repeat.set_sensitive(False) self.g_repeat_arpeggio.set_sensitive(False) self.g_give_up.set_sensitive(False) def update_gui_after_lessonfile_change(self): self.g_music_displayer.clear() self.update_select_question_buttons() if self.m_t.m_P.header.lesson_heading: self.set_lesson_heading(self.m_t.m_P.header.lesson_heading) else: self.set_lesson_heading(_("Identify the chord")) self.g_new.set_sensitive(True) self.update_answer_buttons() def on_prop_button_clicked(self, button): if self.m_t.q_status != self.QSTATUS_NEW: return g = self.m_t.guess_property(button.m_property_name, button.m_property_value) if g: self.g_flashbar.flash(_("Correct")) for btn in self.g_atable.get_children(): if isinstance(btn, Gtk.Button): if btn.m_property_name == button.m_property_name \ and btn.m_property_value == button.m_property_value: btn.get_children()[0].set_name("BoldText") break if g == self.m_t.ALL_CORRECT: self.all_guessed_correct() else: self.g_flashbar.flash(_("Wrong")) def on_prop_button_right_clicked(self, button, event): """ Search for a question in the lesson file with the same properties as the question being asked, but with the one property changed to be the property right-clicked on. Do nothing if no matching question is found. """ if event.button != 3: return if not self.m_t.m_P.has_question(): return if not self.m_t.m_P.header.enable_right_click: self.g_flashbar.flash(_("Right click is not allowed for this lesson file.")) return d = {} for k in self.m_t.m_P.header.qprops: d[k] = self.m_t.m_P.get_question()[k].cval # replace one property with the one we right clicked d[button.m_property_name] = button.m_property_value for idx, question in enumerate(self.m_t.m_P.m_questions): match = True for k in d: if d[k] != question[k].cval: match = False if match: try: self.m_t.m_P.play_question(question) except Exception, e: if not self.standard_exception_handler(e): raise return
class Gui(abstract.LessonbasedGui): # the lesson_header variable is set in the lesson file class # depending on the value of header.flavour def __init__(self, teacher): abstract.LessonbasedGui.__init__(self, teacher) ################ # practise_box # ################ self.g_contents = Gtk.Table() hbox = gu.bHBox(self.practise_box, True, False) hbox.pack_start(Gtk.HBox(), True, True, 0) hbox.pack_start(self.g_contents, True, True, 0) hbox.pack_start(Gtk.HBox(), True, True, 0) self.g_contents.set_col_spacings(gu.PAD) self.g_contents.set_row_spacings(gu.PAD) self.g_music_displayer = mpd.MusicDisplayer() self.g_music_displayer.set_size_request(100, -1) self.g_contents.attach(self.g_music_displayer, 1, 2, 1, 2) self.g_flashbar = gu.FlashBar() self.practise_box.pack_start(self.g_flashbar, False, False, 0) self.std_buttons_add( ('new', self.new_question), ('repeat', lambda w: self.run_exception_handled(self.m_t.m_P.play_question)), ('repeat_arpeggio', lambda w: self.run_exception_handled( self.m_t.m_P.play_question_arpeggio)), ('give_up', self.give_up)) self.practise_box.show_all() ############### # config_grid # ############### self.add_random_transpose_gui(row=0) # ----------------------------------------- self.g_select_questions_category_box, category_box = gu.hig_category_vbox( _("Chord types to ask")) self.g_config_grid.attach(self.g_select_questions_category_box, 0, 1, 3, 1) self.g_select_questions = QuestionNameCheckButtonTable(self.m_t) self.g_select_questions.initialize(4, 0) category_box.pack_start(self.g_select_questions, False, False, 0) self.g_select_questions.show() def update_select_question_buttons(self): """ The g_select_questions widget is used in m_custom_mode to select which questions to ask. This method will show and update the widget to the current lesson file if we are in m_custom_mode. If not, it will hide the widget. """ if self.m_t.m_custom_mode: self.g_select_questions_category_box.show() self.g_select_questions.initialize(self.m_t.m_P.header.fillnum, self.m_t.m_P.header.filldir) self.m_t.check_askfor() for question in self.m_t.m_P.iterate_questions_with_unique_names(): self.g_select_questions.add(question) else: self.g_select_questions_category_box.hide() self.g_select_questions.initialize(0, 0) def update_answer_buttons(self, obj=None): """ Only columns with question properties that are actually used in the lesson file will be displayed. This way, we can make a default configuration: qprops = "name", "toptone", "inversion" qprop_labels = _("Name"), _("Toptone"), _("Inversion") and only lesson files that require other properties have to define these two variables. """ # This part will create the table with the buttons used to answer. try: self.g_atable.destroy() except AttributeError: pass self.g_atable = Gtk.Table() self.g_atable.show() if self.m_t.m_P.header.layout_orientation == 'horiz': self.g_contents.attach(self.g_atable, 1, 2, 2, 3) else: self.g_contents.attach(self.g_atable, 0, 1, 1, 2) # pprops say how many properties are we going to display. # We will not display a property if no questions use it. num_used_props = len([ x for x in list(self.m_t.m_P.m_props.keys()) if self.m_t.m_P.m_props[x] ]) # tcols say how many columns we need. We need a column for each # column separator tcols = num_used_props * 2 - 1 trows = max([len(x) for x in list(self.m_t.m_P.m_props.values())]) + 2 # The column headings for idx, label in enumerate(self.m_t.m_P.header.qprop_labels): self.g_atable.attach(Gtk.Label(label=label), idx * 2, idx * 2 + 1, 0, 1, xoptions=Gtk.AttachOptions.FILL, yoptions=Gtk.AttachOptions.SHRINK, xpadding=gu.PAD_SMALL) # Then we create the buttons used to answer. for x, prop in enumerate(self.m_t.m_P.header.qprops): for y, proplabel in enumerate(self.m_t.m_P.m_props[prop]): button = Gtk.Button(str(proplabel)) button.m_property_name = prop button.m_property_value = proplabel.cval button.connect('clicked', self.on_prop_button_clicked) button.connect('button_release_event', self.on_prop_button_right_clicked) self.g_atable.attach(button, x * 2, x * 2 + 1, y + 2, y + 3, xpadding=gu.PAD_SMALL, yoptions=Gtk.AttachOptions.SHRINK) # The separator below the column headings self.g_atable.attach(Gtk.HSeparator(), 0, tcols, 1, 2, xoptions=Gtk.AttachOptions.FILL, yoptions=Gtk.AttachOptions.FILL, xpadding=0, ypadding=gu.PAD_SMALL) # The vertical separator between columns for idx in range(len(self.m_t.m_P.header.qprops) - 1): self.g_atable.attach(Gtk.VSeparator(), idx * 2 + 1, idx * 2 + 2, 0, trows, xoptions=Gtk.AttachOptions.FILL, yoptions=Gtk.AttachOptions.FILL, xpadding=0, ypadding=gu.PAD_SMALL) self.g_atable.show_all() # self.g_random_transpose.set_text( str(self.m_t.m_P.header.random_transpose)) def on_prop_button_clicked(self, button): if self.m_t.q_status != self.QSTATUS_NEW: return g = self.m_t.guess_property(button.m_property_name, button.m_property_value) if g: self.g_flashbar.flash(_("Correct")) for btn in self.g_atable.get_children(): if not isinstance(btn, Gtk.Button): continue if btn.m_property_name == button.m_property_name \ and btn.m_property_value == button.m_property_value: btn.get_children()[0].set_name("BoldText") break if g == self.m_t.ALL_CORRECT: self.all_guessed_correct() else: self.g_flashbar.flash(_("Wrong")) def on_prop_button_right_clicked(self, button, event): """ Search for a question in the lesson file with the same properties as the question being asked, but with the one property changed to be the property right-clicked on. Do nothing if no matching question is found. """ if event.button != 3: return if not self.m_t.m_P.has_question(): return if not self.m_t.m_P.header.enable_right_click: self.g_flashbar.flash( _("Right click is not allowed for this lesson file.")) return d = {} for k in self.m_t.m_P.header.qprops: d[k] = self.m_t.m_P.get_question()[k].cval # replace one property with the one we right clicked d[button.m_property_name] = button.m_property_value for idx, question in enumerate(self.m_t.m_P.m_questions): match = True for k in d: if d[k] != question[k].cval: match = False if match: try: self.m_t.m_P.play_question(question) except Exception as e: if not self.standard_exception_handler(e): raise return def all_guessed_correct(self): self.run_exception_handled(self.show_answer) self.std_buttons_answer_correct() def new_question(self, widget=None): def exception_cleanup(): soundcard.synth.stop() self.std_buttons_exception_cleanup() self.m_t.q_status = self.QSTATUS_NO # make sure all buttons are sensitive. for x in self.g_atable.get_children(): if isinstance(x, Gtk.Button): # Set name to "" to make all labels not have bold text. x.get_children()[0].set_name("") ## try: self.m_t.new_question() # Here, we could check if new_question returned PICKY, but # we choose to ignore it, because it will only happen once # when the user have clicked 'new question', and then set # the picky option in the preferences window and the clicks # 'new question' again. So clicking 'new question' will just # repeat the not answered question. self.g_music_displayer.clear() self.m_t.m_P.play_question() self.std_buttons_new_question() self.g_give_up.set_sensitive(True) [ btn for btn in self.g_atable.get_children() if isinstance(btn, Gtk.Button) ][-1].grab_focus() except Exception as e: if not self.standard_exception_handler(e, exception_cleanup): raise def on_start_practise(self): self.m_t.m_custom_mode = self.get_bool('gui/expert_mode') super(Gui, self).on_start_practise() for question in self.m_t.m_P.m_questions: question.active = 1 self.g_music_displayer.clear() self.update_select_question_buttons() if self.m_t.m_P.header.new_button_label: self.g_new.set_label(self.m_t.m_P.header.new_button_label) else: self.g_new.set_label(_("_New")) self.update_answer_buttons() self.std_buttons_start_practise() self.g_flashbar.require_size([ _("Click '%s' to begin." % self.g_new.get_label().replace("_", "")), "XXXX, root position, toptone: 5", ]) self.g_flashbar.delayed_flash( self.short_delay, _("Click '%s' to begin." % self.g_new.get_label().replace("_", ""))) def on_end_practise(self): self.m_t.end_practise() self.g_music_displayer.clear() self.std_buttons_end_practise() def give_up(self, widget=None): if self.m_t.q_status == self.QSTATUS_NEW: self.m_t.give_up() self.run_exception_handled(self.show_answer) self.std_buttons_give_up() for button in self.g_atable.get_children(): if isinstance(button, Gtk.Button): if button.m_property_value == self.m_t.m_P.get_question()[ button.m_property_name].cval: button.get_children()[0].set_name('BoldText') else: button.get_children()[0].set_name('') def show_answer(self): """ Show the answer in the music displayer. All callers must check for exceptions. """ fontsize = self.get_int('config/feta_font_size=20') if isinstance(self.m_t.m_P.get_question().music, lessonfile.Chord): clef = mpd.select_clef( self.m_t.m_P.get_music_as_notename_string('music')) self.g_music_displayer.display( r"\staff{\clef %s <%s>}" % (clef, self.m_t.m_P.get_music_as_notename_string('music')), fontsize) else: self.g_music_displayer.display(self.m_t.m_P.get_music(), fontsize)