File: gtklick.py

package info (click to toggle)
gtklick 0.6.4-3
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 340 kB
  • sloc: python: 1,185; makefile: 6
file content (209 lines) | stat: -rw-r--r-- 7,323 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# gtklick
#
# Copyright (C) 2008-2010  Dominic Sacré  <dominic.sacre@gmx.de>
#
# 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 2 of the License, or
# (at your option) any later version.


import pygtk
pygtk.require('2.0')
import gtk
import gtk.glade
import gobject

import getopt
import sys
import os.path
import weakref

import gettext
import locale
import __builtin__
__builtin__._ = gettext.gettext

import klick_backend
import gtklick_config
import main_window
import profiles_pane
import preferences_dialog
import misc


class GTKlick:
    def __init__(self, args, share_dir, locale_dir):
        try:
            locale.setlocale(locale.LC_ALL, '')
        except locale.Error:
            # don't crash when run with unsupported locale
            pass

        for m in gettext, gtk.glade:
            m.bindtextdomain('gtklick', locale_dir)
            m.textdomain('gtklick')

        self.config = None
        self.parse_cmdline(args)

        gtk.gdk.threads_init()

        try:
            self.setup(share_dir)
            if not self.connect:
                self.restore_config()
            else:
                self.query_config()
        except klick_backend.KlickBackendError, e:
            self.error_message(e.msg)
            sys.exit(1)

        # start timer to check if klick is still running
        if self.klick.process:
            self.timer = gobject.timeout_add(1000, misc.weakref_method(self.check_klick))

    def __del__(self):
        if self.config:
            self.config.write()

    # parse command line arguments
    def parse_cmdline(self, args):
        self.port = None
        self.return_port = None
        self.connect = False
        self.verbose = False
        try:
            r = getopt.getopt(args, 'o:q:r:Lh');
            for opt, arg in r[0]:
                if opt == '-o':
                    self.port = arg
                    self.connect = False
                elif opt == '-q':
                    self.port = arg
                    self.connect = True
                elif opt == '-r':
                    self.return_port = arg
                elif opt == '-L':
                    self.verbose = True
                elif opt == '-h':
                    self.print_help()
                    sys.exit(0)
        except getopt.GetoptError, e:
            sys.exit(e.msg)

    def print_help(self):
        print _("Usage:\n" \
                "  gtklick [ options ]\n" \
                "\n" \
                "Options:\n" \
                "  -o port   OSC port to start klick with\n" \
                "  -q port   OSC port of running klick instance to connect to\n" \
                "  -r port   OSC port to be used for gtklick\n" \
                "  -h        show this help")

    # create windows, config, and klick backend
    def setup(self, share_dir):
        self.wtree = gtk.glade.XML(os.path.join(share_dir, 'gtklick.glade'))
        # explicitly call base class method, because get_name() is overridden in AboutDialog. stupid GTK...
        self.widgets = dict([(gtk.Widget.get_name(w), w) for w in self.wtree.get_widget_prefix('')])

        self.config = gtklick_config.GTKlickConfig()

        # load config from file
        self.config.read()

        # start klick process
        self.klick = klick_backend.KlickBackend('gtklick', self.port, self.return_port, self.connect, self.verbose)

        # make "globals" known in other modules
        for m in (main_window, profiles_pane, preferences_dialog):
            m.wtree = self.wtree
            m.widgets = self.widgets
            m.klick = weakref.proxy(self.klick)
            m.config = weakref.proxy(self.config)

        # the actual windows are created by glade, this basically just connects GUI and OSC callbacks
        self.win = main_window.MainWindow()
        self.profiles = profiles_pane.ProfilesPane(self.win)
        self.prefs = preferences_dialog.PreferencesDialog()

        #self.klick.add_method(None, None, self.fallback)

    # restore settings from config file.
    # many settings are just sent to klick, and the OSC notifications will take care of the rest
    def restore_config(self):
        # port connections
        if len(self.config.prefs_connect_ports):
            ports = self.config.prefs_connect_ports.split('\0')
            for p in ports:
                self.prefs.model_ports.append([p])
        else:
            ports = []

        if self.config.prefs_autoconnect:
            misc.do_quietly(lambda: self.widgets['radio_connect_auto'].set_active(True))
            self.klick.send('/config/autoconnect')
        else:
            misc.do_quietly(lambda: self.widgets['radio_connect_manual'].set_active(True))
            self.klick.send('/config/connect', *ports)

        # sound / volume
        if self.config.prefs_sound >= 0:
            self.klick.send('/config/set_sound', self.config.prefs_sound)
        else:
            self.klick.send('/config/set_sound', self.config.prefs_sound_accented, self.config.prefs_sound_normal)

        self.klick.send('/config/set_sound_pitch',
            2 ** (self.config.prefs_pitch_accented / 12.0),
            2 ** (self.config.prefs_pitch_normal / 12.0)
        )
        self.klick.send('/config/set_volume', self.config.volume)

        # metronome state
        misc.do_quietly(lambda: (
            self.widgets['check_speedtrainer_enable'].set_active(self.config.speedtrainer),
            self.widgets['spin_tempo_increment'].set_value(self.config.tempo_increment),
            self.widgets['radio_meter_other'].set_active(self.config.denom != 0)
        ))
        self.widgets['spin_tempo_increment'].set_sensitive(self.config.speedtrainer)
        self.widgets['spin_tempo_start'].set_sensitive(self.config.speedtrainer)

        self.klick.send('/simple/set_tempo', self.config.tempo)
        self.klick.send('/simple/set_tempo_increment', self.config.tempo_increment if self.config.speedtrainer else 0.0)
        self.klick.send('/simple/set_tempo_start', self.config.tempo_start)
        self.klick.send('/simple/set_meter', self.config.beats, self.config.denom if self.config.denom else 4)
        self.klick.send('/simple/set_pattern', self.config.pattern)

    # get current settings from running klick instance
    def query_config(self):
        self.klick.send('/query')

    # start the whole thing
    def run(self):
        self.widgets['window_main'].show()
        gtk.main()

    # check if klick is still running
    def check_klick(self):
        if not self.klick.check_process():
            self.error_message(_("klick seems to have been killed, can't continue without it"))
            sys.exit(1)
        return True

    def fallback(self, path, args, types, src):
        print "message not handled:", path, args, src.get_url()

    def error_message(self, msg):
        m = gtk.MessageDialog(self.wtree.get_widget('window_main'), 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
        m.set_title(_("gtklick error"))
        m.run()
        m.destroy()


if __name__ == '__main__':
    app = GTKlick(sys.argv[1:], 'share', 'build/locale')
    app.run()