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.
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:
- 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.
- 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.
„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.
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.
- Procedury, która utworzy, na potrzeby tego skoroszytu, nowe menu i opcje wyboru.
- Procedury kopiującej wyniki do arkusza Ark2.
- Procedury tworzącej nowy plik zawierający odfiltrowane rekordy bazy.
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.
- Wiersze 3 – 5.
Odpowiadają za załadowanie biblioteki ScriptForge, jeżeli wcześniej nie została już załadowana. - Wiersz 6.
Zmiennej obiektowej oDok (zadeklarowanej jako zmiennaPublic
) zostaje przypisana usługa „Document” „obsługująca” obiektThisComponent
, czyli aktualnie otwarty skoroszyt. - Wiersz 7.
Na obiekcieoDok
zostaje wykonana funkcja (metoda) usunięcia z menu pozycji „Moje menu”. Dlaczego? Przecież żadne menu jeszcze nie powstało... Jest to zabezpieczenie przed sytuacją, kiedy użytkownik wykonałby polecenie „Załaduj ponownie” (co spowodowałoby ponowne uruchomienie procedury), albo spróbowałby ręcznie uruchomić procedurę „NoweMenu”. Te działania utworzyłyby ponownie nowe menu i w aplikacji istniałyby dwie, albo i więcej, pozycji „Moje menu”. Wywołanie funkcjiRemoveMenu()
na wszelki wypadek najpierw usuwa to menu, a jeśli takiego menu nie ma, instrukcja jest ignorowana nie zgłaszając żadnego błędu. - Wiersz 8.
Zmienna obiektowaoMenu
(zadeklarowana w wierszu 2.) staje się obiektem utworzonym przez funkcjęCreateMenu()
obiektuoDok
. Utworzonemu menu zostaje nadana nazwa „Moje menu”. Znak tyldy (~) poprzedzający literę „u” w tej nazwie oznacza, że to ta litera będzie przypisana do skrótu klawiaturowego (Lewy ALT+litera) aktywującego polecenie „Moje menu”. W wyświetlanej w interfejsie nazwie litera ta będzie podkreślona. Drugi parametr tej funkcji określa, przed jaką pozycją w menu głównym ma pojawić się to nowe menu. W metodzie tej niestety pojawia się także błąd. Mianowicie drugi parametr metody, zgodnie z opisem, powinien móc być także liczbą całkowitą, wskazującą, przed którą kolejną pozycją menu głównego ma zostać dodane to nowe menu. Niestety, zastosowanie tej wersji parametru jest sygnalizowane jako błąd, a jest to dość istotna możliwość gdyż pozwala na umieszczenie własnego menu w konkretnym miejscu, niezależnie od języka aplikacji. - Wiersze 9 – 12.
ObiektoMenu
, będący instancją funkcjiCreateMenu()
zawiera metodę (funkcję)AddItem()
, która utworzy nową pozycję na liście menu. Pierwszym argumentem tej metody jest tekst tej pozycji w rozwiniętym menu. KażdyAddItem()
tworzy nową pozycję listy. MetodaAddItem()
automatycznie wybiera litery do wykorzystania w skrócie klawiaturowym.Na etapie tworzenia tego modułu, gdy nieznane są jeszcze szczegóły dalszych działań, ten jedyny parametr wystarczy do przetestowania tego, czy menu się pojawia. Po utworzeniu procedur (makr), które mają realizować funkcje poszczególnych pozycji z listy, można uzupełnić tę instrukcję o kolejny parametr, który przypisze makro do wybranej pozycji.
Przy wskazywaniu makra właściwym parametrem jest
script:="ścieżka dostępu do makra"
. Do pozycji menu można także przypisać polecenie pakietu LibreOffice, takie jak np. Copy, Scal komórki, czy inne dostępne. W takim przypadku musi zostać użyty parametrcommand:="polecenie"
. Te dwa parametry wykluczają się wzajemnie.Przy przypisywaniu makra należy parametr podać w następującej postaci:
Script := "vnd.sun.star.script:Biblioteka.Moduł.Procedura?language=Basic&location=document"
Parametr „location” może przyjąć wartość „document” wówczas makro pobierane jest z kontenera dokumentu lub „application”, wówczas makro pobierane jest z kontenerów „Moje makra” lub „Makra aplikacji”.
Innym wykorzystanym przeze mnie parametrem jest
Icon
. Pozwala on dodać do opisu wybraną ikonę, widać je na rys. 1. Parametr zapisywany jest w postaci:Icon:="cmd/nazwa_pliku_ikony"
.Jak znaleźć potrzebne ikony? Oprogramowanie LibreOffice dostarczane jest z zestawem motywów, a użytkownik może wybrać ten, który mu najbardziej odpowiada. Każdy motyw ma swój zestaw ikon zapisany w pliku typu zip, którego nazwa zaczyna się od „images_”. Pliki te (w systemie Windows) znajdują się w następującej strukturze folderów zainstalowanego pakietu LibreOffice: \share\config. W każdym skompresowanym pliku znajduje się folder „cmd” i to on zawiera wszystkie pliki ikon. Ikony funkcjonalnie sobie odpowiadające, mają taką samą nazwę pliku w każdym zestawie. Można więc podejrzeć obrazy ikon w dowolnym pliku i wybrać wymaganą nazwę. W rzeczywistości wyświetlona zostanie ikona należąca do aktualnie używanego motywu. Ten parametr można oczywiście zdefiniować wcześniej, jeszcze na etapie wstępnego tworzenia menu, albowiem wartości parametrów, które są poprzedzone nazwą parametru (nazwa:=wartość) można wprowadzać w dowolnej kolejności.
Motyw ustalany jest w ustawieniach LibreOffice.
- Wiersz 13.
Nowe menu zostało utworzone w związku z czym obiektoDok
zwalania wszystkie zasoby zawłaszczone przez usługęDocument
.
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
- Wiersz 2.
Zmiennej obiektowejoDok
tym razem przypisana zostaje usługa „Calc”, związana z bieżącym dokumentem (ThisComponent
). - Wiersz 3.
oDok
, będący instancją usługi „Calc” ma właściwość o nazwieRegion()
. Parametrem tej właściwości jest dowolnie wskazana komórka w arkuszu.Region()
wyznacza adres obszaru, który otacza wskazaną komórkę granicami pustych wierszy i kolumn. Przykład, jak taki adres jest wyznaczany, przedstawia rys. 5. W tym przykładzie komórka, względem której zostanie wyznaczony adres obszaru, to ta wyróżniona na ilustracji (F16). Granice obszaru wyznaczają puste kolumny i wiersze, do których można dotrzeć, przemieszczając się wyłącznie poprzez komórki wypełnione. W przypadku tej ilustracji adres obszaru wyglądałby tak: „Arkusz.$D$6:$J$21”. W arkuszu kalkulacyjnym można zobaczyć działanie tego mechanizmu, naciskając skrót klawiaturowy CTRL+* (symbol gwiazdki z klawiatury numerycznej!).
W kodzie instrukcji parametrem jest nazwa komórki: „Wyciąg”. Jest to lewy górny róg obszaru zajętego przez odfiltrowane rekordy. WłaściwośćRegion()
wyznaczy zatem prostokątny obszar w którym znajdzie się ostatni wiersz i ostatnia kolumna odfiltrowanych rekordów. Użycie tej instrukcji jest zasadne, bo proces filtrowania tworzy obszar odfiltrowanych rekordów zależny od parametrów filtrowania, więc z góry nie można określić, ile rekordów spełni warunki, czyli jak duży będzie obszar wynikowy. Adres tego obszaru jest przekazywany do zmiennej tekstowejsReg
. - Wiersze 4 – 7.
Te instrukcje nie są potrzebne do wykonania kopiowania. Wstawiłem je, aby pokazać przydatne właściwości usługi. I tak w wierszu 4. do zmiennejlw
wstawiana jest liczba wierszy znajdujących się w wyznaczonym obszarze, a w wierszu 5. do zmiennejlk
– liczba kolumn. Wiersz 6. generuje komunikat, informujący z ilu wierszy i kolumn składa się obszar. Utworzyłem w tym celu dodatkową funkcję, której zadaniem jest przedstawienie wynikowej liczby z właściwym dla naszego języka dopełnieniem, a więc odpowiednio: 1 wiersz, 2 wiersze, 5 wierszy i analogicznie dla kolumn. Samą funkcję opisałem poniżej.
W wierszu 7. wykorzystałem kolejną właściwość usługi pozwalającą na uzyskanie adresu komórki początkowej i końcowej zadanego obszaru.
- Wiersz 8.
Została tu wykorzystana metodaClearAll()
, która czyści zadany obszar arkusza. Parametr w postaci „Ark2.*” oznacza, że mają zostać wyczyszczone wszystkie komórki w arkuszu Ark2, w ten sposób tworzone jest puste miejsce, na nowe dane, bez analizowania tego co należałoby wyczyścić. - Wiersz 9.
MetodaCopyToCell()
kopiuje obszar wskazany przez pierwszy argument do miejsca, którego położenie definiuje adres komórki, podany w drugim argumencie. Ta metoda przekopiowuje wszystko, a więc formatowanie i formuły. Gdyby rekordy bazy nie zawierały wewnętrznych formuł, to ta instrukcja kończyłaby procedurę. W tym przypadku należy wykonać dodatkowe działania, takie by wszystkie pola zawierały tylko wartości. - Wiersz 10.
Do zmiennejarrTab
reprezentującej tablicę, metodagetValue()
przenosi wszystkie wartości znajdujące się w obszarze, który jest jej parametrem. Jest to ciągle obszar w arkuszu Ark1. - Wiersz 11.
Do zmiennejsReg
wstawiany jest teraz adres obszaru otaczającego komórkę B5 w arkuszu Ark2. Ten obszar to w istocie dopiero co skopiowane dane. Użycie tej instrukcji jest związane z zasadami realizacji instrukcji występującej w następnej linii. - Wiersz 12.
Przy pomocy metodysetValue()
do obszaru wyznaczonego poprzednią instrukcją, wstawiane są wartości zapisane w zmiennejarrTab
. W ten sposób formuły zostają zastąpione wartościami, a istniejące już formatowania pozostają niezmienione. MetodasetValue()
wypełnia wskazany obszar zawartością tablicy. Jeśli docelowy obszar jest większy niż tablica, „nadmiarowe” komórki są opróżniane, a gdy obszar jest mniejszy niż tablica, wypełniane są tylko komórki we wskazanym obszarze. - Wiersz 13.
Zasoby zawłaszczone przez usługę „Calc” zostają zwolnione.
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
- Wiersz 2. i 3.
wystąpił już w procedurze „WykonajKopie”. Wyznacza adres obszaru zawierającego odfiltrowane dane. - Wiersz 4.
WłaściwośćCurrentSelection
aktywuje (zaznacza) w dokumencie obszar, którego adres zawiera zmiennasReg
. W ten sposób zostaje przygotowany obszar dla polecenia „Kopiuj”. - Wiersz 5.
Możliwość korzystania z poleceń systemu dostępna jest w usłudze „Document”, dlatego nowej zmiennej obiektowej zostaje przypisana ta właśnie usługa, ona także dotyczy bieżącego dokumentu. - Wiersz 6.
ObiektoDok2
udostępnia metodęRunCommand()
, której argumentem jest nazwa żądanej do wykonania funkcji, w tym przypadku „Copy”. W efekcie, w schowku znajdzie się zawartość zaznaczonego obszaru.
Przy pomocy tej metody można wywołać każde polecenie pakietu. Nazwa funkcji musi być podana w języku angielskim z uwzględnieniem wielkości liter. Jeśli nazwa zostanie uznana za błędną, instrukcja zostanie zignorowana i nie zostanie zgłoszony żaden błąd. Formalnie w interfejsie API nazwa funkcji prezentowana jest z przedrostkiem.uno:
, w metodzieRunCommand()
można ten przedrostek pomijać. Aby poznać nazwy funkcji, jakie należy wykorzystać w tej metodzie można przejść do okna dialogowego „Narzędzia → Dostosuj… → Klawiatura”. Wskazanie wybranego polecenia w okienku „Funkcja” wyświetli przez chwilę „dymek” podpowiedzi, w którym znajduje się nazwa funkcji. Widać to na poniższej animowanej ilustracji. Efektem wykonania instrukcji z tego wiersza jest skopiowanie zaznaczonego obszaru do schowka systemowego. - Wiersze 7. i 8.
Ponieważ schowek zawiera już wybrany obszar, można zwolnić zasoby obu usług. - Wiersze 9. – 17.
W tym fragmencie utworzona zostanie nazwa pliku, do którego zostanie wklejona zawartość schowka oraz utworzona pełna ścieżka do jego zapisu. - Wiersz 9.
Do zmiennej tekstowejsPlik
pobierana jest pełna identyfikacja pliku źródłowego. Zawiera ona ścieżkę dostępu wraz z nazwą bieżącego dokumentu. Ta instrukcja nie jest elementem ScriptForge. Tę informację uzyskamy z właściwościLocation
obiektuThisCpmponent
. - Wiersz 10.
Do zmiennejFSO
przypisywana jest usługa „FileSystem”. Usługa ta pozwala zarządzać plikami i folderami. - Wiersz 11.
WłaściwośćFileNaming()
określa stosowaną notację dotycząca plików i folderów. Przypisana notacja jest obowiązująca do końca sesji LibreOffice. Notacja zakodowana jako „URL” jest niezależna od systemu operacyjnego. - Wiersz 12 i 13.
Do zmiennej tekstowejsNazwa
wstawiana jest nazwa dokumentu, bez rozszerzenia. Realizuje to metodagetBaseName()
, do zmiennej tekstowejsFolder
, metodagetParentFolderName()
wstawia ścieżkę folderów prowadzącą do pliku. - Wiersz 14.
Nazwa pliku zostaje uzupełniona o datę i czas bieżący. Data wstawiona jest w postaci „dd.mm.rrrr” natomiast czas w postaci „gg:mm:ss”. - Wiersz 15.
W celu uniknięcia nieporozumień przy interpretacji nazw plików, utworzona nazwa zostanie zmieniona w ten sposób, że wszystkie znaki kropki i dwukropka zostaną zamienione na znak podkreślnika. Do tego celu wykorzystana zostanie metodaReplaceRegex()
usługi „SF_String”. Po załadowaniu biblioteki ScriptForge ta usługa jest zawsze dostępna i nie ma potrzeby aktywowania jej poleceniem CreateScriptService. MetodaReplaceRegex()
jest odpowiednikiem funkcji Regex() w aspekcie zamiany znalezionych ciągów na inną treść. - Wiersz 16.
ZmiennasFolder
zawierać będzie pełną ścieżkę dostępu do nowo tworzonego pliku. MetodaBuildPath()
tworzy na podstawie zawartości zmiennychsFolder
orazsNazwa
właściwy ciąg tekstowy, który należy uzupełnić o rozszerzenie nazwy. - Wiersz 17.
Usługa „FileSystem” zostaje zwolniona. - Wiersz 18.
Deklaracja zmiennej obiektowejui
- Wiersz 19.
Zmiennejui
zostaje przypisana usługa „UI”. - Wiersz 20.
Do zmiennejoDok
zostaje przypisany nowo utworzony obiekt programu Calc. Trzeci argument metodyCreateDocument()
(drugi argument jest pominięty) o wartości True oznacza, że utworzony arkusz ma być ukryty. - Wiersz 21.
ZmiennejOtwarte_dokumenty
zostanie przypisana wartość właściwościDocuments
. Będzie to tablica zawierająca nazwy wszystkich dokumentów LibreOffice otwartych w bieżącej sesji.
Tu małe wyjaśnienie, do czego jest to potrzebne. By przypisać jakąś usługę do dokumentu, można w jej drugim parametrze podać nazwę okna, w którym ten dokument się znajduje. Dokument utworzony w wierszu 20., chociaż ukryty, znajduje się w oknie o nazwie „Bez nazwy nr.ods”, gdzie nr to liczba mówiąca o kolejnym nowo utworzonym dokumencie. W uruchomionej sesji LibreOffice może być równocześnie utworzonych więcej dokumentów, utworzonych np. przez Writera, Impres-a lubCalc-a. Wszystkie one mają nazwę rozpoczynająca się od tekstu: „Bez nazwy” z kolejnym numerem dokumentu wyznaczanym w ramach sesji, a nie aplikacji. W tej sytuacji nie możemy być pewni, że utworzony przez nas w wierszu 20. dokument znajduje się w oknie „Bez nazwy 1.ods”. To dlatego pobieramy tablicę nazw wszystkich dokumentów. Tablica reprezentuje stos dokumentów, w którym na pierwszej pozycji znajduje się nazwa okna ostatniego otwieranego lub utworzonego dokumentu. - Wiersz 22.
Zmiennej obiektowejoDok2
przypisuję usługę „Calc”, dotyczącą ostatnio otwartego dokumentu, stąd indeks zerowy tej tablicy. - Wiersz 23.
Można zwolnić usługę „UI”. - Wiersz 24.
Do komórki A1 tego nowego dokumentu wstawiam tekst. - Wiersz 25.
Aktywuję komórkę B4 w dokumencie. - Wiersz 26.
Do zmiennej obiektowejoDok
przypisują usługę „Documents” powiązaną z nowo otwartym dokumentem, tym samym, który jest już powiązany ze zmiennąoDok2
. Robię to, gdyż następne polecenie wykorzysta metodę dostępną tylko w tej usłudze. - Wiersz 27.
W schowku systemowym cały czas znajduje się skopiowany obszar odfiltrowanych rekordów. Wykorzystując metodęRunCommand()
wklejam zawartość schowka. Zostanie ona wstawiona, poczynając od aktywowanej komórki B4. Wykorzystuję polecenie „InsertContents” z podanymi parametrami. Odpowiada ono wykonaniu działania „Wklej specjalnie wartości i formaty”. Pojawia się pytanie, skąd wziąć potrzebne parametry? Przebijanie się przez dokumentację jest niezwykle skomplikowane, najprościej jest zarejestrować tę czynność jako makro i w jego kodzie odnaleźć wymagane parametry. Odpowiedni zarejestrowany fragment kodu wygląda tak: Widać tu nazwę każdego parametru i wymaganą wartość.
Proszę zwrócić uwagę na to, że w metodzieRunCommand()
należy podać zawsze pary: nazwa parametru → wartość. Kolejność tych par nie ma natomiast znaczenia. - Wiersz 28.
Na zmiennejoDok2
, czyli nowym dokumencie, wykonana zostanie metodaSave()
, której parametrami jest pełna ścieżka dostępu do pliku oraz parametr o wartości „True”, oznaczający, że plik może nadpisać istniejący już plik o takiej samej nazwie. - Wiersze 29 i 30.
To sekwencja zamykająca dokument. Ponieważ dokument jest ukryty, to aby go zamknąć, trzeba go najpierw aktywować. - Wiersze 31. i 32.
Następuje zwolnienie obu usług.
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.