diff --git a/PreferencesWindow.py b/PreferencesWindow.py new file mode 100644 index 0000000..9cdb178 --- /dev/null +++ b/PreferencesWindow.py @@ -0,0 +1,136 @@ +import gtk +import Settings +import SubprocessWrapper +import os.path +from gettext import gettext as _ +from DialogSimpleMessages import ShowDialogError +from pygtkutils import * + +def showPreferencesWindow(gladeFile, iconLogo): + prefsWindow = PreferencesWindow(gladeFile, iconLogo) + +class PreferencesWindow(object): + def __init__(self, gladeFile, iconLogo): + self.gladeFile = gladeFile + self.loadControls() + self.dlgPrefs.set_icon_from_file(iconLogo) + signals = { + 'on_cboPlayer_changed': self.on_cboPlayer_changed, + 'on_btnPlayerTest_clicked': self.on_btnPlayerTest_clicked, + 'on_chkCustomWelcome_toggled': self.on_chkCustomWelcome_toggled, + 'on_btnOk_clicked': self.on_btnOk_clicked + } + self.gladeFile.signal_autoconnect(signals) + # Load settings + self.cboPlayer.set_active(Settings.get('PlayMethod')) + self.txtPlayerCommand.set_text(Settings.get('PlayCommand')) + self.chkPlayWelcome.set_active(Settings.get('PlayWelcomeText') == True) + self.chkCustomWelcome.set_active(Settings.get('UseCustomWelcome') == True) + self.txtWelcomeText.set_text(Settings.get('WelcomeText')) + self.chkSaveVoice.set_active(Settings.get('SaveVoiceSettings') == True) + self.chkSaveSize.set_active(Settings.get('SaveWindowSize') == True) + self.chkSingleRecord.set_active(Settings.get('SingleRecord') == True) + self.chkWordWrap.set_active(Settings.get('WordWrap') == True) + self.chkLoadVariants.set_active(Settings.get('LoadVariants') == True) + # Before to use the window property the realize method must be called + self.dlgPrefs.realize() + # Change WM buttons making the window only movable with the closing button + self.dlgPrefs.window.set_functions(gtk.gdk.FUNC_CLOSE | gtk.gdk.FUNC_MOVE) + self.dlgPrefs.run() + self.dlgPrefs.destroy() + + def loadControls(self): + def separator_filter(model, iter, data=None): + return model.get_value(iter, 2) + gw = self.gladeFile.get_widget + self.dlgPrefs = gw('dlgPreferences') + self.cboPlayer = gw('cboPlayer') + self.lblPlayerCommand = gw('lblPlayerCommand') + self.txtPlayerCommand = gw('txtPlayerCommand') + self.btnPlayerTest = gw('btnPlayerTest') + self.chkPlayWelcome = gw('chkPlayWelcome') + self.chkCustomWelcome = gw('chkCustomWelcome') + self.lblCustomWelcome = gw('lblCustomWelcome') + self.txtWelcomeText = gw('txtWelcomeText') + self.chkSaveVoice = gw('chkSaveVoice') + self.chkSaveSize = gw('chkSaveSize') + self.chkSingleRecord = gw('chkRecordSingleTrack') + self.chkWordWrap = gw('chkWordWrap') + self.chkLoadVariants = gw('chkLoadVariants') + self.btnOk = gw('btnOk') + # Prepare model for players combo + listStore = gtk.ListStore(gtk.gdk.Pixbuf, str, bool) + self.cboPlayer.set_model(listStore) + # First is image + cell = gtk.CellRendererPixbuf() + self.cboPlayer.pack_start(cell, False) + self.cboPlayer.add_attribute(cell, 'pixbuf', 0) + # Second is text + cell = gtk.CellRendererText() + self.cboPlayer.pack_start(cell, False) + self.cboPlayer.add_attribute(cell, 'text', 1) + self.cboPlayer.set_row_separator_func(separator_filter) + # Load icons and text for methods + listStore.append([Pixbuf_load_file( + os.path.join(os.path.dirname(__file__), 'alsalogo.png'), (24, 24)), + _('ALSA - Advanced Linux Sound Architecture'), False]) + listStore.append([Pixbuf_load_file( + os.path.join(os.path.dirname(__file__), 'palogo.png'), (24, 24)), + _('PulseAudio sound server'), False]) + listStore.append([None, '_', True]) + listStore.append([None, _('Custom sound application'), False]) + # Change testing button caption + Button_change_stock_description(self.btnPlayerTest, _('_Test'), True) + + def on_chkCustomWelcome_toggled(self, widget, data=None): + self.lblCustomWelcome.set_sensitive(self.chkCustomWelcome.get_active()) + self.txtWelcomeText.set_sensitive(self.chkCustomWelcome.get_active()) + + def on_btnOk_clicked(self, widget, data=None): + "Apply settings" + Settings.set('PlayMethod', self.cboPlayer.get_active()) + Settings.set('PlayCommand', self.txtPlayerCommand.get_text()) + Settings.set('PlayWelcomeText', self.chkPlayWelcome.get_active()) + Settings.set('UseCustomWelcome', self.chkCustomWelcome.get_active()) + Settings.set('WelcomeText', self.txtWelcomeText.get_text()) + Settings.set('SaveVoiceSettings', self.chkSaveVoice.get_active()) + Settings.set('SaveWindowSize', self.chkSaveSize.get_active()) + Settings.set('SingleRecord', self.chkSingleRecord.get_active()) + Settings.set('WordWrap', self.chkWordWrap.get_active()) + Settings.set('LoadVariants', self.chkLoadVariants.get_active()) + + def on_cboPlayer_changed(self, widget, data=None): + "Enable and disable controls if custom command is not set" + active = self.cboPlayer.get_active() + text = self.txtPlayerCommand.get_text() + self.lblPlayerCommand.set_sensitive(active == 3) + self.txtPlayerCommand.set_sensitive(active == 3) + self.btnOk.set_sensitive((active != 3) or bool(text)) + self.btnPlayerTest.set_sensitive((active != 3) or bool(text)) + + def on_btnPlayerTest_clicked(self, widget, data=None): + "Test selected player with testing.wav" + # Set waiting cursor + Window_change_cursor(self.dlgPrefs.window, gtk.gdk.WATCH, True) + players = ('aplay', 'paplay', '', self.txtPlayerCommand.get_text()) + filename = os.path.join(os.path.dirname(__file__), 'testing.wav') + test = SubprocessWrapper.Popen(['cat', filename], + stdout=SubprocessWrapper.PIPE) + play = None + try: + # Try to play with pipe + play = SubprocessWrapper.Popen(players[self.cboPlayer.get_active()], + stdin=test.stdout, + stdout=SubprocessWrapper.PIPE, + stderr=SubprocessWrapper.PIPE) + play.communicate() + except OSError, (errno, strerror): + # Error during communicate" + ShowDialogError(title=_('Audio testing'), showOk=True, + text=_('There was an error during the test for the audio player.\n' + 'Error %s: %s' % (errno, strerror))) + # Terminate test if it's still running, follows a broken pipe error + if test.poll() is None: + test.terminate() + # Restore default cursor + Window_change_cursor(self.dlgPrefs.window, None, False) diff --git a/Settings.py b/Settings.py new file mode 100644 index 0000000..097b4cf --- /dev/null +++ b/Settings.py @@ -0,0 +1,88 @@ +import ConfigParser +import os +from gettext import gettext as _ + +config = None +confdir = os.path.join(os.path.expanduser('~/.gespeaker')) +conffile = os.path.join(confdir, 'settings.conf') +__sectionSettings = 'settings' +__sectionWindowSize = 'window size' +__sectionVoiceSetting = 'voice settings' +__defSettings = None + +if not os.path.exists(confdir): + os.mkdir(confdir) + +def load(filename=conffile): + "Load settings from the configuration file" + global config + config = ConfigParser.RawConfigParser() + loadDefaults() + if os.path.exists(filename): + config.read(filename) + # Settings lookup is made upon __defSettings + for setting in __defSettings.keys(): + # Add section if doesn't exist + if not config.has_section(__defSettings[setting][2]): + config.add_section(__defSettings[setting][2]) + if not config.has_option(__defSettings[setting][2], setting): + config.set(__defSettings[setting][2], setting, str(__defSettings[setting][1])) + +def loadDefaults(): + global __defSettings + strbool = lambda value: value == 'True' + __defSettings = { + 'PlayMethod': [int, 0, __sectionSettings], + 'PlayCommand': [str, 'aplay', __sectionSettings], + 'PlayWelcomeText': [strbool, True, __sectionSettings], + 'UseCustomWelcome': [strbool, False, __sectionSettings], + 'WelcomeText': [str, _('Welcome in Gespeaker'), __sectionSettings], + 'SaveVoiceSettings': [strbool, True, __sectionSettings], + 'SaveWindowSize': [strbool, False, __sectionSettings], + 'SingleRecord': [strbool, True, __sectionSettings], + 'WordWrap': [strbool, False, __sectionSettings], + 'LoadVariants': [strbool, True, __sectionSettings], + 'MainWindowWidth': [int, 440, __sectionWindowSize], + 'MainWindowHeight': [int, 470, __sectionWindowSize], + 'SettingsExpander': [strbool, True, __sectionWindowSize], + 'VoiceVolume': [int, 100, __sectionVoiceSetting], + 'VoicePitch': [int, 50, __sectionVoiceSetting], + 'VoiceSpeed': [int, 170, __sectionVoiceSetting], + 'VoiceDelay': [int, 10, __sectionVoiceSetting], + 'VoiceTypeMale': [strbool, True, __sectionVoiceSetting], + 'VoiceLanguage': [int, -1, __sectionVoiceSetting] + } + +def save(filename=conffile, clearDefaults=False): + "Save settings into the configuration file" + file = open(filename, mode='w') + if clearDefaults: + for setting in __defSettings.keys(): + if config.has_option(__defSettings[setting][2], setting): + if get(setting) == __defSettings[setting][1]: + config.remove_option(__defSettings[setting][2], setting) + config.write(file) + file.close + +def get(setting): + "Returns a specified setting from the configuration or default values" + if config.has_option(__defSettings[setting][2], setting): + return __defSettings[setting][0](config.get(__defSettings[setting][2], setting)) + elif __defSettings.has_key(setting): + return __defSettings[setting][1] + else: + print 'unknown setting: %s' % setting + +def default(setting): + "Returns the default value for a specified setting" + if __defSettings.has_key(setting): + return __defSettings[setting][1] + +def set(setting, value): + "Sets a specific setting to the value." + #" If it's the default then delete it" + #if __defSettings.has_key(setting) and value == __defSettings[setting][1]: + # config.remove_option(__defSettings[setting][2], setting) + #else: + #print __defSettings[setting][2], setting, str(value) + config.set(__defSettings[setting][2], setting, str(value)) diff --git a/alsalogo.png b/alsalogo.png new file mode 100644 index 0000000..2f43667 Binary files /dev/null and b/alsalogo.png differ diff --git a/palogo.png b/palogo.png new file mode 100644 index 0000000..1f80414 Binary files /dev/null and b/palogo.png differ diff --git a/preferences.glade b/preferences.glade new file mode 100644 index 0000000..2c3ddc8 --- /dev/null +++ b/preferences.glade @@ -0,0 +1,423 @@ + + + + + + 5 + Preferences + False + center-on-parent + dialog + False + + + True + vertical + + + True + 2 + vertical + + + True + 0 + 0 + 6 + <b>Audio player</b> + True + + + False + False + 0 + + + + + True + 9 + + + True + 0 + 0.30000001192092896 + 40 + multimedia-player + + + False + False + 0 + + + + + True + vertical + + + True + + + + False + False + 0 + + + + + True + 2 + 8 + + + True + False + 0 + Co_mmand: + True + txtPlayerCommand + + + False + 0 + + + + + True + False + True + + + + + 1 + + + + + gtk-apply + True + True + True + True + + + + 2 + + + + + 2 + 1 + + + + + 1 + + + + + 1 + + + + + True + 0 + <b>Welcome message</b> + True + + + False + False + 5 + 2 + + + + + True + 12 + + + True + vertical + + + Speak _welcome text on program start + True + True + False + True + True + True + + + 0 + + + + + _Use custom welcome message + True + True + False + True + True + + + + 1 + + + + + True + 2 + 6 + + + True + False + Custom mess_age: + True + txtWelcomeText + + + False + 0 + + + + + True + False + True + + + + 1 + + + + + 2 + + + + + + + 3 + + + + + True + 0 + <b>Saving preferences</b> + True + + + False + False + 5 + 4 + + + + + True + 12 + + + True + vertical + + + _Save voice settings automatically + True + True + False + True + True + True + + + 0 + + + + + Save main window s_ize + True + True + False + True + True + + + 1 + + + + + + + 5 + + + + + True + 0 + <b>Recording</b> + True + + + False + False + 5 + 6 + + + + + True + 12 + + + True + vertical + + + Single track _record + True + True + False + True + True + True + + + 0 + + + + + + + 7 + + + + + True + 0 + <b>Other settings</b> + True + + + False + False + 5 + 8 + + + + + True + 12 + + + True + vertical + + + Enable te_xt wrapping + True + True + False + True + True + + + 0 + + + + + Load voice _variants + True + True + False + True + True + + + 1 + + + + + + + 9 + + + + + False + 1 + + + + + True + end + + + gtk-cancel + True + True + True + True + + + False + False + 0 + + + + + gtk-ok + True + True + True + True + + + + False + False + 1 + + + + + False + end + 0 + + + + + + diff --git a/pygtkutils.py b/pygtkutils.py new file mode 100644 index 0000000..8f213cf --- /dev/null +++ b/pygtkutils.py @@ -0,0 +1,42 @@ +import gtk + +def TextBuffer_get_text(buffer): + "Return the whole text on the TextBuffer" + return buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter()) + +def Radio_get_active(group): + "Return the currently active radio button on the group" + for button in group: + if button.get_active(): + return button + +def Pixbuf_load_file(filename, size=None): + "Load an image file with the desired size if requested" + if size and len(size) == 2: + return gtk.gdk.pixbuf_new_from_file_at_size(filename, size[0], size[1]) + else: + return gtk.gdk.pixbuf_new_from_file(filename) + +def Window_change_cursor(window, cursor, refresh=False): + "Change a window's cursor and optionally forces the refresh" + window.set_cursor(cursor and gtk.gdk.Cursor(cursor) or None) + if refresh: + gtk.gdk.flush() + +def Button_change_stock_description(button, caption, use_underline=None): + "Change stock button description" + alignment = button.get_children()[0] + box = alignment.get_children()[0] + first, second = box.get_children() + # Find label + if type(first) is gtk.Label: + label = first + elif type(second) is gtk.Label: + label = second + else: + label = None + if label: + label.set_text(caption) + # Set use_underline + if use_underline is not None: + label.set_use_underline(use_underline) diff --git a/testing.wav b/testing.wav new file mode 100644 index 0000000..3dfc6d7 Binary files /dev/null and b/testing.wav differ