Sehr schön, das wird ja richtig was.
Ich habe trotzdem mal ein paar Änderungen vorgenommen:
Code: Select all
from win32com.client import Dispatch
from time import strftime
import eg
import wx
import os
rateList = ["-5","-4","-3","-2","-1","0","+1","+2","+3","+4","+5"]
dateList = ("None","Local time","Local date","Date and time")
class Speech(eg.PluginClass):
def __init__(self):
maingroup = self.AddGroup(self.name)
maingroup.AddAction(self.TextToSpeech)
try:
self.VoiceObj = Dispatch("Sapi.SpVoice")
except:
eg.PrintError ("Cannot create Voice object")
return
# enumerate all available voices and store them in a dict with their
# name as key
voices = {}
for voice in self.VoiceObj.GetVoices():
voices[voice.GetDescription()] = voice
self.voices = voices
class TextToSpeech(eg.ActionClass):
def __call__(self, voice_name, rate, text, time, volume):
text = eg.parseString(text)
VoiceObj = self.plugin.VoiceObj
try:
VoiceObj.Voice = self.plugin.voices[voice_name]
except:
eg.PrintError ("Voice with name %s is not available" % voice_name)
VoiceObj.Rate = rate
VoiceObj.Volume = volume
if time == 1:
text+= ", " + strftime("%X")
elif time == 2:
text+= ", " + strftime("%x")
elif time == 3:
text+= ", " + strftime("%x") + ", " + strftime("%X")
VoiceObj.Speak(text, 1)
def GetLabel(self, voice_name, rate, text, time, volume):
return "Speak: %s" % text
def SetupConfigDialog(self, dialog, voice_name=None, rate="0",
text="Enter your text here", time=0, volume=100):
plugin = self.plugin
VoiceObj = plugin.VoiceObj
myvoices = plugin.voices.keys()
myvoices.sort()
try:
voice_index = myvoices.index(voice_name)
except:
voice_index = 0
sizer = dialog.sizer
mySizer = wx.FlexGridSizer(cols=2)
desc1 = wx.StaticText(dialog, -1, "Select Voice:")
mySizer.Add(desc1, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5)
choice1 = wx.Choice(dialog, -1, choices=myvoices)
choice1.Select(voice_index)
mySizer.Add(choice1, 0, wx.EXPAND|wx.ALL, 5)
desc2 = wx.StaticText(dialog, -1, "Select Voice Rate:")
mySizer.Add(desc2, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5)
choice2 = wx.ComboBox(dialog, -1, choices=rateList)
choice2.Select(rateList.index(str(rate)))
mySizer.Add(choice2, 0, wx.EXPAND|wx.ALL, 5)
desc3 = wx.StaticText(dialog, -1, "Time and date:")
mySizer.Add(desc3, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5)
choice3 = wx.Choice(dialog, -1, choices=dateList)
choice3.Select(time)
mySizer.Add(choice3, 0, wx.EXPAND|wx.ALL, 5)
desc4 = wx.StaticText(dialog, -1, "Volume (percent):")
mySizer.Add(desc4, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5)
vol_ctrl = eg.SpinIntCtrl(dialog, -1, volume, 0, 100)
mySizer.Add(vol_ctrl, 0, wx.ALL, 5)
sizer.Add(mySizer, 1, wx.EXPAND)
textctrl = wx.TextCtrl(dialog, -1, text)
sizer.Add(textctrl, 0, wx.EXPAND|wx.ALL, 5)
button = wx.Button(dialog, -1, label="Test")
def OnButton(event):
self(*ReturnResult())
button.Bind(wx.EVT_BUTTON, OnButton)
sizer.Add(button, 0, wx.EXPAND|wx.ALL, 5)
def ReturnResult():
return (choice1.GetStringSelection(),
choice2.GetValue(),
textctrl.GetValue(),
choice3.GetSelection(),
vol_ctrl.GetValue())
return ReturnResult
Zuerst habe ich mal alle verfügbaren Stimmen in ein Dictionary speichern lassen, damit man hinterher einfach über den Namen auf eine Stimme zugreifen kann. Dadurch speichert er jetzt auch die gesetzte Sprache richtig und die Konfiguration sollte sich auf andere Rechner übertragen lassen.
Die zweite Änderung ist, dass ich in der __call__ Routine den text zuerst über eg.parseString laufen lasse. Diese Funktion nimmt die Ersetzungen mit dem Geschweifte-Klammern-Syntax vor. Jetzt kann man also auch Variablen in das Textfeld eintragen.
Der Rest sind ein paar Optimierung des Zugriffes auf Objekte.
Vielleicht sollte man auch noch die Lautstärke auswählbar machen. Die geht bei SAPI glaube ich von 0 bis 100 (also in Prozent)? Dafür bietet sich
eg.SpinIntCtrl(dialog, -1, volume, min=0, max=100)
als Control an.
Lässt du EG mittlerweile vom Source-Code laufen? Ich glaube nämlich win32com.Dispatch läuft so nicht direkt bei den Leuten mit einer EXE Installation von EG, weil dieses dynamische Erzeugen der Wrapper über gen_py nicht drin ist. Dafür musste ich bisher immer das erzeugte Interface-File selber in das Plugin legen.