Biblioteka ScriptForge

Uwaga: przeglądasz tę stronę na urządzeniu o niewielkim ekranie (szerokość < 640px). Niektóre zamieszczone w artykule ilustracje i animacje mogą stać się nieczytelne po dopasowaniu ich do rozdzielczości tego ekranu.

Yestok.pl

Jerzy Moruś

© Wszystkie prawa zastrzeżone. Wykorzystanie całości serwisu lub jego fragmentów bez pisemnej zgody autora zabronione.

Biblioteka ScriptForge



Opracowanie powstało z wykorzystaniem wersji oprogramowania istniejącej w trakcie jego pisania. Był to LibreOffice 24.2.5.


W pakiecie LibreOffice od wersji 7. do kontenera „Makra i okna dialogowe aplikacji” włączono nowe biblioteki nazywane skrótowo ScriptForge. Taką nazwę ma główna biblioteka tego zestawu a pozostałe, skojarzone z nią, mają nazwy rozpoczynające się od wielkich liter SF. Zestaw tych bibliotek usprawnia pisanie makr przez użytkowników słabiej znających interfejs API. Z tych bibliotek, mogą także korzystać osoby piszące w języku Python.

Produkt ten nie jest dostępny w Apache OpenOffice, więc makra utworzone z wykorzystaniem ScriptForge nie mogą być uruchomione w środowisku Apache OpenOffice, chociaż same dokumenty można wczytać i przetwarzać w tradycyjny sposób, z uwzględnieniem różnic w rozwoju obu wersji aplikacji.

W styczniu 2021 roku na blogu The Document Foundation tak zapowiedziano pojawienie się tego produktu:

„Biblioteki ScriptForge to rozszerzalny i solidny zbiór zasobów skryptów makr dla LibreOffice, które można wywoływać z makr użytkownika Basic. Użytkownicy zaznajomieni z innymi wariantami makr w języku BASIC często mają trudności z zagłębieniem się w rozbudowany interfejs programowania aplikacji LibreOffice, nawet w przypadku najprostszych operacji. Gromadząc najbardziej wymagane operacje na dokumentach w zestawie łatwych w użyciu i czytelnych procedur, użytkownicy mogą teraz programować makra dokumentów przy znacznie mniejszym wysiłku i szybciej uzyskiwać wyniki. Bogate metody ScriptForge są zorganizowane w moduły wielokrotnego użytku, które w przejrzysty sposób izolują konstrukcje języka programowania Basic od dostępu do treści dokumentów ODF i funkcji interfejsu użytkownika (UI)” [ https://blog.documentfoundation.org/blog/2021/01/28/introducing-the-scriptforge-basic-libraries].

Niniejsze opracowanie jest zorientowane na osoby programujące w Basicu, ale jeszcze raz podkreślę to, co napisałem na wstępie, że korzystanie z tej biblioteki możliwe jest także dla piszących w języku Python.

Wszystkie procedury z bibliotek są uruchamiane jako usługi obsługujące wybrany obiekt.

Usługi te podzielono umownie na cztery kategorie mówiące, do jakiego typu obiektu mogą zostać zastosowane. Oto ten podział.

Kategoria usług Nazwy usług
LibreOffice Basic Array, Dictionary, Exception, FileSystem, String, TextStream
Treść dokumentu Base, Calc, Chart, Database, Dataset, Datasheet, Document, FormDocument, Writer
Interfejs użytkownika Dialog, DialogControl, Form, FormControl, Menu, PopupMenu, Toolbar, ToolbarButton, UI
Narzędzia Basic, L10N, Platform, Region, Services, Session, Timer, UnitTest

Tak więc np. usługa o nazwie Array dotyczy wektorów i tablic przetwarzanych w Basicu, usługa Calc – działań dotyczących arkusza kalkulacyjnego, usługa Document – dokumentu jako takiego a usługa Menu pozwala utworzyć własne menu w tworzonym projekcie.

Każda usługa ma swoje właściwości i metody, które można wykorzystać w stosunku do obiektu, któremu została przypisana.

Do wywołania usługi służy ogólny konstruktor usług o postaci:

CreateScriptService("Nazawa usługi", [parametry…])

a do zakończenia korzystania z niej i zwolnienia zasobów, które usługa zawłaszczyła, służy metoda Dispose() dostępna w każdej usłudze.

Biblioteka oraz udostępnione usługi są dobrze opisane w systemie pomocy. Każdy opis zawiera sposób wywołania usługi, jej właściwości, metody oraz przykłady jej użycia uwzględniając programowanie w Basicu i Pythonie.

Pomoc jest dostępna w standardowym systemie pomocy „Pomoc”, a ponadto poprzez przeglądarkę internetową można poczytać wersję angielskojęzyczną pod tym adresem: https://help.libreoffice.org/latest/en-US/text/sbasic/shared/03/lib_ScriptForge.html [dostęp dn. 3.05.2024], a wersję polską pod tym: https://help.libreoffice.org/lpodatest/pl/text/sbasic/shared/03/lib_ScriptForge.html [dostęp dn. 3.05.2024].

Korzystanie z biblioteki wymaga oczywiście jej załadowania, dlatego koniecznym jest umieszczenie w Basicu, przed pierwszym odwołaniem do biblioteki, instrukcji:

GlobalScope.BasicLibraries.loadLibrary("ScriptForge")

a w przypadku pisania makra w Python-ie:

from scriptforge import CreateScriptService

Artykuł przeznaczony jest dla osób mających już pewne doświadczenia z programowaniem w Basicu, nie rości sobie praw do bycia podręcznikiem ScriptForge, lecz ma zaprezentować możliwości tkwiące w procedurach tej biblioteki. Chcę to przedstawić, wykorzystując konkretny, może niezbyt wyszukany, problem. Najwięcej projektów programistycznych związanych z LibreOffice dotyczy arkusza kalkulacyjnego, dlatego to na jego przykładzie postaram się to przedstawić.

Najpierw jednak opiszę, jakie zagadnienie chcę rozwiązać.

Istniejący skoroszyt składa się z trzech arkuszy o nazwach: Baza, Ark1 i Ark2.

W arkuszu Baza znajduje się pewna baza danych.

Przykładowa baza danych.
Rys. 1: Fragment bazy danych.

Rekordy w bazie zawierają pięć pól. Trzy, to pola zawierające zawartość bezpośrednią (wprowadzaną przez użytkownika) są to „Nazwa”, „Cena U$” i „Ilość”, i dwa pola, których zawartość wyznaczają formuły; pierwsze z nich o nazwie „Razem U$”, jest wynikiem iloczynu Cena U$ × Ilość i drugie o nazwie „Razem PLN”, jest iloczynem obliczonego już Razem U$ i kursu wymiany zapisanego w komórce B1 tego arkusza. Komórce B1 nadałem nazwę „przelicznik”. Zawartość bazy podlega różnego rodzaju filtrowaniu, a odfiltrowane dane umieszczane są w arkuszu Ark1 zaczynając od komórki D3. Tej komórce nadałem nazwę: „Wyciąg”. Taki efekt filtrowania można uzyskać za pomocą filtrowania standardowego albo zaawansowanego. Wiadome jest, że jeśli dane źródłowe zawierały komórki z formułami, to odfiltrowane dane także zawierają te formuły.

To, co chcę oprogramować, to możliwość przekopiowania odfiltrowanych danych do innej lokalizacji. Skopiowane mają zostać tylko wartości oraz ich atrybuty (kolor, pogrubienie itp. bez formuł), a wybrać należy jedną z dwóch możliwości kopiowania:

  1. Kopiowanie odfiltrowanych danych do arkusza o nazwie „Ark2”, znajdującego się w tym samym skoroszycie. Komórką początkową dla odfiltrowanych rekordów będzie B5.
  2. Kopiowanie odfiltrowanych danych do nowego pliku. Utworzony plik ma mieć nazwę pliku macierzystego uzupełnioną o datę i czas jego utworzenia. Plik ma zostać zapisany w tej samej lokalizacji, co skoroszyt macierzysty.

Wybór jednego z powyżej wymienionych działań nastąpi przez wykorzystanie nowej pozycji o nazwie „Moje menu” dodanej do menu głównego programu Calc. Przedstawia to poniższa ilustracja.

Widok zmodyfikowanego menu.
Rys. 2: Zmodyfikowane menu główne programu Calc.

„Moje menu”, zawiera dwie możliwości wyboru. Wywołanie tej pozycji jest możliwe także z wykorzystaniem skrótu klawiaturowego ALT(lewy)+u (litera „u” w nazwie jest podkreślona), a wybór konkretnej akcji po naciśnięciu klawisza „W” lub „K” (podkreślone litery w nazwach).

Samo filtrowanie danych nie jest oprogramowane, gdyż to użytkownik sam wybiera sposób i zasady filtrowania. Jedynym wymogiem jest, aby odfiltrowane dane były przekopiowane w ustalone miejsce. Więc opcje filtru powinny być ustawione jak poniżej.

Widok kryteriów filtrowania.
Rys. 3: Przykładowe kryteria filtrowania.

Powyższy przykład przedstawia kryteria filtra standardowego do odfiltrowania tych rekordów z bazy, w których „Ilość” jest liczbą nieparzystą, ale kluczowe jest tutaj zaznaczenie opcji „Kopiuj wyniki do” i wskazanie tego ustalonego miejsca.

Oprogramowanie

.

W ogólnym podejściu do tego zagadnienia oprogramowanie wymaga trzech podstawowych procedur.

Powyższa lista wskazuje, że zostaną wykorzystane co najmniej usługi: Menu (utworzenie menu), Calc (działania na skoroszycie) i Document (działania na dokumencie). W rzeczywistości zostaną użyte jeszcze inne usługi i zostaną one omówione w dalszej części.

W tym miejscu należy wspomnieć, że w stosunku do arkusza kalkulacyjnego usługi ScriptForge korzystają z odwołań zapisywanych w notacji naturalnej, a więc np. A3, B10:F45, Arkusz2.C3:C8.

Utworzenie własnego menu.

Pierwszą procedurą, która zostanie wykonana, jest procedura „NoweMenu” tworząca nową pozycję w menu głównym Calca.

Tu należy powiedzieć o funkcjonowaniu tak utworzonego menu. Otóż jest ono dostępne wyłącznie w skoroszycie, w którym je utworzono i nie jest zapisywane z dokumentem. W związku z tym, po zamknięciu skoroszytu i ponownym jego otwarciu, to menu się nie pojawi. Ponadto każda zmiana interfejsu użytkownika spowodowana np. wywołaniem akcji tworzenia wykresu albo aktywowaniem obiektu OLE, sprawi, że po powrocie do ekranu podstawowego tego menu także już nie będzie. To oznacza, że w takich sytuacjach należałoby ponownie uruchomić procedurę.

W wersji ostatecznej tego skoroszytu procedurę przypisałem do zdarzenia „Otwórz dokument”, co oznacza, że będzie ona automatycznie uruchomiona przy każdym ładowaniu skoroszytu i dzięki temu użytkownik od razu zobaczy pozycję „Moje menu”. Ponieważ jest to w ogóle pierwsza procedura uruchamiana w skoroszycie, to w niej znajduje się kod ładujący bibliotekę ScriptForge.

Warto pamiętać, że załadowanie każdej biblioteki LibreOffice z kontenera „Makra i okna dialogowe aplikacji” lub „Moje okna dialogowe i makra” udostępnia ją wszystkim aplikacjom i skryptom, aż do zakończenia sesji LibreOffice. Dlatego procedura ładowania biblioteki jest wykonywana warunkowo, wykona się tylko wtedy, gdy biblioteka nie została już wcześniej załadowana.

Oto ta procedura.

Public oDok as Object, FSO As Object
Public sPlik$, sNazwa$, sFolder$
     1. Sub NoweMenu()
     2. Dim oMenu as Object
     3. With GlobalScope.BasicLibraries
     4. If Not .IsLibraryLoaded("ScriptForge") Then .LoadLibrary("ScriptForge")
     5. End With
     6. oDok = CreateScriptService("Document",ThisComponent)
     7. oDok.RemoveMenu("Moje menu")
     8. oMenu = oDok.CreateMenu("Moje men~u","Pomoc")
     9. With oMenu
    10. .AddItem("Wykonaj kopię w arkuszu", Script :=_"vnd.sun.star.script:Standard.Module1.WykonajKopie?language=Basic&location=document",Icon := "cmd/sc_grid.svg")
    11. .AddItem("Kopia przez schowek do pliku", Script :="vnd.sun.star.script:Standard.Module1.PrzezSchowek?language=Basic&location=document",Icon := "cmd/sc_save.svg")
    12. End With
    13. oDok.Dispose()
    14. End Sub
			

Dwa nienumerowane wiersze to deklaracja zmiennych typu Public. Numerowanie dodałem wyłącznie w celu łatwego omówienia poszczególnych fragmentów makra.

Utworzenie kopii w arkuszu Ark2.

Makro ma przekopiować odfiltrowane rekordy w wybrane miejsce w arkuszu Ark2 z zachowaniem atrybutów pól (takich jak np. pogrubienie tekstu, zmiana wielkości czcionki lub jej koloru albo tła pola). Procedura nie jest skomplikowana, ale trzeba wykonać dwa działania. Pierwsze, przekopiować odfiltrowane dane, ta funkcja (metoda) kopiuje wartości, atrybuty i formuły. Drugie – ponownie wstawić „czyste” wartości do docelowego obszaru, ta funkcja (metoda), zachowując przypisane atrybuty, wstawi tylko wartości pól. W tej procedurze chciałem pokazać dodatkowe możliwości wykorzystania biblioteki. Uzupełniłem więc tę procedurę o wygenerowanie komunikatu, z ilu wierszy i kolumn składa się obszar odfiltrowanych rekordów oraz komunikatu podającego położenie pierwszej i ostatniej komórki tego obszaru.

     1. Sub WykonajKopie()
     2. oDok=CreateScriptService("Calc", ThisComponent)
     3. sReg=oDok.Region("Wyciąg")
     4. lw=oDok.Height(sReg)
     5. lk=oDok.Width(sReg)
     6. msgBox ("Znaleziony obszar zawiera: " & chr(10)  & LiczbyMnogie(lw,"wiersz","","e","y") & chr(10) & "i "  & LiczbyMnogie(lk,"kolumn", "ę","y",""),64, "Znaleziony obszar")
     7. msgBox ("Pierwsza komórka w tym obszarze to: " & oDok.FirstCell(sReg) & chr(10) & " a ostatnia to: " & oDok.LastCell("Ark1"),64, "Adres obszaru")
     8. oDok.ClearAll("Ark2.*")
     9. oDok.CopyToCell(sReg,"Ark2.B5")
    10. arrTab=oDok.getValue(sReg)
    11. sReg=oDok.Region("Ark2.B5")
    12. oDok.setValue(sReg,arrTab)
    13. oDok.Dispose()
    14. End Sub
			

Na potrzeby wyświetlanego komunikatu utworzyłem dedykowaną funkcję, która ma zaprezentować liczbę wraz z właściwym dopełnieniem. Ta funkcja nie jest związana z biblioteką ScriptForge, ale na jej przykładzie chciałem zaprezentować pewne możliwości nadawania nazw zmiennym, o których wiele osób nie pamięta, albo nawet nie wie, że są. Utarło się przekonanie, że nazwa zmiennej może składać się wyłącznie z liter alfabetu łacińskiego, cyfr oraz znaku podkreślnika, przy czym pierwszym znakiem nazwy musi być litera albo podkreślnik. W rzeczywistości nazwa może zawierać dowolne znaki i symbole, warunkiem wykorzystania takiej nazwy jest ujęcie jej w nawiasy kwadratowe. I taką właśnie nazwę, wykorzystałem w tej funkcji. Funkcja, na podstawie liczby ma utworzyć treść zgodną z zasadami języka polskiego. W naszym języku dopełnienie liczebnika zależy od jego liczności, tak więc np. przy liczności 1 napiszemy: 1 wiersz, przy licznościach między 2 a 4 napiszemy 2, 3 lub 4 – wiersze. Przy licznościach powyżej 4, napiszemy 5, 6 itd. wierszy. Zasada dotycząca liczności 2 – 4 obowiązuje także dla wszystkich liczb, w których pozycja dziesiątków jest inna niż 1 a ostatnimi cyframi są 2, 3 lub 4, czyli dla liczb np. 23, 44 lub 52, ale nie 14.

Napisana funkcja wymaga czterech parametrów: liczby, dla której ma zostać wyznaczone dopełnienie oraz trzech postaci dopełnień. Parametr czwarty w tej funkcji ma nazwę utworzoną z wykorzystaniem litery spoza alfabetu łacińskiego.

Oto ta funkcja:

Function LiczbyMnogie (liczba, jeden, dwa, [pięć]) As String
	If Liczba=1 Then
		LiczbyMnogie= jeden
	ElseIf liczba >1 and liczba <5 Then
		LiczbyMnogie= dwa
	Elseif liczba mod 100 > 20 and liczba mod 10 > 1 and liczba mod 10 <5 Then
		LiczbyMnogie=dwa
	Else
		LiczbyMnogie= [pięć]
	End If
	LiczbyMnogie=liczba & " " & LiczbyMnogie
End Function
			

Procedura zapisania odfiltrowanych rekordów w nowym pliku.

W tej procedurze wykorzystałem inny sposób uzyskania kopii, mianowicie z wykorzystaniem operacji „Kopiuj”, a następnie operacji „Wklej specjalnie” ze wskazaniem, że mają zostać wklejone wyłącznie wartości i formaty. Taką operację można wykonać w arkuszu przy pomocy poleceń arkusza „Edycja → Kopiuj” oraz „Edycja → Wklej specjalnie → Wklej specjalnie → Wartości i formaty”. Oczywiście, że ten sposób mógł być wykorzystany już w poprzedniej procedurze, ale chodziło mi o pokazanie różnych możliwości zawartych w bibliotece ScriptForge.

Ta procedura jest najbardziej złożona, wymaga skopiowania odfiltrowanych danych, utworzenia nowej nazwy dla dokumentu, który tę kopię będzie zawierał, wklejeniu danych oraz zapisaniu dokumentu z przygotowaną nazwą. Te operacje wymagają więcej zasobów biblioteki ScriprForge.

     1. Sub PrzezSchowek()
     2. oDok=CreateScriptService("Calc",ThisComponent)
     3. sReg=oDok.Region("Wyciąg")
     4. oDok.CurrentSelection=sReg
     5. oDok2=CreateScriptService("Document", ThisComponent)
     6. oDok2.RunCommand("Copy")
     7. oDok.Dispose()
     8. oDok2.Dispose()
     9. sPlik= ThisComponent.Location
    10. FSO = CreateScriptService("FileSystem")
    11. FSO.FileNaming="URL"
    12. sNazwa= FSO.getBaseName(sPlik)
    13. sFolder=FSO.getParentFolderName(sPlik)
    14. sNazwa=sNazwa & "_" & Date & "_" & Time
    15. sNazwa=SF_String.ReplaceRegex(sNazwa,"[\.:]","_")
    16. sFolder=FSO.BuildPath(sFolder,sNazwa) & ".ods"
    17. FSO.Dispose()
    18. Dim ui As Object
    19. ui = CreateScriptService("UI")
    20. oDok = ui.CreateDocument("Calc",,True)
    21. Otwarte_dokumenty=ui.Documents
    22. oDok2=CreateScriptService("Calc", Otwarte_dokumenty(0))
    23. ui.Dispose()
    24. oDok2.SetValue("A1", "To jest wynik transferu")
    25. oDok2.CurrentSelection="B4"
    26. oDok=CreateScriptService("Document",Otwarte_dokumenty(0))
    27. oDok.RunCommand("InsertContents", "Flags", "SVDT", "FormulaCommand", 0, "MoveMode",4, "SkipEmptyCells", false, "Transpose", false,  "AsLink", false)
    28. oDok.SaveAs(sFolder, True)
    29. oDok.Activate
    30. oDok.CloseDocument
    31. oDok2.Dispose()
    32. oDok.Dispose()
    33. End Sub
			

Poniżej znajduje się link, pozwalający pobrać skoroszyt zawierający omówione makra.
SF.ods

Jeżeli chcecie Państwo pobrać plik, pamiętacie, że za taką możliwość odpowiada kliknięcie odnośnika (łącza) prawym przyciskiem myszki i wybranie z wyświetlonego menu pozycji w rodzaju „Zapisz element docelowy jako...” lub „Zapisz link jako...”. Kliknięcie lewym przyciskiem może spowodować inne działania niż pobieranie, gdyż przeglądarka może próbować bezpośredniego odczytania pliku. W szczególności, większość przeglądarek ma obecnie włączoną możliwość bezpośredniego odczytywania plików PDF.


Przedstawione rozwiązanie to tylko niewielka demonstracja możliwości wykorzystania biblioteki ScriptForge. Czytelnik zainteresowany tym tematem powinien zapoznać się z opisem tej biblioteki. Jak już napisałem na wstępie system pomocy szczegółowo omawia każdą z usług.