Emulare CallByName del Vba con esempio Applicativo

Rispondi
Avatar utente
unlucky83
Volontario
Volontario
Messaggi: 2355
Iscritto il: lunedì 7 gennaio 2013, 1:23
Località: Latina

Emulare CallByName del Vba con esempio Applicativo

Messaggio da unlucky83 »

In Vba esiste una funzione chiamata CallByName che consente di richiamare una subroutine o una function dandogli come parametro una stringa che corrisponde al nome della macro.
Il Vb di Aoo e Lo non implementano questa funzione. Sul forum francese ho trovato le macro che emulano CallByName del vba (vedi qui). Ho preso quelle macro e le ho modificate per ciò che mi interessava.

Il Disagio: Quando sviluppo un file calc con molte macro complesse e vari pulsanti di lancio, durante la fase dello sviluppo riorganizzo spesso le varie macro suddividendole nei moduli e non contento rinomino spesso le macro e i moduli :knock:.
Dopo aver fatto queste modifiche una buona parte dei pulsanti non trova più (giustamente) la macro da lanciare dato che gli ho biato il percorso. A questo punto o modifico gli eventi dei pulsanti oppure devo ripristinare i vecchi nomi e le posizioni delle macro in questione. :shock:
Obiettivo: Voglio rendere l'associazione Pulsante/macro più flessibile, ovvero mi piacerebbe poter cambiare/rinominare il modulo in cui è inserita la macro ogni qual volta mi va senza dover essere poi costretto a cambiare l'evento del pulsante. Inoltre, se volessi rinominare la macro, vorrei evitare di modificare gli eventi del pulsante (si aprono varie finestre di dialogo e bisogna selezionare la macro dopo aver selezionato prima il modulo, dopo aver selezionato prima la libreria)...vorrei intervenire facendo una modifica altrettanto efficace ma più rapida.
Soluzione trovata: Mi sto trovando bene nell'assegnare a tutti i pulsanti la stessa macro dal nome banale "Pulsanti", nel classico modulo "Module1". Quando sviluppo macro complesse, in cui anche il nome della sub mi aiuta a dare un ordine mentale, mantenere fissi i nomi "Pulsanti" e "Module1" non mi da alcun problema, detta in altri termini questi nomi sono perfetti così come sono e non mi viene in mente di cambiarli.

Codice: Seleziona tutto

Sub Pulsanti(oEv)
	SubOrFunc= oEv.source.model.label
	call CallByName(SubOrFunc,array())
End Sub

I vari pulsanti eseguiranno quindi tutti la sub "Pulsanti", ma grazie alla macro CallByName riesco ad ottenere comportamenti differenti semplicemente usando come label del pulsante il nome della macro che avrei associato direttamente. Potrei anche usare, invece del label, il name del pulsante.
Ora posso cambiare la posizione delle macro tra i moduli senza alcun problema, basta che non sposti la sub Pulsanti.
Se decido di rinominare le macro, devo solo impostare nel label il nuovo nome della macro.
Se devo inserire 100 pulsanti, una volta inserito il primo e impostata l'associazione con sub "Pulsanti", posso copiare il pulsante e incollarlo 99 volte. Successivamente mi basta solo cambiare i vari label e ecco che tutti i pulsanti sono sistemati.

Codice: Seleziona tutto

sub CallByName(sFonction as string ,aParams as variant)
    dim oDS as object       'objet dispatch helper utilisé pour le call de la sub
    dim sURL as string      'string qui contient l'URL de la fonction a appeler

    on error goto CallByName_error
       'initilisation objet dispatchhelper
       oDS = createUnoService("com.sun.star.frame.DispatchHelper")
       'initialisation de l'URL du script Basic a appeler : Standard,CallByName."+sFonction+"?language=Basic&location=document
       'le script est dans la bibliothèque Standard du document contenant les macro
       'le script a appeler est dans le module CallByName
       'le nom du script basic a appeller est dans la variable sFonction
       sURL = "vnd.sun.star.script:Standard."+getModule(sFonction)+"."+sFonction+"?language=Basic&location=document"
       'appel du script
       oDS.executeDispatch(ThisComponent.CurrentController.frame,sURL,"",0,aParams())
       exit sub
    CallByName_error:
	   on error resume Next
       msgbox "erreur dans la procédure CallByName"
       on error goto 0
end sub
"CallByName" così impostato, cerca all'interno della libreria Standard del documento, il nome della macro tra i vari moduli sfruttando un'altra funzione chiamata "getModule"

Codice: Seleziona tutto

function getModule(sFunction as string) as string
    CONST LibraryName = "Standard"

    dim oLib as object      'objet libarie Standard
    dim aModules()         'tableau de tous les modules de la librairie Standard
    dim iC as Integer      'indice de boucle
    dim sModule   'le module à analyser (texte basic)

    on error goto getModule_error

       oLib = BasicLibraries.getByName( LibraryName )   'initialise l'objet librarie
       getModule = ""
       aModules = oLib.getElementNames()         'initialise le tableau qui contient tous les modules
       for iC = 0 to ubound(aModules)            'pour chacun des modules
          sModule = oLib.getByName(aModules(iC))   'charge le contenu texte du module dans la variable Smodule
           if instr(sModule,"Sub " & sFunction) > 0 Then
              getModule = aModules(iC)
              exit Function
           Elseif instr(sModule,"function " & sFunction) >0 Then
              getModule = aModules(iC)
              exit function
           end if
       next iC
       exit function
    getModule_error:
       on error resume next
       msgbox "erreur dans la procédure getModule"
       on error goto 0
end function

Delle macro trovate nel forum francese, ho modificato la funzione "getmodule" in cui ho scelto di usare la funzione "Instr" per stabilire se il modulo presenta o no la macro cercata.

Criticità All'interno dei vari moduli non devono essere presenti tra i commenti "sub nomemacro" o "function nomemacro" altrimenti viene individuato il modulo sbagliato. Inoltre tra sub e il nomemacro deve esserci uno e un solo spazio, stessa cosa se si tratta di una function

Conclusioni: Forse altri utenti troveranno utile queste macro per i miei stessi motivi o trovarle utili per risolvere un problema di compatibilità con il vba qualora fosse impiegata proprio la funzione CallByName o forse ci sarà qualcuno che le troverà semplicemente interessanti o di spunto per fare tutt'altro.
Di certo ci sarà chi sbadiglierà :lol:
Allegati
PulsantiSvincolatiDaModuli.ods
esempio applicativo
(11.63 KiB) Scaricato 272 volte
LibO:Versione: 6.2.8.2
Build ID: 1:6.2.8~rc2-0ubuntu0.16.04.1- 32-bit
-
Se risolvi:
1. Condividi la soluzione qui con noi
2. Metti [Risolto] al titolo del primo messaggio come spiegato qui
Avatar utente
charlie
Site Admin
Site Admin
Messaggi: 8785
Iscritto il: mercoledì 19 dicembre 2012, 10:50
Contatta:

Re: Emulare CallByName del Vba con esempio Applicativo

Messaggio da charlie »

unlucky83 ha scritto:Di certo ci sarà chi sbadiglierà
........
Ma un asin bigio, rosicchiando un cardo
Rosso e turchino, non si scomodò:
Tutto quel chiasso ei non degnò d’un guardo
E a brucar serio e lento seguitò.

G. Carducci: Rime nuove/Libro V/Davanti San Guido

Cercheremo di non essere così.
Intanto complimenti!
charlie
macOS 14.3 Sonoma: Open Office 4.1.15 - LibreOffice 7.5.7.2
http://www.charlieopenoffice.altervista.org
Avatar utente
unlucky83
Volontario
Volontario
Messaggi: 2355
Iscritto il: lunedì 7 gennaio 2013, 1:23
Località: Latina

Re: Emulare CallByName del Vba con esempio Applicativo

Messaggio da unlucky83 »

Hai letto fino alla fine il messaggio, complimenti a te @charlie
LibO:Versione: 6.2.8.2
Build ID: 1:6.2.8~rc2-0ubuntu0.16.04.1- 32-bit
-
Se risolvi:
1. Condividi la soluzione qui con noi
2. Metti [Risolto] al titolo del primo messaggio come spiegato qui
nickGiard
Messaggi: 65
Iscritto il: lunedì 14 maggio 2012, 22:04

Re: Emulare CallByName del Vba con esempio Applicativo

Messaggio da nickGiard »

Grazie unlucky83, la soluzione con il relativo URL mi sembra proprio interessante.
Nicola con LibreOffice 7.1 (x64) su Windows 11
nickGiard
Messaggi: 65
Iscritto il: lunedì 14 maggio 2012, 22:04

Re: Emulare CallByName del Vba con esempio Applicativo

Messaggio da nickGiard »

Grazie unlucky83,
rileggevo la tua soluzione, dove esponevi alcune debolezze legate al fatto di poter avere del testo che ripete il nome della funzione, cosa comune nelle mie macro dove il nome della funzione fa parte anche di messaggi di errore ed altro.
Forse la cosa si può superare tentando di chiamare direttamente la funzione (ovviamente in un ciclo) racchiusa in un try: ossia in un On Error Resume Next ed uscendo dal ciclo con l'errore 0, o simile.
Ossia con una gestione degli errori, cosa molto comune ad esempio in Python, ed un po' negletta in Basic dove con alcune accortezze può funzionare egregiamente.
Un caro saluto
Nicola con LibreOffice 7.1 (x64) su Windows 11
geovign
Messaggi: 214
Iscritto il: domenica 13 gennaio 2019, 11:19
Località: Modena

Re: Emulare CallByName del Vba con esempio Applicativo

Messaggio da geovign »

Ciao @unluky83
ho usato più volte "l'emulatore" di CallByName, ma sempre in presenza della sola libreria Standard.
Ora però l'ho provata in un documento che presenta diverse librerie, ma ovviamente ho dovuto modificare la funzione getModule in modo tale ricercare la macro da lanciare fra le diverse librerie. A tale scopo ho utilizzato, di volta in volta, il metodo getChildNodes() presente nel servizio com.sun.star.script.browse.BrowseNode (servizio supportato) per ottenere la lista dei sottolivelli, poi ho sfruttato la loro proprietà Name per ottenere il nome del sottolivello ed infine la proprietà URI per ottenere l'indirizzo della macro scelta nella forma "vnd.sun.star.script.NomeDellaLibreria.NomeDelModulo.NomeDellaSub?language=Basic&location=document" .
In sintesi questi sono i passaggi:
- Dalla propietà ScriptProvider del documento (Doc) ho avuto accesso alla lista dei sottolivelli con il metodo getChildNodes()
- Trovato il sottolivello "basic" (che contiene tutti gli script basic) ho avuto accesso alla lista dei relativi sottolivelli (in questo caso le librerie) con il metodo getChildNodes()
- Con lo stesso procedimento indicato al punto precedente, sono sceso di livello (librerie poi moduli ) fino ad ottenere la lista delle macro presenti in ogni modulo di ogni libreria.
- A questo punto trovata la giusta macro (sottolivello con lo stesso nome della macro ricercata), ho sfruttato la sua proprietà URI per ottenere l'indirizzo della macro nella forma sopra indicata.
Questa mia "nuova versione" di getModule (da me rinominata OttieniIndirizzoMacro) non fa differenza tra subroutine o funzione.
Devo anche dire che ho superato le criticità da te segnalate al tuo primo post. Nel metodo da me utilizzato è possibile menzionare le sub, in qualunque forma, all'interno dei commenti. Almeno, io non ho riscontrato problemi.
Non so se il percorso è concettualmente corretto, ma sono riuscito ad ottenere il risultato voluto.
Resta chiaro che nell'intero progetto non possono esistere subroutine e/o funzioni con lo stesso nome.
Di seguito posto i codici completi da me modificati

Codice: Seleziona tutto

rem Codice associato ai pulsanti; il nome del pulsante deve coincidere con la macro da lanciare
Sub Pulsanti(oEv)
	Dim NomeSubOrFunc As String	'dichiarazione variabile nome della sub o funzione da lanciare
	NomeSubOrFunc = oEv.source.model.Name	 'definizione nome della sub o funzione preso dal nome del pulsante
	Call CallByName(NomeSubOrFunc,array())	'chiamata alla macro CallByName
End Sub

rem Codice per esecuzione della macro
Sub CallByName(SubOrFunc As String ,aParams As Variant)
	Dim oDS As Object       'oggetto dispatch helper utilizzato per la chiamata della sub
	Dim sURL As String       'indirizzo della funzione da chiamare
	On Error GoTo CallByName_error	'chiamata per la gestione di errore della sub
	oDS = createUnoService("com.sun.star.frame.DispatchHelper")	'definizione dell'oggetto dispatchhelper
	'Definizione dell'indirizzo della subroutine da chiamare; la funzione OttieniIndirizzoMacro restituisce l'indirizzo
	'nella forma "vnd.sun.star.script.NomeDellaLibreria.NomeDelModulo.NomeDellaSub?language=Basic&location=document"
	sURL = OttieniIndirizzoMacro(SubOrFunc)
	'chiamata del codice
	oDS.executeDispatch(ThisComponent.CurrentController.frame,sURL,"",0,aParams())
	Exit Sub
	CallByName_error:		'gestione dell'errore
	On Error Resume Next
	msgbox "Errore durante la procedura CallByName"
	On Error GoTo 0
End Sub


Function OttieniIndirizzoMacro(NomeScript As string) As String
rem dichiarazioni variabili
	Dim Doc As Object							'
	Dim oListaTipiScript() As Object
	Dim iScript As Integer
	Dim oPrimoNodo As Object
	Dim oScriptBasic As Object
	Dim oListaLibrerieBasic() As Object
	Dim iLibrerie As Integer
	Dim oLibreriaBasic As Object
	Dim oListaModuliBasic() As Object
	Dim iModuli As Integer
	Dim oModuloBasic As Object
	Dim oListaMacroBasic() As Object
	Dim iMacro As Integer
	
rem inizio istruzioni
	On Error GoTo OttieniIndirizzoMacro_error		'chiamata per la gestione di errori della sub
	OttieniIndirizzoMacro = ""		'azzero il valore della variabile
	Doc = ThisComponent		'definizione dell'oggetto Doc
	'ottengo la lista dei nodi presenti nel documento
	oListaTipiScript = Doc.ScriptProvider.getChildNodes()		' script Java, basic e forse altri
	'primo ciclo tra i vari tipi di script 
	For iScript = 0 To Ubound(oListaTipiScript)
		'Print oListaTipiScript(iScript).Name
		If oListaTipiScript(iScript).Name = "Basic" Then
			'accedo all'insieme degli script basic
			oScriptBasic = Doc.ScriptProvider.ChildNodes(iScript)
			'ottengo la lista dei sottonodi presenti nel nodo basic; corrisponde all'elenco delle librerie
			oListaLibrerieBasic = oScriptBasic.getChildNodes()
			'secondo ciclo tra le librerie
			For iLibrerie = 0 To UBound(oListaLibrerieBasic)
				'Print oListaLibrerieBasic(iLibrerie).Name
				'accedo nella libreria
				oLibreriaBasic = oScriptBasic.ChildNodes(iLibrerie)
				'ottengo la lista dei sottonodi presenti nel nodo libreria; corrisponde alla lista dei moduli nella libreria
				oListaModuliBasic = oLibreriaBasic.getChildNodes()
				'terzo ciclo tra i moduli della libreria
				For iModuli = 0 To Ubound(oListaModuliBasic)
					'Print oListaModuliBasic(iModuli).Name
					'accedo al modulo
					oModuloBasic = oLibreriaBasic.ChildNodes(iModuli)
					'ottengo la lista dei sottonodi presenti nel nodo modulo; corrisponde alla lista delle macro nel modulo 
					oListaMacroBasic = oModuloBasic.getChildNodes()
					'quarto ciclo tra le macro presenti nel modulo
					For iMacro = 0 To Ubound(oListaMacroBasic)
						'Print oListaMacroBasic(iMacro).Name
						'verifica del nome nodo (macro) è uguale al nome della macro da lanciare
						If oListaMacroBasic(iMacro).Name = NomeScript Then
							'definizione del valore che deve restiuire la funzione espressa nella forma
							' "vnd.sun.star.script:NomeLibreria.NomeModulo.NomeMacro?language=Basic&location=document"
							OttieniIndirizzoMacro = oListaMacroBasic(iMacro).URI	'è corretto uri e non url
							'Print OttieniIndirizzoMacro
							Exit Function
						End if
					Next iMacro
				Next iModuli
			Next iLibrerie
		End If
	Next iScript
	msgbox "Non ho trovato la macro corrispondente al pulsante"	'messaggio di av	viso al termine del primo ciclo se non trova quanto cercato
	Exit Function
	OttieniIndirizzoMacro_error:	'gestione errore della funzione getModule
	On Error Resume Next
	msgbox "Errore durante la procedura OttieniIndirizzoMacro"
	On Error GoTo 0		
End Function
Inoltre allego file con pulsanti per l'esecuzione di diverse macro.
Provala/Provatela.
Saluti
Geo
Allegati
CallByName_geo.ods
(18.2 KiB) Scaricato 92 volte
LibO 7 su LinuxMint 21
Avatar utente
unlucky83
Volontario
Volontario
Messaggi: 2355
Iscritto il: lunedì 7 gennaio 2013, 1:23
Località: Latina

Re: Emulare CallByName del Vba con esempio Applicativo

Messaggio da unlucky83 »

Ciao a tutti quelli che sono intervenuti in questa discussione e a chi si trova a leggerla.
Mi fa piacere sapere che qualcuno alla fine ha trovato utile la macro e che l'abbia ampliata...quando stavo scrivendo il messaggio non ero affatto convinto che potesse interessare a qualcun altro, ma noto dagli scaricamenti che mi sbagliavo.
geovign ha scritto: Inoltre allego file con pulsanti per l'esecuzione di diverse macro.
Provala/Provatela.
Saluti
Geo
Grazie, la scarico e la proverò durante le feste
LibO:Versione: 6.2.8.2
Build ID: 1:6.2.8~rc2-0ubuntu0.16.04.1- 32-bit
-
Se risolvi:
1. Condividi la soluzione qui con noi
2. Metti [Risolto] al titolo del primo messaggio come spiegato qui
Rispondi