19 oct. 2020

[Dynamo += Python] Et si on parlait de Revit? (MAJ/Update)

 


Oups !, il y a une petite erreur dans le Titre....

Et si on parlait de à Revit? ... via Dynamo 


Voici un exemple d'utilisation conjointe du moteur de reconnaissance vocale du Framework .Net et des "ExternalEvent" de l'API Revit

Les opérations sont issues de l'énumération PostableCommand, elles sont déclenchées via des Évents (classe API ExternalEvent)  qui sont eux même encapsulés dans des objets (via un héritage de Classe).




Note 1 : dans l'exemple ci-dessus, les commandes vocales seront "MirrorProject",  "Space", etc…


Note 2 : pourquoi passer par des Events ?
Les 
PostableCommands doivent être exécutés dans le Thread principal (API context)

"Posts the command to the Revit message queue to be invoked when control returns from the current API context."

Plus de détails ici


Note 3 : il faut que le moteur de reconnaissance vocale soit encapsulé dans un Thread continu, ici, on utilise la boucle d'événements  (Even Loop) d'un objet Winform (avec une fenêtre non bloquante .Show() ), cela évite de construire un nouveau Thread.


Note 4 : l'exemple ci-dessous utilise le TTS anglais, si la reconnaissance vocale anglaise est disponible sur votre Windows, vous pouvez, pour de meilleurs résultats, remplacer cette ligne :

self.sre = SpeechRecognitionEngine()
par celle-ci
self.sre = SpeechRecognitionEngine(greatBritainCulture)

Néanmoins, une customisation de la classe EventUtils reste possible pour l'adapter à un moteur vocal Français (via un dictionnaire de correspondance par exemple).


le code IronPython:


#coding: utf-8
#written by Cyril POUPIN
#v5
import sys
import clr
import System 

clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *

clr.AddReference('RevitAPIUI')
from Autodesk.Revit.UI import *

clr.AddReference("System.Drawing")
clr.AddReference("System.Windows.Forms")
import System.Drawing
import System.Windows.Forms

from System.Drawing import *
from System.Windows.Forms import *
from System import Array 

clr.AddReference("System.Speech, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")
from System.Speech.Recognition import (SpeechRecognitionEngine, GrammarBuilder, Grammar, Choices, RecognizeMode)
from System.Speech.Synthesis import SpeechSynthesizer, VoiceGender, VoiceAge


class ModExtEvent(IExternalEventHandler):
    def __init__(self, comandePostable):
        self._comandePostable = comandePostable
        self.name = comandePostable.ToString()

    def Execute(self, _uiap):
        _uiap.PostCommand(RevitCommandId.LookupPostableCommandId(self._comandePostable)) 
        
    def GetName(self):
        return self.name    

class EventUtils(ModExtEvent):
    def __init__(self, comandePostable):
        ModExtEvent.__init__(self, comandePostable)
        self.ext_event = ExternalEvent.Create(self)
        
            
class Form1(Form):
    def __init__(self, lstObj_handler):
        lstName = Array[System.String]( [x.name for x in lstObj_handler])
        self.lstComandName = lstName[:]
        self._lstObj_handler = lstObj_handler
        greatBritainCulture = System.Globalization.CultureInfo("en-GB")
        self.sre = SpeechRecognitionEngine()
        self.sre.SetInputToDefaultAudioDevice()
        self.sre.UnloadAllGrammars()
        gb = GrammarBuilder()
        gb.Append(Choices(lstName))
        self.sre.LoadGrammar(Grammar(gb))
        self.sre.MaxAlternates = 1
        
        self.spk = SpeechSynthesizer()
        self.spk .SelectVoiceByHints(VoiceGender.Female, VoiceAge.Senior, 0, greatBritainCulture)
        self.InitializeComponent()
    
    def InitializeComponent(self):
        self.spk.Speak("Start Speech Recognition Engine")
        self._buttonStop = System.Windows.Forms.Button()
        self._listBox1 = System.Windows.Forms.ListBox()
        self._label1 = System.Windows.Forms.Label()
        self.SuspendLayout()
        self.sre.SpeechRecognized += self.OnSpeechRecognized
        self.sre.RecognizeAsync(RecognizeMode.Multiple)
        # 
        # buttonStop
        self._buttonStop.Location = System.Drawing.Point(51, 190)
        self._buttonStop.Name = "buttonStop"
        self._buttonStop.Size = System.Drawing.Size(178, 50)
        self._buttonStop.TabIndex = 0
        self._buttonStop.Text = "Stop Engine"
        self._buttonStop.UseVisualStyleBackColor = True
        self._buttonStop.Click += self.ButtonStopClick
        # 
        # listBox1
        self._listBox1.FormattingEnabled = True
        self._listBox1.Items.AddRange(System.Array[System.Object](self.lstComandName))
        self._listBox1.Location = System.Drawing.Point(51, 34)
        self._listBox1.Name = "listBox1"
        self._listBox1.Size = System.Drawing.Size(178, 134)
        self._listBox1.TabIndex = 1
        # 
        # label1
        self._label1.Location = System.Drawing.Point(51, 5)
        self._label1.Name = "label1"
        self._label1.Size = System.Drawing.Size(178, 23)
        self._label1.TabIndex = 2
        self._label1.Text = "List Voice Commands available"
        # 
        # Form1
        self.ClientSize = System.Drawing.Size(284, 252)
        self.Controls.Add(self._label1)
        self.Controls.Add(self._listBox1)
        self.Controls.Add(self._buttonStop)
        self.Name = "SpeechRecognition"
        self.Text = "Speech Recognition"
        self.ResumeLayout(False)

    def ButtonStopClick(self, sender, e):
        self.spk.Speak("Stop Speech Recognition Engine, see you soon!")
        for objEvent in self._lstObj_handler:
            objEvent.ext_event.Dispose()    
        self.sre.Dispose()
        self.spk.Dispose()
        self.Close()    

    def OnSpeechRecognized(self, sender, e):
        self.sre.RecognizeAsyncStop()
        self.spk.Speak(e.Result.Text + "Command Processing")
        for objEvent in self._lstObj_handler:
            if e.Result.Text == objEvent.name:
                objEvent.ext_event.Raise()      
        self.sre.RecognizeAsync(RecognizeMode.Multiple)

lstEnumsPCmd = System.Enum.GetNames(PostableCommand)
lstObj_handler = [EventUtils(eval("PostableCommand." + nameCommand)) for nameCommand in IN[0] if nameCommand in lstEnumsPCmd ]

objForm = Form1(lstObj_handler) 
objForm.Show()
        
OUT = objForm.lstComandName, lstEnumsPCmd


Amusez-vous bien !

Attention les PostableCommands  ne sont pas forcément les mêmes suivant les versions de Revit.




0 commentaires:

Enregistrer un commentaire