Exemplo n.º 1
0
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
Exemplo n.º 2
0
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
Exemplo n.º 3
0
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
Exemplo n.º 4
0
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()
Exemplo n.º 5
0
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)