lessonfile_editor_main.py :  » Business-Application » GNU-Solfege » solfege-3.16.3 » solfege » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Business Application » GNU Solfege 
GNU Solfege » solfege 3.16.3 » solfege » lessonfile_editor_main.py
# -*- coding: iso-8859-1 -*-
# GNU Solfege - free ear training software
# Copyright (C) 2004, 2005, 2006, 2007, 2008  Tom Cato Amundsen
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

from __future__ import absolute_import
from __future__ import division

import os
import sys

import pygtk
pygtk.require("2.0")
import gtk

from solfege import mpd
from solfege import gu
from solfege import lessonfile
from solfege import dataparser
from solfege import htmlwidget
from solfege import stock

gtk.stock_add([('solfege-notehead', _("Add noteheads"), 0, 0, ''),
               ('solfege-sharp', _("Add sharps"), 0, 0, ''),
               ('solfege-double-sharp', _("Add double-sharps"), 0, 0, ''),
               ('solfege-natural', _("Remove accidentals"), 0, 0, ''),
               ('solfege-flat', _("Add flats"), 0, 0, ''),
               ('solfege-double-flat', _("Add double-flats"), 0, 0, ''),
               ('solfege-erase', _("Delete tones"), 0, 0, ''),
               ])
app_version = "0.1.4"

class HelpWindow(gtk.Window):
    def __init__(self, parent):
        gtk.Window.__init__(self)
        self.set_title(_("GNU Solfege lesson file editor") )
        self.set_default_size(400, 400)
        self.g_parent = parent
        self.vbox = gtk.VBox()
        self.vbox.set_spacing(8)
        self.add(self.vbox)
        self.connect('delete_event', self.delete_cb)
        self.g_htmlwidget = htmlwidget.HtmlWidget(None, None)
        self.vbox.pack_start(self.g_htmlwidget)
        self.vbox.pack_start(gtk.HSeparator(), False)
        bbox = gtk.HButtonBox()
        bbox.set_border_width(8)
        self.vbox.pack_start(bbox, False)
        b = gtk.Button(stock=gtk.STOCK_CLOSE)
        b.connect('clicked', self.close_cb)
        bbox.pack_start(b)
        self.show_all()
        self.set_focus(b)
    def source(self, html):
        self.g_htmlwidget.source(html)
    def delete_cb(self, *v):
        self.g_parent.g_help_window = None
    def close_cb(self, w):
        self.g_parent.g_help_window = None
        self.destroy()


window_actions = [
    ('FileMenu', None, _('_File')),
    ('NewLessonfile', gtk.STOCK_NEW, None, None, 'new file', 'file_new_cb'),
    ('Open', gtk.STOCK_OPEN, None, None, 'Open lesson file', 'file_open_cb'),
    ('Save', gtk.STOCK_SAVE, None, None, 'Save the lesson file', 'file_save_cb'),
    ('SaveAs', gtk.STOCK_SAVE_AS, None, '<shift><ctrl>s', 'Save the lesson file with a new name', 'file_save_as_cb'),
    ('Quit', gtk.STOCK_QUIT, None, None, 'Quit program', 'quit_cb'),
    ('HelpMenu', None, _('_Help')),
    ('HelpHelp', gtk.STOCK_HELP, None, None, None, 'help_cb'),
    ('HelpAbout', None, _('_About'), '', '', 'about_cb'),
]
lessonfile_actions = [
    ('GotoFirstQuestion', gtk.STOCK_GOTO_FIRST, None, None,
     _('Go to the first question'), 'goto_first_question_cb'),
    ('GoBackQuestion', gtk.STOCK_GO_BACK, None, None,
     _('Go to the previous question'), 'go_back_question_cb'),
    ('GoForwardQuestion', gtk.STOCK_GO_FORWARD, None, None,
     _('Go to the next question'), 'go_forward_question_cb'),
    ('GotoLastQuestion', gtk.STOCK_GOTO_LAST, None, None,
     _('Go to the last question'), 'goto_last_question_cb'),
    ('NewQuestion', gtk.STOCK_ADD, None, None,
     _('Add a new question'), 'new_question_cb'),
    ('NoteheadCursor', 'solfege-notehead', None, None,
     _('Add noteheads'), 'select_cursor_notehead_cb'),
    ('SharpCursor', 'solfege-sharp', None, None,
     _('Add sharps'), 'select_cursor_sharp_cb'),
    ('DoubleSharpCursor', 'solfege-double-sharp', None, None,
     _('Add double-sharps'), 'select_cursor_2sharp_cb'),
    ('NaturalCursor', 'solfege-natural', None, None,
     _('Remove accidentals'), 'select_cursor_natural_cb'),
    ('FlatCursor', 'solfege-flat', None, None,
     _('Add flats'), 'select_cursor_flat_cb'),
    ('DoubleFlatCursor', 'solfege-double-flat', None, None,
     _('Add double-flats'), 'select_cursor_2flat_cb'),
    ('EraseCursor', 'solfege-erase', None, None,
     _('Delete tones'), 'select_cursor_erase_cb'),
]
ui_string = """<ui>
  <menubar name='Menubar'>
    <menu action='FileMenu'>
      <menuitem action='NewLessonfile'/>
      <menuitem action='Open'/>
      <menuitem action='Save'/>
      <menuitem action='SaveAs'/>
      <separator/>
      <menuitem action='Quit'/>
    </menu>
    <menu action='HelpMenu'>
      <menuitem action='HelpHelp'/>
      <menuitem action='HelpAbout'/>
    </menu>
  </menubar>
  <toolbar name='Toolbar'>
    <toolitem action='GotoFirstQuestion'/>
    <toolitem action='GoBackQuestion'/>
    <toolitem action='GoForwardQuestion'/>
    <toolitem action='GotoLastQuestion'/>
    <toolitem action='NewQuestion'/>
    <separator/>
    <toolitem action='NoteheadCursor'/>
    <toolitem action='DoubleSharpCursor'/>
    <toolitem action='SharpCursor'/>
    <toolitem action='NaturalCursor'/>
    <toolitem action='FlatCursor'/>
    <toolitem action='DoubleFlatCursor'/>
    <toolitem action='EraseCursor'/>
  </toolbar>
</ui>"""

def fix_actions(actions, instance):
    "Helper function to map methods to an instance"
    retval = []
    for i in range(len(actions)):
        curr = actions[i]
        if len(curr) > 5:
            curr = list(curr)
            curr[5] = getattr(instance, curr[5])
            curr = tuple(curr)
        retval.append(curr)
    return retval

class EditorLessonfile(object):
    def __init__(self):
        self.m_filename = None
        self.m_changed = False
        self.header = lessonfile._Header({'module': 'chord'})
        self.m_questions = [dataparser.Question()]
        self.m_questions[-1].music = lessonfile.Music("", "chord")
        self.m_questions[-1].name = ""
        self._idx = 0


class MainWin(gtk.Window):
    def __init__(self, datadir):
        gtk.Window.__init__(self)
        self.icons = stock.EditorIconFactory(self, datadir)
        self.connect('destroy', lambda w: gtk.main_quit())
        self.g_help_window = None
        # toplevel_vbox:
        #   -menubar
        #   -toolbar
        #   -notebook
        #   -statusbar
        self.toplevel_vbox = gtk.VBox()
        self.add(self.toplevel_vbox)
        self.create_menu_and_toolbar()
        self.g_notebook = gtk.Notebook()
        self.toplevel_vbox.pack_start(self.g_notebook)
        self.vbox = gtk.VBox()
        self.toplevel_vbox.pack_start(self.vbox)
        self.create_mainwin_ui()
        self.show_all()
    def create_mainwin_ui(self):
        qbox = gu.hig_dlg_vbox()
        self.g_notebook.append_page(qbox, gtk.Label(_("Questions")))
        gu.bLabel(qbox, _("Enter new chords using the mouse"), False, False)
        hbox = gu.bHBox(qbox, False, False)
        self.g_displayer = mpd.musicdisplayer.ChordEditor()
        self.g_displayer.connect('clicked', self.on_displayer_clicked)
        self.g_displayer.clear(2)
        gu.bLabel(hbox, "")
        hbox.pack_start(self.g_displayer, False)
        gu.bLabel(hbox, "")
        ##
        self.g_question_name = gtk.Entry()
        qbox.pack_start(gu.hig_label_widget(_("Question title:"), self.g_question_name, None), False)
        self.g_navinfo = gtk.Label("")
        qbox.pack_start(self.g_navinfo, False)

        ##
        self.m_P = EditorLessonfile()
        cvbox = gtk.VBox()
        self.g_notebook.append_page(cvbox, gtk.Label(_("Lessonfile header")))
        ## Header section
        sizegroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
        self.g_title = gtk.Entry()
        cvbox.pack_start(gu.hig_label_widget(_("File title:"), self.g_title, 
                        sizegroup))
        self.g_content_chord = gtk.RadioButton(None, "chord")
        self.g_content_chord_voicing = gtk.RadioButton(self.g_content_chord, "chord-voicing")
        self.g_content_idbyname = gtk.RadioButton(self.g_content_chord, "id-by-name")
        box = gtk.HBox()
        box.pack_start(self.g_content_chord)
        box.pack_start(self.g_content_chord_voicing)
        box.pack_start(self.g_content_idbyname)
        cvbox.pack_start(gu.hig_label_widget(_("Content:"), box, sizegroup))
        self.g_random_transpose = gtk.Entry()
        cvbox.pack_start(gu.hig_label_widget(_("Random transpose:"), 
            self.g_random_transpose, sizegroup))
        #
        #self.g_statusbar = gtk.Statusbar()
        #self.toplevel_vbox.pack_start(self.g_statusbar, False)
        self.update_appwin()
    def proceed_if_changed(self):
        if not self.m_P.m_changed:
            return True
        dialog = gtk.MessageDialog(self, gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION,
              gtk.BUTTONS_YES_NO, _("You have unsaved data. Proceed anyway?"))
        dialog.hide()
        if dialog.run() == gtk.RESPONSE_YES:
            dialog.destroy()
            return True
        dialog.destroy()
        return False
    def update_appwin(self):
        self.update_score()
        self.set_navinfo()
        self.g_title.set_text(self.m_P.header.title)
        self.g_random_transpose.set_text(str(self.m_P.header.random_transpose))
        {'chord': self.g_content_chord,
         'chordvoicing': self.g_content_chord_voicing,
         'idbyname': self.g_content_idbyname}[self.m_P.header.module].set_active(True)
    def set_navinfo(self):
        if self.m_P.m_filename:
            self.set_title(self.m_P.m_filename)
        else:
            self.set_title(_("No file"))
        self.g_navinfo.set_text(_("question %(idx)i of %(count)i") % {
            'idx': self.m_P._idx + 1,
            'count': len(self.m_P.m_questions)})
        self.g_question_name.set_text(self.m_P.m_questions[self.m_P._idx].name)
    def load_file(self, filename):
        self.m_P = lessonfile.ChordLessonfile(filename)
        self.m_P.m_changed = False
        if self.m_P.m_questions:
            self.m_P._idx = 0
            self.set_navinfo()
        else:
            # Do a little trick to make an empty question
            self.m_P.m_questions = [dataparser.Question()]
            self.m_P.m_questions[-1].music = lessonfile.Music("", "chord")
            self.m_P.m_questions[-1].name = ""
            self.m_P._idx = 0

        if self.m_P.header.module not in ('idbyname', 'chord', 'chordvoicing'):
                dialog = gtk.MessageDialog(self, gtk.DIALOG_MODAL,
                    gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE,
                    _("The exercise module '%s' is not supported yet. Cannot edit this file.") % c)
                dialog.run()
                dialog.destroy()
                self.m_P = EditorLessonfile()
        self.update_appwin()
    def file_open_cb(self, *v):
        dialog = gtk.FileChooserDialog(_("Open..."), self,
                                   gtk.FILE_CHOOSER_ACTION_OPEN,
                                   (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                                    gtk.STOCK_OPEN, gtk.RESPONSE_OK))
        dialog.set_default_response(gtk.RESPONSE_OK)
        if dialog.run() == gtk.RESPONSE_OK:
            filename = gu.decode_filename(dialog.get_filename())
            try:
                self.load_file(filename)
            except Exception, e:
                dialog.destroy()
                m = gtk.MessageDialog(self, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR,
                        gtk.BUTTONS_CLOSE,
                        _("Loading file '%(filename)s' failed: %(msg)s") %
                            {'filename': filename, 'msg': e})
                m.run()
                m.destroy()
            else:
                dialog.destroy()
        else:
            dialog.destroy()
    def file_new_cb(self, action, v=None):
        if self.proceed_if_changed():
            self.m_P = EditorLessonfile()
            self.update_appwin()
    def file_save_as_cb(self, *v):
        self.store_data_from_ui()
        dialog = gtk.FileChooserDialog(_("Save as..."), self,
                                  gtk.FILE_CHOOSER_ACTION_SAVE,
                                   (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                                    gtk.STOCK_SAVE, gtk.RESPONSE_OK))
        dialog.set_default_response(gtk.RESPONSE_OK)

        if dialog.run() == gtk.RESPONSE_OK:
            self.m_P.m_filename = gu.decode_filename(dialog.get_filename())
            self.save_file()
        dialog.destroy()
    def file_save_cb(self, *v):
        self.store_data_from_ui()
        if self.m_P.m_filename is None:
            dialog = gtk.FileChooserDialog(_("Save..."), self,
                                   gtk.FILE_CHOOSER_ACTION_SAVE,
                                   (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                                    gtk.STOCK_SAVE, gtk.RESPONSE_OK))
            dialog.set_default_response(gtk.RESPONSE_OK)

            if dialog.run() == gtk.RESPONSE_OK:
                self.m_P.m_filename = gu.decode_filename(dialog.get_filename())
            dialog.destroy()
        if self.m_P.m_filename:
            self.update_appwin()
            self.save_file()
    def save_file(self):
        if not self.m_P.m_filename:
            raise "No filename. Cannot save."
        ofile = open(self.m_P.m_filename, 'w')
        ofile.write("# Creator: GNU Solfege lesson file editor %s\n\n"
                    % app_version)
        ofile.write("header {\n    module = %s\n" % self.m_P.header.module)
        if type(self.m_P.header.random_transpose) == list:
            ofile.write("    random_transpose = %s, %s, %s\n" % (self.m_P.header.random_transpose[0],
                            self.m_P.header.random_transpose[1], self.m_P.header.random_transpose[2]))
        else:
            ofile.write("    random_transpose = yes\n")
        if self.m_P.header.lesson_id:
            ofile.write('    lesson_id = "%s"\n' % self.m_P.header.lesson_id)
        ofile.write('    title = "%s"\n}\n' % self.m_P.header.title)
        for q in self.m_P.m_questions:
            print >> ofile, 'question {'
            print >> ofile, '    name = "%s"' % q.name
            print >> ofile, '    music = music("%s", chord)' % q.music.m_musicdata
            print >> ofile, '}'
        ofile.close()
        self.m_P.m_changed = False

    def quit_cb(self, *v):
        if self.proceed_if_changed():
            gtk.main_quit()
    def help_cb(self, *v):
        if not self.g_help_window:
            self.g_help_window = HelpWindow(self)
            self.g_help_window.source("""<html>
<body>
<h2>GNU Solfege lesson file editor %s</h2>
<p>This is the very first unfinished release. Backup the files you
edit, since it can screw up.</p>
<p>The parser can create files for the chord exercise. It can parse more
advanced lesson files than it can write. So you might loose data if you
edit your hand written lesson files with this program.</p>
</body>
</html>
""" % app_version)
            self.g_help_window.show()
        else:
            self.g_help_window.present()
    def about_cb(self, *v):
        dialog = gtk.MessageDialog(self, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO,
            gtk.BUTTONS_CLOSE, "GNU Solfege lesson file editor %s\nCopyright (C) 2004, 2005 Tom Cato Amundsen <tca@gnu.org>" % app_version)
        dialog.run()
        dialog.destroy()
    def goto_first_question_cb(self, *v):
        self.store_data_from_ui()
        self.m_P._idx = 0
        self.update_appwin()
    def go_back_question_cb(self, *v):
        self.store_data_from_ui()
        self.m_P._idx = max(0, self.m_P._idx - 1)
        self.update_appwin()
    def go_forward_question_cb(self, *v):
        self.store_data_from_ui()
        self.m_P._idx = min(self.m_P._idx + 1, len(self.m_P.m_questions) - 1)
        self.update_appwin()
    def goto_last_question_cb(self, *v):
        self.store_data_from_ui()
        self.m_P._idx = len(self.m_P.m_questions) - 1
        self.update_appwin()
    def new_question_cb(self, *v):
        self.store_data_from_ui()
        self.m_P.m_questions.append(dataparser.Question())
        self.m_P.m_questions[-1].music = lessonfile.Music("", "chord")
        self.m_P.m_questions[-1].name = ""
        self.m_P._idx = len(self.m_P.m_questions) - 1
        self.update_appwin()
    def select_cursor_2flat_cb(self, *v):
        self.g_displayer.set_cursor("-2")
    def select_cursor_flat_cb(self, *v):
        self.g_displayer.set_cursor(-1)
    def select_cursor_natural_cb(self, *v):
        self.g_displayer.set_cursor(0)
    def select_cursor_sharp_cb(self, *v):
        self.g_displayer.set_cursor("1")
    def select_cursor_2sharp_cb(self, *v):
        self.g_displayer.set_cursor("2")
    def select_cursor_erase_cb(self, *v):
        self.g_displayer.set_cursor("erase")
    def select_cursor_notehead_cb(self, *v):
        self.g_displayer.set_cursor("notehead")
    def update_score(self):
        """
        Set m_chord_tones based on the data in the lesson file.
        Then call g_displayer.display to show the music.
        """
        assert self.m_P
        self.m_chord_tones = {}
        for n in self.m_P.m_questions[self.m_P._idx].music.m_musicdata.split():
            p = mpd.MusicalPitch.new_from_notename(n)
            self.m_chord_tones[p.steps()] = p
        #
        if self.m_chord_tones:
            s = ""
            for n in self.m_chord_tones.values():
                s += " " + n.get_octave_notename()
            self.g_displayer.display("\staff{ < %s >}\staff{\clef bass}" % s, "20-tight")
        else:
            self.g_displayer.display("\staff{ }\staff{\clef bass}", "20-tight")
        self.g_displayer.set_size_request(400, -1)
    def store_data_from_ui(self):
        self.m_P.m_questions[self.m_P._idx].name = self.g_question_name.get_text()
        self.m_P.header.title = self.g_title.get_text()
        self.m_P.header.random_transpose = eval(self.g_random_transpose.get_text())
        if self.g_content_chord.get_active():
            self.m_P.header.module = 'chord'
        if self.g_content_chord_voicing.get_active():
            self.m_P.header.module = 'chordvoicing'
        if self.g_content_idbyname.get_active():
            self.m_P.header.module = 'idbyname'
    def on_displayer_clicked(self, ed, steps):
        self.m_P.m_changed = True
        notename = ("c", "d", "e", "f", "g", "a", "b")[6-(steps % 7)]
        n = mpd.MusicalPitch.new_from_notename(notename)
        n.m_octave_i = 1-(steps // 7)
        if self.g_displayer.m_cursor == 'notehead':
            if n.steps() not in self.m_chord_tones:
                self.m_chord_tones[n.steps()] = n
        elif self.g_displayer.m_cursor == 'erase':
            if n.steps() in self.m_chord_tones:
                del self.m_chord_tones[n.steps()]
        else:
            if n.steps() not in self.m_chord_tones:
                return
            else:
                self.m_chord_tones[n.steps()].m_accidental_i = int(self.g_displayer.m_cursor)
        v = self.m_chord_tones.values()
        v.sort()
        v = [y.get_octave_notename() for y in v]
        self.m_P.m_questions[self.m_P._idx].music.m_musicdata = " ".join(v)
        self.update_score()

class UIManagerMainWin(MainWin):
    def __init__(self, datadir):
        MainWin.__init__(self, datadir)
    def create_menu_and_toolbar(self):
        self.window_ag = gtk.ActionGroup('WindowActions')
        self.lessonfile_ag = gtk.ActionGroup('LessonfileActions')

        self.window_ag.add_actions(fix_actions(window_actions, self))
        self.lessonfile_ag.add_actions(fix_actions(lessonfile_actions, self))
        self.ui = gtk.UIManager()
        self.ui.insert_action_group(self.window_ag, 0)
        self.ui.insert_action_group(self.lessonfile_ag, 1)
        self.ui.add_ui_from_string(ui_string)
        self.add_accel_group(self.ui.get_accel_group())
        self.toplevel_vbox.pack_start(self.ui.get_widget('/Menubar'), False)
        self.ui.get_widget('/Toolbar').set_style(gtk.TOOLBAR_ICONS)
        self.toplevel_vbox.pack_start(self.ui.get_widget('/Toolbar'), False)

def main(datadir):
    mpd.engravers.fetadir = os.path.join(datadir, "feta")
    w = UIManagerMainWin(datadir)
    if len(sys.argv) == 2:
        w.load_file(sys.argv[1])
    w.show()
    gtk.main()
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.