GUI w skryptach – porównanie
Zadanie
zadanie jest proste – napisać GUI, które pozwoli wyłączyć maszynę w Azure z opcją force. tego normalnie z interfejsu zrobić się nie da, a operacja potrzebna. chodzi o przeniesienie zadania z 2giej czy 3ciej linii wsparcia do 1szej, czyli nie ma opcji na jakąkolwiek linię poleceń. musi być GUI.
poniżej dwa skrypty, które realizują to samo zadanie. jeden napisany w tym co PS daje [z małym wyjątkiem], drugi w Formsach. przykład ma posłużyć do porównania tych metod i pozwolić się zastanowić jakie są 'za’ i 'przeciw’ dla obu.
(prawie) natywny PowerShell
pełny skrypt można pobrać z GitHuba, poniżej omówienie fragmentów.
założeniem tej wersji jest szybkość działania i uniwersalność. nie korzystam póki co z wersji PS Core ale na pewno korzystanie z biblioteki forms nie będzie możliwe. dla Core można korzystać z XAML UI, którego również jeszcze nie testowałem. jak już pisałem – jedną z bolączek pisania skryptów z GUI jest już sam dobór bibliotek i decyzja 'gdzie ten skrypt będzie uruchamiany i przez kogo’ /:
dla tego 'natywne’ skrypty zachowują lepszą kompatybilność i uniwersalność.
niestety natywnych kontrolek jest niezmiernie mało. najważniejszą jest out-GridView, które umożliwia wyświetlenie listy lub obiektów w postaci interaktywnej tabeli, pozwalając na sortowanie, filtrowanie etc. pozwala również na wybranie pojedynczego lub wielu elementów – czyli może być przydatny również wtedy, kiedy chcemy pozwolić użytkownikowi wybrać obiektów do dalszego przetworzenia. i choć daje wiele możliwości, to równocześnie bardzo niewielką kontrolę.
fragment kodu do którego się odwołuję to:
function select-RG { param( $RGList ) write-host -ForegroundColor Yellow "Select Resource Group VM resides on" $RG=$RGList|out-gridview -title 'Select Resource Group' -OutputMode Single if([string]::isNullOrEmpty($RG)) { write-host 'cancelled by user. quitting' -foreground Yellow exit -1 } write-host "$($RG.ResourceGroupName) chosen. reading VMs..." return $RG.ResourceGroupName }
w szczególności linia pokazująca listę odczytanych Resource Groups w postaci interaktywnej. Out-GridView 'daje-to-co-daje’ czyli nie da się kontrolować wyglądu okna. to co możemy zrobić to:
- title: ustawić tytuł okna. ponieważ nie jest to coś, co rzuca się specjalnie w oczy, staram się poprzedzać to dodatkowo odpowiednim komunikatem na ekranie. zwiększy to prawdopodobieństwo, że użytkownik tego nie przegapi i będzie widział po co w ogóle jakieś okno mu wyskaqje na ekran.
- OutputMode: przyjmuje wartości 'single’ lub 'multiple’ dzięki czemu możemy kontrolować czy oczekujemy pojedynczego wyniku czy listy. np. jeśli piszemy skrypt, który usuwa pliki – chcemy mieć możliwość wybrania od razu wielu. tutaj użyte jest 'single’ co bloqje opcję multi-choice. dodatkowo ten parametr *zastępuje* passthru.
- PassThru: to starsza wersja, nie jestem pewien w której wersji pojawiło się OutputMode… w każdym razie sam 'ogv’ [bo taki ma alias] po prostu wyświetla. można się pobawić i zamknąć. tyle. natomiast dodanie opcji PassThru [lub użycie OutputMode] spowoduje dodanie guzika wyboru 'OK’, po wybraniu którego wybrane elementy zostaną przesłane jako wyniki [pipelined].
siła tkwi w prostocie. chwilę później jednak można zobaczyć:
do { #choose RG $ResourceGroupName=select-RG -RGList $RGsInThisSub $VMlist=get-VMList -RGname $ResourceGroupName if([string]::isNullOrEmpty($VMlist)) { write-host "This Resource Group do not contain any VMs. Do you want to select another RG?" switch ( [System.Windows.Forms.MessageBox]::show($this,"Choose another Resource Group?",'CONFIRM','OKCancel') ) { 'OK' { #do nothing - VMList is null which will trigger loop } 'Cancel' { Write-Host -ForegroundColor Yellow "operation cancelled by the user. quitting." exit -1 } } } } while( [string]::isNullOrEmpty($VMlist) )
czemu nagle pojawia się Forms? to niestety sqcha twórców PowerShell – o ile WScript dysponował prostym okienkiem OK/Cancel, o ile jest OGV w PS… o tyle nie ma takiego prostego okienka OK/Cancel w PS.
co ciekawe, można takie okienko wyświetlić bez Forms! w taki sposób:
Add-Type -AssemblyName PresentationFramework [System.Windows.MessageBox]::show("Choose another Resource Group?",'CONFIRM','OKCancel')
pomimo, że wyglądają bardzo podobnie, i dają niemal ten sam efekt, pod spodem wydarzają się trochę inne rzeczy. jeden jest częścią PresentationFramework drugi Forms, mają inne konstruktory. lepiej używać Formsowego z dwóch powodów:
- zazwyczaj to nie będzie jedyny element GUI, więc jeśli importujesz biblioteki .NET, lepiej już wybrać tą bardziej rozbudowaną w tym kierunq
- wersja z PF nie pozwala w konstruktorze na zdefiniowanie okna nadrzędnego – magiczne '$this’ na pierwszej pozycji show($this,”Choose another Resource Group?”,’CONFIRM’,’OKCancel’) – a w efekcie takie okienko często lądowało pod spodem czegoś aktywnego.
a co z readKey?!
jak już coś pisaliście co wymaga interakcji, powinniście zadać pytanie – czemu nie użyć readKey? natywne, nie wymaga importu bibliotek, szybsze … i tak dalej.
zgoda. ale celem nadrzędnym jest wygoda endusera i założenie, że nie jest bystrym inżynierem (; kiedy zrobiłem readKey, a skrypt miał mieć GUI, sam łapałem się na tym, że czekałem i czekałem… aż zorientowałem się, że to skrypt czeka na mnie, a nie odwrotnie. załamanie logiki – tutaj interfejs a tu linia poleceń – jest bardzo złym podejściem. albo decydujemy się robić skrypt tak, albo inaczej – ale musi być spójnie. inaczej ani nie jest łatwy w użyciu ani prosty w strukturze.
readKey to nie GUI tylko metoda na interakcję bez GUI.
wersja Forms
całość do pobrania z GitHub . kilka uwag n00ba dotyczących pisania PS+Forms.
pierwszą rzeczą jaka rzuca się oczy to narzut ilości kodu. do wykonania mamy jedno polecenie (jedna linijka). w wersji opisywanej wcześniej wyszło 126 linii kodu, tutaj mamy już prawie 19o. jasne się staje że podstawowym problemem będzie możliwy bałagan wynikający z samej ilości. dla tego części dotyczące rysowania samego interfejsu warto oddzielić od reszty. niektórzy decydują się na utworzenie projektu, w ramach którego jest kilka oddzielnych pliqw, i definicje interfejsu wyrzuca się do takiego oddzielnego pliq. spoko, ale ze względu na spójność, przy takich małych toolach wolę trzymać wszystko w pojedynczym pliq. łatwiej go przesłać do niedoświadczonych użytkowników, wytłumaczyć że to taki plik i już, niż kazać tworzyć jakąś strukturę, czy martwić się, że ktoś kawałek usunął czy nie skopiował. ale to oczywiście kwestia wyboru – ważne, żeby sobie oczyścić kod i możliwie mocno oddzielić logikę od definicji.
nie jest to oczywiście do końca możliwe, bo tak, jak opisywałem poprzednio, logika jest de facto związana z interfejsem – trigger-action. klikniemy guzik to uruchomi się jakiś kawałek. całą sekwencyjność szlag trafia.
ja póki co przyjąłem metodę tworzenia regionów – widać znaczniki #region i #endregion które dzielą skrypt na bloki – tutaj 3 bloki – interfejs, akcje interfejsu i cała reszta. zaleta jest taka, że większość edytorów rozpoznaje ten znacznik i pozwala zwinąć tą część kodu żeby nie pasqdziła widoq.
na koniec
niewątpliwie pisanie skryptów z GUI jest dużo trudniejsze i dużo bardziej czasochłonne. tak, jak przy zwykłym skrypcie 6o-8o% czasu to praca nad logiką działania, tak tutaj, ok. 9o% czasu zjadło mi badanie reguł interakcji między poszczególnymi elementami. ok, uczę się więc potem będzie łatwiej, ale nawet przy tak banalnym skrypciq sprawdzenie czy odpowiednio wyczyszczą się wartości konkretnych kontrolek, czy finalnie ustawiona/przeczytana zmienna jest prawidłowa, czy może pozostało coś z klikania usera, czy guziki są aktywne kiedy trzeba, do tego jak to zoptymalizować, że nie ładowało w kółko po pół minuty…
do tego commandlety Az są… hmm.. nie działają zbyt dobrze q: connect-AzAccount się regularnie [niedeterministycznie] zawiesza po zabiciu okna logowania [cancel], zawiesza się dużo bardziej w Vistual Studio Code, a get-AzContex nie implementuje prawidłowo ErrorAction SilentlyContinue…. żeby tak podać najważniejsze. ogólnie to dość trudna biblioteka do współpracy, bez względu na GUI.
eN.
wf-refresh
w-files’om strzeliła w tym rokq piętnastka… czas w IT liczy się trochę inaczej – 15 lat to kilka epok. jeśli przypomnieć sobie jak wyglądał ten świat w 2oo5… trochę sobie przypomniałem przeglądając stare artyqły… długa droga to była i wyboista ale jakże emocjonująca. praca (i po części hobby) zajmuje większość życia – więc ten nLog, choć przez pryzmat technologii i zawodu, ale opowiada ciekawą historię – zarówno o ewolucji technologii jak i mnie samego.
<chwila nostalgii>
wszystkiego naj, wfilesowicze! z tej okazji drobny refresh strony – lżejszy, kompatybilny z aktualnymi wersjami i wyświetla się prawidłowo na mobilu… które w czasach tworzenia poprzedniego szablonu nie służyły za codzienne narzędzie do oglądania treści w Internecie. no i bezpieczniejszy. mam nadzieję, że wytrzyma następną epokę.
pro publico bono.
eN.