niedziela, 30 kwietnia 2017

Nawet przypisywanie przycisków może być podchwytliwe.

Okazało się, że skróty klawiszowe nie działały poprawnie. Trochę potrwało zanim znalazłem przyczynę tego zachowania. Po pierwsze dodając skrót do drop down menu, nie wystarczy skorzystać z argumentu "accelerate". Przekazany w ten sposób argument, jest tylko i wyłącznie tekstem wyświetlonym po prawej stronie etykiety, aby dany skrót działał, należy go również zbindować.

Odbywa się to przez metodę "bind_all". Strona będąca moim głównym źródłem informacji na temat Tkinter nie działała, wiec nie mogłem się dowiedzieć, jak to dokładnie działa. Według mojego rozumowania, co jest w zasadzie czystą spekulacją, metoda "bind" dotyczy, czy raczej jest wywoływana na obiekcie interfejsu, który jest aktualnie "sfokusowany". Wygląda na to, że przed wywołaniem callback'a tej metody, wyszukiwany jest widget posiadający focus, który mógł uzyskać np. poprzez kliknięcie lewym przyciskiem myszy. Jeżeli fokus widget'u zgadzał się z koordynatami event'u, to callback był wykonywany. Natomiast metoda "bind_all", bind'uje niejako daną komendę do wszystkich obecnych elementów. Chociaż zaznaczam, że są to tylko moje spekulacje.

W związku z powyższym, aby skróty z drop down menu działały, zostały zbindowane do obiektu, main_frame, który swoją wielkością obejmuje niemal całe okno. Był niejako krok zapobiegawczy, ponieważ obiekt main_frame, był kolejnym głównym obiektem, zaraz po root, gdyż nie wiem, czy bind_all nie jest związane w jakiś sposób z hierarchią widget'ów w oknie. Jednak okazało się, że przypisanie "bind_all", tylko do wyznaczonego obiektu, np przycisku, powoduje oczekiwane działanie i skrót można wywołać w każdym miejscu okna.

Inną rzeczą było zapisywanie pliku graficznego posiadającego naniesione markery. Szukałem jak najłatwiejszej metody na osiągnięcie tego celu, jednak okazało się, że nie można w prosty sposób dokonać zapisania canvas'u Tkinter za pomocą biblioteki Pillow. Konieczne jest utworzenie obiektu Image i przekazanie go do klasy ImageDraw, w której następuje dodawanie elementów, do instancji klasy Image. Metody wchodzące w skład klasy ImageDraw, są podobne do metod Tkinter canvas. Wystarczyło pobrać listę wszystkich markerów, skonwertować ich kolory do postaci "RGBA" i nanieść na obraz przeznaczony do zapisania do pliku. W związku z tym implementacja tej funkcjonalności nie była najprostsza, ale nadal okazała się być dość prostym zadaniem.

sobota, 29 kwietnia 2017

Trudne implementacje.

Powoli przygotowuję grunt pod umożliwienie zapisywania zmodyfikowanego zdjęcia, to znaczy zdjęcia z naniesionymi znacznikami. Optymalnie było by zapisywać zmodyfikowane zdjęcie do formatu pozwalającego na dodawanie warstw. W ten sposób każda grupa znaczników znajdowała by się na osobnej warstwie, co mocno ułatwiło by dalszą pracę z plikiem. Nie wiem czy biblioteka "pillow" pozwala na zapis do takiego formatu. Na razie zadowolę się zapisem do któregoś z najpopularniejszych formatów.

Dobrym wyborem wydaje się ".png", ze względu na dobrą jakość obrazu, aczkolwiek kosztem wielkości pliku. Dobrym pomysłem wydaje się również sprawdzanie automatyczne wczytanego pliku i zapis do tego samego formatu. Zwalnia to z ewentualnych problemów związanych z konwersją między formatami. Jednak poza ewentualną utratą jakości obrazu, taka konwersja nie powinna stanowić żadnego problemu.

Najlepszym rozwiązaniem wydaje się również zapis zdjęcia w jego oryginalnym rozmiarze. To znaczy bez skalowania, które odbyło się w oknie programu. Wyszedłem z założenia, że ta zaimplementowana funkcja powiększenia, jest tylko i wyłącznie na potrzeby podglądu.

środa, 26 kwietnia 2017

CSV format popularny chociaż nieznany.

Zaimplementowałem eksport statystyk do pliku w formacie ".csv" (coma separated values). Format ten nie wyróżnia się niczym szczególnym, a plik zapisany z takim rozszerzeniem można otworzyć najprostszym programem do edycji tekstu. Po wczytaniu pliku, pojawią się rzędy wartości, które w kolumnach oddzielone są od siebie średnikami, albo innymi znakami. Pierwszy rząd może stanowić nagłówek, pozwalający na zorientowanie się, czego dotyczą poszczególne kolumny danych.
Plik w tym formacie można bez problemu zaimportować do arkusza kalkulacyjnego, a wczytane dane poddać następnie statystycznej obróbce.

W zastosowanej implementacji nie eksportuję wartości procentowych do pliku. W założeniu wartości te miały pomóc ocenie ilościowej, w przypadku pracy tylko z samym BioCounter'em. Ponadto po importowaniu pliku do arkusza kalkulacyjnego dostępnych jest mnóstwo lepszych narzędzi, pozwalających na wykonanie rozmaitych analiz statystycznych.

Język Python posiada wbudowany moduł ułatwiający zapis pliku do formatu ".csv". Dzięki czemu implementacja eksportu do tego formatu staje się bardzo łatwa. Dodatkowo użyłem również modułu "codecs", pozwalającego na zapisywanie plików z czcionkami zawierającymi polskie znaki diakrytyczne.

niedziela, 23 kwietnia 2017

Małymi krokami, ale do przodu.

Wiele nie napisałem. Zastanawiałem się głównie nad tym, gdzie umieścić opcję eksportu statystyk. Na razie padło na panel "Statistics", ale rozważałem również nad dodaniem tej funkcji do drop down menu "File". Chodzi mi o zachowanie jak największej intuicyjności oraz funkcjonalności. Jednak najczęściej sporo rzeczy wychodzi podczas codziennego użytkowania, wtedy najłatwiej wprowadzać wszelkie udogodnienia.

Sprawdziłem również jak zmienia się rozmiar instancji klasy, w przypadku kiedy zawiera ona statyczną metodę, w porównaniu z instancją, która tej metody nie posiada. Okazało się, że statyczna metoda przypisana do klasy nie zmienia wielkości instancji. Być może robię coś, źle ponieważ po dodaniu metody, która dodawała do instancji klasy kolejny atrybut, wielkość instancji nadal nie uległa zmianie. Nawet po użyciu tej metody i przypisaniu nowego atrybutu do instancji.

Ilość miejsca w pamięci zajmowanej przez dany element programu można sprawdzić poprzez importowanie modułu "sys" i użycie komendy:

sys.getsizeof(element_to_check)

Jest to bardzo wygodne polecenie, do kontroli wielkość poszczególnych elementów w pamięci, w celu optymalizacji pracy programu. Moduł "sys" zawiera dużo więcej przydatnych metod, do sprawdzania działania programu i jego pracy w systemie.

sobota, 22 kwietnia 2017

Błędów nie sposób przewidzieć.

Miałem zamiar zaimplementować eksport statystyk do pliku. Jednak odkryłem kolejny błąd i zająłem się jego naprawą. W tym wypadku debugowanie zajęło mi bardzo dużo czasu. Wiedziałem co nie działa i jak powinno działać, ale nie mogłem odnaleźć miejsca odpowiedzialnego za błędy i go naprawić. Okazało się, że kod wymagał więcej niż korekcji tylko w jednym miejscu.

Przyczyną było skalowanie załadowanego obrazka. Wraz ze zdjęciem skalowaniu powinny ulegać również markery, to znaczy nie one same, ale ich pozycja powinna być odpowiednio przeliczana. Dodatkowo, ważne było, aby obiekt "marker" trzymał informacje dotyczące jego absolutnej pozycji.

To był właściwy problem. Nieodpowiednie przeliczanie pozycji. Podczas dodawania markera do "canvas" nie uwzględniałem skali, więc zarejestrowane pozycje markerów, były obarczone błędem związanym z ustawionym powiększeniem dla wczytanego obrazka. Innym problemem było uwzględnianie skali podczas "rysowania" samego markera. Działanie to jest konieczne, podczas przeliczania pozycji, po zmianie skalowania wczytanego zdjęcia, ale w programie zachodziło za każdym razem, kiedy program nanosił marker na "canvas". Implementacja usuwania markerów i nanoszenia nowych, po zmianie przybliżenia, spowodowała w tym wypadku wydłużenie czasu odnajdywania błędu. Jednak nie znalazłem lepszego sposobu na zaimplementowanie tej funkcjonalności.

Nanoszenie nowego markera jest uwzględniane i podczas tej czynności program nie uwzględnia skali załadowanego obrazka. Natomiast w celu wyliczania i uwzględniania absolutnych pozycji markerów utworzyłem dodatkową klasę posiadającą statyczną metodę. Nie wiem, czy w Pythonie instancja klasy zawiera również statyczne metody, więc wybrałem to rozwiązanie, aby niepotrzebnie nie zapychać pamięci komputera,ponieważ markerów naniesionym na "canvas" może być bardzo dużo.

poniedziałek, 17 kwietnia 2017

Nieprzewidziane funkcje, czyli naprawa błędów.

Nawet w nieskomplikowanym kodzie mogą znajdować się błędy. Problem ten dotyka każdego oprogramowania, nawet tego najprostszego. Przeczytałem kiedyś, że na 100 linijek kodu, w 1-3 znajduje się błąd. Wszakże tekst dotyczył programów napisanych w C++, aczkolwiek problem ten ma uniwersalny wymiar i dotyczy wszelkich języków programowania.

Pierwszy błąd, który znalazłem dotyczył "nieskończonych" wymiarów obiektu Tkinter "canvas". Okazało się, że po wczytaniu zdjęcia, ograniczałem rozmiar "canvas'u", do wymiarów zdjęcia, gdyż było to potrzebne do przypisania belek scroll'owania. Jednak sam "canvas" tworzony był dużo wcześniej i nie posiadał tych ograniczeń. Pomimo, że belki przewijania był niewidoczne, "canvas" można było przewijać w pionie, za pomocą rolki myszy oraz w poziomie trzymając wciśnięty klawisz shift i używając w tym czasie rolki myszy. Nie wiem jakie są realne ograniczenia w wymiarach "canvas", ale ja zaniechałem dalszego przewijania przy 35 tysiącach pikseli w poziomie. Nie wiem nawet, czy eksportując obraz w takiej rozdzielczości udało by się tego dokonać dla jakiegoś sensownego formatu np. jpg. Akurat nie pamiętam, czy te koordynaty były przechowywane jako float, czy integer. Jednak Python jest w stanie przechowywać dużo większe liczby całkowite, niż tradycyjne języki programowania. Udało mi się obliczyć 1000000! i zapisać tą wartość do pliku, bez stosowania żadnych dodatkowych bibliotek.

Drugi błąd dotyczył usuwania naniesionych markerów. Obiekty do usunięcia są rejestrowane, poprzez sprawdzenie, jakie obiekty pokrywają się z "testowym" wirtualnym obiektem, a badane jest to w miejscu naciśnięcia prawego przycisku myszy. Okazało się, że wczytany obraz, zawsze się pokrywał z tym obiektem, a co za tym idzie zawsze był usuwany. Naprawa tego błędu była prosta. Przed usunięciem pokrywających się obiektów, sprawdzane jest, czy dany obiekt przeznaczony do usunięcia posiada "tag" znajdujący się na liście "tag'ów" przypisywanych markerom. Jeśli posiada, to jest usuwany.

Mimo, że znalezione błędy były łatwe do uniknięcia, to mimo tego jakoś przesmyknęły się do programu. Pokazuje to jasno, że nawet w najprostszej funkcjonalności może kryć się nieprzewidziany błąd.

niedziela, 16 kwietnia 2017

Dokumentacja kodu.

Jako, że stworzyłem już sporą część programu, a sprawę tą dotychczas zaniedbywałem, postanowiłem dopisać trochę dokumentacji do kodu. Mimo, że staram się zawsze, aby nazwy zmiennych, funkcji, metod i klas, były jak najbardziej opisowe, to lubię dopisać te kilka dodatkowych linijek dokumentacji. Jest to o tyle dobre, że po dłuższej przerwie ułatwi w przypomnieniu struktury danego programu.

Tworząc ten program nie skupiam się tylko na jednym fragmencie, to znaczy nie tworzę go w całości od początku, do końca. Często zmieniam klasę lub element funkcjonalny programu, do którego dopisuję kod. Nie wiem, czy ta metodyka sprawdzała by się dla innych osób. Jednak mi pomaga zapamiętać strukturę całego programu i wydaje mi się, że jest to związane ze sposobem, w jaki funkcjonuje pamięć. Powtarzając i przypominając sobie cyklicznie o danych fragmentach programu tworzę ich mapę i utrwalam ją w pamięci. Oczywiście nie jest to z dokładnością do jednej zmiennej, czy metody. W budowanym od podstaw programie nie ms to znaczenia, ze względu na często dokonywane zmiany. Jednak pomaga mi w ogólnym rozeznaniu na temat klas i metod.

Inną sprawą jest zmiana aktualnie kodowanego segmentu. Pomaga mi to uniknąć znużenia, które mogło by być związane, z pracą tylko i wyłącznie nad jedną klasą, czy funkcją. Po powrocie do kodowania danego elementu, mam na niego niejako świrze spojrzenie, co pomaga wyłapywać pewne błędy i usprawniać kod. Oczywiście idealnie byłoby pisać kod nie wymagający poprawek i usprawnień, jednak na razie wydaje się to poza moim zasięgiem.

To świeże spojrzenie jest związane z tym jak funkcjonują neurony. Otóż pracując nad jednym elementem, tylko jedna grupa neuronów ulega pobudzaniu wraz z częścią neuronów sąsiadujących. Początkowo siła z jaką są one wzbudzone utrzymuje się na wysokim poziomie, a nawet rośnie. Jednak po pewnym czasie ich aktywność spada, co wiąże się z wyczerpaniem neuroprzekaźników uwalnianych do przestrzeni synapsy. Z takim miejscowym pobudzeniem wiąże się, to że myśli i pomysły są ukierunkowane w pewien sposób.

Prostym dowodem na to jest sytuacja, w której staramy się przypomnieć np. nazwisko aktora, który grał w danym filmie. Może się wydawać, że np. miał nazwisko zaczynające się na literę "B". Intensywnie myślimy wtedy o takich nazwiskach - Bronson, Bogart, itp. Im dłużej o tym myślimy, tym więcej nazwisk się przypomina. Jednak, często brakuje tego jednego, konkretnego nazwiska. Po pewnym czasie rezygnujemy, gdyż nie możemy sobie tego aktora przypomnieć. Odkładamy temat na bok, zmieniamy na inny. Wtedy, często się zdarza, że po dłuższej chwili wpadamy na to konkretne nazwisko, o które chodziło od samego początku.

Jest to właśnie związane wzbudzaniem określonej grupy neuronów, która ukierunkowuje w pewien sposób aktualne myślenie. W socjotechnice często wykorzystuje się podobne działanie neuronów.

sobota, 15 kwietnia 2017

Skalowanie obrazu.

Wydawało by się, że skalowanie obrazu jest czymś bardo prostym. Jednak to zagadnienie jest dużo bardziej skomplikowane, niż początkowo mogło by się wydawać. Przywykliśmy do tej opcji tak bardzo, że w programach graficznych nawet nie zwracamy na nią szczególnej uwagi.Niestety nie znam kodu źródłowego Gimp'a na wcale, więc nie mogę przedstawić jak to wygląda w rzeczywistości, mogę opisać jedynie na jakie trudności natrafiłem implementując to w kodzie
Bio Countera.

Okazało się, że musiałem sporo ograniczyć powiększanie obrazu, ponieważ już kilkukrotnie powiększony obraz zajmował bardzo dużą ilość ramu. Ponadto proces samego skalowania obrazu wydłużał się znacznie, wraz z powiększaniem skali. Nie jestem pewien do końca dlaczego, być może powodem jest to, że PIL przeliczając wielkość obrazu korzysta z CPU zamiast z GPU. Rozwiązaniem tego problemu byłoby skalowanie tylko niewielkiego wycinka obrazu, bezpośrednio widocznego dla użytkownika. Dzięki temu problem niewielkiej ilości dostępnego ramu staje się nieistotny, a czas potrzebny do przeskalowania tylko niewielkiego wycinka obrazu ulega istotnemu skróceniu. Innym rozwiązaniem jest skorzystanie z biblioteki do obróbki obrazu, która do transformacji obrazu będzie wykorzystywała GPU. Chociaż w tym wypadku, problem z rosnącą wielkością obrazu pozostanie.

Na razie nie mam czasu, na skupianie się nad implementacją takich rozwiązań. Pozostało jeszcze sporo do zrobienia i wciąż brakuje pewnych obowiązkowych funkcjonalności programu.

wtorek, 11 kwietnia 2017

Wygoda użytkowania, znaczenie pierwszorzędowe.

Nastąpił niewielki commit, ale znaczący. Wreszcie połączyłem przyciski powiększające i zmniejszające rozmiar markerów, z samymi markerami naniesionymi na Tkinter "canvas". Tym oto sposobem, wszystkie podstawowe funkcjonalności markerów zostały dodane.

Do finiszu, pozostało jeszcze daleko. W najbliższym czasie zamierzam dodać możliwość pomniejszania oraz powiększania wczytanego obrazu. Nie wiem, czy w związku z tym nie będzie konieczne dodanie możliwości automatycznego skalowania wielkości markerów. Bądź umożliwienie włączenia takiej funkcji, poprzez zaznaczenie odpowiedniego "checkbox'a" w panelu opcji.

Pod względem funkcjonalności i wygody użytkowania, przeciętny program od programu dobrego oddzielają właśnie takie drobne rzeczy. Może wydawać się to niuansem, nie wartym uwagi na tym etapie rozwoju programu, gdyż zawsze można wprowadzić zmiany, ale wraz z powiększaniem programu, coraz trudniej czegoś takiego dokonać. Kilkakrotnie spotykałem dobre oprogramowanie, które odpychało przeciętnie wykonanym interfejsem użytkownika. Chyba najlepszym tego przykładem był Blender w wersji do 2.49c (chodzi mi o tą wersję posiadającą stary wygląd). Sam program był bardzo dobry i po zapoznaniu się z niezbędnymi skrótami klawiaturowymi korzystało się z niego bardzo przyjemnie, ale ze względu na przestarzałe GUI, początki pracy z tym programem były dla mnie trudne. W przypadku Blendera na pewnym etapie rozrostu tego programu, postanowiono przepisać cały kod aplikacji i zapadła decyzja, że będzie to również związane z powstaniem nowego GUI.

Zagadnienia związane z neuroergonomią mogą pomóc w zaprojektowaniu wygodnego interfejsu. Jednak mimo tego, że pewne rozwiązania wyglądają doskonale w teorii, nie oznacza, że równie dobrze sprawdzą się w praktyce. Trzeba też brać pod uwagę, że ciężko przewidzieć jak dane rozwiązania sprawdzą się w przypadku, kiedy wraz z rozwojem programu, na danej zakładce przybędzie elementów dodających kolejne funkcjonalności.

Bio Counter w założeniu ma być prostym programem i chciałbym, aby korzystanie z niego było wygodne i przyjemne. Samo umieszczenie opcji pozwalającej na zmianę wielkości markera na odrębnej zakładce, było związane z przypisaniem odpowiednich funkcji, do odpowiednich kategorii GUI. Było to również podyktowane ograniczeniem minimalnej rozdzielczości do 800x600, którą to w późniejszym czasie ograniczyłem do 1024x768, ponieważ stwierdziłem, że ekrany posiadające rozdzielczość 800x600 nie są już stosowane. Dobrym rozwiązaniem wydaje się dodanie do opcji zmiany wielkości markera, jakiegoś łatwego do zapamiętania skrótu klawiaturowego np. alt + rolka myszy. Aczkolwiek z rolką myszy jest już związanych kilka funkcji i chciałbym uniknąć sytuacji, w której staną się one mylące.

niedziela, 9 kwietnia 2017

Usuwanie markerów, zaginiona funkcjonalność.

To było to, czego brakowało w tym programie. To znaczy, kiedy dodałem możliwość nanoszenia markerów na "canvas" nie dałem użytkownikowi możliwości usuwania markerów już istniejących. Konieczność odnotowywania błędów "na kartce", ze względu na brak możliwości korekcji pomyłek w programie, była by bardzo irytująca. Bez niej program, nie nadawałby się do użycia.

Ta funkcjonalność była zaplanowana. Jednak zanim ją zaimplementowałam, chciałem dodać klasę oraz elementy interfejsu odpowiedzialne, za zliczanie klikniętych elementów. Gdyż był to element, niejako będący w centrum, chciałem mieć pewność, że wszystko działa tak jak należy.

Okazało się, że sposób w jaki zaimplementowałem zmianę koloru markerów, spowodował niemałe problemy. Spędziłem sporo czasu, na dojście do tego, co działa nie tak jak powinno i dlaczego nie funkcjonuje zgodnie z moim zamysłem.

Podczas dodawania elementów do "canvas" w Tkinter, są one przechowywane w zmiennej typu "touple", a "nazwa" tych elementów jest zgodna z kolejnością ich dodania. Usuwanie markerów działało doskonale, do czasu aż zmieniałem barwę, którychś z istniejących markerów. Problemem była numeracja tych elementów. Program zmieniając kolory wybranych elementów, nie zmienia tak na prawdę ich barwy. Stare elementy są usuwane z "canvas", a na ich miejsce tworzone są nowe posiadające zaktualizowany kolor. Jako, że program przechowuje również informacje o wszystkich markerach w instancjach odpowiednich klas, zapisanych w liście, powstał problem z powiązaniem elementów między tymi dwoma zbiorami. Marker, który był nr 2 na liście markerów, po zmianie barwy stawał się ostatnim elementem w zbiorze ("touple") obiektów "canvas",

Jako, że ktoś mądry powiedział kiedyś, że o wiele łatwiej jest dodać coś do klasy, niż coś z niej usunąć. Postanowiłem dodać do klasy "Marker" zmienną przechowującą informację odnoszącą się do nazwy danego elementu na "canvas". W przypadku zmiany barwy elementu, podczas ponownego rysowania obiektów na "canvas" ich nazwy przechowywane przez instancję klasy "Marker" są aktualizowane. Rozwiązanie to wydaje się proste i jest również skuteczne.

sobota, 8 kwietnia 2017

Statystyki, nie działały.

Testowanie kodu nie jest moją mocną stroną. Wiem jak testy jednostkowe przeprowadzać, ale rzadko je stosuję. Zawsze wydaje mi się, że kod jest za prosty, za łatwy lub za oczywisty, aby wymagało to tworzenia specjalnych testów jednostkowych. Pisanie takich testów nie jest trudne, ale jest czasochłonne. Czasami testowanie prostego kodu zajmuje więcej niż wyłapywanie ewentualnych błędów. Dostałem gorzką nauczkę.

Wydawało mi się, że napisałem prosty kod zliczający kliknięcia i obliczający statystyki. Nie był on w żaden sposób skomplikowany, aczkolwiek struktura nie należała do najbardziej czytelnych. Commit poszedł kilka dni temu, a ja nie zastanawiałem się nad tym, czy to aby faktycznie działa.

Okazało się, że to nie działało od samego początku. Debugowanie kodu nie było zbyt proste, ze względu na brak czytelności. Nie pomagało nawet zastosowanie kilku linii z "print". Dopiero napisanie całego fragmentu kodu od nowa załatwiło sprawę. Nie było to najlepsze rozwiązanie i zostało zastąpione lepszym i bardziej czytelnym. Mimo, że nie przepadam za strukturą zagnieżdżonych słowników, zastosowanie jej do przechowywania ilości markerów i statystyk wydawało się rozwiązaniem najlepszym. Wyłapanie i naprawienie pomyłek zajęło mi sporo czasu.

Zastosowanie testów jednostkowych pozwoliło by na wyłapanie tych błędów na samym początku. Ponadto nie stracił bym niepotrzebnego czasu na debugging kodu, który i tak ostatecznie został usunięty. Okazuje się również, że czasami nawet prosty w założeniu kod może sprawiać problemy i zawierać błędy.

wtorek, 4 kwietnia 2017

Markery, mniej statyczne niż by się zdawało.

Marker miał być prostym elementem graficznym naniesionym na "canvas" w Tkinter, w celu odróżnienia elementów policzonych od niepoliczonych. Mimo, podstawowych założeń, element ten jest daleki od statycznego.

Na razie zaimplementowałem dodawanie pojedynczych markerów oraz usuwanie wszystkich już naniesionych. Jeżeli kolor dla markera określonej próbki zostanie zmieniony, to wszystkie naniesione markery danej próbki aktualizują swój kolor. To znaczy usuwane są wszystkie naniesione, których kolor uległ zmianie i nanoszone są na nowo posiadając aktualny kolor. Jest to możliwe dzięki nadawaniu określonych tagów elementom nanoszonym na "canvas".

W zasadzie nie jest to niczym skomplikowanym. Wszystkie utworzone elementy są przeszukiwane, a następnie sprawdzane są ich tagi. Jeśli dany tag należy do grupy, której zmieniono kolor, id danego elementu jest dodawane do listy. Następnie elementy obecne na liście są usuwane z "canvas'u". W kolejnym kroku jest przeszukiwana główna lista wszystkich naniesionych markerów i jeśli dany marker należy do grupy, której zmieniono barwę, to jest on nanoszony na "canvas" z nową barwą.

W założeniach jest to bardzo proste, ale miałem pewien problem z implementacją, gdyż okazało się, że tagi Tkinter "canvas" działają nieco inaczej, niż to zrozumiałem z dokumentacji. Warto również wspomnieć, że tagi podczas ich przypisywania do elementów, są automatycznie konwertowane do string'a  przechowywanego w touple.

Zastosowane rozwiązania mogą również pozwolić na wybiórcze usuwane danej grupy markerów, co może umożliwić na dodanie czegoś w rodzaju warstw. Przydatnym narzędziem do dodania wydaje się również, możliwość usuwania pojedynczych markerów, na wypadek gdyby, któryś element został oznaczonym przez pomyłkę.

sobota, 1 kwietnia 2017

Statystyki, zakładka której brakowało.

Długo zastanawiałem się w jaki sposób przedstawić tą zakładkę, aby wszystko było czytelne i logicznie rozmieszczone. Niestety Tkinter nie posiada żadnego wbudowanego widgetu, który dawałby funkcjonalność siatki podobnej do tej z arkusza kalkulacyjnego, pozwalającej na równe rozmieszczenie elementów w kolumnach i rzędach. Istnieje biblioteka, która dodaje tą funkcjonalność, ale jak już wcześniej zaznaczałem, chcę ograniczyć korzystanie z zewnętrznych bibliotek do absolutnego minimum.

Pozostaje ręczne budowanie interface'u. Wykorzystanie kopiuj/wklej przy budowie interface'ów w Tkinter to absolutna podstawa. Przydatna jest również opcja "zamień". Czasami jeśli wymaga tego sytuacja, pomagam sobie również, arkuszem kalkulacyjnym. Niewiele osób zdaje sobie sprawę, ale programu te posiadają bardzo przydatne funkcje pozwalające na obróbkę tekstu. Bardzo łatwo przygotować sobie dwie odrębne kolumny w programie tekstowym, a następnie scalić je w arkuszy kalkulacyjnym.

Użyłem tego sposobu do utworzenia słownika, który posiadał rozbudowany klucz będący ciągiem znaków. Jest to bardzo przydatne, ponieważ pozwala zaoszczędzić sporo czasu, na ręcznej zmianie poszczególnych znaków.