From e16844fcce6f77403a052628dcb94f0793fde1c0 Mon Sep 17 00:00:00 2001 From: Mateusz Skoczek Date: Thu, 6 Aug 2020 18:10:55 +0200 Subject: [PATCH 01/29] 4.0 Alpha (Build 19346) --- ...{GeneratorCSV.iml => GeneratorCSV 3.1.iml} | 2 +- .idea/modules.xml | 2 +- .idea/workspace.xml | 47 +-- __pycache__/format.cpython-36.pyc | Bin 2070 -> 0 bytes __pycache__/format.cpython-38.pyc | Bin 2019 -> 0 bytes __pycache__/moduly.cpython-36.pyc | Bin 1689 -> 0 bytes __pycache__/moduly.cpython-38.pyc | Bin 1720 -> 0 bytes changelog-UC.txt | 6 + changelog.txt | 1 + components/__pycache__/dialog.cpython-38.pyc | Bin 0 -> 977 bytes .../__pycache__/load_config.cpython-38.pyc | Bin 0 -> 538 bytes components/dialog.py | 49 +++ components/load_config.py | 20 + components/main.py | 220 ++++++++++ config.cfg | 1 + format.py | 141 ------- generator.py | 399 +----------------- instrukcja.txt => instruction.txt | 0 moduly.py | 51 --- 19 files changed, 326 insertions(+), 613 deletions(-) rename .idea/{GeneratorCSV.iml => GeneratorCSV 3.1.iml} (79%) delete mode 100644 __pycache__/format.cpython-36.pyc delete mode 100644 __pycache__/format.cpython-38.pyc delete mode 100644 __pycache__/moduly.cpython-36.pyc delete mode 100644 __pycache__/moduly.cpython-38.pyc create mode 100644 changelog-UC.txt create mode 100644 components/__pycache__/dialog.cpython-38.pyc create mode 100644 components/__pycache__/load_config.cpython-38.pyc create mode 100644 components/dialog.py create mode 100644 components/load_config.py create mode 100644 components/main.py delete mode 100644 format.py rename instrukcja.txt => instruction.txt (100%) delete mode 100644 moduly.py diff --git a/.idea/GeneratorCSV.iml b/.idea/GeneratorCSV 3.1.iml similarity index 79% rename from .idea/GeneratorCSV.iml rename to .idea/GeneratorCSV 3.1.iml index a94fbfe..9c88284 100644 --- a/.idea/GeneratorCSV.iml +++ b/.idea/GeneratorCSV 3.1.iml @@ -2,7 +2,7 @@ - + diff --git a/.idea/modules.xml b/.idea/modules.xml index 1b13c30..84256e7 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -2,7 +2,7 @@ - + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml index aaff619..396011c 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -1,7 +1,7 @@ - + - + - - + - + - - - - @@ -89,16 +65,12 @@ - - 1574872723273 + + 1575129057993 @@ -106,7 +78,6 @@ - - + \ No newline at end of file diff --git a/__pycache__/format.cpython-36.pyc b/__pycache__/format.cpython-36.pyc deleted file mode 100644 index 14c6217d47956ff78f6aa4560e6f5f12594afe2e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2070 zcmbVN%~K;)5P$vVlMu297!pK8MKp@I?yhbX<;Rww*4AP?;4YeivWD~!Lc&aVGZbWM za$>#nZ|I-lpYcvsdCEnrJZ@QP_k_gV6)w)y^y}$)uRnj?b9-VU=l=f7?$6Ug{3@Qh zB=Ps?iS7_VfzTE;K#9ghRoNkmYL(Rpc{~UU7b|*I2#<|=OyURV93bG`AkY#ab##Sn`6K~Lo z<>86rW4Q5#k&bFjF7@a$ya}!u-$eua$2JNy)&6cBn z8t%dyIEgRd6i(v|zKBJf#S)fr4qw7~d>I$;6a8axeq8tNKQgRfyR6+l^6RbQL?zz3hRmA+S%}}#bZpu8U_h`omYwRtjF2mYAV$Rl5 zK}fho*jkAR%_uW$Ef6x`5Mp_WDT^>wWb6?NjpO3BIJOBNTsga%l}oW!#ihwIqLzrO znCdMSRG7TYztO4KOSmRa+cs4U$4Q zYIi;9J|w&aSLS{Qvdys34A|3%JCQwir3q6~-KDot>bW<$mRaf_Q@c{C2}b0RXowzM zz*)cmyMSHn$?jwfslP6YP16g`a{^8%EcO44WXAfzEj zS8_46>^8Ue(WV$`gC5Nj*Z-5|UH>m0damLQ%OIk2kG%GY|G843%4SE_YKno7 zRbJa(r{f1$8Kd8uM`JGEQHdX)hAN?!sw_q+l6KIAj+FuBQu8&iG2w)aTNa1&{>00`rCq^DskC2}*_weCOzU-k2Flay0D~TLj3h|`ve=^R_F!o*PmR-lS-TXh$aI`%D diff --git a/__pycache__/format.cpython-38.pyc b/__pycache__/format.cpython-38.pyc deleted file mode 100644 index 4cdd0af50da72ebf8ddab98f7992a21c49367ba2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2019 zcmZ`)-A^P%5bx^wWLZ935LiS+MRfJV^DF9U^dg*w9N9C$JC#7pur;iEyUZ*-gSeAf zU(oma6Tml~{SWlN=qD2sll8?XUyM%(3`35sqL=%RrRlG?v9KU1P%X>U$%dm z5aL&Ud3Z>Axk*p*1_=}hEwKqG(fUqoO0?0T*+LgRnr-xPh-L>1I83vPupuhN9-S1` z8t`;BT$-ooS)nKSokS{faSRA2a75OdmYPk;#}RZPwb;v%Tov0(U6~8=CBBzsacrkf zE|2I0dbEG>eEQh~Zk&+FEZ2UH` zAt)b9)xTh&$9dK8jpD5I_zHSv^)HRy5Wo5?JQkd+p2NZ>q2-4gn{xe*hCsuPhMES{ z5Ng=fP}lI4hK7dw8k!nf8um0q8e$D?4N60zA=PlGp`#(IxY}xkk#>@HtC4D}5vAHu zK~xX5z1xUTTS=;D6WrGJPAfpg?l&i*gS}97zKj|n#@S)qilcDTO|oX(>TLQkV%7*+ zol~J+rrm$OOXAjztvgY;sv2L1TWe7iZhfNS`(ZQfEVNZzSHa%TKOSy<97drE(par* z+}qlX)n1S;w>!GH(N@7BX17~G5{!_sWS)e868LfyW}zTU(uZ-HeaX7(vIy!G+Hrwy z44kuQ==L@(azTVxCUOy>Q5tg16FuMri|< zCMl=3dV$v!7B8}Ro}gDD8ABdQDzqHq?3+%Iql>N*`!uac-(8rH6cHhczcZLbkB z>Vz{W`k>yVRgEcpJ{S}SX+YheMGk6)N&w2jl=BRMopYMG0jTM3sj>mr{uykdYA zyJ|A4;lO~aKtG#=c3t}`S?6vo3!_W3dCr#gxZa33afxYe#1CKEP?G8$(p^XoXC}*dmq9g1QNUe`s~>FGS>iF3_en zVm|<7=aOtWbPe3Ds2$KspvO^DdvI8b?liRs^vhzJb^qV*^_yMS9sEao&upxu4rd63B1v<{rP*oi5$iv6qVzIG{tL$c z)qc;t+K+hUkHv5`lZAq*v5!8@X(vwNouFR{+#S?QEM8;5Up@5} zi$@A~Xkg(3`Zr<93+h6)EPy1wjq=)ZVGbtgnSmM0r-JrLI|@Z>a-blgn6Z;!Dm diff --git a/__pycache__/moduly.cpython-36.pyc b/__pycache__/moduly.cpython-36.pyc deleted file mode 100644 index d94317453ba9ae5815eb41511683f4b5882d24f1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1689 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 literal 0 HcmV?d00001 diff --git a/components/__pycache__/load_config.cpython-38.pyc b/components/__pycache__/load_config.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a0178d6966fb618ee701f430583a9bb57d74e5d6 GIT binary patch literal 538 zcmah_%}T>S5Z*MYmSDl7$AzAHuvk4QB0?4M&>sjYlt|cS*OI^8ut_Y*QQBAV9eVZ= zY~Mk8)MxPSY(N@M4$RLt!|eCX-Zh&h0;{b)A3q>}DNwZj| zbQo+5M!j?&s^3@ZP!HPrX4i43a2= , -# - -# Przykład: -# 1a BS Nowak, Adam 1234567u -# - - -# Format danych dla nauczycieli: -# , -# - -# Przykład: -# Nowak, Adam 1234567 -# - - - - -# Inne: -# - skrypt akceptuje prefix 'ks.', nieuwzględnia go w przetwarzaniu -# - skrypt akceptuje nazwiska holenderskie (typu 'van X', 'van der X' itp.) i uwzględnia je w przetwarzaniu -# - skrypt nie akceptuje nazwisk złożonych (np. Nowak-Kowalska) -# - skrypt wymaga kodowania ANSI diff --git a/generator.py b/generator.py index f6919a6..4d64128 100644 --- a/generator.py +++ b/generator.py @@ -1,9 +1,14 @@ +""" # GeneratorCSV -# Wersja 3.0.1 +# Wersja 4.0: UC 1 # by Mateusz Skoczek # luty 2019 - grudzień 2019 # dla ZSP Sobolew +# +# Główny skrypt uruchamiający +# +""" @@ -12,28 +17,9 @@ +# ----------------------------------------- # Definicja kodów dialogowych # ------------------------------------------ # -## Defincja błędów ############################################################################## - -# E001 - Brak pliku składowego -E001x00 = "Brak pliku formatu 'moduly.py'.\nPrzywróć plik. (E001x00)" -E001x01 = "Brak pliku formatu 'format.py'.\nPrzywróć plik. (E001x01)" -E001x02 = "Brak pliku konfiguracyjnego 'config.cfg'.\nPrzywróć plik. (E001x02)" -E001x03 = "Brak pliku 'instrukcja.txt'.\nPrzywróć plik. (E001x03)" - -# E002 - Błąd pliku składowego -E002x02 = "Nieokreślony błąd pliku konfiguracyjnego 'config.cfg'.\nPrzywróć domyślny plik lub popraw ustawienia. (E002x02)" -E002x021 = "Błąd pliku konfiguracyjnego 'config.cfg'.\nPodane kodowanie nie jest obsługiwane\nPrzywróć domyślny plik lub popraw ustawienia. (E002x021)" - -# E003 - Błąd lokalizacji plików I/O -E003x01 = "Nie podano lokalizacji plików do importu. (E003x01)" -E003x02 = "Nie podano lokalizacji zapisu wygenerowanych plików. (E003x02)" -E003x111 = "Plik podany w sciezce 1 nie istnieje (E003x111)" -E003x112 = "Plik podany w sciezce 2 nie istnieje (E003x112)" -E003x113 = "Plik podany w sciezce 3 nie istnieje (E003x113)" -E003x114 = "Plik podany w sciezce 4 nie istnieje (E003x114)" - -#_______________________________________________________________________________________________# +E000x00 = "Brak głównego pliku składowego programu ('main.py').\nPrzywróć plik. (E000x00)" @@ -42,21 +28,10 @@ E003x114 = "Plik podany w sciezce 4 nie istnieje (E003x114)" +# ---------------------------------------- # Import bibliotek zewnętrznych # ----------------------------------------- # - -## Import bibliotek zewnętrznych ################################################################ - -import tkinter as tk -import codecs as cd -import os -import time as tm -import sys as ss - -# Definicja składowych biblioteki interfejsu graficznego -from tkinter import filedialog as TKfld -from tkinter import messagebox as TKmsb - -#_______________________________________________________________________________________________# +import os as OS +import sys as SS @@ -65,351 +40,13 @@ from tkinter import messagebox as TKmsb - - -## Weryfikacja istnienia plików składowych programu ############################################# +# ----------------------------------------- # Uruchomienie głównego modułu # ----------------------------------------- # try: - x = open('moduly.py') -except FileNotFoundError: - Message = 'Wystąpił błąd!\n' + E001x00 - tk.showerror('Błąd', Message) - ss.exit(0) - -try: - x = open('format.py') -except FileNotFoundError: - Message = 'Wystąpił błąd!\n' + E001x01 - tk.showerror('Błąd', Message) - ss.exit(0) - -#_______________________________________________________________________________________________# - - - - - - - - - - -## Import modułów programu ###################################################################### - -# Import modułów składowych programu -from moduly import ErrorDialog as MDerr -from moduly import FileCheck as MDfck -from moduly import PolishLetterRemover as MDplr -from moduly import ClassTagCreator as MDctc - -# Import skryptu przetwarzającego dane -import format as ft - -#_______________________________________________________________________________________________# - - - - - - - - - - - -## Weryfikacja istnienia plików składowych ###################################################### - -MDfck('format.py', E001x01) -MDfck('config.cfg', E001x02) -MDfck('instrukcja.txt', E001x03) - -#_______________________________________________________________________________________________# - - - - - - - - - - - -## Wczytywanie pliku konfiguracyjnego ########################################################### - -try: - with open('config.cfg', 'r') as config: - config = config.read().split('\n') - Kodowanie = str(config[0].strip('Kodowanie: ')) - TypyKodowania = ['utf-8', 'cp1252', 'iso-8859-1'] - if Kodowanie not in TypyKodowania: - MDerr(E002x021) + fck = open("components\main.py") except: - MDerr(E002x02) - -#_______________________________________________________________________________________________# - - - - - - - - - - - -## Inicjacja skryptu przetwarzającego dane ###################################################### - -def Main(): - if TKmsb.askokcancel('Ostrzeżenie', "Czy na pewno chcesz rozpocząć generowanie?\nProgram utworzy w podanej lokalizacji pliki 'email.csv' i 'office.csv'.\nJeżeli w podanej lokalizacji istnieją pliki o takich nazwach zostaną one nadpisane."): - sciezka1 = Pole1.get() - sciezka1_puste = True - sciezka2 = Pole2.get() - sciezka2_puste = True - sciezka3 = Pole3.get() - sciezka3_puste = True - sciezka4 = Pole3.get() - sciezka4_puste = True - sciezkaExport = PoleExport.get() - sciezkaExport_puste = True - - if sciezka1 != '': - sciezka1_puste = False - if sciezka2 != '': - sciezka2_puste = False - if sciezka3 != '': - sciezka3_puste = False - if sciezka4 != '': - sciezka4_puste = False - if sciezkaExport != '': - sciezkaExport_puste = False - - if sciezka1_puste and sciezka2_puste and sciezka3_puste and sciezka4_puste: - MDerr(E003x01) - if sciezkaExport_puste: - MDerr(E003x02) - - KontenerDanych = [] - if not sciezka1_puste: - try: - x = open(sciezka1) - except FileNotFoundError: - MDerr(E003x111) - else: - with open(sciezka1, 'r') as plik1: - KontenerDanych += ft.przetworz(plik1.read()) - if not sciezka2_puste: - try: - x = open(sciezka2) - except FileNotFoundError: - MDerr(E003x112) - else: - with open(sciezka2, 'r') as plik2: - KontenerDanych += ft.przetworz(plik2.read()) - if not sciezka3_puste: - try: - x = open(sciezka3) - except FileNotFoundError: - MDerr(E003x113) - else: - with open(sciezka3, 'r') as plik3: - KontenerDanych += ft.przetworz(plik3.read()) - if not sciezka4_puste: - try: - x = open(sciezka4) - except FileNotFoundError: - MDerr(E003x114) - else: - with open(sciezka4, 'r') as plik4: - KontenerDanych += ft.przetworz(plik4.read()) - - KontenerEmail = [] - KontenerOffice = [] - for osoba in KontenerDanych: - if osoba[-1]: - Klasa = osoba[0] - Imie = osoba[2] - Inicjaly = Imie[0] - Nazwisko = '' - NazwiskoDoEmaila = '' - for x in osoba[1]: - Nazwisko += x + ' ' - NazwiskoDoEmaila += ('.' + x) - Inicjaly += x[0] - Nazwisko = Nazwisko[:-1] - ZnacznikKlasy = MDctc(Klasa) - Login = osoba[3] - Adres = MDplr(Imie).lower() + MDplr(NazwiskoDoEmaila).lower() + ZnacznikKlasy + '@losobolew.pl' - Email = Adres + ',' + Login + ':' + MDplr(Inicjaly) + ',500' - Office = Adres + ',' + Imie + ',' + Nazwisko + ',' + Imie + ' ' + Nazwisko + ',uczeń,' + Klasa + ',,,,,,,,,Rzeczypospolita Polska' - KontenerEmail.append(Email) - KontenerOffice.append(Office) - else: - Imie = osoba[1] - Inicjaly = Imie[0] - Nazwisko = '' - NazwiskoDoEmaila = '' - for x in osoba[0]: - Nazwisko += x + ' ' - NazwiskoDoEmaila += ('.' + x) - Inicjaly += x[0] - Nazwisko = Nazwisko[:-1] - Login = osoba[2] - Adres = MDplr(Imie).lower() + MDplr(NazwiskoDoEmaila).lower() + '@losobolew.pl' - Email = Adres + ',' + Login + ':' + MDplr(Inicjaly) + ',500' - Office = Adres + ',' + Imie + ',' + Nazwisko + ',' + Imie + ' ' + Nazwisko + ',nauczyciel,,,,,,,,,,Rzeczpospolita Polska' - KontenerEmail.append(Email) - KontenerOffice.append(Office) - sciezkaEmail = sciezkaExport + '/email.csv' - sciezkaOffice = sciezkaExport + '/office.csv' - with cd.open(sciezkaEmail, 'w', Kodowanie) as plikEmail: - for x in KontenerEmail: - plikEmail.writelines(x + '\n') - plikEmail.close() - with cd.open(sciezkaOffice, 'w', Kodowanie) as plikOffice: - for x in KontenerOffice: - plikOffice.writelines(x + '\n') - plikOffice.close() - TKmsb.showinfo('Zakończono', 'Operacja zakończona pomyślnie') - ss.exit(0) - else: - ss.exit(0) - - -#_______________________________________________________________________________________________# - - - - - - - - - - - -## Inicjacja okna ############################################################################### - -# Zmienne globalne środowiska graficznego -SzerokoscOpisu = 17 -SzerokoscPola = 91 -TytulProgramu = 'GeneratorCSV' -Autorzy = 'Mateusz Skoczek' -Wersja = '3.0.1' -Lata = '2019' - -# Tworzenie okna -OknoGlowne = tk.Tk() -OknoGlowne.title(TytulProgramu) -OknoGlowne.resizable(width = False, height = False) - -# Nazwa programu -Tytul = tk.Label(OknoGlowne, text = TytulProgramu, font = ('Segoe UI Semilight', 20), borderwidth = 7, justify = 'center', bg = 'Gainsboro', width = 47) -Tytul.grid(row = 0) - - -# Tworzenie frame dla ścieżek plików do importu -Ramka1 = tk.LabelFrame(OknoGlowne, text = 'Pliki do importu zawierające dane') -Ramka1.grid(row = 1) - -# Ścieżka pliku do importu 1 -wiersz1 = 0 -text1 = tk.StringVar() -OpisPola1 = tk.Label(Ramka1, text = 'Plik z danymi (1)', justify = 'left', width = SzerokoscOpisu) -OpisPola1.grid(row = wiersz1, column = 0) -Pole1 = tk.Entry(Ramka1, textvariable = text1, width = SzerokoscPola) -Pole1.grid(row = wiersz1, column = 1) -def Browse1_Dialog(): - Browse1.filename = TKfld.askopenfilename(initialdir="/", title="Wybierz plik", filetypes=(("Pliki txt", "*.txt"), ("Wszystkie pliki", "*.*"))) - Pole1.delete(0, 'end') - Pole1.insert(0, Browse1.filename) -Browse1 = tk.Button(Ramka1, text = '...', command = Browse1_Dialog, background = 'silver', relief = 'flat') -Browse1.grid(row = wiersz1, column = 2, padx = 5, pady = 3) - -# Ścieżka pliku do importu 2 -wiersz2 = 1 -text2 = tk.StringVar() -OpisPola2 = tk.Label(Ramka1, text = 'Plik z danymi (2)', justify = 'left', width = SzerokoscOpisu) -OpisPola2.grid(row = wiersz2, column = 0) -Pole2 = tk.Entry(Ramka1, textvariable = text2, width = SzerokoscPola) -Pole2.grid(row = wiersz2, column = 1) -def Browse2_Dialog(): - Browse2.filename = TKfld.askopenfilename(initialdir="/", title="Wybierz plik", filetypes=(("Pliki txt", "*.txt"), ("Wszystkie pliki", "*.*"))) - Pole2.delete(0, 'end') - Pole2.insert(0, Browse2.filename) -Browse2 = tk.Button(Ramka1, text = '...', command = Browse2_Dialog, background = 'silver', relief = 'flat') -Browse2.grid(row = wiersz2, column = 2, padx = 5, pady = 3) - -# Ścieżka pliku do importu 3 -wiersz3 = 2 -text3 = tk.StringVar() -OpisPola3 = tk.Label(Ramka1, text = 'Plik z danymi (3)', justify = 'left', width = SzerokoscOpisu) -OpisPola3.grid(row = wiersz3, column = 0) -Pole3 = tk.Entry(Ramka1, textvariable = text3, width = SzerokoscPola) -Pole3.grid(row = wiersz3, column = 1) -def Browse3_Dialog(): - Browse3.filename = TKfld.askopenfilename(initialdir="/", title="Wybierz plik", filetypes=(("Pliki txt", "*.txt"), ("Wszystkie pliki", "*.*"))) - Pole3.delete(0, 'end') - Pole3.insert(0, Browse3.filename) -Browse3 = tk.Button(Ramka1, text = '...', command = Browse3_Dialog, background = 'silver', relief = 'flat') -Browse3.grid(row = wiersz3, column = 2, padx = 5, pady = 3) - -# Ścieżka pliku do importu 4 -wiersz4 = 3 -text4 = tk.StringVar() -OpisPola4 = tk.Label(Ramka1, text = 'Plik z danymi (4)', justify = 'left', width = SzerokoscOpisu) -OpisPola4.grid(row = wiersz4, column = 0) -Pole4 = tk.Entry(Ramka1, textvariable = text4, width = SzerokoscPola) -Pole4.grid(row = wiersz4, column = 1) -def Browse4_Dialog(): - Browse4.filename = TKfld.askopenfilename(initialdir="/", title="Wybierz plik", filetypes=(("Pliki txt", "*.txt"), ("Wszystkie pliki", "*.*"))) - Pole4.delete(0, 'end') - Pole4.insert(0, Browse4.filename) -Browse4 = tk.Button(Ramka1, text = '...', command = Browse4_Dialog, background = 'silver', relief = 'flat') -Browse4.grid(row = wiersz4, column = 2, padx = 5, pady = 3) - - -# Tworzenie frame dla plików export -Ramka2 = tk.LabelFrame(OknoGlowne, text = 'Ustawienia eksportu') -Ramka2.grid(row = 2) - -# Ścieżka folderu do zapisu wygenerowanych plików -text4 = tk.StringVar() -OpisPolaExport = tk.Label(Ramka2, text = 'Lokalizacja', justify = 'left', width = SzerokoscOpisu) -OpisPolaExport.grid(row = 0, column = 0) -PoleExport = tk.Entry(Ramka2, textvariable = text4, width = SzerokoscPola) -PoleExport.grid(row = 0, column = 1) -def BrowseExport_Dialog(): - BrowseExport.filename = TKfld.askdirectory() - PoleExport.delete(0, 'end') - PoleExport.insert(0, BrowseExport.filename) -BrowseExport = tk.Button(Ramka2, text = '...', command = BrowseExport_Dialog, background = 'silver', relief = 'flat') -BrowseExport.grid(row = 0, column = 2, padx = 5, pady = 3) - - -# Przycisk START -Przycisk = tk.Button(OknoGlowne, text = 'START', justify = 'center', width = 50, command = Main, relief = 'flat', background = 'silver') -Przycisk.grid(row = 3, pady = 15) - - -# Pasek dolny -PasekDolny = tk.LabelFrame(OknoGlowne, bd = 0, background = 'Gainsboro') -PasekDolny.grid(row = 4) -info = TytulProgramu + ' ' + Wersja + ' | © ' + Autorzy + ' '+ Lata + ' dla ZSP Sobolew' -InfoLabel = tk.Label(PasekDolny, text = info, justify = 'left', width = 93, anchor = 'w', background = 'Gainsboro') -InfoLabel.grid(row= 0, column = 0) -def InfoOpen(): - try: - x = open('instrukcja.txt') - except FileNotFoundError: - MDerr(E001x03) - else: - os.system("notepad instrukcja.txt") -Przycisk = tk.Button(PasekDolny, text = 'Instrukcja', justify = 'center', foreground = 'blue', relief = 'flat', command = InfoOpen, background = 'Gainsboro') -Przycisk.grid(row = 0, column = 1) - - -tk.mainloop() - -#_______________________________________________________________________________________________# + print('Nieoczekiwany wyjatek - nie mozna wygenerowac okna dialogowego bledu\n\nBŁĄD KRYTYCZNY!\n%s') % E000x00 + wait = input('Naciśnij ENTER aby zakończyć') + SS.exit(0) +else: + OS.system("components\main.py") diff --git a/instrukcja.txt b/instruction.txt similarity index 100% rename from instrukcja.txt rename to instruction.txt diff --git a/moduly.py b/moduly.py deleted file mode 100644 index d98987d..0000000 --- a/moduly.py +++ /dev/null @@ -1,51 +0,0 @@ -import sys as ss -import time as tm -from tkinter import messagebox as TKmsb - -def ErrorDialog(KodBledu): - Message = 'Wystąpił błąd!\n' + KodBledu - TKmsb.showerror('Błąd', Message) - ss.exit(0) - -def FileCheck(Plik, KodBledu): - try: - x = open(Plik) - except FileNotFoundError: - ErrorDialog(KodBledu) - -def PolishLetterRemover(text): - text1 = text.replace('ę', 'e') - text2 = text1.replace('ó', 'o') - text3 = text2.replace('ą', 'a') - text4 = text3.replace('ś', 's') - text5 = text4.replace('ł', 'l') - text6 = text5.replace('ż', 'z') - text7 = text6.replace('ź', 'z') - text8 = text7.replace('ć', 'c') - text9 = text8.replace('ń', 'n') - text10 = text9.replace('Ę', 'E') - text11 = text10.replace('Ó', 'O') - text12 = text11.replace('Ą', 'A') - text13 = text12.replace('Ś', 'S') - text14 = text13.replace('Ł', 'L') - text15 = text14.replace('Ż', 'Z') - text16 = text15.replace('Ź', 'Z') - text17 = text16.replace('Ć', 'C') - text = text17.replace('Ń', 'N') - return text - -def ClassTagCreator(Klasa): - czas = tm.localtime() - miesiac = czas[1] - if miesiac >= 9: - rokpodst = czas[0] - else: - rokpodst = czas[0] - 1 - nrklasy = int(Klasa[0]) - literaklasy = Klasa[1] - szkola = Klasa.split(' ')[1] - if szkola == 'BS': - znacznik = str((4 - nrklasy) + rokpodst) + szkola - else: - znacznik = str((5 - nrklasy) + rokpodst) + literaklasy - return znacznik \ No newline at end of file From fe6950342d15ef34132b76b01164943183133de4 Mon Sep 17 00:00:00 2001 From: Mateusz Skoczek Date: Thu, 6 Aug 2020 18:12:58 +0200 Subject: [PATCH 02/29] 4.0 Alpha (Build 19346.1) --- .idea/vcs.xml | 6 + .idea/workspace.xml | 17 ++ changelog-UC.txt | 11 +- components/__pycache__/dialog.cpython-38.pyc | Bin 977 -> 1005 bytes .../__pycache__/load_config.cpython-38.pyc | Bin 538 -> 2469 bytes components/dialog.py | 13 +- components/load_config.py | 111 ++++++- components/main.py | 283 ++++++++++++------ config.cfg | 4 +- generator.py | 4 +- 10 files changed, 348 insertions(+), 101 deletions(-) create mode 100644 .idea/vcs.xml diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..9661ac7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 396011c..80357d7 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -15,9 +15,13 @@ + + + + + + + \ No newline at end of file diff --git a/changelog-UC.txt b/changelog-UC.txt index 7f8e910..ad257c0 100644 --- a/changelog-UC.txt +++ b/changelog-UC.txt @@ -3,4 +3,13 @@ - Dodanie w głównym (nadrzędnym) folderze programu, skryptu inicjującego główny moduł programu - Dodanie zapisu opcji czarnego motywu do 'config.cfg' - Wstępne stworzenie skryptu pozwalającego na zapis do pliku 'config.cfg' -- Błąd o braku głównego pliku składowego programu ('main.py') oraz modułu wywołującego okna dialogowe ('dialog.py') \ No newline at end of file +- Błąd o braku głównego pliku składowego programu ('main.py') oraz modułu wywołującego okna dialogowe ('dialog.py') + +4.0 Alpha (Build 19346.1) +- Przebudowanie skryptu odczytującego plik konfiguracyjny +- Dodanie skryptu zapisującego ustawienia do pliku konfiguracyjnego +- Scalenie dwóch powyższych skryptów do modułu 'load_config.py' +- Przeprojektowanie kodu +- Lekkie przeprojektowanie układu interfejsu +- Stworzenie palety barw motywu jasnego i ciemnego +- Wprowadzenie motywu jasnego i ciemnego (z możliwością zmiany jedynie przez plik 'config.cfg' diff --git a/components/__pycache__/dialog.cpython-38.pyc b/components/__pycache__/dialog.cpython-38.pyc index 8a7dee90d0780d7e880dcfe374ca20a63074b755..057de7db0e717b4fe8f4f464fbb675f1dbb125f6 100644 GIT binary patch delta 245 zcmcb}{+69Dl$V!_0SJ719>?s^6oIsi*g(;XpldE(hP%D?Rf_rLSYEfcIevxx<7?2yDT2!2ss9>UJV5Jc1tY9>; zwS?bKlkpZ;ZfbFHVtQ&)e#K-C#_fy=lMgVOn8rYrhvucE7AZLA=M|R}l_r;D=I6yE z=jRsW=cVSA6vw1wCg$X)>lIYqVs delta 243 zcmaFMevzFol$V!_0SJnYJc_-+Jdsb!H7gSr2GoM$$X648NDVSV>FQmsfbC=&n?K$OU)}Oj!DT(%*jvJE2zB1>{?W$ zFj=2z6Qla%M@;>exS20_03h^*<@NlqmumJ!ksX&SV diff --git a/components/__pycache__/load_config.cpython-38.pyc b/components/__pycache__/load_config.cpython-38.pyc index a0178d6966fb618ee701f430583a9bb57d74e5d6..d0e2ef022b6a1b73d3294f34acc3e594a9744bc6 100644 GIT binary patch literal 2469 zcmb_ePj3@P6rb5Y_Qr8S0wN?(!2+s?E0H=@TeY;RDw0sOC@#f;5X&fCb|&t|&W@Sg zWwR^iP!ft%RjPXFg&u<1OV4}+;!8imUODjrdg-a}jU5OmNLAIf_RZUwo%i0p-|xNg zjggT8f$QAQ&u*V767mcm4qg^Kd8q=FvU1NqCb!Mv3U^z9KEYGZV zuAWbeO0()i%fzo3gcn)`=~IF~KFo%;DW?;t7g=GO)<<}Lca-HHQ%Y8duiYYSm=$+O z{RkUjqcD!KBWw)Dac0#>X*~KBLMoiMKj%J|PAKGD?It`fa~Z5V_IoqsOZJU9y8=^p z5_{1Jc@(5}ttH%)w_vgzg^67$&wgZIw3{+wsmFUeY62En+p+7lYj#cC5p5n93g=;R zQLw0Y-%cHw?mb|sv%dGhP3%tFYk7RbZV7+QYev#>lXXAYfF02i1bA6GEqg=ljO;jx zMelxu8+lXMqUAd_^PIM5ikREe7y9~4C%I6XDO{6j63eGg_8ux9GxqdCxm?~X&z7Pq z`-ahZ^r~w#Jbl4c(agAO%{Q8wMN>;r3vKo8J4|``7sjd{?}(0cVhG-Ai{2v$F!mtp zX=2BHBZtRc*({e!5&Q4{xXm1&gOk3y$ew+pBS_N!>8SEXN0riZ_#je^>L@*~=;g(Z z56MW@fpsn5m+x`lKne+qlP1zt#E$zK`~cgXHfK?xaJl#0-p;&z_4>-v%3Qs=a<=ex z5S>Buj_dUv`QEy{P+eNMZaYBi)M<&{j+-WX4_%6p3@!skEZ#Y|gFiu}lOgGnZOUj% zmlGir@iyI}P?)C;5^7MGAM^_|)OU^0d|8BPIiy{}^eNc81%=<$45E{kA?wTlx#k;` z_|_rzzCU0u_tG9Sn{-Q0C&8vMMY?(`FMmH|_uhfs2U`Mx($7_&Yw$%G-IVyr&rzgi z6s}$Tuw-R=oBNsW`C(SL>9ixhAf=F*xj4_-%}j@M49YLf#!NB8IDQFzP6SNAUy6g&YGO@06+Er!tJ z0ctX!1_f%oGXO)|)k9+dW#bmXH-SdlPhW1)F5NXBlU+Cg9s^C9WDAHgPi{|c8C`uh z-!)(bb~1B{;QqVS(CX^jpmp_SqLKH>*W^0+4Zy__I)5MLzl$@F1pG+sWEqtiEu`8d zTOzwlLzy8ufc5wlug$9>ydol>DYPXrUk-s#$YQ6<49OjqnE|xSFv~d|*vV8rN~S!5 zZ|dkG*LNpJ(14Btk?o6d)TTh(ggcl3K~!cT-x_A!pcb8gTfOs2F=Cc68kOZ=K#~?s54R?l=U9%;3xm zo3J`S!g_&)K~Dk>MMUB(te1{v`PEe>+|^aZ20TLhfgthaFbeqVgCQkBrqya$u73`z zxU*w;?7zmZ%PFsA2sqA)XJ2RLQ$O)uY WaU8j$ej}Eu-BA_)SkXFVjsFAX>w+}^ delta 333 zcmZ1~Jd4FEl$V!_0SK6mJc_*mq#uJg$bbpRaRB0C2_TWe5XF$fn8MV;5XG3noWcU6 znNnDT88q2Sr%l`^!)&Z)IPr0nMKVw+3Sb2CoPoHQ1xTbY1T(B;^jpbL1Tq^zeD$B~ z&uYS=05oLsTvq9NmZH?e6kd=nkU9p&B9LanMi+rBLzs%hVg^dwVo6QOED->*V5YJH t*?yYLx7g$3Q}UDJ check > 1: + error = int('x') + except ValueError: + MDdlg.Err(E002x01) + + # Linia 2 (utf-8) + DostepneKodowanieWyjsciowe = ['utf-8'] + try: + if settings[1] not in DostepneKodowanieWyjsciowe: + error = int('x') + except ValueError: + MDdlg.Err(E002x02) + + + +# Odczytywanie ustawień z pliku konfiguracyjnego +def read(): + try: + check = open('.\config.cfg') + except FileNotFoundError: + MDdlg.Err(E001x01) + else: + with open('.\config.cfg', 'r') as cfg: + config = cfg.read().split('\n') + settings = [] + for x in config: + settings.append(x.split(': ')[1]) + CheckConfig(settings) + return settings + + + +# Zapis ustawień do pliku konfiguracyjnego +def edit(settings): + CheckConfig(settings) + try: + check = open('.\config.cfg') + except FileNotFoundError: + MDdlg.Err(E001x01) + else: + SettingsToSave = [] + SettingsToSave.append('Ciemny motyw(0/1): ' + str(settings[0]) + '\n') + SettingsToSave.append('Kodowanie wyjsciowe: ' + str(settings[1])) + with open('.\config.cfg', 'w') as cfg: + for x in SettingsToSave: + cfg.write(x) \ No newline at end of file diff --git a/components/main.py b/components/main.py index f84fc20..b686366 100644 --- a/components/main.py +++ b/components/main.py @@ -1,6 +1,6 @@ """ # GeneratorCSV -# Wersja 4.0: UC 1 +# Wersja 4.0: UC 2 # by Mateusz Skoczek # luty 2019 - grudzień 2019 # dla ZSP Sobolew @@ -14,6 +14,15 @@ +# -------------------------------------------- # Informacje o programie # -------------------------------------------- # + +Nazwa = 'GeneratorCSV' +Wersja = '4.0: UC 2' + + + + + @@ -21,6 +30,7 @@ E000x01 = "Brak modułu wywołującego okna dialogowe ('dialog.py').\nPrzywróć plik. (E000x01)" E000x02 = ["Brak modułu zarządzającego plikiem konfiguracyjnym ('load_config.py').\nPrzywróć plik. (E000x02)", True] +E001x02 = ["Brak pliku instrukcji ('instruction.txt').\nPrzywróć plik. (E001x02)", False] @@ -35,6 +45,8 @@ E000x02 = ["Brak modułu zarządzającego plikiem konfiguracyjnym ('load_config. import os as OS import sys as SS + + # Moduły składowe programu try: import dialog as MDdlg @@ -42,11 +54,14 @@ except ModuleNotFoundError: print('Nieoczekiwany wyjatek - nie mozna wygenerowac okna dialogowego bledu\n\nBŁĄD KRYTYCZNY!\n%s') %E000x01 wait = input('Naciśnij ENTER aby zakończyć') SS.exit(0) + try: import load_config as MDlcg except ModuleNotFoundError: MDdlg.Err(E000x02) + + # Biblioteki zewnętrzne interfejsu graficznego from tkinter import filedialog as TKfld import tkinter as TK @@ -61,125 +76,201 @@ import tkinter as TK # ------------------------------------- # Uruchomienie interfejsu graficznego # -------------------------------------- # -# Informacje o programie -Nazwa = 'GeneratorCSV' -Wersja = '4.0' #Todo wersja - # Zmienne globalne środowiska graficznego - -CiemnyMotyw = +if int(MDlcg.read()[0]) == 1: + CiemnyMotyw = True +else: + CiemnyMotyw = False SzerokoscOpisu = 17 -SzerokoscPola = 91 +SzerokoscPola = 122 + + + +# Kolorystyka okna +if CiemnyMotyw: + PaletaBarw = ['#1F1F1F', '#191919', '#B8B8B8', '#FFFFFF', '#404040', '#FFFFFF', '#1F1F1F', 1] +else: + PaletaBarw = ['#F0F0F0', '#D4D4D4', '#000000', '#000000', '#A6A6A6', '#000000', '#FFFFFF', 2] + +B_tlo = PaletaBarw[0] +B_tytultlo = PaletaBarw[1] +B_tytultext = PaletaBarw[2] +B_text = PaletaBarw[3] +B_przycisktlo = PaletaBarw[4] +B_przycisktext = PaletaBarw[5] +B_entrytlo = PaletaBarw[6] +B_framewielkosc = PaletaBarw[7] + + + def settings(): SettingsWindow = TK.Tk() - SettingsWindow.title('ustawienia') + SettingsWindow.title('Ustawienia programu') + SettingsWindow.resizable(width = False, height = False) + SettingsWindow.configure() + SettingsWindow.mainloop() + + def main(): # Tworzenie okna głównego MainWindow = TK.Tk() MainWindow.title(Nazwa + ' ' + Wersja) MainWindow.resizable(width = False, height = False) - - Tytul = TK.Label(MainWindow, text='GeneratorCSV', font=('Segoe UI Semilight', 20), borderwidth=7, justify='center', - bg='Gainsboro', width=47) - Tytul.grid(row=0) - - MainWindow.mainloop() -main() - -""" -def gui(): - # Tworzenie okna - OknoGlowne = TK.Tk() - OknoGlowne.title('GeneratorCSV') - OknoGlowne.resizable(width=False, height=False) - - # Nazwa programu + MainWindow.configure(background = B_tlo) - # Tworzenie frame dla ścieżek plików do importu - Ramka1 = TK.LabelFrame(OknoGlowne, text='Pliki do importu zawierające dane') - Ramka1.grid(row=1) + # Tytul + Tytul = TK.Label(MainWindow) + Tytul.config(text = Nazwa) + Tytul.config(width = 41) + Tytul.config(bg = B_tytultlo, fg = B_tytultext) + Tytul.config(font = ('Segoe UI Semilight', 30)) + Tytul.grid(row = 0) + + + # Frame1 - Import + Ramka1 = TK.LabelFrame(MainWindow) + Ramka1.config(text = ' Pliki do importu zawierające dane ') + Ramka1.config(bg = B_tlo, fg = B_text) + Ramka1.config(borderwidth = B_framewielkosc) + Ramka1.grid(row = 1) + # Ścieżka pliku do importu 1 - wiersz1 = 0 + wiersz = 1 text1 = TK.StringVar() - OpisPola1 = TK.Label(Ramka1, text='Plik z danymi (1)', justify='left', width=SzerokoscOpisu) - OpisPola1.grid(row=wiersz1, column=0) - Pole1 = TK.Entry(Ramka1, textvariable=text1, width=SzerokoscPola) - Pole1.grid(row=wiersz1, column=1) + + OpisPola1 = TK.Label(Ramka1) + OpisPola1.config(text = 'Plik z danymi (1)') + OpisPola1.config(width = SzerokoscOpisu) + OpisPola1.config(bg = B_tlo, fg = B_text) + OpisPola1.grid(row = wiersz, column = 0) + + Pole1 = TK.Entry(Ramka1) + Pole1.config(textvariable = text1) + Pole1.config(width = SzerokoscPola) + Pole1.config(bg = B_entrytlo, fg = B_text) + Pole1.grid(row = wiersz, column = 1) def Browse1_Dialog(): - Browse1.filename = TKfld.askopenfilename(initialdir="/", title="Wybierz plik", - filetypes=(("Pliki txt", "*.txt"), ("Wszystkie pliki", "*.*"))) + Browse1.filename = TKfld.askopenfilename(initialdir = "/", title = "Wybierz plik do importu", filetypes = (("Pliki txt", "*.txt"), ("Wszystkie pliki", "*.*"))) Pole1.delete(0, 'end') Pole1.insert(0, Browse1.filename) - Browse1 = TK.Button(Ramka1, text='...', command=Browse1_Dialog, background='silver', relief='flat') - Browse1.grid(row=wiersz1, column=2, padx=5, pady=3) + Browse1 = TK.Button(Ramka1) + Browse1.config(text = '...') + Browse1.config(command = Browse1_Dialog) + Browse1.config(bg = B_przycisktlo, fg = B_przycisktext, relief='flat', activebackground = B_przycisktlo) + Browse1.grid(row = wiersz, column = 2, padx=5, pady=3) + # Ścieżka pliku do importu 2 - wiersz2 = 1 + wiersz = 2 text2 = TK.StringVar() - OpisPola2 = TK.Label(Ramka1, text='Plik z danymi (2)', justify='left', width=SzerokoscOpisu) - OpisPola2.grid(row=wiersz2, column=0) - Pole2 = TK.Entry(Ramka1, textvariable=text2, width=SzerokoscPola) - Pole2.grid(row=wiersz2, column=1) + + OpisPola2 = TK.Label(Ramka1) + OpisPola2.config(text = 'Plik z danymi (2)') + OpisPola2.config(width = SzerokoscOpisu) + OpisPola2.config(bg = B_tlo, fg = B_text) + OpisPola2.grid(row = wiersz, column = 0) + + Pole2 = TK.Entry(Ramka1) + Pole2.config(textvariable = text2) + Pole2.config(width = SzerokoscPola) + Pole2.config(bg = B_entrytlo, fg = B_text) + Pole2.grid(row = wiersz, column = 1) def Browse2_Dialog(): - Browse2.filename = TKfld.askopenfilename(initialdir="/", title="Wybierz plik", - filetypes=(("Pliki txt", "*.txt"), ("Wszystkie pliki", "*.*"))) + Browse2.filename = TKfld.askopenfilename(initialdir = "/", title = "Wybierz plik do importu", filetypes = (("Pliki txt", "*.txt"), ("Wszystkie pliki", "*.*"))) Pole2.delete(0, 'end') Pole2.insert(0, Browse2.filename) - Browse2 = TK.Button(Ramka1, text='...', command=Browse2_Dialog, background='silver', relief='flat') - Browse2.grid(row=wiersz2, column=2, padx=5, pady=3) + Browse2 = TK.Button(Ramka1) + Browse2.config(text = '...') + Browse2.config(command = Browse1_Dialog) + Browse2.config(bg = B_przycisktlo, fg = B_przycisktext, relief = 'flat', activebackground = B_przycisktlo) + Browse2.grid(row = wiersz, column = 2, padx = 5, pady = 3) + # Ścieżka pliku do importu 3 - wiersz3 = 2 + wiersz = 3 text3 = TK.StringVar() - OpisPola3 = TK.Label(Ramka1, text='Plik z danymi (3)', justify='left', width=SzerokoscOpisu) - OpisPola3.grid(row=wiersz3, column=0) - Pole3 = TK.Entry(Ramka1, textvariable=text3, width=SzerokoscPola) - Pole3.grid(row=wiersz3, column=1) + + OpisPola3 = TK.Label(Ramka1) + OpisPola3.config(text = 'Plik z danymi (3)') + OpisPola3.config(width = SzerokoscOpisu) + OpisPola3.config(bg = B_tlo, fg = B_text) + OpisPola3.grid(row = wiersz, column = 0) + + Pole3 = TK.Entry(Ramka1) + Pole3.config(textvariable = text3) + Pole3.config(width = SzerokoscPola) + Pole3.config(bg = B_entrytlo, fg = B_text) + Pole3.grid(row = wiersz, column = 1) def Browse3_Dialog(): - Browse3.filename = TKfld.askopenfilename(initialdir="/", title="Wybierz plik", - filetypes=(("Pliki txt", "*.txt"), ("Wszystkie pliki", "*.*"))) + Browse3.filename = TKfld.askopenfilename(initialdir = "/", title = "Wybierz plik do importu", filetypes = (("Pliki txt", "*.txt"), ("Wszystkie pliki", "*.*"))) Pole3.delete(0, 'end') Pole3.insert(0, Browse3.filename) - Browse3 = TK.Button(Ramka1, text='...', command=Browse3_Dialog, background='silver', relief='flat') - Browse3.grid(row=wiersz3, column=2, padx=5, pady=3) + Browse3 = TK.Button(Ramka1) + Browse3.config(text = '...') + Browse3.config(command = Browse1_Dialog) + Browse3.config(bg = B_przycisktlo, fg = B_przycisktext, relief = 'flat', activebackground = B_przycisktlo) + Browse3.grid(row = wiersz, column = 2, padx = 5, pady = 3) + # Ścieżka pliku do importu 4 - wiersz4 = 3 + wiersz = 4 text4 = TK.StringVar() - OpisPola4 = TK.Label(Ramka1, text='Plik z danymi (4)', justify='left', width=SzerokoscOpisu) - OpisPola4.grid(row=wiersz4, column=0) - Pole4 = TK.Entry(Ramka1, textvariable=text3, width=SzerokoscPola) - Pole4.grid(row=wiersz4, column=1) + + OpisPola4 = TK.Label(Ramka1) + OpisPola4.config(text = 'Plik z danymi (4)') + OpisPola4.config(width = SzerokoscOpisu) + OpisPola4.config(bg = B_tlo, fg = B_text) + OpisPola4.grid(row = wiersz, column = 0) + + Pole4 = TK.Entry(Ramka1) + Pole4.config(textvariable = text4) + Pole4.config(width = SzerokoscPola) + Pole4.config(bg = B_entrytlo, fg = B_text) + Pole4.grid(row = wiersz, column = 1) def Browse4_Dialog(): - Browse4.filename = TKfld.askopenfilename(initialdir="/", title="Wybierz plik", - filetypes=(("Pliki txt", "*.txt"), ("Wszystkie pliki", "*.*"))) + Browse4.filename = TKfld.askopenfilename(initialdir = "/", title = "Wybierz plik do importu", filetypes = (("Pliki txt", "*.txt"), ("Wszystkie pliki", "*.*"))) Pole4.delete(0, 'end') Pole4.insert(0, Browse4.filename) - Browse4 = TK.Button(Ramka1, text='...', command=Browse4_Dialog, background='silver', relief='flat') - Browse4.grid(row=wiersz4, column=2, padx=5, pady=3) + Browse4 = TK.Button(Ramka1) + Browse4.config(text = '...') + Browse4.config(command = Browse1_Dialog) + Browse4.config(bg = B_przycisktlo, fg = B_przycisktext, relief = 'flat', activebackground = B_przycisktlo) + Browse4.grid(row = wiersz, column = 2, padx = 5, pady = 3) - # Tworzenie frame dla plików export - Ramka2 = TK.LabelFrame(OknoGlowne, text='Ustawienia eksportu') - Ramka2.grid(row=2) + + # Frame2 - Eksport + Ramka2 = TK.LabelFrame(MainWindow) + Ramka2.config(text = ' Ustawienia eksportu ') + Ramka2.config(bg = B_tlo, fg = B_text) + Ramka2.config(borderwidth = B_framewielkosc) + Ramka2.grid(row = 2) # Ścieżka folderu do zapisu wygenerowanych plików - text4 = TK.StringVar() - OpisPolaExport = TK.Label(Ramka2, text='Lokalizacja', justify='left', width=SzerokoscOpisu) + textExport = TK.StringVar() + + OpisPolaExport = TK.Label(Ramka2) + OpisPolaExport.config(text = 'Lokalizacja') + OpisPolaExport.config(width = SzerokoscOpisu) + OpisPolaExport.config(bg = B_tlo, fg = B_text, relief = 'flat', activebackground = B_przycisktlo) OpisPolaExport.grid(row=0, column=0) - PoleExport = TK.Entry(Ramka2, textvariable=text4, width=SzerokoscPola) + + PoleExport = TK.Entry(Ramka2) + PoleExport.config(textvariable = textExport) + PoleExport.config(width = SzerokoscPola) + PoleExport.config(bg = B_entrytlo, fg = B_text) PoleExport.grid(row=0, column=1) def BrowseExport_Dialog(): @@ -187,34 +278,56 @@ def gui(): PoleExport.delete(0, 'end') PoleExport.insert(0, BrowseExport.filename) - BrowseExport = TK.Button(Ramka2, text='...', command=BrowseExport_Dialog, background='silver', relief='flat') + BrowseExport = TK.Button(Ramka2) + BrowseExport.config(text='...') + BrowseExport.config(command=BrowseExport_Dialog) + BrowseExport.config(bg = B_przycisktlo, fg = B_przycisktext, relief='flat', activebackground = B_przycisktlo) BrowseExport.grid(row=0, column=2, padx=5, pady=3) + # Przycisk START - Przycisk = TK.Button(OknoGlowne, text='START', justify='center', width=50, relief='flat', background='silver') - command=Main - Przycisk.grid(row=3, pady=15) + def PathPreprocess(): + pass + + PrzyciskSTART = TK.Button(MainWindow) + PrzyciskSTART.config(text = 'START') + PrzyciskSTART.config(command = PathPreprocess) + PrzyciskSTART.config(width = 50) + PrzyciskSTART.config(bg = B_przycisktlo, fg = B_przycisktext, relief = 'flat', activebackground = B_przycisktlo) + PrzyciskSTART.grid(row = 3, pady = 15) + # Pasek dolny - PasekDolny = TK.LabelFrame(OknoGlowne, bd=0, background='Gainsboro') - PasekDolny.grid(row=4) - InfoLabel = TK.Label(PasekDolny, text='GeneratorCSV 3.0 | © Mateusz Skoczek 2019 dla ZSP Sobolew', justify='left', - width=93, anchor='w', background='Gainsboro') + PasekDolny = TK.LabelFrame(MainWindow) + PasekDolny.config(bd = 0, bg = B_tytultlo, fg = B_tytultext) + PasekDolny.grid(row = 4) + + InfoLabel = TK.Label(PasekDolny) + InfoLabel.config(text = 'GeneratorCSV 3.0 | © Mateusz Skoczek 2019 dla ZSP Sobolew') + InfoLabel.config(justify = 'left', anchor='w', width=107) + InfoLabel.config(bg = B_tytultlo, fg = B_tytultext) InfoLabel.grid(row=0, column=0) def InfoOpen(): try: x = open('instrukcja.txt') except FileNotFoundError: - DG.err(E001x03) + MDdlg.Err(E001x02) else: OS.system("notepad instrukcja.txt") - Przycisk = TK.Button(PasekDolny, text='Instrukcja', justify='center', foreground='blue', relief='flat', - command=InfoOpen, background='Gainsboro') - Przycisk.grid(row=0, column=1) + PrzyciskINFO = TK.Button(PasekDolny) + PrzyciskINFO.config(text = 'Instrukcja') + PrzyciskINFO.config(command = InfoOpen) + PrzyciskINFO.config(bg = B_przycisktlo, fg = B_przycisktext, relief = 'flat', activebackground = B_przycisktlo) + PrzyciskINFO.grid(row = 0, column = 1, padx = 5, pady = 5) - TK.mainloop() + PrzyciskUSTAWIENIA = TK.Button(PasekDolny) + PrzyciskUSTAWIENIA.config(text = 'Ustawienia') + PrzyciskUSTAWIENIA.config(command = settings) + PrzyciskUSTAWIENIA.config(bg = B_przycisktlo, fg = B_przycisktext, relief = 'flat') + PrzyciskUSTAWIENIA.grid(row = 0, column = 2, padx = 5, pady = 5) -gui() -""" + MainWindow.mainloop() + +main() \ No newline at end of file diff --git a/config.cfg b/config.cfg index 2e25a57..8d37a77 100644 --- a/config.cfg +++ b/config.cfg @@ -1,2 +1,2 @@ -Ciemny motyw: 1 -Kodowanie: utf-8 \ No newline at end of file +Ciemny motyw(0/1): 0 +Kodowanie wyjsciowe: utf-8 \ No newline at end of file diff --git a/generator.py b/generator.py index 4d64128..e810ba3 100644 --- a/generator.py +++ b/generator.py @@ -1,6 +1,6 @@ """ # GeneratorCSV -# Wersja 4.0: UC 1 +# Wersja 4.0: UC 2 # by Mateusz Skoczek # luty 2019 - grudzień 2019 # dla ZSP Sobolew @@ -28,7 +28,7 @@ E000x00 = "Brak głównego pliku składowego programu ('main.py').\nPrzywróć p -# ---------------------------------------- # Import bibliotek zewnętrznych # ----------------------------------------- # +# ----------------------------------- # Import bibliotek zewnętrznych i modułów # ------------------------------------ # import os as OS import sys as SS From 092e56c82ba1250affee59f5d71a79dfcb061421 Mon Sep 17 00:00:00 2001 From: Mateusz Skoczek Date: Thu, 6 Aug 2020 18:14:39 +0200 Subject: [PATCH 03/29] 4.0 Alpha (Build 19346.2) --- .idea/workspace.xml | 8 ++++++-- changelog-UC.txt | 3 +++ components/main.py | 40 +++++++++++++++++++++++++++++++++++++++- config.cfg | 2 +- 4 files changed, 49 insertions(+), 4 deletions(-) diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 80357d7..4a5810d 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -76,6 +76,8 @@ 1575129057993 + + @@ -87,7 +89,9 @@ - + + @@ -95,6 +99,6 @@ - + \ No newline at end of file diff --git a/changelog-UC.txt b/changelog-UC.txt index ad257c0..6a5fc21 100644 --- a/changelog-UC.txt +++ b/changelog-UC.txt @@ -13,3 +13,6 @@ - Lekkie przeprojektowanie układu interfejsu - Stworzenie palety barw motywu jasnego i ciemnego - Wprowadzenie motywu jasnego i ciemnego (z możliwością zmiany jedynie przez plik 'config.cfg' + +4.0 Alpha (Build 19346.2) +- Stworzenie okna ustawień programu diff --git a/components/main.py b/components/main.py index b686366..2b9b8e5 100644 --- a/components/main.py +++ b/components/main.py @@ -105,15 +105,53 @@ B_framewielkosc = PaletaBarw[7] def settings(): + # Tworzenie okna ustawień SettingsWindow = TK.Tk() SettingsWindow.title('Ustawienia programu') SettingsWindow.resizable(width = False, height = False) - SettingsWindow.configure() + SettingsWindow.configure(background = B_tlo) + + + # Tytul + Tytul = TK.Label(SettingsWindow) + Tytul.config(text = 'Ustawienia') + Tytul.config(width = 20) + Tytul.config(bg = B_tytultlo, fg = B_tytultext) + Tytul.config(font = ('Segoe UI Semilight', 20)) + Tytul.grid(row = 0) + + + # Frame1 - Motyw + Ramka1 = TK.LabelFrame(SettingsWindow) + Ramka1.config(text = ' Motyw programu ') + Ramka1.config(bg = B_tlo, fg = B_text) + Ramka1.config(borderwidth = B_framewielkosc) + Ramka1.grid(row = 1) + + + # Radiobutton (motyw) + RB_var = TK.StringVar() + RB_var.set(MDlcg.read()[0]) + + RB_ciemny = TK.Radiobutton(Ramka1) + RB_ciemny.config(text = 'Ciemny') + RB_ciemny.config(variable = RB_var, value = '1') + RB_ciemny.config(bg = B_tlo, fg = B_text) + + RB_jasny = TK.Radiobutton(Ramka1) + RB_jasny.config(text = 'Jasny') + RB_jasny.config(variable = RB_var, value = '0') + RB_jasny.config(bg = B_tlo, fg = B_text) + + RB_ciemny.grid(row = 0, column = 0, padx = 40) + RB_jasny.grid(row = 0, column = 1, padx = 40) + SettingsWindow.mainloop() + def main(): # Tworzenie okna głównego MainWindow = TK.Tk() diff --git a/config.cfg b/config.cfg index 8d37a77..b559d4d 100644 --- a/config.cfg +++ b/config.cfg @@ -1,2 +1,2 @@ -Ciemny motyw(0/1): 0 +Ciemny motyw(0/1): 1 Kodowanie wyjsciowe: utf-8 \ No newline at end of file From 9fc6239a3564a899bc6f683dd1f5414358c17f72 Mon Sep 17 00:00:00 2001 From: Mateusz Skoczek Date: Thu, 6 Aug 2020 18:17:00 +0200 Subject: [PATCH 04/29] 4.0 Alpha (Build 19349) --- .idea/workspace.xml | 2 + changelog-UC.txt | 3 + components/__pycache__/dialog.cpython-38.pyc | Bin 1005 -> 987 bytes .../__pycache__/load_config.cpython-38.pyc | Bin 2469 -> 2587 bytes components/dialog.py | 2 +- components/load_config.py | 7 +- components/main.py | 67 +++++++++++++----- generator.py | 2 +- 8 files changed, 63 insertions(+), 20 deletions(-) diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 4a5810d..2946b7e 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -78,6 +78,7 @@ + @@ -100,5 +101,6 @@ + \ No newline at end of file diff --git a/changelog-UC.txt b/changelog-UC.txt index 6a5fc21..5246ea5 100644 --- a/changelog-UC.txt +++ b/changelog-UC.txt @@ -16,3 +16,6 @@ 4.0 Alpha (Build 19346.2) - Stworzenie okna ustawień programu + +4.0 Alpha (Build 19349) +- Ukończenie okna ustawień diff --git a/components/__pycache__/dialog.cpython-38.pyc b/components/__pycache__/dialog.cpython-38.pyc index 057de7db0e717b4fe8f4f464fbb675f1dbb125f6..7ef5676d21813b4ea7e14cf572db33c245c6dcca 100644 GIT binary patch delta 60 zcmaFMew&>)l$V!_0SFq|zs1^3Y^SHZQSAhjqnH#M&$F=u196(ghjm4#TmsF&X8xs z!jQ_H%~e#!$N++=JSp7GjH!%bAU1CbPcvf_Un<7}{uIuIjEoGC40%iu3@N-Rd@T%7 z0xA3{0zg_YMKDDONDHNK1v6+0moA>TPo7u7wW1)kC^I)TuOu;Ndh z%G|{C#Jp66g8aPvav)t=RGOTTpPQKqGN_;^KfNe1x72O33o{oJqt9f2RznsA=iso( zb6M3HZ8slfEo5Yjm~75&%eY{2DZ2?{*5p;}_KGUC%r#6d46%H*EH%svIBHlx9xSXV zVP3#l!UbYa;s|Bro*c%Z&wY#8GcWBHi>HBs;pA2hYmH2xPl_c#fy~Ik$i&FQ$j2za z#KI`WC delta 337 zcmbO&vQ(Hil$V!_0SJ^PK8}^2$Scd(Gf`VVC5J7Fy^1G=J%uBOBZ@PeAl_>w6P{XB zoRz2$nwOGVq~M&NS6ot5np~2ZpEvm{t2(3gW?8mEM#iwo{p_}k(+81!{ovc%U8=%!@Pi_h6UuF!ivdB9HESylXr0FPyWPVt(Fe-aIqN3+l(BHOpH8? ze2fB2ER0f&Vn8+rW04|IMpI~VD5u)w2F?IR#mN^r)s*Cbf-IseKzT6a Date: Thu, 6 Aug 2020 18:19:38 +0200 Subject: [PATCH 05/29] 4.0 Alpha (Build 19349.1) --- .idea/workspace.xml | 3 +- changelog-UC.txt | 5 + .../__pycache__/dataprocess.cpython-38.pyc | Bin 0 -> 1231 bytes components/__pycache__/format.cpython-38.pyc | Bin 0 -> 2031 bytes .../__pycache__/processing.cpython-38.pyc | Bin 0 -> 1656 bytes components/dataprocess.py | 38 +++++ components/format.py | 141 ++++++++++++++++++ components/main.py | 87 ++++++++++- components/processing.py | 56 +++++++ generator.py | 25 +++- 10 files changed, 345 insertions(+), 10 deletions(-) create mode 100644 components/__pycache__/dataprocess.cpython-38.pyc create mode 100644 components/__pycache__/format.cpython-38.pyc create mode 100644 components/__pycache__/processing.cpython-38.pyc create mode 100644 components/dataprocess.py create mode 100644 components/format.py create mode 100644 components/processing.py diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 2946b7e..f5214e2 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -79,6 +79,7 @@ + @@ -101,6 +102,6 @@ - + \ No newline at end of file diff --git a/changelog-UC.txt b/changelog-UC.txt index 5246ea5..74da955 100644 --- a/changelog-UC.txt +++ b/changelog-UC.txt @@ -19,3 +19,8 @@ 4.0 Alpha (Build 19349) - Ukończenie okna ustawień + +4.0 Alpha (Build 19349.1) +- Doprowadzenie programu do podstawowego stanu użyteczności (występuje błąd: nie można dodać drugiej ścieżki poprzez wybór) +- Utworzenie systemu crashlogów +- Zastosowanie systemu crashlogów dla głównego modułu diff --git a/components/__pycache__/dataprocess.cpython-38.pyc b/components/__pycache__/dataprocess.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a1976a87b367aae47b82cf8a57464b75ebf7874c GIT binary patch literal 1231 zcma)*Id2p(6vyp3c4p5U@XK>2@70gC9o}QaG6!W1yKN3b9RISuCWosUEzX9*%-Je zir{g!3tSQ<@C2I#mqi&o#iqd(Q32Q447e();90gCToX0$9NPmP5hLKeIgg4_@IE$= z_?Q?2?`H?V*cVRV;PR?EuB|tchTDoZKTDeE*b7PhoPNBMG}ZJ}(n!;HN&A|<=Cbut z(m)5EOB(CgYe_F}=V@O_x}swky`bm4m9(kpiKN#xeUsGF^d* z{8IByD@eM%Bo5;&Z81OdF)s+y^l)!Yn!SjdNSYpkA++)zA6FIsPc^#;s3dtcZvZm# zfqo>ak{O*Ls*0J>vH1b`CVVrlXkxSuFEa${eTF=vsB63e%&_qu)C1nrHUH4NTCHRM`+{O7}xf$b9O`U zYRyaFy%K*Q;VsYn1Nax`6{(VWMB)V@U}o)DO-VM|IcLXbX1%6%J<1q zlxx7t@Udx~rFWg)=ywvaNX0QAoWK!zZyIVLrWi-igV^9GM{+~#toCFoD3yjL+i>_#&?0)%tADVx;85A|H=N zJ_OZcwfq+>_qnbbzExb6K3_pU+x<&Ve~dp}77hz8R^MV_lBmd!w<~h%t_DxTu7;Wh z)ZlB_(@@v&wT6a<2O63hS{m9K0u7;tjs~S6(hzGn)X>$Clx%IZ{6Je#r`3qH(FkH~ zDKDt|+T3ddsEsIAbO`QfbGPN8%EC_v2W?+YhRI>r3Io4lM@chmbt`U&m^8dr z_f)8B1oqF{ByQcP-VOW>)%eD*ZU%v0{Zxey{AS!;?x?V?y!Nj@A69Ssfv>zcRLkqz z_p7zA-66k0998#1)%N1GPFLr*JIXu6tx-dnnD_M74 z=0UB|iF4HBB)Fm(_1>dRDu^6wL^L8d%8Y1BL=`xOSPlth5WxyeL?WcIU)VR1F7nhW zXSb*LY82m8R-jW;y~O*MS(I3;k|>!P%4(878Gw3~1y^VwUZb79GR&{+C5Mfu8M0l( zG!WpzB&Dg|r2V;M3{r6TEE!JqToS-+rX;ARs7pAB+%O5bpW_tN>tv#?lPDP)HZO-Z zK7_W}ZupEf;tGlZC>{(ort{gupg744)D7CCpysI^peVA|o+psp+5}9kapUt?;g)7y z;If8y46tODv&w2X$iS6gP)$O+rQP+Ud#{%G!G+a4W6M@pZv<}_o6 z{#-G+?fA`be@E>|Y^nM}oyN*o|IJ{Q5fDPq~oshc494~?Gr zr3maV1e!Eb%!i=NRFW@?u7O7vjRx8YbU14o6&7pJo}+PrZc)s!?*HeV!QkuJqu=Q4 z&=>o0F3>xqcVi>9jsQe#rgpYksgtdC>ZazA1d5y5sl#7&t4H^B?4;s^EcOc{pCjBC z#vi*RelClDdEB$8<`Yr*AIEq(k-1#fW1oDM$ZN}rS*Dm?iuu?7n}_8;BucKj%3AIi zoxAU~4*Xk6g(~a*tZNjrG`n(bd!3FSU}=p0UwJgU@*S1<@nNVEo@7=Sg}dIM6W(Q; zS6IBkf=Q&_Vev%cj*UEgM88H%c|lXilsS;Zw@Ka`HY~ynz4I_{xYW=tX(u6X%#1WS znpZTjJ|OFoqwQLVel4oV8eWt}cI+`A8Ki8CxbL?^JZSkJjx!i0y(`_K>$tX?{|`@$ Bv}FJQ literal 0 HcmV?d00001 diff --git a/components/__pycache__/processing.cpython-38.pyc b/components/__pycache__/processing.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2c3e1dcaf56523856fe32febbe44aace32ba5c0b GIT binary patch literal 1656 zcmb_dPj4GV6rb5Y_ImAgoWvrHfYt{NNoA!LYK2rGD2fA$0!e9As74Mj>`cto>z&Q+ zrmk1Bw{RvWE?n#*XAXP-lrOO-Bu@DNaL5JTtZj%$RdHZO``+*UnK$n@J6=C=92>#- z`Sm{z->)F_n=-Br4U7ly#CJh3#ITPDF0@AULL0;^bcvLi#`L~MEM_qC6p;$UkI`o7 z0#>5^lUpDULLO1~8EvI>AkI(SR0OmYisAVD2S4!mhpyUgF`59NCA1As{5?eaDAVB4 zCF-IxeE4f-NQ2>Zz)=&BwvI9rv<|u?ONCu#Mh5L<74Vzuuvpv;mL zR+e^wD-w`e1^)7JS6YhJx2md@pb3`d6lYbaV}Yjz9!-{}c%n~Hfh#k-hGcaOvC10C z9O*EqENftcugEG-F<9Wq>hmb{QDj+jt!#LtvK`PS=sM&tNo``t`V?a%bwEoB9oJ^O zUdk6b2fnxe8{ad0eD^Bj`8mFV=l{fU=@r~CYe2LiF4mz$Il55z61Q@^b)f@Hod>>%#n-~;qpi@t#O+Ib zKQ-F-I-L^>;_VYR<>#mE^j3Q@9;Mt($DxQr;d=>fJrsUCpy};)Kp}bTdfZ>6w%bMW zwOsoSA5zcXa^vS|^^Q0?@?5Tr9FKBrZ`O13lP*g_qPvMp^w5uT(-$Ki)oMXCq92q{TO2PH#1{mX_odEl= 9: + rokpodst = czas[0] + else: + rokpodst = czas[0] - 1 + nrklasy = int(Klasa[0]) + literaklasy = Klasa[1] + szkola = Klasa.split(' ')[1] + if szkola == 'BS': + znacznik = str((4 - nrklasy) + rokpodst) + szkola + else: + znacznik = str((5 - nrklasy) + rokpodst) + literaklasy + return znacznik \ No newline at end of file diff --git a/components/format.py b/components/format.py new file mode 100644 index 0000000..69472dc --- /dev/null +++ b/components/format.py @@ -0,0 +1,141 @@ +# Oznaczenia zmiennych: +# K - Klasa +# N - Nazwisko +# I - Imie +# L - Login do librusa + + + + + +import codecs as cd + +def SprawdzKlasa(K): + if len(K.split(' ')) != 2: # Wywołuje błąd jeżeli napis nie dzieli się w pożądanym formacie + blad = int('x') # + + for x in range(0,10): # + if K[1:].find(str(x)) != -1: # Wywołuje bląd jeżeli w nazwie klasy (poza numerem klasy) znajduje się liczba + blad = int('x') # + + numery_niedozwolone = [0,9,8,7,6,5,4] # Określa numery klas które nie istnieją + + for x in numery_niedozwolone: # + if K[0] == str(x): # Wywołuje błąd jeżeli numer klasy jest równy numerowi niedozwolonemu + blad = int('x') # + + szkoly = ['BS', 'LO'] # Określa istniejące szkoly + + if K.split(' ')[1] not in szkoly: # Wywołuje błąd jeżeli szkola nie należy do szkół istniejących + blad = int('x') # + + oddzialy = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'r', 's', 't', 'w', 'y', 'z'] # Określa istniejące oddzialy + + if K[1] not in oddzialy: # Wywołuje błąd jeżeli oddział nie należy do oddziałów istniejących + blad = int('x') # + + +def SprawdzNazwisko(N): + for a in N: + for x in range(0,10): # + if a.find(str(x)) != -1: # Wywoluje blad jeżeli nazwisko zawiera liczbę + blad = int('x') # + + +def SprawdzImie(I): + for x in range(0,10): # + if I.find(str(x)) != -1: # Wywoluje blad jeżeli imie zawiera liczbę + blad = int('x') # + + +def SprawdzLogin(L, CzyUczen): + if CzyUczen and L[-1] != 'u': # Wywoluje blad jeżeli login ucznia nie zawiera na końcu 'u' + blad = int('x') # + + if CzyUczen: # + blad = int(L[:-1]) # Wywoluje blad jeżeli login (-'u' dla ucznia) nie jest liczbą + else: # + blad = int(L) # + + + + +def przetworz(dane): + dane = dane.split('\n\n') # dzielenie danych na pojedyńcze osoby + + przetworzone = [] # tworzenie kontenera na przetworzone dane + + for osoba in dane: # + try: # + x = int(osoba[0]) # + except ValueError: # Sprawdza czy osoba jest nauczycielem czy uczniem + CzyUczen = False # + else: # + CzyUczen = True # + + # Dla uczniów + if CzyUczen: + K = osoba.split(', ')[0].split(' ')[:2] + K = K[0] + ' ' + K[1] + N = osoba.split(', ')[0].split(' ')[2:] + I = osoba.split(', ')[1].split(' ')[0] + L = osoba.split(', ')[1].split(' ')[2] + + # Sprawdzenie poprawności + SprawdzKlasa(K) + SprawdzNazwisko(N) + SprawdzImie(I) + SprawdzLogin(L, CzyUczen) + + dane = [K, N, I, L, CzyUczen] + przetworzone.append(dane) + # Dla nauczycieli + else: + N = osoba.split(', ')[0].split(' ') + I = osoba.split(', ')[1].split(' ')[-4] + L = osoba.split(', ')[1].split(' ')[-2] + + # Sprawdzenie poprawnosci + SprawdzNazwisko(N) + SprawdzImie(I) + SprawdzLogin(L, CzyUczen) + + dane = [N, I, L, CzyUczen] + przetworzone.append(dane) + return przetworzone + + + + + + +# Legenda do części dokumentacji poniżej: +# X - Dane nieznaczące +# Q - Pusta linia + + +# Format danych dla uczniów: +# , +# + +# Przykład: +# 1a BS Nowak, Adam 1234567u +# + + +# Format danych dla nauczycieli: +# , +# + +# Przykład: +# Nowak, Adam 1234567 +# + + + + +# Inne: +# - skrypt akceptuje prefix 'ks.', nieuwzględnia go w przetwarzaniu +# - skrypt akceptuje nazwiska holenderskie (typu 'van X', 'van der X' itp.) i uwzględnia je w przetwarzaniu +# - skrypt nie akceptuje nazwisk złożonych (np. Nowak-Kowalska) +# - skrypt wymaga kodowania ANSI diff --git a/components/main.py b/components/main.py index 19ae784..e98f86d 100644 --- a/components/main.py +++ b/components/main.py @@ -30,7 +30,17 @@ Wersja = '4.0 Experimental' E000x01 = "Brak modułu wywołującego okna dialogowe ('dialog.py').\nPrzywróć plik. (E000x01)" E000x02 = ["Brak modułu zarządzającego plikiem konfiguracyjnym ('load_config.py').\nPrzywróć plik. (E000x02)", True] +E001x01 = ["Brak pliku formatu 'format.py'.\nPrzywróć plik. (E001x01)", True] E001x02 = ["Brak pliku instrukcji ('instruction.txt').\nPrzywróć plik. (E001x02)", False] +E003x01 = ["Nie podano lokalizacji plików do importu. (E003x01)", False] +E003x02 = ["Nie podano lokalizacji zapisu wygenerowanych plików. (E003x02)", False] + +E003x111 = ["Plik podany w sciezce 1 nie istnieje (E003x111)", False] +E003x112 = ["Plik podany w sciezce 2 nie istnieje (E003x112)", False] +E003x113 = ["Plik podany w sciezce 3 nie istnieje (E003x113)", False] +E003x114 = ["Plik podany w sciezce 4 nie istnieje (E003x114)", False] + +A001 = "Czy na pewno chcesz rozpocząć generowanie?\nProgram utworzy w podanej lokalizacji pliki 'email.csv' i 'office.csv'.\nJeżeli w podanej lokalizacji istnieją pliki o takich nazwach zostaną one nadpisane." @@ -60,6 +70,15 @@ try: except ModuleNotFoundError: MDdlg.Err(E000x02) +try: + import format as MDfmt +except ModuleNotFoundError: + MDdlg.Err(E000x02) + +try: + import processing as MDprc +except ModuleNotFoundError: + MDdlg.Err(E000x02) # Biblioteki zewnętrzne interfejsu graficznego @@ -360,7 +379,73 @@ def main(): # Przycisk START def PathPreprocess(): - pass + if MDdlg.Ask(A001): + while True: + sciezka1 = Pole1.get() + sciezka1_puste = True + sciezka2 = Pole2.get() + sciezka2_puste = True + sciezka3 = Pole3.get() + sciezka3_puste = True + sciezka4 = Pole3.get() + sciezka4_puste = True + sciezkaExport = PoleExport.get() + sciezkaExport_puste = True + + if sciezka1 != '': + sciezka1_puste = False + if sciezka2 != '': + sciezka2_puste = False + if sciezka3 != '': + sciezka3_puste = False + if sciezka4 != '': + sciezka4_puste = False + if sciezkaExport != '': + sciezkaExport_puste = False + + if sciezka1_puste and sciezka2_puste and sciezka3_puste and sciezka4_puste: + MDdlg.Err(E003x01) + break + if sciezkaExport_puste: + MDdlg.Err(E003x02) + break + KontenerDanych = [] + if not sciezka1_puste: + try: + x = open(sciezka1) + except FileNotFoundError: + MDdlg.Err(E003x111) + else: + with open(sciezka1, 'r') as plik1: + KontenerDanych += MDfmt.przetworz(plik1.read()) + if not sciezka2_puste: + try: + x = open(sciezka2) + except FileNotFoundError: + MDdlg.Err(E003x112) + else: + with open(sciezka2, 'r') as plik2: + KontenerDanych += MDfmt.przetworz(plik2.read()) + if not sciezka3_puste: + try: + x = open(sciezka3) + except FileNotFoundError: + MDdlg.Err(E003x113) + else: + with open(sciezka3, 'r') as plik3: + KontenerDanych += MDfmt.przetworz(plik3.read()) + if not sciezka4_puste: + try: + x = open(sciezka4) + except FileNotFoundError: + MDdlg.Err(E003x114) + else: + with open(sciezka4, 'r') as plik4: + KontenerDanych += MDfmt.przetworz(plik4.read()) + break + MDprc.do(KontenerDanych, sciezkaExport) + else: + pass PrzyciskSTART = TK.Button(MainWindow) PrzyciskSTART.config(text = 'START') diff --git a/components/processing.py b/components/processing.py new file mode 100644 index 0000000..7c9b316 --- /dev/null +++ b/components/processing.py @@ -0,0 +1,56 @@ +import dataprocess as MDdtp +import load_config as MDlcg +import dialog as MDdlg +import codecs as CD + +I002 = ['Operacja zakończona pomyślnie', False] + +def do(KontenerDanych, sciezkaExport): + KontenerEmail = [] + KontenerOffice = [] + for osoba in KontenerDanych: + if osoba[-1]: + Klasa = osoba[0] + Imie = osoba[2] + Inicjaly = Imie[0] + Nazwisko = '' + NazwiskoDoEmaila = '' + for x in osoba[1]: + Nazwisko += x + ' ' + NazwiskoDoEmaila += ('.' + x) + Inicjaly += x[0] + Nazwisko = Nazwisko[:-1] + ZnacznikKlasy = MDdtp.ctc(Klasa) + Login = osoba[3] + Adres = MDdtp.plr(Imie).lower() + MDdtp.plr(NazwiskoDoEmaila).lower() + ZnacznikKlasy + '@losobolew.pl' + Email = Adres + ',' + Login + ':' + MDdtp.plr(Inicjaly) + ',500' + Office = Adres + ',' + Imie + ',' + Nazwisko + ',' + Imie + ' ' + Nazwisko + ',uczeń,' + Klasa + ',,,,,,,,,Rzeczypospolita Polska' + KontenerEmail.append(Email) + KontenerOffice.append(Office) + else: + Imie = osoba[1] + Inicjaly = Imie[0] + Nazwisko = '' + NazwiskoDoEmaila = '' + for x in osoba[0]: + Nazwisko += x + ' ' + NazwiskoDoEmaila += ('.' + x) + Inicjaly += x[0] + Nazwisko = Nazwisko[:-1] + Login = osoba[2] + Adres = MDdtp.plr(Imie).lower() + MDdtp.plr(NazwiskoDoEmaila).lower() + '@losobolew.pl' + Email = Adres + ',' + Login + ':' + MDdtp.plr(Inicjaly) + ',500' + Office = Adres + ',' + Imie + ',' + Nazwisko + ',' + Imie + ' ' + Nazwisko + ',nauczyciel,,,,,,,,,,Rzeczpospolita Polska' + KontenerEmail.append(Email) + KontenerOffice.append(Office) + sciezkaEmail = sciezkaExport + '/email.csv' + sciezkaOffice = sciezkaExport + '/office.csv' + with CD.open(sciezkaEmail, 'w', MDlcg.read()[1]) as plikEmail: + for x in KontenerEmail: + plikEmail.writelines(x + '\n') + plikEmail.close() + with CD.open(sciezkaOffice, 'w', MDlcg.read()[1]) as plikOffice: + for x in KontenerOffice: + plikOffice.writelines(x + '\n') + plikOffice.close() + MDdlg.Inf(I002) \ No newline at end of file diff --git a/generator.py b/generator.py index 911865e..af82ea4 100644 --- a/generator.py +++ b/generator.py @@ -17,9 +17,20 @@ -# ----------------------------------------- # Definicja kodów dialogowych # ------------------------------------------ # +# ------------ # Import bibliotek zewnętrznych i modułów oraz inicjacja funkcji zapisywania crashlogów # ------------- # -E000x00 = "Brak głównego pliku składowego programu ('main.py').\nPrzywróć plik. (E000x00)" +# Funkcja zapisująca crashlogi +def crash(ErrorCode): + import sys as SS + import time as TM + d = TM.localtime() + name = 'crashlogs/crash_' + str(d[2]) + str(d[1]) + str(d[0]) + str(d[3]) + str(d[4]) + str(d[5]) + '.txt' + with open(name, 'w') as crash: + crash.write('Critical error!\n' + ErrorCode) + SS.exit(0) + +# Błędy +E000x00 = "Brak głównego pliku składowego programu ('main.py'). Przywróć plik. (E000x00)" @@ -31,7 +42,7 @@ E000x00 = "Brak głównego pliku składowego programu ('main.py').\nPrzywróć p # ----------------------------------- # Import bibliotek zewnętrznych i modułów # ------------------------------------ # import os as OS -import sys as SS + @@ -43,10 +54,8 @@ import sys as SS # ----------------------------------------- # Uruchomienie głównego modułu # ----------------------------------------- # try: - fck = open("components\main.py") + fck = open("components/main.py") except: - print('Nieoczekiwany wyjatek - nie mozna wygenerowac okna dialogowego bledu\n\nBŁĄD KRYTYCZNY!\n%s') % E000x00 - wait = input('Naciśnij ENTER aby zakończyć') - SS.exit(0) + crash(E000x00) else: - OS.system("components\main.py") + OS.system("components\main.py") \ No newline at end of file From cdb254129272e493c9f068618cb6de5e3c3f7717 Mon Sep 17 00:00:00 2001 From: Mateusz Skoczek Date: Thu, 6 Aug 2020 18:20:52 +0200 Subject: [PATCH 06/29] 4.0 Alpha (Build 19349.2) --- .idea/workspace.xml | 3 +- changelog-UC.txt | 6 + components/__pycache__/dialog.cpython-38.pyc | Bin 987 -> 1733 bytes .../__pycache__/load_config.cpython-38.pyc | Bin 2587 -> 1874 bytes .../__pycache__/load_format.cpython-38.pyc | Bin 0 -> 632 bytes .../__pycache__/processing.cpython-38.pyc | Bin 1656 -> 1656 bytes components/dataprocess.py | 38 ----- components/dialog.py | 46 +++++- components/format.py | 141 ------------------ components/load_config.py | 38 ++--- components/load_format.py | 40 +++++ components/main.py | 28 ++-- components/processing.py | 1 + format.fmt | 0 generator.py | 40 +---- 15 files changed, 119 insertions(+), 262 deletions(-) create mode 100644 components/__pycache__/load_format.cpython-38.pyc delete mode 100644 components/dataprocess.py delete mode 100644 components/format.py create mode 100644 components/load_format.py create mode 100644 format.fmt diff --git a/.idea/workspace.xml b/.idea/workspace.xml index f5214e2..75e322a 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -80,6 +80,7 @@ + @@ -102,6 +103,6 @@ - + \ No newline at end of file diff --git a/changelog-UC.txt b/changelog-UC.txt index 74da955..8e7a1d8 100644 --- a/changelog-UC.txt +++ b/changelog-UC.txt @@ -24,3 +24,9 @@ - Doprowadzenie programu do podstawowego stanu użyteczności (występuje błąd: nie można dodać drugiej ścieżki poprzez wybór) - Utworzenie systemu crashlogów - Zastosowanie systemu crashlogów dla głównego modułu + +4.0 Alpha (Build 19349.2) +- Nowy system dialogowy +- Stworzenie pliku formatu +- Usunięcie plików format.py i dataprocess.py +- Przygotowanie do dalszych prac diff --git a/components/__pycache__/dialog.cpython-38.pyc b/components/__pycache__/dialog.cpython-38.pyc index 7ef5676d21813b4ea7e14cf572db33c245c6dcca..ebafbb8183fd8c7de5390409ca6f611b62030691 100644 GIT binary patch literal 1733 zcmb_c&2QsG6rZu3CUKKeU=gasVOD!tBM~J9A@LzZ+0CvBYBnN;m8_s@;+eXRKgJr{ zB#xF#wH5yW2c*5UN6x)+Yx7U|%4tuW;LIDlMJ?J$ATic_yf-uN{pR=H^Ed6biJ;k^ z{Q2GQb%cIX!p&xY@HzD4Cm0yAk&A51@u|iz(_HNgV^m7|Ey=hg)ow{^tX|y%OFGxV zQsaikU`=KQHG2)_^`IU$>?TI6#oEu2ZL$sa8qDi#lf4deig3aiyMo+zN=?T(bghm#l#6^OFU~K2EYEky z{eHJM??SWoGarn8u{^#!VgJq;0Ek5_=?u_$fmpr(t1}M>TI6I_nON=axy+p-vIspIkx(pu<&cUh5_w2nMNu~apeG-~z;KG5p)JI; zE%Y|}0jH{L^=J4@!{}dRVgzB1yvhlZ^G@Td6Hh9#2%#hq)fHDrc~~- zH5jVod#PlrR^Wb7+y5#|CU!G90`jC3vd~7O!r*f+wKUu8E7fkq_}s4VRq%g7vH{os zcnm}T z>?O)F!kf4SpHzRiTTCg~HRvT%9ayC36a!n@HCrV^m81hh@BV`!V_=)R(V>tbb*8jr zMe1P2s!3(mZYUN!I8fkSspyjMYeg2eo9b3cL;3bwBPQ2DkZq;WlRPOOUer_Hi&6-S%FYr(8IJO8 srL`MGsG!M`jmD=T{9FV6nTk}RUTVB$;En6gfVE+?8nsrd(QGvS0w229bpQYW delta 594 zcmZXR&1w`u5P++DdbWFZXCWXY?jabAh^vy5w}cQAB#Pu9L4^TXhUr!}J3AxQvxGpx zl8_gWxp#!@wz`<1*1>PW2%RXWidH+-IB|r`f>}HE%VFX|sT~9+KnvG>8Qz^95&u z*SX2&?U$}u`#2$Bx9jp@ Oo)4=VCWvDb8S@(lICBR8 diff --git a/components/__pycache__/load_config.cpython-38.pyc b/components/__pycache__/load_config.cpython-38.pyc index 64f87e9c5be16d785d9e26e35fafb19761bcade0..3474cb22a1d8aeb773a9f00fb997562fd71fa263 100644 GIT binary patch delta 1058 zcmZ9L&2JM&6u{?eKfG~FB4|EDD;5w{+zQvUmx2&d1xXv)`hW=p3mPqX6Klg}*B#H2 z*s>KhsnjDv(@T|$qy7Qyy*=W8pz4MB4|40Nhn|?(pa9*G-kaH(_wjpg=HSdYrD z9@&fh=1S{H%=85D z0GT3qjU!?b(0Jzv8!fEpJGtDr{P__z^Qxn1!c$G-RO;Yfl;nI-h)ihb+gp&dHRVF6 zADpYWqW*9qd|7?qevGHovirqtAg^ORTt(8hlkSsew}*IuN9+)d9AJlrppAxfKyIN& zb3->;-K-fs>9b{9%Az>J8ocH2q;%T9ZeLQuZ&=2z VC15kYLvR^auy4jx*+1(~zXB0o326WT literal 2587 zcmb_e&2QX96d!->I?g6d8Z}K?1TFH8<>&_hWg8^n{> z2_2!Md!#-_PtY+K$LTmd3FAp>)JSzHlsZ8^AH+jU&nF~2T+l?=jXN?t+#F2RX=-}JT#;iHB zRH;<9D|6*=`A{($51%)Uif1m^nK!fcR`aE*=1|pY*hXFbdlt>C{0n7Ohjn>ZSP?ky zxV--W9E=l)X;o6Y`_<- z@J`%+;Cc=Nd~_Y(^7uJp5l{p(?cnhv5!<)Tq~ll~fT8~z159Gr;*QnCPwH}yM=*vW zv~Tl{!%$II@TRaj;qntOK&oLKslze=JCWr>cN++?}yuW8B z;IJ*BCxc6c5xd0#JpS)MlBt07$gV`Cwjw42=o-7yjszXxRh0xXbb$Br4iG5&YM?!< z_M{R>Jwlafklle!)00)AkhUu7R0X=_sS@$@WAuG8 z`55VgBhtkq(gb=;q54FIFG{6tfuH;YNuq_p*2VYBdaAh0OBKfpQsai@hHOa)E>i8< zB6XXo0&i&%N)_RL$jluw^JO*FeHH|c*Ys13<&ySoX1D8S7P%j=uE(x&3h9HCuEj0i zcAz)>Lc|!j@aN}1mM%5E@Yteo?y|-e&tr`h!CL?_Xhk_-9zTj2PkW;Y&9Pzg4j>HL zq2F-1MK=fCp&O^e3ut;F>lPn@a{qN8M3$uz+@sQ%g51=AFGz>;;w0>{aqHow0fQ$? zhKBEeNlF2n9gtBnK!5}Ycxw=Id0z?CLChQL1a|^jt9HZ2n!Eg&<&q z#+H~$B9)9pzGMmnad{|Jc^6VTzU;WH%7bMddNfO_K!A%OAXx&jWu~fN7ELuDdT)>x ztS(5>EFNV|oWPw5nkeMv#0V--kYECHGLGCdkac+cNgyPPEW}&JsH>7LO~RAyd9LO# z%IHlN<$YlCijy$fP+fdU(fq@xW8{}XRd%Y?qz|Ul20S-xMSsfn4(M;v-xiUl_ z%SxQWo&}OBegIV%2`6Bwa>d(9)hiV^ z;Z5)F$w261f>h}YB&P&3B}trzwemz- z+}xzx-rUSUf=6`j71o0oMgo6YL||!wld+%5wOU%p5Buy((h2;xcmaE<))m-eG{@_P zL8?X;;7w)QPLKx!0hBp#1X&rz5MHVNA^vo@>|>0vZvs(IA+EAp)<^WLvPZKI87t{$ H_3?iI;JK^c diff --git a/components/__pycache__/load_format.cpython-38.pyc b/components/__pycache__/load_format.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d5847b21f47fc0d86506c66ae61e2767e42b6f67 GIT binary patch literal 632 zcmYjOv2GJV5Zyc5i8(oGkZ5RDN`nim3!+JcP$Y6e2y7w_2v|ne+B;Zl@9m!UZn5u5 zhbZ5G+(1c9ONZ|#uBGAwpk~fN5i{DiJ8woaJ2S6#c07WzIQa9e4+1o!g|?tDS!Em7_p z!rhRx8+L^$y*>IE6d0Cj^C*0c<{6k|%IN)$PlH7aMns@eCebs>XqTm6lbnV#J-&^e&wk%X0z6#|?o_E*b5Ir!>?fEn)6?dq{RQx5bj?%;f6}p0nxtVw}-f zikX0jzSkyVsl~`&_&w6ItU>`aS4=`Kw9+)%WUw?gdSX~)=^ZYZ)RRt}wY@4f(#3bh zutc$BT+iA1@;cUgsyQ7M%k?L24{6~03*Yzm?d_@x8;fG4#FPfTm%$6lu;F<&EmrHx zwpVTshg^?`!?KoSN$G}Rxph^wY;=#goRl}K#w7G~dZMl3fic>Yjo1j4mJOj|o0j!C z6Ns=7=~rS}SaSovtK|lVgXap4jrahACrZKK)aWV9aJv86{PAr*xZV}TWevtUinYS^ lBm=1#A70H4r%*N@R5OzB(80{fE<*iQv*tQ2XV+<>{sp}r!NC9k literal 0 HcmV?d00001 diff --git a/components/__pycache__/processing.cpython-38.pyc b/components/__pycache__/processing.cpython-38.pyc index 2c3e1dcaf56523856fe32febbe44aace32ba5c0b..51255a34bd6c7b8b9c96ce794322cd6014bbda69 100644 GIT binary patch delta 111 zcmeyt^Mi*sl$V!_0SL}IeT#M5$m__$7&Y0S#S2KzVwukV*>JuK?ZX$f*}(J69C|27HI$g delta 111 zcmeyt^Mi*sl$V!_0SLBBe~Y!>$m__$cx$phix-fb#WJ5!cCr;~I3vg8Nv!FN@{@nD px>!j7)fUNv2w7A@4#>U5VUwGmQks)$#|Y#VgAC?i1VbhcCIB8(7cu|< diff --git a/components/dataprocess.py b/components/dataprocess.py deleted file mode 100644 index 9bc6c9b..0000000 --- a/components/dataprocess.py +++ /dev/null @@ -1,38 +0,0 @@ -import time as TM - -def plr(text): - text1 = text.replace('ę', 'e') - text2 = text1.replace('ó', 'o') - text3 = text2.replace('ą', 'a') - text4 = text3.replace('ś', 's') - text5 = text4.replace('ł', 'l') - text6 = text5.replace('ż', 'z') - text7 = text6.replace('ź', 'z') - text8 = text7.replace('ć', 'c') - text9 = text8.replace('ń', 'n') - text10 = text9.replace('Ę', 'E') - text11 = text10.replace('Ó', 'O') - text12 = text11.replace('Ą', 'A') - text13 = text12.replace('Ś', 'S') - text14 = text13.replace('Ł', 'L') - text15 = text14.replace('Ż', 'Z') - text16 = text15.replace('Ź', 'Z') - text17 = text16.replace('Ć', 'C') - text = text17.replace('Ń', 'N') - return text - -def ctc(Klasa): - czas = TM.localtime() - miesiac = czas[1] - if miesiac >= 9: - rokpodst = czas[0] - else: - rokpodst = czas[0] - 1 - nrklasy = int(Klasa[0]) - literaklasy = Klasa[1] - szkola = Klasa.split(' ')[1] - if szkola == 'BS': - znacznik = str((4 - nrklasy) + rokpodst) + szkola - else: - znacznik = str((5 - nrklasy) + rokpodst) + literaklasy - return znacznik \ No newline at end of file diff --git a/components/dialog.py b/components/dialog.py index e022e84..36c1e05 100644 --- a/components/dialog.py +++ b/components/dialog.py @@ -14,11 +14,41 @@ + + + +# ----------------------------------------------- # Kody dialogowe # ------------------------------------------------- # + +E = [] # Błędy +E.append(["Nie znaleziono pliku konfiguracyjnego (config.cfg).\nPrzywróć plik. (E01x0000)", True]) #0 +E.append(["Błąd pliku konfiguracyjnego (config.cfg).\nNiepoprawna ilość wierszy w pliku\nPrzywróć plik. (E01x0001)", True]) #1 +E.append(["Bład pliku konfiguracyjnego (config.cfg).\nNiepoprawne dane w wierszu 1\nPrzywróć plik. (E01x0011)", True]) #2 +E.append(["Bład pliku konfiguracyjnego (config.cfg).\nNiepoprawne dane w wierszu 2\nPrzywróć plik. (E01x0012)", True]) #3 +E.append(["Nie znaleziono pliku składowego (instruction.txt)\nPrzywróć plik. (E03x0010)", False]) #4 + + + +I = [] # Informacje +I.append(["Pomyślnie zapisano!\nDla niektórych zmian może być wymagane ponowne uruchomienie programu", False]) #0 (I0001) + + + +A = [] # Zapytania + + + + + + + + # ----------------------------------- # Import bibliotek zewnętrznych i modułów # ------------------------------------ # # Biblioteki zewnętrzne import sys as SS + + # Biblioteki zewnętrzne interfejsu graficznego from tkinter import messagebox as TKmsb @@ -32,25 +62,25 @@ from tkinter import messagebox as TKmsb # --------------------------------------------------- # Funkcje # ---------------------------------------------------- # # Okno dialogowe błędu -def Err(KodBledu): - Message = 'Wystąpił błąd!\n' + KodBledu[0] +def err(ErrorIndex): + Message = 'Wystąpił błąd!\n' + E[ErrorIndex][0] TKmsb.showerror('Błąd', Message) - if KodBledu[1]: + if E[ErrorIndex][1]: SS.exit(0) # Okno dialogowe informacyjne -def Inf(KodInformacji): - TKmsb.showinfo('Informacja', KodInformacji[0]) - if KodInformacji[1]: +def inf(InfoIndex): + TKmsb.showinfo('Informacja', I[InfoIndex][0]) + if I[InfoIndex][1]: SS.exit(0) # Okno dialogowe zapytania -def Ask(KodZapytania): - if TKmsb.askokcancel('Pytanie', KodZapytania): +def Ask(AskIndex): + if TKmsb.askokcancel('Pytanie', A[AskIndex]): return True else: return False diff --git a/components/format.py b/components/format.py deleted file mode 100644 index 69472dc..0000000 --- a/components/format.py +++ /dev/null @@ -1,141 +0,0 @@ -# Oznaczenia zmiennych: -# K - Klasa -# N - Nazwisko -# I - Imie -# L - Login do librusa - - - - - -import codecs as cd - -def SprawdzKlasa(K): - if len(K.split(' ')) != 2: # Wywołuje błąd jeżeli napis nie dzieli się w pożądanym formacie - blad = int('x') # - - for x in range(0,10): # - if K[1:].find(str(x)) != -1: # Wywołuje bląd jeżeli w nazwie klasy (poza numerem klasy) znajduje się liczba - blad = int('x') # - - numery_niedozwolone = [0,9,8,7,6,5,4] # Określa numery klas które nie istnieją - - for x in numery_niedozwolone: # - if K[0] == str(x): # Wywołuje błąd jeżeli numer klasy jest równy numerowi niedozwolonemu - blad = int('x') # - - szkoly = ['BS', 'LO'] # Określa istniejące szkoly - - if K.split(' ')[1] not in szkoly: # Wywołuje błąd jeżeli szkola nie należy do szkół istniejących - blad = int('x') # - - oddzialy = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'r', 's', 't', 'w', 'y', 'z'] # Określa istniejące oddzialy - - if K[1] not in oddzialy: # Wywołuje błąd jeżeli oddział nie należy do oddziałów istniejących - blad = int('x') # - - -def SprawdzNazwisko(N): - for a in N: - for x in range(0,10): # - if a.find(str(x)) != -1: # Wywoluje blad jeżeli nazwisko zawiera liczbę - blad = int('x') # - - -def SprawdzImie(I): - for x in range(0,10): # - if I.find(str(x)) != -1: # Wywoluje blad jeżeli imie zawiera liczbę - blad = int('x') # - - -def SprawdzLogin(L, CzyUczen): - if CzyUczen and L[-1] != 'u': # Wywoluje blad jeżeli login ucznia nie zawiera na końcu 'u' - blad = int('x') # - - if CzyUczen: # - blad = int(L[:-1]) # Wywoluje blad jeżeli login (-'u' dla ucznia) nie jest liczbą - else: # - blad = int(L) # - - - - -def przetworz(dane): - dane = dane.split('\n\n') # dzielenie danych na pojedyńcze osoby - - przetworzone = [] # tworzenie kontenera na przetworzone dane - - for osoba in dane: # - try: # - x = int(osoba[0]) # - except ValueError: # Sprawdza czy osoba jest nauczycielem czy uczniem - CzyUczen = False # - else: # - CzyUczen = True # - - # Dla uczniów - if CzyUczen: - K = osoba.split(', ')[0].split(' ')[:2] - K = K[0] + ' ' + K[1] - N = osoba.split(', ')[0].split(' ')[2:] - I = osoba.split(', ')[1].split(' ')[0] - L = osoba.split(', ')[1].split(' ')[2] - - # Sprawdzenie poprawności - SprawdzKlasa(K) - SprawdzNazwisko(N) - SprawdzImie(I) - SprawdzLogin(L, CzyUczen) - - dane = [K, N, I, L, CzyUczen] - przetworzone.append(dane) - # Dla nauczycieli - else: - N = osoba.split(', ')[0].split(' ') - I = osoba.split(', ')[1].split(' ')[-4] - L = osoba.split(', ')[1].split(' ')[-2] - - # Sprawdzenie poprawnosci - SprawdzNazwisko(N) - SprawdzImie(I) - SprawdzLogin(L, CzyUczen) - - dane = [N, I, L, CzyUczen] - przetworzone.append(dane) - return przetworzone - - - - - - -# Legenda do części dokumentacji poniżej: -# X - Dane nieznaczące -# Q - Pusta linia - - -# Format danych dla uczniów: -# , -# - -# Przykład: -# 1a BS Nowak, Adam 1234567u -# - - -# Format danych dla nauczycieli: -# , -# - -# Przykład: -# Nowak, Adam 1234567 -# - - - - -# Inne: -# - skrypt akceptuje prefix 'ks.', nieuwzględnia go w przetwarzaniu -# - skrypt akceptuje nazwiska holenderskie (typu 'van X', 'van der X' itp.) i uwzględnia je w przetwarzaniu -# - skrypt nie akceptuje nazwisk złożonych (np. Nowak-Kowalska) -# - skrypt wymaga kodowania ANSI diff --git a/components/load_config.py b/components/load_config.py index f8cca29..4032213 100644 --- a/components/load_config.py +++ b/components/load_config.py @@ -17,35 +17,19 @@ -# ----------------------------------------- # Definicja kodów dialogowych # ------------------------------------------ # - -E000x01 = "Brak modułu wywołującego okna dialogowe ('dialog.py').\nPrzywróć plik. (E000x01)" -E001x01 = ["Brak pliku konfiguracyjnego ('config.cfg').\nPrzywróć plik. (E001x01)", True] -E002x00 = ["Bład pliku konfiguracyjnego ('config.cfg').\nNiepoprawna ilość wierszy w pliku\nPrzywróć plik. (E002x00)", True] -E002x01 = ["Bład pliku konfiguracyjnego ('config.cfg').\nNiepoprawne dane w wierszu 1\nPrzywróć plik. (E002x01)", True] -E002x02 = ["Bład pliku konfiguracyjnego ('config.cfg').\nNiepoprawne dane w wierszu 2\nPrzywróć plik. (E002x02)", True] - -I001 = ["Pomyślnie zapisano!\nDla niektórych zmian może być wymagane ponowne uruchomienie programu", False] - - - - - - - - - # ----------------------------------- # Import bibliotek zewnętrznych i modułów # ------------------------------------ # # Biblioteki zewnętrzne import sys as SS + + # Moduły składowe programu try: import dialog as MDdlg except ModuleNotFoundError: - print('Nieoczekiwany wyjatek - nie mozna wygenerowac okna dialogowego bledu\n\nBŁĄD KRYTYCZNY!\n%s') %E000x01 - wait = input('Naciśnij ENTER aby zakończyć') + print('Nie znaleziono modułu programu (dialog.py)\nNie można załadować programu\nKod błędu: E00x0001') + wait = input('Naciśnij ENTER aby wyjść') SS.exit(0) @@ -64,7 +48,7 @@ def CheckConfig(settings): if len(settings) != 2: error = int('x') except ValueError: - MDdlg.Err(E002x00) + MDdlg.err(1) # Linia 1 (0/1) try: @@ -72,15 +56,15 @@ def CheckConfig(settings): if 0 > check > 1: error = int('x') except ValueError: - MDdlg.Err(E002x01) - + MDdlg.err(2) # Linia 2 (utf-8) + DostepneKodowanieWyjsciowe = ['utf-8'] try: if settings[1] not in DostepneKodowanieWyjsciowe: error = int('x') except ValueError: - MDdlg.Err(E002x02) + MDdlg.err(3) @@ -89,7 +73,7 @@ def read(): try: check = open('.\config.cfg') except FileNotFoundError: - MDdlg.Err(E001x01) + MDdlg.err(0) else: with open('.\config.cfg', 'r') as cfg: config = cfg.read().split('\n') @@ -107,7 +91,7 @@ def edit(settings): try: check = open('.\config.cfg') except FileNotFoundError: - MDdlg.Err(E001x01) + MDdlg.err(0) else: SettingsToSave = [] SettingsToSave.append('Ciemny motyw(0/1): ' + str(settings[0]) + '\n') @@ -115,4 +99,4 @@ def edit(settings): with open('.\config.cfg', 'w') as cfg: for x in SettingsToSave: cfg.write(x) - MDdlg.Inf(I001) \ No newline at end of file + MDdlg.inf(0) \ No newline at end of file diff --git a/components/load_format.py b/components/load_format.py new file mode 100644 index 0000000..6bda8c5 --- /dev/null +++ b/components/load_format.py @@ -0,0 +1,40 @@ +""" +# GeneratorCSV +# Wersja 4.0 Experimental +# by Mateusz Skoczek +# luty 2019 - grudzień 2019 +# dla ZSP Sobolew + +# +# Moduł zarządzający plikiem formatu +# +""" + + + + + + + + +# ----------------------------------- # Import bibliotek zewnętrznych i modułów # ------------------------------------ # + +# Biblioteki zewnętrzne +import sys as SS + +# Moduły składowe programu +try: + import dialog as MDdlg +except ModuleNotFoundError: + print('Nie znaleziono modułu programu (dialog.py)\nNie można załadować programu\nKod błędu: E00x0001') + wait = input('Naciśnij ENTER aby wyjść') + SS.exit(0) + + + + + + + + +# --------------------------------------------------- # Funkcje # ---------------------------------------------------- # \ No newline at end of file diff --git a/components/main.py b/components/main.py index e98f86d..daffd0a 100644 --- a/components/main.py +++ b/components/main.py @@ -14,6 +14,9 @@ + + + # -------------------------------------------- # Informacje o programie # -------------------------------------------- # Nazwa = 'GeneratorCSV' @@ -28,10 +31,6 @@ Wersja = '4.0 Experimental' # ----------------------------------------- # Definicja kodów dialogowych # ------------------------------------------ # -E000x01 = "Brak modułu wywołującego okna dialogowe ('dialog.py').\nPrzywróć plik. (E000x01)" -E000x02 = ["Brak modułu zarządzającego plikiem konfiguracyjnym ('load_config.py').\nPrzywróć plik. (E000x02)", True] -E001x01 = ["Brak pliku formatu 'format.py'.\nPrzywróć plik. (E001x01)", True] -E001x02 = ["Brak pliku instrukcji ('instruction.txt').\nPrzywróć plik. (E001x02)", False] E003x01 = ["Nie podano lokalizacji plików do importu. (E003x01)", False] E003x02 = ["Nie podano lokalizacji zapisu wygenerowanych plików. (E003x02)", False] @@ -61,24 +60,31 @@ import sys as SS try: import dialog as MDdlg except ModuleNotFoundError: - print('Nieoczekiwany wyjatek - nie mozna wygenerowac okna dialogowego bledu\n\nBŁĄD KRYTYCZNY!\n%s') %E000x01 - wait = input('Naciśnij ENTER aby zakończyć') + print('Nie znaleziono modułu programu (dialog.py)\nNie można załadować programu\nKod błędu: E00x0001') + wait = input('Naciśnij ENTER aby wyjść') SS.exit(0) try: import load_config as MDlcg except ModuleNotFoundError: - MDdlg.Err(E000x02) + print('Nie znaleziono modułu programu (load_config.py)\nNie można załadować programu\nKod błędu: E00x0002') + wait = input('Naciśnij ENTER aby wyjść') + SS.exit(0) try: - import format as MDfmt + import load_format as MDlfm except ModuleNotFoundError: - MDdlg.Err(E000x02) + print('Nie znaleziono modułu programu (load_format.py)\nNie można załadować programu\nKod błędu: E00x0003') + wait = input('Naciśnij ENTER aby wyjść') + SS.exit(0) try: import processing as MDprc except ModuleNotFoundError: - MDdlg.Err(E000x02) + print('Nie znaleziono modułu programu (processing.py)\nNie można załadować programu\nKod błędu: E00x0004') + wait = input('Naciśnij ENTER aby wyjść') + SS.exit(0) + # Biblioteki zewnętrzne interfejsu graficznego @@ -93,7 +99,7 @@ import tkinter as TK - +#TODO # ------------------------------------- # Uruchomienie interfejsu graficznego # -------------------------------------- # # Zmienne globalne środowiska graficznego diff --git a/components/processing.py b/components/processing.py index 7c9b316..12f2077 100644 --- a/components/processing.py +++ b/components/processing.py @@ -1,3 +1,4 @@ +#TODO import dataprocess as MDdtp import load_config as MDlcg import dialog as MDdlg diff --git a/format.fmt b/format.fmt new file mode 100644 index 0000000..e69de29 diff --git a/generator.py b/generator.py index af82ea4..b765a01 100644 --- a/generator.py +++ b/generator.py @@ -17,45 +17,13 @@ -# ------------ # Import bibliotek zewnętrznych i modułów oraz inicjacja funkcji zapisywania crashlogów # ------------- # - -# Funkcja zapisująca crashlogi -def crash(ErrorCode): - import sys as SS - import time as TM - d = TM.localtime() - name = 'crashlogs/crash_' + str(d[2]) + str(d[1]) + str(d[0]) + str(d[3]) + str(d[4]) + str(d[5]) + '.txt' - with open(name, 'w') as crash: - crash.write('Critical error!\n' + ErrorCode) - SS.exit(0) - -# Błędy -E000x00 = "Brak głównego pliku składowego programu ('main.py'). Przywróć plik. (E000x00)" - - - - - - - - -# ----------------------------------- # Import bibliotek zewnętrznych i modułów # ------------------------------------ # - -import os as OS - - - - - - - - - # ----------------------------------------- # Uruchomienie głównego modułu # ----------------------------------------- # try: fck = open("components/main.py") except: - crash(E000x00) + print('Nie znaleziono głównego modułu programu (main.py)\nNie można załadować programu\nKod błędu: E00x0000') + wait = input('Naciśnij ENTER aby wyjść') else: - OS.system("components\main.py") \ No newline at end of file + import os + os.system("components\main.py") \ No newline at end of file From 684ef6ae2aa6e1552871b12a74f922a53ec8cc3c Mon Sep 17 00:00:00 2001 From: Mateusz Skoczek Date: Thu, 6 Aug 2020 18:22:56 +0200 Subject: [PATCH 07/29] 4.0 Alpha (Build 19350) --- .idea/workspace.xml | 3 +- changelog-UC.txt | 7 + components/__pycache__/dialog.cpython-38.pyc | Bin 1733 -> 3691 bytes .../__pycache__/load_config.cpython-38.pyc | Bin 1874 -> 2071 bytes .../__pycache__/load_format.cpython-38.pyc | Bin 632 -> 2670 bytes components/dialog.py | 16 +- components/load_config.py | 13 +- components/load_format.py | 114 ++- components/main.py | 814 ++++++++++-------- config.cfg | 5 +- format.fmt | 5 + 11 files changed, 629 insertions(+), 348 deletions(-) diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 75e322a..0eb6511 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -81,6 +81,7 @@ + @@ -103,6 +104,6 @@ - + \ No newline at end of file diff --git a/changelog-UC.txt b/changelog-UC.txt index 8e7a1d8..8de5c3d 100644 --- a/changelog-UC.txt +++ b/changelog-UC.txt @@ -30,3 +30,10 @@ - Stworzenie pliku formatu - Usunięcie plików format.py i dataprocess.py - Przygotowanie do dalszych prac + +4.0 Alpha (Build 19350) +- Zastosowanie klas +- Wstępne stworzenie systemu formatu +- Modyfikacja interfejsu +- Dodanie możliwości dołączenia plików csv do eksportowanych plików (tylko kontrolki) +- Usprawnienia kodu diff --git a/components/__pycache__/dialog.cpython-38.pyc b/components/__pycache__/dialog.cpython-38.pyc index ebafbb8183fd8c7de5390409ca6f611b62030691..11877d6626e689d75a6882a163566580e65fb734 100644 GIT binary patch literal 3691 zcmcgv&2Jk;6yNpQv9n3qLR;EEfvI}wBCz7LRDcjr+O&#L(g=qlS%zq`J8?GqG1lx3 zo3+X%YQ#UlmsF72Bj;YZHRg}-l~ctTi6g&RCy}?c!I9dowIA=z%zM9i@6F85U(C#y z8m>PI|9th=aZUSE34>P=!Y8<+RbA5@t)aEFhR*c+1xI%Zt-=Fc*D}e-NyVI0%1IMW zd0<>6*EX4x%$ziplcsahOintMlaA-46FKRnob+-|I+>GB9Y~nJ!3@m*^uau!DY7Co zuc&A^XPmQssWE}~tX~c)jY;TUbzZxxHB9H6^E%#B&Ku5~cuzZL>zYO6&xo&if!t&v z6Ex=HTKzV}FPVt8D7jpnBMaMYCftC9G4;{1o{}XRGZ}SA-Q#wLc~JT?PRaaS?S1kN zX$k3cT-LptDbVQnlr-uqq|Vp5&l1zT0Ob;QWOs)oX~MfZa%*qbP6_uyN*tH^yu}lS zZt@nuExU|#Lh7@Q%R^4uzUxWi@o>X!NkQ#&D`YKBF4~!xU5y_!RV*moxNRWJYxXR=50X}pmTlRy$7p_ivV>}lJrfCc388d-S@)B}V zo3n1ng;6#)xCm%0$;JLhbt8zM&aX=Ed_TSN`zNzI+=8mA{j45_IqPYoLE4?ree^J? znv47SMgc0F2PN@*8mY`b)t!VGyTziIp!8I=)DD3mY-*w9%I=v1;|>-c#d10#xJ#D& z>A8aoX(w?bj~)@*WlR3}+_AZV%R=|4(B8G=56>MM_Chy2YC^AA+0HpewXMhqJ9mG7 z8Jd}cl~uE<^7HX#JIZEAv940AFW)^_7udF{^4Iaq{^yDvuswXy4lBQbL8F&$4NLcz z7lCj{5sn8C%lF=k0Qht%j)xCRcxh~W4(}w~=Q#GT<$CTwO=gF<+U4ixP&Ci(P`y0# zWKwLQhR8O7e1!*T_nxnI_YQ5l5e@ly^Ey}!G+zAZp-Amb(g|D|l7M%AXW&SRBBTLr zsq?!H+LGWcaU5>)Y^#A%@Rp!~lyAeqH82Sr7j2e+;Mkis1Gy5s(+0iT+l8r?B4<3I z@b~d!^w*Xxy=Mk2ifD_i^KBdS`0y&=OA+U5xT7n0IC`wz(PlNLFsq%>zSUzzpp85F zgMzN@w-jeJF&`WEL7g)iseu}nmgPx|{xXf?z1_A8vbLU)rt_vy?&Jg>{T=49lC4rt zU+9%qKMSJu#$>b!8XyE0y+Xa-E3$1jwh9e%L8%r)hix~?OMUpiX`+JRf8WGo;e+O_ zkX;w|lBhOD_F_!dH(9)t_WYV(k(5>VBq%>%?2KnQ4t2rSS6v% zHYzF#E;{t|MWMna%BodwOsY{?7_zzdACqY78R&i+ACc;=>8BL#X?%^weGaXIxE~Y* z{w~xt$H1-f2$bcOG*-p4dc!jM(57hQanGiq&HSEz6__elBX2k)G<*#w`;boP#ez5n z*_!E<*VY_vudNAHZ@mfHZnMx4QxL?A(il-1WfSj}W6up^q(xO{1|dsFHn+^$Kp<4o qL>7(8hXMX3`0OKYEvH$-W=v6H0=2WFLJzbfsRQ?5oiG(2l delta 166 zcmaDYbCj1al$V!_0SKb3zs0_1pU5Y}G=*WJPJl-`Llko=^8%(6juiHEriF}*3_uZ< z6wVZ`9M&i{AkCe_p34!%$;gnxlfv7=5XF_km%INki$(J` z)BH|OeFsKcTL-pu$Muq3XZ37jNbjU@ZwPoQf+0s3h&|mtdwFP4qGUeu8VK`)uVfWv zUNGgGC94f*%v0-B&bK)D;DMSn@2sy*9xGJAys|5oCef`6HGxNJwc4H3X~j`H?5HBu z&{fL+2PN`m5gO*Vy#TXjL7aeZ=89N>w?>Kc+tMyGYG9uq2$|6iw(C;6W%eJF9Wl8h znEZ$-95Qs11_`ogUW&?t_b8AnIc2xlMYhSVF_f7^@)~N2zDD8{Rv5KW;gOyb=C*U}>T#+&LfX_0WTCUnm~qk6qIu&~A#XlA7Zygpp~0v{g7L&gUdhxlr>&E2=?`5q BkgWg! delta 525 zcmZutze^)Q6rQ&;v%Aq`j6}sKxFU*|!ooAfU9HGfHo1r%qzKvFup(=2cGVCPPSE~} zDdez;rKRQl1qW9OHq%}e(AL^{3wjm~yl=iY@5j9FT^8?zs22)j#GLNur$#+@EB=6B z#&wNt8cY7Fe{fcSYXGuOVr!51Ty?DVHT%sAsga7aGqOy+hVBT=EfISBI1Sf6ji_?z z2ViQQR@n`aLd7(MC3R0%ELl{HJ?!SN1n1C922Ee{q;nXh+HQ6_LF~(-iBz~OU;=H0 zZEd%X;xKB+@{{pKH+QBev$_|CcY zb9q4f))r}q0qfJD(`Oj{-c=2|%-kJ0se1MTl+-WV`=Q~qlFMe4Q>L$+MtD^uXN{|A zl1j}_Wt0Y*MsaA=WV55BZ=x2YX%x4UuSd1!Ng&JE;2l+(1%8z7n`nYf3AkWEz61h_ p7K1sMfxLOjgY;C-S%jRrayC5NUA7MCA^X3lj7_LVXO+&olh5GWXnp_y diff --git a/components/__pycache__/load_format.cpython-38.pyc b/components/__pycache__/load_format.cpython-38.pyc index d5847b21f47fc0d86506c66ae61e2767e42b6f67..6d8186b91e41f39bc6a3fb3dbf105388781fd50d 100644 GIT binary patch literal 2670 zcmZ`*&2Jk;6rWk|+K%I-NgEo{reSHJ)GZB8=m!mq=*GX25S!>^X{@y)*mgn(M6iv5k|MFr@)LYibrhqwJbq z%$qrr74*9{FBnc4cQ!FRk}Mn2noO@+MxIqo+p_h7>!GX{xxK`VLRmisrjfUohKp;l zh}>JSH-EMae8lE`1MG^i@pyZ0!$$;RU>mBF;GAhw6_p1`|wG=TY{by_2!43J53J6cz2w5FUTb-EI* z4tetp-18JOnp621hsvzg1DMYtl{gCKbI!}B6KJTQ?&Pm}phgj?Au^Z?@M0U}x(^xJ zg$%clKeR#q>O*$yLPlE1KYd8ui*}vzpby*W!w$OfMm;RY@ z??dnLp>N;KdtvXm!(Q}Ix^!!g7 z&k7&%dG6iu+(K{hqVTb{=!_4&cNcn#7lp63MStT%@7qB`AG`$np&wp`0XP5$VGs_% z5WE69yb6cm2)qWb!%=txjzJ8L!wGm3PQoy}1@Warb**}|rXd@H$cE&D_LE$2UE$BR zB3M^eo?#ttRYrj8z9>d~Q4my|(WD?;zDRP6>Kh&<_OtP|&oX6N1hOdP&gfW)J#~X+q1|Wy=u(6HR2!$d_4y zbDIlodJ^(WBEUG01x3)4taP-Tty-pCG1=XM!%-H9&~S&%)7_SVYin7P<>gws7c~m( zYQ@gm7E4=3&itP6a|rnB8k)pd=DNiuxp|Leu2>e!%y4^|!{k9 ztX(LgE6XWm^0onsUPLH|qI2RCmd#DMqIv?Imp(&7s7j+Wq9}APw%xP`d)@!j>v30m zaP`GrUu#bKilT>hd$#7zQ-y}*lkmsi#R#%8jIB$>j!dyDsGnn}8g)=GQ6x!ABq@sI z@m6UmjQ}XE(pp#`awiI^^3@cIFm0$$Nkfw&TBl1yN->siUp-J$8o^osS5QnPb|vYA zYKlBOYsx%P$Z7Hsxk0|eGL-7GOp8oRTvNl?1m(}WBI6}iG~843d_>Sl zEG$&J$Sl!2W#(DZcBZg$z^xX38bSPwM5>$_AhZ&mb&d!cMFfD$Moh(8lZg@mSvzMX zLKcu)6rLHALrKu5c$fC0A;_thJ!w_scQ4-Vo?XY4e34vszrc<|YDE5XWSP1wQ{PcE z>U)8=Se{)VE|>Cn^SIO?Ao*-@IbKsh9l(RCAK8>5+)wJN^bH}*OHW;^X^l_~b3!qn z9@8y`!%04l5%Kq=xgd?y1OC1=o+wmeVV5F)QO;eGW}8r+z@bo=ZPOB3g_{nG1wHbn za3S60mHn)pGVZYjm(X<7KgTaick;#C;aI2)Oy^6SYmpq@rrD!dmOa0U>#;5oT3iG> zySOL 0: + error = int('x') + except ValueError: + MDdlg.err(11) + + try: + if uczniowiefmt.count('K') != 1: + error = int('x') + except ValueError: + MDdlg.err(6) + + try: + if uczniowiefmt.count('O') != 1: + error = int('x') + except ValueError: + MDdlg.err(7) + + try: + if uczniowiefmt.count('N') != 1: + error = int('x') + except ValueError: + MDdlg.err(8) + + try: + if uczniowiefmt.count('I') != 1: + error = int('x') + except ValueError: + MDdlg.err(9) + + try: + if uczniowiefmt.count('L') != 1: + error = int('x') + except ValueError: + MDdlg.err(10) + + try: + if format[1].count('') > 0: + error = int('x') + except ValueError: + MDdlg.err(12) + + nauczycielefmt = '' + for x in format[1]: + nauczycielefmt += x + + try: + if nauczycielefmt.count('N') != 1: + error = int('x') + except ValueError: + MDdlg.err(13) + + try: + if nauczycielefmt.count('I') != 1: + error = int('x') + except ValueError: + MDdlg.err(14) + + try: + if nauczycielefmt.count('L') != 1: + error = int('x') + except ValueError: + MDdlg.err(15) + + NiedozwoloneZnaki = ['1','2','3','4','5','6','7','8','9','0','W','E','R','T','Y','U','P','A','S','D','F','G','H','J','Z','C','V','B','M'] + try: + for x in NiedozwoloneZnaki: + if x in nauczycielefmt+uczniowiefmt: + error = int('x') + except ValueError: + MDdlg.err(16) + + + +# Odczytywanie ustawień z pliku formatu +def read(): + try: + check = open(r'.\format.fmt') + except FileNotFoundError: + MDdlg.err(5) + else: + with open(r'.\format.fmt', 'r') as fmt: + fmt = fmt.read().split('\n\n') + format = [] + for x in fmt: + format.append(x.split('\n')) + CheckFormat(format) + return format + + + +# Zapis ustawień do pliku formatu +def edit(format): + CheckFormat(format) + try: + check = open(r'.\format.fmt') + except FileNotFoundError: + MDdlg.err(5) + else: + FormatToSaveX = [] + for x in format: + FormatToSaveX.append('\n'.join(x)) + FormatToSave = FormatToSaveX[0] + '\n\n' + FormatToSaveX[1] + with open(r'.\format.fmt', 'w') as fmt: + fmt.write(FormatToSave) \ No newline at end of file diff --git a/components/main.py b/components/main.py index daffd0a..6521d30 100644 --- a/components/main.py +++ b/components/main.py @@ -21,6 +21,8 @@ Nazwa = 'GeneratorCSV' Wersja = '4.0 Experimental' +LataPracy = '2019' +Autorzy = 'Mateusz Skoczek' @@ -77,14 +79,14 @@ except ModuleNotFoundError: print('Nie znaleziono modułu programu (load_format.py)\nNie można załadować programu\nKod błędu: E00x0003') wait = input('Naciśnij ENTER aby wyjść') SS.exit(0) - +""" try: import processing as MDprc except ModuleNotFoundError: print('Nie znaleziono modułu programu (processing.py)\nNie można załadować programu\nKod błędu: E00x0004') wait = input('Naciśnij ENTER aby wyjść') SS.exit(0) - +""" # Biblioteki zewnętrzne interfejsu graficznego @@ -99,7 +101,7 @@ import tkinter as TK -#TODO + # ------------------------------------- # Uruchomienie interfejsu graficznego # -------------------------------------- # # Zmienne globalne środowiska graficznego @@ -108,390 +110,520 @@ if int(MDlcg.read()[0]) == 1: else: CiemnyMotyw = False SzerokoscOpisu = 17 +SzerokoscOpisu2 = 30 +SzerokoscOpisu3 = 10 SzerokoscPola = 122 +SzerokoscPola2 = 107 +SzerokoscPola3 = 130 -# Kolorystyka okna +# Motyw if CiemnyMotyw: - PaletaBarw = ['#1F1F1F', '#191919', '#B8B8B8', '#FFFFFF', '#404040', '#FFFFFF', '#1F1F1F', 1] + ZmienneMotywu = ['#1F1F1F', '#191919', '#B8B8B8', '#FFFFFF', '#404040', '#FFFFFF', '#1F1F1F', 1] else: - PaletaBarw = ['#F0F0F0', '#D4D4D4', '#000000', '#000000', '#A6A6A6', '#000000', '#FFFFFF', 2] + ZmienneMotywu = ['#F0F0F0', '#D4D4D4', '#000000', '#000000', '#A6A6A6', '#000000', '#FFFFFF', 2] -B_tlo = PaletaBarw[0] -B_tytultlo = PaletaBarw[1] -B_tytultext = PaletaBarw[2] -B_text = PaletaBarw[3] -B_przycisktlo = PaletaBarw[4] -B_przycisktext = PaletaBarw[5] -B_entrytlo = PaletaBarw[6] -B_framewielkosc = PaletaBarw[7] +M_tlo = ZmienneMotywu[0] +M_tytultlo = ZmienneMotywu[1] +M_tytultext = ZmienneMotywu[2] +M_text = ZmienneMotywu[3] +M_przycisktlo = ZmienneMotywu[4] +M_przycisktext = ZmienneMotywu[5] +M_entrytlo = ZmienneMotywu[6] +M_framewielkosc = ZmienneMotywu[7] - -def settings(): - # Tworzenie okna ustawień - SettingsWindow = TK.Tk() - SettingsWindow.title('Ustawienia programu') - SettingsWindow.resizable(width = False, height = False) - SettingsWindow.configure(background = B_tlo) +class Main(TK.Tk): + def __init__(self): + # Ustawienia okna + TK.Tk.__init__(self) + self.title(Nazwa + " " + Wersja) + self.resizable(width = False, height = False) + self.configure(bg = M_tlo) - # Tytul - Tytul = TK.Label(SettingsWindow) - Tytul.config(text = 'Ustawienia') - Tytul.config(width = 20) - Tytul.config(bg = B_tytultlo, fg = B_tytultext) - Tytul.config(font = ('Segoe UI Semilight', 20)) - Tytul.grid(row = 0) + # Tytuł + Tytul = TK.Label(self) + Tytul.config(text = Nazwa) + Tytul.config(width = 41) + Tytul.config(bg = M_tytultlo) + Tytul.config(fg = M_tytultext) + Tytul.config(font = ('Segoe UI Semilight', 30)) + Tytul.grid(row = 0) - # Frame1 - Motyw - Ramka1 = TK.LabelFrame(SettingsWindow) - Ramka1.config(text = ' Motyw programu ') - Ramka1.config(bg = B_tlo, fg = B_text) - Ramka1.config(borderwidth = B_framewielkosc) - Ramka1.grid(row = 1, pady = 5) - - Motyw_var = TK.StringVar() - if int(MDlcg.read()[0]) == 1: - Motyw_var.set('Ciemny') - Motyw_index = 1 - else: - Motyw_var.set('Jasny') - Motyw_index = 0 - - Motyw_list = TKttk.Combobox(Ramka1) - Motyw_list.config(textvariable = Motyw_var, state = 'readonly') - Motyw_list.config(width = 43) - Motyw_list.grid(row = 0, pady = 5, padx = 5) - Motyw_list['values'] = ('Jasny', 'Ciemny') - Motyw_list.current(Motyw_index) + # Frame1 - Pliki z danymi + Ramka1 = TK.LabelFrame(self) + Ramka1.config(text=' Pliki tekstowe zawierające dane (wymagany przynajmniej jeden) ') + Ramka1.config(borderwidth = M_framewielkosc) + Ramka1.config(bg = M_tlo) + Ramka1.config(fg = M_text) + Ramka1.grid(row = 1) - # Frame2 - Kodowanie - Ramka2 = TK.LabelFrame(SettingsWindow) - Ramka2.config(text = ' Kodowanie wyjściowe ') - Ramka2.config(bg = B_tlo, fg = B_text) - Ramka2.config(borderwidth = B_framewielkosc) - Ramka2.grid(row = 2, pady = 5) + # Ścieżka pliku txt nr 1 + wiersz = 1 + text1 = TK.StringVar() - Code_var = TK.StringVar() - Code_var.set(MDlcg.read()[1]) + Pole1Label = TK.Label(Ramka1) + Pole1Label.config(text = 'Plik z danymi (1)') + Pole1Label.config(width = SzerokoscOpisu) + Pole1Label.config(bg = M_tlo) + Pole1Label.config(fg = M_text) + Pole1Label.grid(row = wiersz, column = 0) - Code_list = TKttk.Combobox(Ramka2) - Code_list.config(textvariable = Code_var, state = 'readonly') - Code_list.config(width = 43) - Code_list.grid(row = 0, pady = 5, padx = 5) - Code_list['values'] = ('utf-8') - Code_list.set(MDlcg.read()[1]) + Pole1 = TK.Entry(Ramka1) + Pole1.config(textvariable = text1) + Pole1.config(width = SzerokoscPola) + Pole1.config(bg = M_entrytlo) + Pole1.config(fg = M_text) + Pole1.grid(row = wiersz, column = 1) + + def Pole1BrowseDialog(): + Pole1Browse.filename = TKfld.askopenfilename(initialdir = "C:/", title = "Wybierz plik tekstowy z danymi", filetypes = (("Pliki txt", "*.txt"), ("Wszystkie pliki", "*.*"))) + Pole1.delete(0, 'end') + Pole1.insert(0, Pole1Browse.filename) + + Pole1Browse = TK.Button(Ramka1) + Pole1Browse.config(text = '...') + Pole1Browse.config(command = Pole1BrowseDialog) + Pole1Browse.config(bg = M_przycisktlo) + Pole1Browse.config(fg = M_przycisktext) + Pole1Browse.config(relief = 'flat') + Pole1Browse.config(activebackground = M_przycisktlo) + Pole1Browse.grid(row = wiersz, column = 2, padx=5, pady=3) + + + # Ścieżka pliku txt nr 2 + wiersz = 2 + text2 = TK.StringVar() + + Pole2Label = TK.Label(Ramka1) + Pole2Label.config(text = 'Plik z danymi (2)') + Pole2Label.config(width = SzerokoscOpisu) + Pole2Label.config(bg = M_tlo) + Pole2Label.config(fg = M_text) + Pole2Label.grid(row = wiersz, column = 0) + + Pole2 = TK.Entry(Ramka1) + Pole2.config(textvariable = text2) + Pole2.config(width = SzerokoscPola) + Pole2.config(bg = M_entrytlo) + Pole2.config(fg = M_text) + Pole2.grid(row = wiersz, column = 1) + + def Pole2BrowseDialog(): + Pole2Browse.filename = TKfld.askopenfilename(initialdir = "C:/", title = "Wybierz plik tekstowy z danymi", filetypes = (("Pliki txt", "*.txt"), ("Wszystkie pliki", "*.*"))) + Pole2.delete(0, 'end') + Pole2.insert(0, Pole2Browse.filename) + + Pole2Browse = TK.Button(Ramka1) + Pole2Browse.config(text = '...') + Pole2Browse.config(command = Pole2BrowseDialog) + Pole2Browse.config(bg = M_przycisktlo) + Pole2Browse.config(fg = M_przycisktext) + Pole2Browse.config(relief = 'flat') + Pole2Browse.config(activebackground = M_przycisktlo) + Pole2Browse.grid(row = wiersz, column = 2, padx = 5, pady = 3) + + # Ścieżka pliku txt nr 3 + wiersz = 3 + text3 = TK.StringVar() + + Pole3Label = TK.Label(Ramka1) + Pole3Label.config(text = 'Plik z danymi (3)') + Pole3Label.config(width = SzerokoscOpisu) + Pole3Label.config(bg = M_tlo) + Pole3Label.config(fg = M_text) + Pole3Label.grid(row = wiersz, column = 0) + + Pole3 = TK.Entry(Ramka1) + Pole3.config(textvariable = text3) + Pole3.config(width = SzerokoscPola) + Pole3.config(bg = M_entrytlo) + Pole3.config(fg = M_text) + Pole3.grid(row = wiersz, column = 1) + + def Pole3BrowseDialog(): + Pole3Browse.filename = TKfld.askopenfilename(initialdir = "C:/", title = "Wybierz plik tekstowy z danymi", filetypes = (("Pliki txt", "*.txt"), ("Wszystkie pliki", "*.*"))) + Pole3.delete(0, 'end') + Pole3.insert(0, Pole3Browse.filename) + + Pole3Browse = TK.Button(Ramka1) + Pole3Browse.config(text = '...') + Pole3Browse.config(command = Pole3BrowseDialog) + Pole3Browse.config(bg = M_przycisktlo) + Pole3Browse.config(fg = M_przycisktext) + Pole3Browse.config(relief = 'flat') + Pole3Browse.config(activebackground = M_przycisktlo) + Pole3Browse.grid(row = wiersz, column = 2, padx = 5, pady = 3) - # Przycisk ZAPISZ - def zapis(): - X1 = Motyw_list.get() - if X1 == 'Jasny': - X1 = '0' - else: - X1 = '1' - X2 = Code_list.get() - ToSave = [X1, X2] - MDlcg.edit(ToSave) - SettingsWindow.destroy() - PrzyciskZAPISZ = TK.Button(SettingsWindow) - PrzyciskZAPISZ.config(text = 'ZAPISZ') - PrzyciskZAPISZ.config(command = zapis) - PrzyciskZAPISZ.config(width = 40) - PrzyciskZAPISZ.config(bg = B_przycisktlo, fg = B_przycisktext, relief = 'flat', activebackground = B_przycisktlo) - PrzyciskZAPISZ.grid(row = 3, pady = 8) + # Frame2 - Dołącz pliki .csv + Ramka2 = TK.LabelFrame(self) + Ramka2.config(text = ' Dołącz pliki .csv (opcjonalne) ') + Ramka2.config(borderwidth = M_framewielkosc) + Ramka2.config(bg = M_tlo) + Ramka2.config(fg = M_text) + Ramka2.grid(row = 2) - SettingsWindow.mainloop() + # Sciezka do konta.csv + wiersz = 1 + textKonta = TK.StringVar() + + PoleKontaLabel = TK.Label(Ramka2) + PoleKontaLabel.config(text = 'Ściezka do pliku .csv dla poczty') + PoleKontaLabel.config(width = SzerokoscOpisu2) + PoleKontaLabel.config(bg = M_tlo) + PoleKontaLabel.config(fg = M_text) + PoleKontaLabel.grid(row = wiersz, column = 0) + + PoleKonta = TK.Entry(Ramka2) + PoleKonta.config(textvariable = textKonta) + PoleKonta.config(width = SzerokoscPola2) + PoleKonta.config(bg = M_entrytlo) + PoleKonta.config(fg = M_text) + PoleKonta.grid(row = wiersz, column = 1) + + def PoleKontaBrowseDialog(): + PoleKontaBrowse.filename = TKfld.askopenfilename(initialdir="C:/", title="Wybierz plik .csv dla poczty", filetypes=(("Pliki csv", "*.csv"), ("Wszystkie pliki", "*.*"))) + PoleKonta.delete(0, 'end') + PoleKonta.insert(0, PoleKontaBrowse.filename) + + PoleKontaBrowse = TK.Button(Ramka2) + PoleKontaBrowse.config(text = '...') + PoleKontaBrowse.config(command = PoleKontaBrowseDialog) + PoleKontaBrowse.config(bg = M_przycisktlo) + PoleKontaBrowse.config(fg = M_przycisktext) + PoleKontaBrowse.config(relief = 'flat') + PoleKontaBrowse.config(activebackground = M_przycisktlo) + PoleKontaBrowse.grid(row = wiersz, column = 2, padx = 5, pady = 3) + # Sciezka do office.csv + wiersz = 2 + textOffice = TK.StringVar() + + PoleOfficeLabel = TK.Label(Ramka2) + PoleOfficeLabel.config(text = 'Ściezka do pliku .csv dla office365') + PoleOfficeLabel.config(width = SzerokoscOpisu2) + PoleOfficeLabel.config(bg = M_tlo) + PoleOfficeLabel.config(fg = M_text) + PoleOfficeLabel.grid(row = wiersz, column = 0) + + PoleOffice = TK.Entry(Ramka2) + PoleOffice.config(textvariable = textOffice) + PoleOffice.config(width = SzerokoscPola2) + PoleOffice.config(bg = M_entrytlo) + PoleOffice.config(fg = M_text) + PoleOffice.grid(row = wiersz, column = 1) + + def PoleOfficeBrowseDialog(): + PoleOfficeBrowse.filename = TKfld.askopenfilename(initialdir = "C:/", title = "Wybierz plik .csv dla poczty", filetypes = (("Pliki csv", "*.csv"), ("Wszystkie pliki", "*.*"))) + PoleOffice.delete(0, 'end') + PoleOffice.insert(0, PoleOfficeBrowse.filename) + + PoleOfficeBrowse = TK.Button(Ramka2) + PoleOfficeBrowse.config(text = '...') + PoleOfficeBrowse.config(command = PoleKontaBrowseDialog) + PoleOfficeBrowse.config(bg = M_przycisktlo) + PoleOfficeBrowse.config(fg = M_przycisktext) + PoleOfficeBrowse.config(relief = 'flat') + PoleOfficeBrowse.config(activebackground = M_przycisktlo) + PoleOfficeBrowse.grid(row = wiersz, column = 2, padx = 5, pady = 3) -def main(): - # Tworzenie okna głównego - MainWindow = TK.Tk() - MainWindow.title(Nazwa + ' ' + Wersja) - MainWindow.resizable(width = False, height = False) - MainWindow.configure(background = B_tlo) + # Frame3 - Eksport + Ramka3 = TK.LabelFrame(self) + Ramka3.config(text=' Eksport ') + Ramka3.config(borderwidth = M_framewielkosc) + Ramka3.config(bg = M_tlo) + Ramka3.config(fg = M_text) + Ramka3.grid(row = 3) - # Tytul - Tytul = TK.Label(MainWindow) - Tytul.config(text = Nazwa) - Tytul.config(width = 41) - Tytul.config(bg = B_tytultlo, fg = B_tytultext) - Tytul.config(font = ('Segoe UI Semilight', 30)) - Tytul.grid(row = 0) + # Sciezka do pliku poczty + wiersz = 1 + textKontaEksport = TK.StringVar() + PoleKontaEksportLabel = TK.Label(Ramka3) + PoleKontaEksportLabel.config(text = 'Poczta') + PoleKontaEksportLabel.config(width = SzerokoscOpisu3) + PoleKontaEksportLabel.config(bg = M_tlo) + PoleKontaEksportLabel.config(fg = M_text) + PoleKontaEksportLabel.grid(row = wiersz, column = 0) - # Frame1 - Import - Ramka1 = TK.LabelFrame(MainWindow) - Ramka1.config(text = ' Pliki do importu zawierające dane ') - Ramka1.config(bg = B_tlo, fg = B_text) - Ramka1.config(borderwidth = B_framewielkosc) - Ramka1.grid(row = 1) + PoleKontaEksport = TK.Entry(Ramka3) + PoleKontaEksport.config(textvariable = textKontaEksport) + PoleKontaEksport.config(width = SzerokoscPola3) + PoleKontaEksport.config(bg = M_entrytlo) + PoleKontaEksport.config(fg = M_text) + PoleKontaEksport.grid(row = wiersz, column = 1) + def PoleKontaEksportBrowseDialog(): + PoleKontaEksportBrowse.filename = TKfld.saveasfilename(initialdir = "C:/", title = "Zapisz", filetypes = (("Pliki csv", "*.csv"), ("Wszystkie pliki", "*.*"))) + PoleKontaEksport.delete(0, 'end') + PoleKontaEksport.insert(0, PoleKontaEksportBrowse.filename) - # Ścieżka pliku do importu 1 - wiersz = 1 - text1 = TK.StringVar() + PoleKontaEksportBrowse = TK.Button(Ramka3) + PoleKontaEksportBrowse.config(text = '...') + PoleKontaEksportBrowse.config(command = PoleKontaEksportBrowseDialog) + PoleKontaEksportBrowse.config(bg = M_przycisktlo) + PoleKontaEksportBrowse.config(fg = M_przycisktext) + PoleKontaEksportBrowse.config(relief = 'flat') + PoleKontaEksportBrowse.config(activebackground = M_przycisktlo) + PoleKontaEksportBrowse.grid(row = wiersz, column = 2, padx = 5, pady = 3) - OpisPola1 = TK.Label(Ramka1) - OpisPola1.config(text = 'Plik z danymi (1)') - OpisPola1.config(width = SzerokoscOpisu) - OpisPola1.config(bg = B_tlo, fg = B_text) - OpisPola1.grid(row = wiersz, column = 0) + # Sciezka do pliku office + wiersz = 2 + textOfficeEksport = TK.StringVar() - Pole1 = TK.Entry(Ramka1) - Pole1.config(textvariable = text1) - Pole1.config(width = SzerokoscPola) - Pole1.config(bg = B_entrytlo, fg = B_text) - Pole1.grid(row = wiersz, column = 1) + PoleOfficeEksportLabel = TK.Label(Ramka3) + PoleOfficeEksportLabel.config(text = 'Office') + PoleOfficeEksportLabel.config(width = SzerokoscOpisu3) + PoleOfficeEksportLabel.config(bg = M_tlo) + PoleOfficeEksportLabel.config(fg = M_text) + PoleOfficeEksportLabel.grid(row = wiersz, column = 0) - def Browse1_Dialog(): - Browse1.filename = TKfld.askopenfilename(initialdir = "/", title = "Wybierz plik do importu", filetypes = (("Pliki txt", "*.txt"), ("Wszystkie pliki", "*.*"))) - Pole1.delete(0, 'end') - Pole1.insert(0, Browse1.filename) + PoleOfficeEksport = TK.Entry(Ramka3) + PoleOfficeEksport.config(textvariable = textOfficeEksport) + PoleOfficeEksport.config(width = SzerokoscPola3) + PoleOfficeEksport.config(bg = M_entrytlo) + PoleOfficeEksport.config(fg = M_text) + PoleOfficeEksport.grid(row = wiersz, column = 1) - Browse1 = TK.Button(Ramka1) - Browse1.config(text = '...') - Browse1.config(command = Browse1_Dialog) - Browse1.config(bg = B_przycisktlo, fg = B_przycisktext, relief='flat', activebackground = B_przycisktlo) - Browse1.grid(row = wiersz, column = 2, padx=5, pady=3) + def PoleOfficeEksportBrowseDialog(): + PoleOfficeEksportBrowse.filename = TKfld.saveasfilename(initialdir = "C:/", title = "Zapisz", filetypes = (("Pliki csv", "*.csv"), ("Wszystkie pliki", "*.*"))) + PoleOfficeEksport.delete(0, 'end') + PoleOfficeEksport.insert(0, PoleOfficeEksportBrowse.filename) + PoleOfficeEksportBrowse = TK.Button(Ramka3) + PoleOfficeEksportBrowse.config(text = '...') + PoleOfficeEksportBrowse.config(command = PoleOfficeEksportBrowseDialog) + PoleOfficeEksportBrowse.config(bg = M_przycisktlo) + PoleOfficeEksportBrowse.config(fg = M_przycisktext) + PoleOfficeEksportBrowse.config(relief = 'flat') + PoleOfficeEksportBrowse.config(activebackground = M_przycisktlo) + PoleOfficeEksportBrowse.grid(row = wiersz, column = 2, padx = 5, pady = 3) - # Ścieżka pliku do importu 2 - wiersz = 2 - text2 = TK.StringVar() - - OpisPola2 = TK.Label(Ramka1) - OpisPola2.config(text = 'Plik z danymi (2)') - OpisPola2.config(width = SzerokoscOpisu) - OpisPola2.config(bg = B_tlo, fg = B_text) - OpisPola2.grid(row = wiersz, column = 0) - - Pole2 = TK.Entry(Ramka1) - Pole2.config(textvariable = text2) - Pole2.config(width = SzerokoscPola) - Pole2.config(bg = B_entrytlo, fg = B_text) - Pole2.grid(row = wiersz, column = 1) - - def Browse2_Dialog(): - Browse2.filename = TKfld.askopenfilename(initialdir = "/", title = "Wybierz plik do importu", filetypes = (("Pliki txt", "*.txt"), ("Wszystkie pliki", "*.*"))) - Pole2.delete(0, 'end') - Pole2.insert(0, Browse2.filename) - - Browse2 = TK.Button(Ramka1) - Browse2.config(text = '...') - Browse2.config(command = Browse1_Dialog) - Browse2.config(bg = B_przycisktlo, fg = B_przycisktext, relief = 'flat', activebackground = B_przycisktlo) - Browse2.grid(row = wiersz, column = 2, padx = 5, pady = 3) - - - # Ścieżka pliku do importu 3 - wiersz = 3 - text3 = TK.StringVar() - - OpisPola3 = TK.Label(Ramka1) - OpisPola3.config(text = 'Plik z danymi (3)') - OpisPola3.config(width = SzerokoscOpisu) - OpisPola3.config(bg = B_tlo, fg = B_text) - OpisPola3.grid(row = wiersz, column = 0) - - Pole3 = TK.Entry(Ramka1) - Pole3.config(textvariable = text3) - Pole3.config(width = SzerokoscPola) - Pole3.config(bg = B_entrytlo, fg = B_text) - Pole3.grid(row = wiersz, column = 1) - - def Browse3_Dialog(): - Browse3.filename = TKfld.askopenfilename(initialdir = "/", title = "Wybierz plik do importu", filetypes = (("Pliki txt", "*.txt"), ("Wszystkie pliki", "*.*"))) - Pole3.delete(0, 'end') - Pole3.insert(0, Browse3.filename) - - Browse3 = TK.Button(Ramka1) - Browse3.config(text = '...') - Browse3.config(command = Browse1_Dialog) - Browse3.config(bg = B_przycisktlo, fg = B_przycisktext, relief = 'flat', activebackground = B_przycisktlo) - Browse3.grid(row = wiersz, column = 2, padx = 5, pady = 3) - - - # Ścieżka pliku do importu 4 - wiersz = 4 - text4 = TK.StringVar() - - OpisPola4 = TK.Label(Ramka1) - OpisPola4.config(text = 'Plik z danymi (4)') - OpisPola4.config(width = SzerokoscOpisu) - OpisPola4.config(bg = B_tlo, fg = B_text) - OpisPola4.grid(row = wiersz, column = 0) - - Pole4 = TK.Entry(Ramka1) - Pole4.config(textvariable = text4) - Pole4.config(width = SzerokoscPola) - Pole4.config(bg = B_entrytlo, fg = B_text) - Pole4.grid(row = wiersz, column = 1) - - def Browse4_Dialog(): - Browse4.filename = TKfld.askopenfilename(initialdir = "/", title = "Wybierz plik do importu", filetypes = (("Pliki txt", "*.txt"), ("Wszystkie pliki", "*.*"))) - Pole4.delete(0, 'end') - Pole4.insert(0, Browse4.filename) - - Browse4 = TK.Button(Ramka1) - Browse4.config(text = '...') - Browse4.config(command = Browse1_Dialog) - Browse4.config(bg = B_przycisktlo, fg = B_przycisktext, relief = 'flat', activebackground = B_przycisktlo) - Browse4.grid(row = wiersz, column = 2, padx = 5, pady = 3) - - - # Frame2 - Eksport - Ramka2 = TK.LabelFrame(MainWindow) - Ramka2.config(text = ' Ustawienia eksportu ') - Ramka2.config(bg = B_tlo, fg = B_text) - Ramka2.config(borderwidth = B_framewielkosc) - Ramka2.grid(row = 2) - - # Ścieżka folderu do zapisu wygenerowanych plików - textExport = TK.StringVar() - - OpisPolaExport = TK.Label(Ramka2) - OpisPolaExport.config(text = 'Lokalizacja') - OpisPolaExport.config(width = SzerokoscOpisu) - OpisPolaExport.config(bg = B_tlo, fg = B_text, relief = 'flat', activebackground = B_przycisktlo) - OpisPolaExport.grid(row=0, column=0) - - PoleExport = TK.Entry(Ramka2) - PoleExport.config(textvariable = textExport) - PoleExport.config(width = SzerokoscPola) - PoleExport.config(bg = B_entrytlo, fg = B_text) - PoleExport.grid(row=0, column=1) - - def BrowseExport_Dialog(): - BrowseExport.filename = TKfld.askdirectory() - PoleExport.delete(0, 'end') - PoleExport.insert(0, BrowseExport.filename) - - BrowseExport = TK.Button(Ramka2) - BrowseExport.config(text='...') - BrowseExport.config(command=BrowseExport_Dialog) - BrowseExport.config(bg = B_przycisktlo, fg = B_przycisktext, relief='flat', activebackground = B_przycisktlo) - BrowseExport.grid(row=0, column=2, padx=5, pady=3) - - - # Przycisk START - def PathPreprocess(): - if MDdlg.Ask(A001): - while True: - sciezka1 = Pole1.get() - sciezka1_puste = True - sciezka2 = Pole2.get() - sciezka2_puste = True - sciezka3 = Pole3.get() - sciezka3_puste = True - sciezka4 = Pole3.get() - sciezka4_puste = True - sciezkaExport = PoleExport.get() - sciezkaExport_puste = True - - if sciezka1 != '': - sciezka1_puste = False - if sciezka2 != '': - sciezka2_puste = False - if sciezka3 != '': - sciezka3_puste = False - if sciezka4 != '': - sciezka4_puste = False - if sciezkaExport != '': - sciezkaExport_puste = False - - if sciezka1_puste and sciezka2_puste and sciezka3_puste and sciezka4_puste: - MDdlg.Err(E003x01) - break - if sciezkaExport_puste: - MDdlg.Err(E003x02) - break - KontenerDanych = [] - if not sciezka1_puste: - try: - x = open(sciezka1) - except FileNotFoundError: - MDdlg.Err(E003x111) - else: - with open(sciezka1, 'r') as plik1: - KontenerDanych += MDfmt.przetworz(plik1.read()) - if not sciezka2_puste: - try: - x = open(sciezka2) - except FileNotFoundError: - MDdlg.Err(E003x112) - else: - with open(sciezka2, 'r') as plik2: - KontenerDanych += MDfmt.przetworz(plik2.read()) - if not sciezka3_puste: - try: - x = open(sciezka3) - except FileNotFoundError: - MDdlg.Err(E003x113) - else: - with open(sciezka3, 'r') as plik3: - KontenerDanych += MDfmt.przetworz(plik3.read()) - if not sciezka4_puste: - try: - x = open(sciezka4) - except FileNotFoundError: - MDdlg.Err(E003x114) - else: - with open(sciezka4, 'r') as plik4: - KontenerDanych += MDfmt.przetworz(plik4.read()) - break - MDprc.do(KontenerDanych, sciezkaExport) - else: + # Przycisk START + def PathPreprocess(): pass - PrzyciskSTART = TK.Button(MainWindow) - PrzyciskSTART.config(text = 'START') - PrzyciskSTART.config(command = PathPreprocess) - PrzyciskSTART.config(width = 50) - PrzyciskSTART.config(bg = B_przycisktlo, fg = B_przycisktext, relief = 'flat', activebackground = B_przycisktlo) - PrzyciskSTART.grid(row = 3, pady = 15) + PrzyciskSTART = TK.Button(self) + PrzyciskSTART.config(text = 'START') + PrzyciskSTART.config(command = PathPreprocess) + PrzyciskSTART.config(width = 50) + PrzyciskSTART.config(bg = M_przycisktlo) + PrzyciskSTART.config(fg = M_przycisktext) + PrzyciskSTART.config(relief = 'flat') + PrzyciskSTART.config(activebackground = M_przycisktlo) + PrzyciskSTART.grid(row = 4, pady = 15) + + # Pasek dolny + PasekDolny = TK.LabelFrame(self) + PasekDolny.config(bd = 0) + PasekDolny.config(bg = M_tytultlo) + PasekDolny.config(fg = M_tytultext) + PasekDolny.grid(row=5) + + InfoLabel = TK.Label(PasekDolny) + InfoLabel.config(text = Nazwa + ' ' + Wersja + ' | © ' + Autorzy + ' ' + LataPracy + ' dla ZSP Sobolew') + InfoLabel.config(width = 107) + InfoLabel.config(justify = 'left') + InfoLabel.config(anchor='w') + InfoLabel.config(bg = M_tytultlo) + InfoLabel.config(fg = M_tytultext) + InfoLabel.grid(row = 0, column = 0) + + def InfoOpen(): + try: + x = open('.\instruction.txt') + except FileNotFoundError: + MDdlg.err(4) + else: + OS.system("notepad .\instruction.txt") + + PrzyciskINFO = TK.Button(PasekDolny) + PrzyciskINFO.config(text = 'Instrukcja') + PrzyciskINFO.config(command = InfoOpen) + PrzyciskINFO.config(bg = M_przycisktlo) + PrzyciskINFO.config(fg = M_przycisktext) + PrzyciskINFO.config(relief = 'flat') + PrzyciskINFO.config(activebackground = M_przycisktlo) + PrzyciskINFO.grid(row = 0, column = 1, padx = 5, pady = 5) + + PrzyciskUSTAWIENIA = TK.Button(PasekDolny) + PrzyciskUSTAWIENIA.config(text = 'Ustawienia') + PrzyciskUSTAWIENIA.config(command = self.settingsButton) + PrzyciskUSTAWIENIA.config(bg = M_przycisktlo) + PrzyciskUSTAWIENIA.config(fg = M_przycisktext) + PrzyciskUSTAWIENIA.config(relief = 'flat') + PrzyciskUSTAWIENIA.grid(row = 0, column = 2, padx = 5, pady = 5) + + def settingsButton(self): + self.child = Settings(self) + + def run(self): + self.mainloop() + +class Settings(TK.Toplevel): + def __init__(self, parent): + # Ustawienia okna + TK.Toplevel.__init__(self, parent) + self.title('Ustawienia') + self.resizable(width = False, height = False) + self.configure(bg = M_tlo) - # Pasek dolny - PasekDolny = TK.LabelFrame(MainWindow) - PasekDolny.config(bd = 0, bg = B_tytultlo, fg = B_tytultext) - PasekDolny.grid(row = 4) + # Tytuł + Tytul = TK.Label(self) + Tytul.config(text = 'Ustawienia') + Tytul.config(width = 40) + Tytul.config(bg = M_tytultlo) + Tytul.config(fg = M_tytultext) + Tytul.config(font = ('Segoe UI Semilight', 20)) + Tytul.grid(row = 0) - InfoLabel = TK.Label(PasekDolny) - InfoLabel.config(text = 'GeneratorCSV 3.0 | © Mateusz Skoczek 2019 dla ZSP Sobolew') - InfoLabel.config(justify = 'left', anchor='w', width=107) - InfoLabel.config(bg = B_tytultlo, fg = B_tytultext) - InfoLabel.grid(row=0, column=0) - def InfoOpen(): - try: - x = open('instrukcja.txt') - except FileNotFoundError: - MDdlg.Err(E001x02) + # Frame1 - Motyw + Ramka1 = TK.LabelFrame(self) + Ramka1.config(text = ' Motyw programu ') + Ramka1.config(bg = M_tlo) + Ramka1.config(fg = M_text) + Ramka1.config(borderwidth = M_framewielkosc) + Ramka1.grid(row = 1, pady = 5) + + Motyw_var = TK.StringVar() + if int(MDlcg.read()[0]) == 1: + Motyw_var.set('Ciemny') + Motyw_list_set = 'Ciemny' else: - OS.system("notepad instrukcja.txt") + Motyw_var.set('Jasny') + Motyw_list_set = 'Jasny' - PrzyciskINFO = TK.Button(PasekDolny) - PrzyciskINFO.config(text = 'Instrukcja') - PrzyciskINFO.config(command = InfoOpen) - PrzyciskINFO.config(bg = B_przycisktlo, fg = B_przycisktext, relief = 'flat', activebackground = B_przycisktlo) - PrzyciskINFO.grid(row = 0, column = 1, padx = 5, pady = 5) + Motyw_list = TKttk.Combobox(Ramka1) + Motyw_list.config(textvariable = Motyw_var) + Motyw_list.config(state = 'readonly') + Motyw_list.config(width = 93) + Motyw_list.grid(row = 0, pady = 5, padx = 5) + Motyw_list['values'] = ('Jasny', 'Ciemny') + Motyw_list.set(Motyw_list_set) - PrzyciskUSTAWIENIA = TK.Button(PasekDolny) - PrzyciskUSTAWIENIA.config(text = 'Ustawienia') - PrzyciskUSTAWIENIA.config(command = settings) - PrzyciskUSTAWIENIA.config(bg = B_przycisktlo, fg = B_przycisktext, relief = 'flat') - PrzyciskUSTAWIENIA.grid(row = 0, column = 2, padx = 5, pady = 5) - MainWindow.mainloop() + # Frame2 - Kodowanie + Ramka2 = TK.LabelFrame(self) + Ramka2.config(text = ' Kodowanie wyjściowe ') + Ramka2.config(bg = M_tlo) + Ramka2.config(fg = M_text) + Ramka2.config(borderwidth = M_framewielkosc) + Ramka2.grid(row = 2, pady = 5) -main() \ No newline at end of file + Code_var = TK.StringVar() + Code_var.set(MDlcg.read()[1]) + + Code_list = TKttk.Combobox(Ramka2) + Code_list.config(textvariable = Code_var) + Code_list.config(state = 'readonly') + Code_list.config(width = 93) + Code_list.grid(row = 0, pady = 5, padx = 5) + Code_list['values'] = ('utf-8') + Code_list.set(MDlcg.read()[1]) + + + # Frame3 - Format plików wejściowych + SzerokoscPolaWej = 35 + WysokoscPolaWej = 8 + + Ramka3 = TK.LabelFrame(self) + Ramka3.config(text = ' Format plików wejściowych ') + Ramka3.config(bg = M_tlo) + Ramka3.config(fg = M_text) + Ramka3.config(borderwidth = M_framewielkosc) + Ramka3.grid(row = 3, pady = 5) + + UczniowieLabel = TK.Label(Ramka3) + UczniowieLabel.config(text = 'Uczniowie') + UczniowieLabel.config(justify = 'center') + UczniowieLabel.config(bg = M_tlo) + UczniowieLabel.config(fg = M_text) + UczniowieLabel.grid(row = 0, column = 0) + + uczfmt = MDlfm.read()[0] + uczfmt = '\n'.join(uczfmt) + UczniowieFormat = TK.Text(Ramka3) + UczniowieFormat.config(width = SzerokoscPolaWej) + UczniowieFormat.config(height = WysokoscPolaWej) + UczniowieFormat.config(bg = M_entrytlo) + UczniowieFormat.config(fg = M_text) + UczniowieFormat.grid(row = 1, column = 0, padx = 5, pady = 5) + UczniowieFormat.insert(TK.END, uczfmt) + + NauczycieleLabel = TK.Label(Ramka3) + NauczycieleLabel.config(text = 'Nauczyciele') + NauczycieleLabel.config(justify = 'center') + NauczycieleLabel.config(bg = M_tlo) + NauczycieleLabel.config(fg = M_text) + NauczycieleLabel.grid(row = 0, column = 1) + + nczfmt = MDlfm.read()[1] + nczfmt = '\n'.join(nczfmt) + NauczycieleFormat = TK.Text(Ramka3) + NauczycieleFormat.config(width = SzerokoscPolaWej) + NauczycieleFormat.config(height = WysokoscPolaWej) + NauczycieleFormat.config(bg = M_entrytlo) + NauczycieleFormat.config(fg = M_text) + NauczycieleFormat.grid(row = 1, column = 1, padx = 5, pady = 5) + NauczycieleFormat.insert(TK.END, nczfmt) + + + # Frame4 - Stałe + Ramka4 = TK.LabelFrame(self) + Ramka4.config(text = ' Stałe ') + Ramka4.config(bg = M_tlo) + Ramka4.config(fg = M_text) + Ramka4.config(borderwidth = M_framewielkosc) + Ramka4.grid(row = 4, pady = 5) + + DomenaLabel = TK.Label(Ramka4) + DomenaLabel.config(text = 'Domena') + DomenaLabel.config(width = SzerokoscOpisu3) + DomenaLabel.config(bg = M_tlo) + DomenaLabel.config(fg = M_text) + DomenaLabel.grid(row = 0, column = 0) + + text1 = TK.StringVar() + PoleDomena = TK.Entry(Ramka4) + PoleDomena.config(textvariable = text1) + PoleDomena.config(width = 83) + PoleDomena.config(bg = M_entrytlo) + PoleDomena.config(fg = M_text) + PoleDomena.grid(row = 0, column = 1, padx = 5, pady = 5) + PoleDomena.insert(0, MDlcg.read()[2]) + + QuotaLabel = TK.Label(Ramka4) + QuotaLabel.config(text = 'Quota (MB)') + QuotaLabel.config(width = SzerokoscOpisu3) + QuotaLabel.config(bg = M_tlo) + QuotaLabel.config(fg = M_text) + QuotaLabel.grid(row = 1, column = 0) + + value2 = TK.IntVar() + PoleQuota = TK.Spinbox(Ramka4) + PoleQuota.config(textvariable = value2) + PoleQuota.config(from_ = 1, to = 100000) + PoleQuota.config(width = 81) + PoleQuota.config(bg = M_entrytlo) + PoleQuota.config(fg = M_text) + PoleQuota.grid(row=1, column=1, padx=5, pady=5) + PoleQuota.insert(0, int(MDlcg.read()[3])) + print(int(MDlcg.read()[3])) + + + + +OknoGlowne = Main() +OknoGlowne.run() \ No newline at end of file diff --git a/config.cfg b/config.cfg index b559d4d..3e913f8 100644 --- a/config.cfg +++ b/config.cfg @@ -1,2 +1,5 @@ Ciemny motyw(0/1): 1 -Kodowanie wyjsciowe: utf-8 \ No newline at end of file +Kodowanie wyjsciowe: utf-8 +Domena: losobolew.pl +Quota: 500 +Kraj: Rzeczypospolita Polska \ No newline at end of file diff --git a/format.fmt b/format.fmt index e69de29..1ac672d 100644 --- a/format.fmt +++ b/format.fmt @@ -0,0 +1,5 @@ +K O N, I X L X +Q + +N, I X L X +Q \ No newline at end of file From e67eaac836d10520ba86b53bf85b3b3cca8b7ffd Mon Sep 17 00:00:00 2001 From: Mateusz Skoczek Date: Thu, 6 Aug 2020 18:25:27 +0200 Subject: [PATCH 08/29] 4.0 Alpha (Build 19353) --- .idea/workspace.xml | 4 +- changelog-UC.txt | 3 + components/__pycache__/dialog.cpython-38.pyc | Bin 3691 -> 3979 bytes .../__pycache__/load_config.cpython-38.pyc | Bin 2071 -> 2295 bytes .../__pycache__/load_format.cpython-38.pyc | Bin 2670 -> 3215 bytes components/dialog.py | 27 +- components/load_config.py | 20 +- components/load_format.py | 238 +++++++++++------- components/main.py | 208 +++++++++++++-- config.cfg | 4 +- 10 files changed, 377 insertions(+), 127 deletions(-) diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 0eb6511..f3fed0c 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -82,6 +82,8 @@ + + @@ -104,6 +106,6 @@ - + \ No newline at end of file diff --git a/changelog-UC.txt b/changelog-UC.txt index 8de5c3d..83368fe 100644 --- a/changelog-UC.txt +++ b/changelog-UC.txt @@ -37,3 +37,6 @@ - Modyfikacja interfejsu - Dodanie możliwości dołączenia plików csv do eksportowanych plików (tylko kontrolki) - Usprawnienia kodu + +4.0 Alpha (Build 19353) +- Ukończenie ustawień diff --git a/components/__pycache__/dialog.cpython-38.pyc b/components/__pycache__/dialog.cpython-38.pyc index 11877d6626e689d75a6882a163566580e65fb734..b077ec01c1ea53437c1ad98a76ba2eee842405ac 100644 GIT binary patch delta 436 zcmaDY(=E>z%FD~e00aULf5++wPUMqeteU8uR?nW!w2+aJA(e3fQwmoKGLIXD$AiM- zMd9(G@c2=90w_E|6rNBDdp(l%!YGm=DNIOmq9{Bu7%!b6iaC`T=yY+IUZ4m|Dhp6V z0_-giOA^FNkxG%yVU1z~(lR;hxg1fPKrz`AX}K1LD6SOw6a^s7ouZhc1f+RVq=OkW zl{dFADsWErXZB(=n>>qIiqT}UFS8G$Ihbuac@fuS NZVpx+9u7_p4ggXlOJV>3 diff --git a/components/__pycache__/load_config.cpython-38.pyc b/components/__pycache__/load_config.cpython-38.pyc index 5ec830194ef65e6bf3edb92bba0e8db551ddcba3..974b39d16233770f8a408c55d93dad60b0b475ca 100644 GIT binary patch delta 741 zcmZuvO>fgc5Z&2bJ8s%I&PNj(5cQ=+k>G$TF1@w_aj66Wnj#e`#7^XrIAOgd5G5*7 z#TAhDfDo7P6FBl8IKst8#3fQW7r})c(=Vj%Xy1`4sdi5T;Ze(qe9EV4g=X5emv;J``2RTCl3Vv1@sUQE)Jh zPEk^vA3VtFYT;;zx$qlaY()J|AY-%9@B5)AN}8z}qJRag*=omTvlq64w#cE5Arwzw z9dS@MA}GVt5&8YCCN-r%e81 zvI*n-W3XO=2VQQUzNw7ah^~`ud(1~{k{WSjpZ?w?H^?1wm()pOV17fC^}7?}hK04j z?}jhjZcnP$W*QdMd-Jv)=6Ywh-HV!TCusV+-BnjuS~4sgUq2R&aM&C8JDR~CB8EZL zN_-L(eD+0z6IaD$EMbupu`v*V^tG9Zb?E3ryXH$7gze~Fuhw|xi$!uvi_dX2=Drum z9j%F64FoKpFb6g`ltblAl=Q6yVo42CE@ah*)XE}`Fqw*2!t8WhNj9&3rmnE7R_-sx CWtgD= delta 437 zcmZvYKTE?v7{>3tOVadDVp6fT61uKEE{ZPR!S5b;?uF-hKg=6L-_x`R;^$f!*9MV0{Tc8lVlOZb zTd0Ea;qsfzN@S7s$uSu0l{m1>wxm@|T90#?m+1t5d{TEBzEW>B z9k)R>@rMW)zc7VLa+KMVoP{JC$+LKbn5Goqo)wkL;w=F3zyeF_1S zhM}$`+>-~73{?vYy^q$=Hrhc4D2T0>=_FeTWnhYJhDsMcbn;;;0@8%Qw7_?Cq0$^a zX?xU{JJj`TpSVqjR|$fPK2^P9d!E~Dw0B#Tqf?vCh_Qbv`I2q9-XV9}84d(0V2Gta r2Lnrx{<#T=aU%va$ExZaMA?~I$Oh@eMrcmpZ&iUZ*`vBd=2i0xyuDgA diff --git a/components/__pycache__/load_format.cpython-38.pyc b/components/__pycache__/load_format.cpython-38.pyc index 6d8186b91e41f39bc6a3fb3dbf105388781fd50d..4cfe6fa39235d9b03975bba33e91107153adb7e8 100644 GIT binary patch literal 3215 zcmbVOO>7&-6`q-0E>|l`k^dt{b~kmKnogtAjvfENaGh9^?KoC!Q?X=1hOp*at1D8w z)NpA^#4-i^gZLJmiUhvUXfL@Y1=>Rn1u6kO6-AD72nrNE5a_iR7ijz5F3Au9A-LVe zyxEzZ`R2|0-pqR+jf_|fpT~dy*WKDa#{Nc){>Q||MJ!c70j$7)gDCTYXk_9qt)R~{ ze^O^|s$|I-&ggXw&>nHD)1W`%1=AI+A>hxk=MMA50)rG7k6FQjG?-Wq!w^_lXF%jx zcDVKom$Hu7mtEhLP8i5b`PcV%_Ou}@Cs?CJH2E4@|M51lgpxwm0ocS5&TZP@uz zP;9s*>@3&94f{my_*wfcds)_?;knz7;|}}@Wydb$Z`k?ZUQl-HmURR>uLn@uerPuw z+1Ppl4QFNRNpZujl)aMYuG;qlx$1;99N9+o*lf)q>A7~pcgk+V3;e)djS*^gMFz{# zS*_Ve!E?&N@?>QrYth)%VEb#|!6mjIIuO*ItxtMmt!n|;_qHEyeGavA_H-_{p3CKq z*IuJloT9h=%=cF8>DjsITegE6u5YYtKim4GC`g&kGlcaaESIoUeI5DYO@T#=v`t;E zfN1Iw2W^~9vAdaOD$=9WY1Y)&4mJ)a{VfLKIgTjlaw_D+UDH5z&*&@^$nM7pyP?+7 zAw_IPOjg2F3)!ZYZ-LPici5)BwvzDNI2h@OnMO>rq{+_)V1CnwIn=@2dI?OcgZYm> z%=3w!n_M32Lmf^~v3Fue+=*D%OoEG}X9gla?n55wAwPHtC!NLrt^sMr5LeC(y!VWBmXt8F&*IUG4Cs()sS58(rS()9~$% zhPs^FQA4Att(g(s+$U$}6q6ipAH~<(!&uKKeyvR%Z6ogeu$_+U**3)$#jm#M&Qkn? zHbtR!hQtVoQ4+79Q2e;!Clr5M@so<5Qv4moPb+>#@w1BO6rWdoTJc+o&nbRK@sAY0 zq4>Ls=M|q)9M`|B_!Y(9SG=J3CB<(m{+{C3+hd5GO{-KfsQIDNJQUV#r(AQVr3|D> zU7v#TveH~BmB{Cn?yh@bcBr7=ave}c5)2AvC8$WJ?z@Qd3%6KOnOd>odqLfE@2`eZ zBmGkdfqs$iqlPb$ZZmQGdK1B` ze?`H#j<10w!!05BZmdVSMQuSvU~5p%7>+S|W5#gI|K79rd$ULfTJiruyTAoEQP;74 zJy1h=5%*Z6jdmZ5hm5=|j*d_9qmcV0B#!^^dG!kIvRU7x`4jt(zbS@;ieB+ek4qN;E0%2n+6B}K`VVk$o5z-Z7 zLZpK+j%mjMhF=)O4zOm5uwe)Z9BUep(K4eLZN5VY5@F?;uY;CPC>frW)3}HBfAE=+ z@8YL?j|A;lPLW71wEA3jNWM?)t0XRxm?3eQL?0t7f#<7KU3#ILO~n;Dvv}zN(Fa4z&8A+xZtE~1=D*Qe7QmKlhTQcSOm0GCuy5oiN3{FipO+FLD m_h}gGN*<-m^OPEw-3w&D*sb*arj{saqRL|#^OtPI-1T1)qw;J3 literal 2670 zcmZ`*&2Jk;6rWk|+K%I-NgEo{reSHJ)GZB8=m!mq=*GX25S!>^X{@y)*mgn(M6iv5k|MFr@)LYibrhqwJbq z%$qrr74*9{FBnc4cQ!FRk}Mn2noO@+MxIqo+p_h7>!GX{xxK`VLRmisrjfUohKp;l zh}>JSH-EMae8lE`1MG^i@pyZ0!$$;RU>mBF;GAhw6_p1`|wG=TY{by_2!43J53J6cz2w5FUTb-EI* z4tetp-18JOnp621hsvzg1DMYtl{gCKbI!}B6KJTQ?&Pm}phgj?Au^Z?@M0U}x(^xJ zg$%clKeR#q>O*$yLPlE1KYd8ui*}vzpby*W!w$OfMm;RY@ z??dnLp>N;KdtvXm!(Q}Ix^!!g7 z&k7&%dG6iu+(K{hqVTb{=!_4&cNcn#7lp63MStT%@7qB`AG`$np&wp`0XP5$VGs_% z5WE69yb6cm2)qWb!%=txjzJ8L!wGm3PQoy}1@Warb**}|rXd@H$cE&D_LE$2UE$BR zB3M^eo?#ttRYrj8z9>d~Q4my|(WD?;zDRP6>Kh&<_OtP|&oX6N1hOdP&gfW)J#~X+q1|Wy=u(6HR2!$d_4y zbDIlodJ^(WBEUG01x3)4taP-Tty-pCG1=XM!%-H9&~S&%)7_SVYin7P<>gws7c~m( zYQ@gm7E4=3&itP6a|rnB8k)pd=DNiuxp|Leu2>e!%y4^|!{k9 ztX(LgE6XWm^0onsUPLH|qI2RCmd#DMqIv?Imp(&7s7j+Wq9}APw%xP`d)@!j>v30m zaP`GrUu#bKilT>hd$#7zQ-y}*lkmsi#R#%8jIB$>j!dyDsGnn}8g)=GQ6x!ABq@sI z@m6UmjQ}XE(pp#`awiI^^3@cIFm0$$Nkfw&TBl1yN->siUp-J$8o^osS5QnPb|vYA zYKlBOYsx%P$Z7Hsxk0|eGL-7GOp8oRTvNl?1m(}WBI6}iG~843d_>Sl zEG$&J$Sl!2W#(DZcBZg$z^xX38bSPwM5>$_AhZ&mb&d!cMFfD$Moh(8lZg@mSvzMX zLKcu)6rLHALrKu5c$fC0A;_thJ!w_scQ4-Vo?XY4e34vszrc<|YDE5XWSP1wQ{PcE z>U)8=Se{)VE|>Cn^SIO?Ao*-@IbKsh9l(RCAK8>5+)wJN^bH}*OHW;^X^l_~b3!qn z9@8y`!%04l5%Kq=xgd?y1OC1=o+wmeVV5F)QO;eGW}8r+z@bo=ZPOB3g_{nG1wHbn za3S60mHn)pGVZYjm(X<7KgTaick;#C;aI2)Oy^6SYmpq@rrD!dmOa0U>#;5oT3iG> zySOL 0: - error = int('x') - except ValueError: - MDdlg.err(11) - - try: - if uczniowiefmt.count('K') != 1: - error = int('x') - except ValueError: - MDdlg.err(6) - - try: - if uczniowiefmt.count('O') != 1: - error = int('x') - except ValueError: - MDdlg.err(7) - - try: - if uczniowiefmt.count('N') != 1: - error = int('x') - except ValueError: - MDdlg.err(8) - - try: - if uczniowiefmt.count('I') != 1: - error = int('x') - except ValueError: - MDdlg.err(9) - - try: - if uczniowiefmt.count('L') != 1: - error = int('x') - except ValueError: - MDdlg.err(10) - - try: - if format[1].count('') > 0: - error = int('x') - except ValueError: - MDdlg.err(12) - - nauczycielefmt = '' - for x in format[1]: - nauczycielefmt += x - - try: - if nauczycielefmt.count('N') != 1: - error = int('x') - except ValueError: - MDdlg.err(13) - - try: - if nauczycielefmt.count('I') != 1: - error = int('x') - except ValueError: - MDdlg.err(14) - - try: - if nauczycielefmt.count('L') != 1: - error = int('x') - except ValueError: - MDdlg.err(15) - - NiedozwoloneZnaki = ['1','2','3','4','5','6','7','8','9','0','W','E','R','T','Y','U','P','A','S','D','F','G','H','J','Z','C','V','B','M'] - try: - for x in NiedozwoloneZnaki: - if x in nauczycielefmt+uczniowiefmt: + try: + if format[0].count('') > 0: error = int('x') - except ValueError: - MDdlg.err(16) + except ValueError: + MDdlg.err(11) + if Read: + SS.exit(0) + else: + poprawne = False + break + + try: + if uczniowiefmt.count('K') != 1: + error = int('x') + except ValueError: + MDdlg.err(6) + if Read: + SS.exit(0) + else: + poprawne = False + break + + try: + if uczniowiefmt.count('O') != 1: + error = int('x') + except ValueError: + MDdlg.err(7) + if Read: + SS.exit(0) + else: + poprawne = False + break + + try: + if uczniowiefmt.count('N') != 1: + error = int('x') + except ValueError: + MDdlg.err(8) + if Read: + SS.exit(0) + else: + poprawne = False + break + + try: + if uczniowiefmt.count('I') != 1: + error = int('x') + except ValueError: + MDdlg.err(9) + if Read: + SS.exit(0) + else: + poprawne = False + break + + try: + if uczniowiefmt.count('L') != 1: + error = int('x') + except ValueError: + MDdlg.err(10) + if Read: + SS.exit(0) + else: + poprawne = False + break + + try: + if format[1].count('') > 0: + error = int('x') + except ValueError: + MDdlg.err(12) + if Read: + SS.exit(0) + else: + poprawne = False + break + + nauczycielefmt = '' + for x in format[1]: + nauczycielefmt += x + + try: + if nauczycielefmt.count('N') != 1: + error = int('x') + except ValueError: + MDdlg.err(13) + if Read: + SS.exit(0) + else: + poprawne = False + break + + try: + if nauczycielefmt.count('I') != 1: + error = int('x') + except ValueError: + MDdlg.err(14) + if Read: + SS.exit(0) + else: + poprawne = False + break + + try: + if nauczycielefmt.count('L') != 1: + error = int('x') + except ValueError: + MDdlg.err(15) + if Read: + SS.exit(0) + else: + poprawne = False + break + + NiedozwoloneZnaki = ['1','2','3','4','5','6','7','8','9','0','W','E','R','T','Y','U','P','A','S','D','F','G','H','J','Z','C','V','B','M'] + try: + for x in NiedozwoloneZnaki: + if x in nauczycielefmt+uczniowiefmt: + error = int('x') + except ValueError: + MDdlg.err(16) + if Read: + SS.exit(0) + else: + poprawne = False + break + check = False + return poprawne @@ -131,22 +191,28 @@ def read(): format = [] for x in fmt: format.append(x.split('\n')) - CheckFormat(format) + CheckFormat(True, format) return format # Zapis ustawień do pliku formatu def edit(format): - CheckFormat(format) - try: - check = open(r'.\format.fmt') - except FileNotFoundError: - MDdlg.err(5) + xformat = [] + for x in format: + xformat.append(x.split('\n')[:-1]) + if CheckFormat(False, xformat): + try: + check = open(r'.\format.fmt') + except FileNotFoundError: + MDdlg.err(5) + else: + FormatToSaveX = [] + for x in xformat: + FormatToSaveX.append('\n'.join(x)) + FormatToSave = FormatToSaveX[0] + '\n\n' + FormatToSaveX[1] + with open(r'.\format.fmt', 'w') as fmt: + fmt.write(FormatToSave) + return True else: - FormatToSaveX = [] - for x in format: - FormatToSaveX.append('\n'.join(x)) - FormatToSave = FormatToSaveX[0] + '\n\n' + FormatToSaveX[1] - with open(r'.\format.fmt', 'w') as fmt: - fmt.write(FormatToSave) \ No newline at end of file + return False diff --git a/components/main.py b/components/main.py index 6521d30..2759a1a 100644 --- a/components/main.py +++ b/components/main.py @@ -478,6 +478,7 @@ class Settings(TK.Toplevel): self.resizable(width = False, height = False) self.configure(bg = M_tlo) + liczbawierszy = 0 # Tytuł Tytul = TK.Label(self) @@ -490,6 +491,7 @@ class Settings(TK.Toplevel): # Frame1 - Motyw + liczbawierszy += 1 Ramka1 = TK.LabelFrame(self) Ramka1.config(text = ' Motyw programu ') Ramka1.config(bg = M_tlo) @@ -497,24 +499,22 @@ class Settings(TK.Toplevel): Ramka1.config(borderwidth = M_framewielkosc) Ramka1.grid(row = 1, pady = 5) - Motyw_var = TK.StringVar() - if int(MDlcg.read()[0]) == 1: - Motyw_var.set('Ciemny') - Motyw_list_set = 'Ciemny' - else: - Motyw_var.set('Jasny') - Motyw_list_set = 'Jasny' + if int(MDlcg.read()[0]) == 1: + Motyw_list_set = 1 + else: + Motyw_list_set = 0 Motyw_list = TKttk.Combobox(Ramka1) - Motyw_list.config(textvariable = Motyw_var) + Motyw_list.config(textvariable = TK.StringVar()) Motyw_list.config(state = 'readonly') Motyw_list.config(width = 93) Motyw_list.grid(row = 0, pady = 5, padx = 5) Motyw_list['values'] = ('Jasny', 'Ciemny') - Motyw_list.set(Motyw_list_set) + Motyw_list.current(Motyw_list_set) # Frame2 - Kodowanie + liczbawierszy += 1 Ramka2 = TK.LabelFrame(self) Ramka2.config(text = ' Kodowanie wyjściowe ') Ramka2.config(bg = M_tlo) @@ -522,11 +522,8 @@ class Settings(TK.Toplevel): Ramka2.config(borderwidth = M_framewielkosc) Ramka2.grid(row = 2, pady = 5) - Code_var = TK.StringVar() - Code_var.set(MDlcg.read()[1]) - Code_list = TKttk.Combobox(Ramka2) - Code_list.config(textvariable = Code_var) + Code_list.config(textvariable = TK.StringVar()) Code_list.config(state = 'readonly') Code_list.config(width = 93) Code_list.grid(row = 0, pady = 5, padx = 5) @@ -538,6 +535,7 @@ class Settings(TK.Toplevel): SzerokoscPolaWej = 35 WysokoscPolaWej = 8 + liczbawierszy += 1 Ramka3 = TK.LabelFrame(self) Ramka3.config(text = ' Format plików wejściowych ') Ramka3.config(bg = M_tlo) @@ -579,50 +577,214 @@ class Settings(TK.Toplevel): NauczycieleFormat.grid(row = 1, column = 1, padx = 5, pady = 5) NauczycieleFormat.insert(TK.END, nczfmt) + OpisFmt = TK.LabelFrame(Ramka3) + OpisFmt.config(bg=M_tlo) + OpisFmt.config(fg=M_text) + OpisFmt.config(borderwidth=0) + OpisFmt.grid(row=2, pady=5, columnspan=4) + + Opis1 = TK.Label(OpisFmt) + Opis1.config(text='Dozwolone znaki:') + Opis1.config(bg=M_tlo) + Opis1.config(fg=M_text) + Opis1.grid(row=0, columnspan=7) + + Opis2_1 = TK.Label(OpisFmt) + Opis2_1.config(text='K - Klasa') + Opis2_1.config(bg=M_tlo) + Opis2_1.config(fg=M_text) + Opis2_1.grid(row=1, column=0) + + Opis2_2 = TK.Label(OpisFmt) + Opis2_2.config(text='O - Oddzial') + Opis2_2.config(bg=M_tlo) + Opis2_2.config(fg=M_text) + Opis2_2.grid(row=1, column=1) + + Opis2_3 = TK.Label(OpisFmt) + Opis2_3.config(text='N - Nazwisko') + Opis2_3.config(bg=M_tlo) + Opis2_3.config(fg=M_text) + Opis2_3.grid(row=1, column=2) + + Opis2_4 = TK.Label(OpisFmt) + Opis2_4.config(text='I - Imię') + Opis2_4.config(bg=M_tlo) + Opis2_4.config(fg=M_text) + Opis2_4.grid(row=1, column=3) + + Opis2_5 = TK.Label(OpisFmt) + Opis2_5.config(text='L - Login') + Opis2_5.config(bg=M_tlo) + Opis2_5.config(fg=M_text) + Opis2_5.grid(row=1, column=4) + + Opis2_6 = TK.Label(OpisFmt) + Opis2_6.config(text = 'X - Dane nieznaczące') + Opis2_6.config(bg = M_tlo) + Opis2_6.config(fg = M_text) + Opis2_6.grid(row = 1, column = 5) + + Opis2_6 = TK.Label(OpisFmt) + Opis2_6.config(text='Q - Pusta linia') + Opis2_6.config(bg=M_tlo) + Opis2_6.config(fg=M_text) + Opis2_6.grid(row=1, column=6) + + Opis3 = TK.Label(OpisFmt) + Opis3.config(text='Pozostałe znaki oprócz cyfr i pozostałych liter') + Opis3.config(bg=M_tlo) + Opis3.config(fg=M_text) + Opis3.grid(row=2, columnspan = 7) + # Frame4 - Stałe + liczbawierszy += 1 Ramka4 = TK.LabelFrame(self) - Ramka4.config(text = ' Stałe ') + Ramka4.config(text = ' Ustawienia generowania ') Ramka4.config(bg = M_tlo) Ramka4.config(fg = M_text) Ramka4.config(borderwidth = M_framewielkosc) Ramka4.grid(row = 4, pady = 5) + + # Długość liceum i branżowej + RamkaDl = TK.LabelFrame(Ramka4) + RamkaDl.config(bg = M_tlo) + RamkaDl.config(fg = M_text) + RamkaDl.config(borderwidth = 0) + RamkaDl.grid(row = 0, pady = 5, columnspan = 2) + + DlLicLabel = TK.Label(RamkaDl) + DlLicLabel.config(text = 'Lata nauki w liceum') + DlLicLabel.config(width = SzerokoscOpisu + 5) + DlLicLabel.config(bg = M_tlo) + DlLicLabel.config(fg = M_text) + DlLicLabel.grid(row = 0, column = 0) + + DlLicValue = TK.IntVar() + DlLicPole = TK.Spinbox(RamkaDl) + DlLicPole.config(textvariable = DlLicValue) + DlLicPole.config(from_ = 1, to = 10) + DlLicPole.config(width = 18) + DlLicPole.config(bg = M_entrytlo) + DlLicPole.config(fg = M_text) + DlLicPole.grid(row = 0, column = 1, padx = 5, pady = 5) + DlLicPole.delete(0, 'end') + DlLicPole.insert(0, int(MDlcg.read()[5])) + + DlBrLabel = TK.Label(RamkaDl) + DlBrLabel.config(text='Lata nauki w branżowej') + DlBrLabel.config(width = SzerokoscOpisu + 5) + DlBrLabel.config(bg = M_tlo) + DlBrLabel.config(fg = M_text) + DlBrLabel.grid(row = 0, column = 2) + + DlBrValue = TK.IntVar() + DlBrPole = TK.Spinbox(RamkaDl) + DlBrPole.config(textvariable = DlBrValue) + DlBrPole.config(from_ = 1, to=10) + DlBrPole.config(width = 18) + DlBrPole.config(bg = M_entrytlo) + DlBrPole.config(fg = M_text) + DlBrPole.grid(row = 0, column = 3, padx = 5, pady = 5) + DlBrPole.delete(0, 'end') + DlBrPole.insert(0, int(MDlcg.read()[6])) + + + # Domena + DomenaLabel = TK.Label(Ramka4) DomenaLabel.config(text = 'Domena') - DomenaLabel.config(width = SzerokoscOpisu3) + DomenaLabel.config(width = SzerokoscOpisu + 5) DomenaLabel.config(bg = M_tlo) DomenaLabel.config(fg = M_text) - DomenaLabel.grid(row = 0, column = 0) + DomenaLabel.grid(row = 2, column = 0) text1 = TK.StringVar() PoleDomena = TK.Entry(Ramka4) PoleDomena.config(textvariable = text1) - PoleDomena.config(width = 83) + PoleDomena.config(width = 69) PoleDomena.config(bg = M_entrytlo) PoleDomena.config(fg = M_text) - PoleDomena.grid(row = 0, column = 1, padx = 5, pady = 5) + PoleDomena.grid(row = 2, column = 1, padx = 5, pady = 5) PoleDomena.insert(0, MDlcg.read()[2]) + + # Quota + QuotaLabel = TK.Label(Ramka4) QuotaLabel.config(text = 'Quota (MB)') - QuotaLabel.config(width = SzerokoscOpisu3) + QuotaLabel.config(width = SzerokoscOpisu) QuotaLabel.config(bg = M_tlo) QuotaLabel.config(fg = M_text) - QuotaLabel.grid(row = 1, column = 0) + QuotaLabel.grid(row = 3, column = 0) value2 = TK.IntVar() PoleQuota = TK.Spinbox(Ramka4) PoleQuota.config(textvariable = value2) PoleQuota.config(from_ = 1, to = 100000) - PoleQuota.config(width = 81) + PoleQuota.config(width = 67) PoleQuota.config(bg = M_entrytlo) PoleQuota.config(fg = M_text) - PoleQuota.grid(row=1, column=1, padx=5, pady=5) + PoleQuota.grid(row = 3, column = 1, padx = 5, pady = 5) + PoleQuota.delete(0, 'end') PoleQuota.insert(0, int(MDlcg.read()[3])) - print(int(MDlcg.read()[3])) + # Kraj + + KrajLabel = TK.Label(Ramka4) + KrajLabel.config(text = 'Kraj') + KrajLabel.config(width = SzerokoscOpisu + 5) + KrajLabel.config(bg = M_tlo) + KrajLabel.config(fg = M_text) + KrajLabel.grid(row = 4, column = 0) + + KrajValue = TK.StringVar() + KrajPole = TK.Entry(Ramka4) + KrajPole.config(textvariable = KrajValue) + KrajPole.config(width = 69) + KrajPole.config(bg = M_entrytlo) + KrajPole.config(fg = M_text) + KrajPole.grid(row = 4, column = 1, padx = 5, pady = 5) + KrajPole.insert(0, MDlcg.read()[4]) + + + # Przycisk ZAPISZ + def save(): + if MDdlg.ask(1): + motyw = Motyw_list.get() + if motyw == 'Jasny': + motyw = '0' + else: + motyw = '1' + kodowanie = Code_list.get() + uczniowiefmt = UczniowieFormat.get('1.0', 'end') + nauczycielefmt = NauczycieleFormat.get('1.0', 'end') + liclata = DlLicPole.get() + brlata = DlBrPole.get() + domena = PoleDomena.get() + quota = PoleQuota.get() + kraj = KrajPole.get() + SettingsToSave = [motyw, kodowanie, domena, quota, kraj, liclata, brlata] + FormatToSave = [uczniowiefmt, nauczycielefmt] + if MDlfm.edit(FormatToSave): + MDlcg.edit(SettingsToSave) + MDdlg.inf(0) + self.destroy() + else: + pass + PrzyciskZAPISZ = TK.Button(self) + PrzyciskZAPISZ.config(text = 'ZAPISZ') + PrzyciskZAPISZ.config(command = save) + PrzyciskZAPISZ.config(width = 50) + PrzyciskZAPISZ.config(bg = M_przycisktlo) + PrzyciskZAPISZ.config(fg = M_przycisktext) + PrzyciskZAPISZ.config(relief = 'flat') + PrzyciskZAPISZ.config(activebackground = M_przycisktlo) + PrzyciskZAPISZ.grid(row = liczbawierszy + 1, pady = 15) + OknoGlowne = Main() diff --git a/config.cfg b/config.cfg index 3e913f8..59688b9 100644 --- a/config.cfg +++ b/config.cfg @@ -2,4 +2,6 @@ Ciemny motyw(0/1): 1 Kodowanie wyjsciowe: utf-8 Domena: losobolew.pl Quota: 500 -Kraj: Rzeczypospolita Polska \ No newline at end of file +Kraj: Rzeczypospolita Polska +Dlugosc liceum: 4 +Dlugosc branzowej: 3 \ No newline at end of file From 660ec71f7f46e835587a0898bc192d82fc8dcca6 Mon Sep 17 00:00:00 2001 From: Mateusz Skoczek Date: Thu, 6 Aug 2020 18:26:57 +0200 Subject: [PATCH 09/29] 4.0 Alpha (Build 19354) --- .idea/workspace.xml | 4 +- changelog-UC.txt | 5 + .../__pycache__/dataprocess.cpython-38.pyc | Bin 1231 -> 0 bytes components/__pycache__/dialog.cpython-38.pyc | Bin 3979 -> 0 bytes components/__pycache__/format.cpython-38.pyc | Bin 2031 -> 0 bytes .../__pycache__/load_config.cpython-38.pyc | Bin 2295 -> 0 bytes .../__pycache__/load_format.cpython-38.pyc | Bin 3215 -> 0 bytes .../__pycache__/processing.cpython-38.pyc | Bin 1656 -> 0 bytes components/main.py | 791 ------------------ components/processing.py | 57 -- generator.py | 782 ++++++++++++++++- modules/__init__.py | 0 {components => modules}/dialog.py | 0 {components => modules}/load_config.py | 14 +- {components => modules}/load_format.py | 15 +- 15 files changed, 806 insertions(+), 862 deletions(-) delete mode 100644 components/__pycache__/dataprocess.cpython-38.pyc delete mode 100644 components/__pycache__/dialog.cpython-38.pyc delete mode 100644 components/__pycache__/format.cpython-38.pyc delete mode 100644 components/__pycache__/load_config.cpython-38.pyc delete mode 100644 components/__pycache__/load_format.cpython-38.pyc delete mode 100644 components/__pycache__/processing.cpython-38.pyc delete mode 100644 components/main.py delete mode 100644 components/processing.py create mode 100644 modules/__init__.py rename {components => modules}/dialog.py (100%) rename {components => modules}/load_config.py (80%) rename {components => modules}/load_format.py (86%) diff --git a/.idea/workspace.xml b/.idea/workspace.xml index f3fed0c..010d93d 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -84,6 +84,8 @@ + + @@ -106,6 +108,6 @@ - + \ No newline at end of file diff --git a/changelog-UC.txt b/changelog-UC.txt index 83368fe..66df564 100644 --- a/changelog-UC.txt +++ b/changelog-UC.txt @@ -40,3 +40,8 @@ 4.0 Alpha (Build 19353) - Ukończenie ustawień + +4.0 Alpha (Build 19354) +- Przeniesienie głównego modułu do folderu nadrzędnego zamiast pliku inicjacji +- Zmieniony sposób ładowania modułów +- Ulepszona obsługa wyjątków dla ładowania modułów diff --git a/components/__pycache__/dataprocess.cpython-38.pyc b/components/__pycache__/dataprocess.cpython-38.pyc deleted file mode 100644 index a1976a87b367aae47b82cf8a57464b75ebf7874c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1231 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 Bv}FJQ diff --git a/components/__pycache__/load_config.cpython-38.pyc b/components/__pycache__/load_config.cpython-38.pyc deleted file mode 100644 index 974b39d16233770f8a408c55d93dad60b0b475ca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2295 zcmZuy&2Jk;6rb4-d*e8%)6g^tsH{|hYD9xm0fGo2q$EX9V%oYispS%Fb|z-+^^Td{ zt>cw*X<90Afh+1IRk?8H%8lg4U$93wMdApTa^bzPlUR*+wQqLb$IR^e{odQJCMI$O zuEjt8yZ`q%A%Edy__AQ~2|OVK!AO-b$~2!=wIr|J^s2E=f(3(cvze3EDZ!g(n7&70 z%raw-RxPgWk1_fTH1|lLZ4hR%%p+3Gu`IJ-9A{%J2ViQ`a-)~%%N{f z9R3zKu+`y1hl!4J@Z`idcSA6H>+s>hPb^-tS4*XxQmM2Uzlys!u6Ot}@EZ1N`OfNX z+kp@7bQ_0H51zOb4>Pz-7!kK+c*3v2lFUY=NA@VAOHD>fQgXpAb$STu8ZU+V>fi+^i?}pYhPuF9aI^ETiSy)q_ zBR##Dl|PS&J{XG5o<=7Hb(*X?4YKa|^#2U`TW9>oPWA~uF1 z@~07u0AnZ0M_EX9N_UPB28rTG1Cf|Ru3~ zxx0=Z^HnK@OwF|w=GRl5O9?(!$fL*$>S1bv0?qC>+-+9pS40@`cEEu*z@)(A>)l4^ zdZNQ6?)wXoufXu{7a&%bYF`F?MS2f-?Xw`@wVP5jcoPa`z|`M=?bJ@n&W5_HxuVq; z0aRL8^M%7UT~!7^Zd)f-QKf7!6^!-fF01J?mW#$ORe_M{SJ=MN?`a03bd*l|mO`ZeLI70J6 z$g|}a`2|Q0_Jjp@yRI-#sZ41u(Uxp!z;TOKYKS%R&A~cPQ zDx1n;DhuTIj3uiq)(Fd(vgAfs#+4;M!ZM*OlOrtWl%>$OAjKVZY8VnenE z903lzSRdd%!25hzUW7y`n3L&c_#p&c=;BegbEWjoVi6FZ19NiWqzJJw_p5NghQwMC z@W*msio{N`w_*|D3>!g`xgnj#l6{1{NeU}|To<8h`<~0=7WiN{Po|I6Tha*hY_@lj`Si9#8Nauc2y@UgSK)Je&~eQ;Uv~UYJG%1U!Q-BO*lloF=Lti OqgrsVLf(4Yn))9Y1rfdg diff --git a/components/__pycache__/load_format.cpython-38.pyc b/components/__pycache__/load_format.cpython-38.pyc deleted file mode 100644 index 4cfe6fa39235d9b03975bba33e91107153adb7e8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3215 zcmbVOO>7&-6`q-0E>|l`k^dt{b~kmKnogtAjvfENaGh9^?KoC!Q?X=1hOp*at1D8w z)NpA^#4-i^gZLJmiUhvUXfL@Y1=>Rn1u6kO6-AD72nrNE5a_iR7ijz5F3Au9A-LVe zyxEzZ`R2|0-pqR+jf_|fpT~dy*WKDa#{Nc){>Q||MJ!c70j$7)gDCTYXk_9qt)R~{ ze^O^|s$|I-&ggXw&>nHD)1W`%1=AI+A>hxk=MMA50)rG7k6FQjG?-Wq!w^_lXF%jx zcDVKom$Hu7mtEhLP8i5b`PcV%_Ou}@Cs?CJH2E4@|M51lgpxwm0ocS5&TZP@uz zP;9s*>@3&94f{my_*wfcds)_?;knz7;|}}@Wydb$Z`k?ZUQl-HmURR>uLn@uerPuw z+1Ppl4QFNRNpZujl)aMYuG;qlx$1;99N9+o*lf)q>A7~pcgk+V3;e)djS*^gMFz{# zS*_Ve!E?&N@?>QrYth)%VEb#|!6mjIIuO*ItxtMmt!n|;_qHEyeGavA_H-_{p3CKq z*IuJloT9h=%=cF8>DjsITegE6u5YYtKim4GC`g&kGlcaaESIoUeI5DYO@T#=v`t;E zfN1Iw2W^~9vAdaOD$=9WY1Y)&4mJ)a{VfLKIgTjlaw_D+UDH5z&*&@^$nM7pyP?+7 zAw_IPOjg2F3)!ZYZ-LPici5)BwvzDNI2h@OnMO>rq{+_)V1CnwIn=@2dI?OcgZYm> z%=3w!n_M32Lmf^~v3Fue+=*D%OoEG}X9gla?n55wAwPHtC!NLrt^sMr5LeC(y!VWBmXt8F&*IUG4Cs()sS58(rS()9~$% zhPs^FQA4Att(g(s+$U$}6q6ipAH~<(!&uKKeyvR%Z6ogeu$_+U**3)$#jm#M&Qkn? zHbtR!hQtVoQ4+79Q2e;!Clr5M@so<5Qv4moPb+>#@w1BO6rWdoTJc+o&nbRK@sAY0 zq4>Ls=M|q)9M`|B_!Y(9SG=J3CB<(m{+{C3+hd5GO{-KfsQIDNJQUV#r(AQVr3|D> zU7v#TveH~BmB{Cn?yh@bcBr7=ave}c5)2AvC8$WJ?z@Qd3%6KOnOd>odqLfE@2`eZ zBmGkdfqs$iqlPb$ZZmQGdK1B` ze?`H#j<10w!!05BZmdVSMQuSvU~5p%7>+S|W5#gI|K79rd$ULfTJiruyTAoEQP;74 zJy1h=5%*Z6jdmZ5hm5=|j*d_9qmcV0B#!^^dG!kIvRU7x`4jt(zbS@;ieB+ek4qN;E0%2n+6B}K`VVk$o5z-Z7 zLZpK+j%mjMhF=)O4zOm5uwe)Z9BUep(K4eLZN5VY5@F?;uY;CPC>frW)3}HBfAE=+ z@8YL?j|A;lPLW71wEA3jNWM?)t0XRxm?3eQL?0t7f#<7KU3#ILO~n;Dvv}zN(Fa4z&8A+xZtE~1=D*Qe7QmKlhTQcSOm0GCuy5oiN3{FipO+FLD m_h}gGN*<-m^OPEw-3w&D*sb*arj{saqRL|#^OtPI-1T1)qw;J3 diff --git a/components/__pycache__/processing.cpython-38.pyc b/components/__pycache__/processing.cpython-38.pyc deleted file mode 100644 index 51255a34bd6c7b8b9c96ce794322cd6014bbda69..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1656 zcmb_d&2Jk;6rb4-d%gBL&WA7&h5EoDsjM_Yt&l2&3UP{}KvG&2s*wW>I}@|@dS|n{ zsq5A3Eu6`T3m5yynLh#L$Um?rBu@SV;E)TvS=$his^Y+m=Dpwhm^be?yIwzW92>#- z<-dK2Vf$Rp}Lqpg(o#l@MMih#C4F&JI^;0GRm;Hu*mV+{bbgmz%X-$SH_G7Xk4 zQ3svlqhB*a8Vs)kj@AHa>nJlp>!3@rRM=%^WYA7l0l&Ep`;Im;TUyd)r6zh8WtOb4 zva}0ak$}`L@Rv_I(o(d(RaLD7jj=Q*IIBV(3p_ROXtFfHV|{`OT$$k}lGP?+l_ts@ z=`g4)YhZ(~$SO}TSm4R(^CRoXbCvP@9$&%pf8x0G3T~J+Ali_aErVW8%yCIJ*h*3!TN9+7=S2T* zBU_Og@LU6)E8_}0!PO>GcXO_t+yIX~K^T3G?xIi8BlHv%HI}c{Se5#`#_GRnd?i<} z)u@hZiKEV72ww2DgXy0zA`I{8RK37CnksyWTRGmE>Ht&czAs|&web0HEA%gM`x4(v zjrQH`?NbZl?Nc}97iaDCW_vLnrQA(Np@>7_dkJkl6n@;N>8*A^A$jC_++U=&+ePxV zT>Ca3P|x3T zZ*nJwrqe#%eG!T%Awq5CFZ_QV@iy@fyarp7kKdX@JGZtpy|+y z`+_tUs3VF(G{-N9Ljvlif!9~NN5uSC^t^zWAFznW#GD;injchKv{kgRXmTE`gud6G z<=YC*+&`k|4R~t5?}OOA*F6k)C-R>2?p_e^?!!nt Date: Thu, 6 Aug 2020 18:29:52 +0200 Subject: [PATCH 10/29] 4.0 Alpha (Build 19355) --- .idea/GeneratorCSV 3.1.iml | 4 +- .idea/workspace.xml | 3 +- changelog-UC.txt | 7 +++ generator.py | 28 ++++----- modules/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 164 bytes modules/__pycache__/dialog.cpython-38.pyc | Bin 0 -> 4266 bytes .../__pycache__/load_config.cpython-38.pyc | Bin 0 -> 2665 bytes .../__pycache__/load_format.cpython-38.pyc | Bin 0 -> 3541 bytes modules/dialog.py | 42 ++++++++------ modules/load_config.py | 18 +++--- modules/load_format.py | 54 ++++++++++++------ instruction.txt => readme.txt | 0 12 files changed, 98 insertions(+), 58 deletions(-) create mode 100644 modules/__pycache__/__init__.cpython-38.pyc create mode 100644 modules/__pycache__/dialog.cpython-38.pyc create mode 100644 modules/__pycache__/load_config.cpython-38.pyc create mode 100644 modules/__pycache__/load_format.cpython-38.pyc rename instruction.txt => readme.txt (100%) diff --git a/.idea/GeneratorCSV 3.1.iml b/.idea/GeneratorCSV 3.1.iml index 9c88284..ed5ea5b 100644 --- a/.idea/GeneratorCSV 3.1.iml +++ b/.idea/GeneratorCSV 3.1.iml @@ -1,7 +1,9 @@ - + + + diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 010d93d..27fa3ca 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -86,6 +86,7 @@ + @@ -108,6 +109,6 @@ - + \ No newline at end of file diff --git a/changelog-UC.txt b/changelog-UC.txt index 66df564..def0dfb 100644 --- a/changelog-UC.txt +++ b/changelog-UC.txt @@ -45,3 +45,10 @@ - Przeniesienie głównego modułu do folderu nadrzędnego zamiast pliku inicjacji - Zmieniony sposób ładowania modułów - Ulepszona obsługa wyjątków dla ładowania modułów + +4.0 Alpha (Build 19355) +- Naprawienie przycisku instrukcji +- Zmiana nazwy pliku instrukcji na 'readme.txt' +- Naprawienie przycisków filedialog +- Zmiana zmiennej treści błędu na string +- Ulepszona obsługa wyjątków dla ładowania pliku konfiguracyjnego, formatu i 'readme.txt' \ No newline at end of file diff --git a/generator.py b/generator.py index 5d9ad7c..7aec5bc 100644 --- a/generator.py +++ b/generator.py @@ -51,7 +51,7 @@ except ModuleNotFoundError: except Exception as exc: print('Wystąpił krytyczny błąd!') print('Nieznany błąd podczas ładowania jednego z modułów programu (dialog.py). Nie można załadować programu.') - print('Treść błędu: ' + exc) + print('Treść błędu: ' + str(exc)) print('Kod błędu: E00x0010') wait = input('Naciśnij ENTER aby wyjść') SS.exit(0) @@ -67,7 +67,7 @@ except ModuleNotFoundError: except Exception as exc: print('Wystąpił krytyczny błąd!') print('Nieznany błąd podczas ładowania jednego z modułów programu (load_config.py). Nie można załadować programu.') - print('Treść błędu: ' + exc) + print('Treść błędu: ' + str(exc)) print('Kod błędu: E00x0020') wait = input('Naciśnij ENTER aby wyjść') SS.exit(0) @@ -83,7 +83,7 @@ except ModuleNotFoundError: except Exception as exc: print('Wystąpił krytyczny błąd!') print('Nieznany błąd podczas ładowania jednego z modułów programu (load_format.py). Nie można załadować programu.') - print('Treść błędu: ' + exc) + print('Treść błędu: ' + str(exc)) print('Kod błędu: E00x0030') wait = input('Naciśnij ENTER aby wyjść') SS.exit(0) @@ -184,7 +184,7 @@ class Main(TK.Tk): Pole1.grid(row = wiersz, column = 1) def Pole1BrowseDialog(): - Pole1Browse.filename = TKfld.askopenfilename(initialdir = "C:/", title = "Wybierz plik tekstowy z danymi", filetypes = (("Pliki txt", "*.txt"), ("Wszystkie pliki", "*.*"))) + Pole1Browse.filename = TKfld.askopenfilename(title = "Wybierz plik tekstowy z danymi", filetypes = (("Pliki txt", "*.txt"), ("Wszystkie pliki", "*.*"))) Pole1.delete(0, 'end') Pole1.insert(0, Pole1Browse.filename) @@ -217,7 +217,7 @@ class Main(TK.Tk): Pole2.grid(row = wiersz, column = 1) def Pole2BrowseDialog(): - Pole2Browse.filename = TKfld.askopenfilename(initialdir = "C:/", title = "Wybierz plik tekstowy z danymi", filetypes = (("Pliki txt", "*.txt"), ("Wszystkie pliki", "*.*"))) + Pole2Browse.filename = TKfld.askopenfilename(title = "Wybierz plik tekstowy z danymi", filetypes = (("Pliki txt", "*.txt"), ("Wszystkie pliki", "*.*"))) Pole2.delete(0, 'end') Pole2.insert(0, Pole2Browse.filename) @@ -249,7 +249,7 @@ class Main(TK.Tk): Pole3.grid(row = wiersz, column = 1) def Pole3BrowseDialog(): - Pole3Browse.filename = TKfld.askopenfilename(initialdir = "C:/", title = "Wybierz plik tekstowy z danymi", filetypes = (("Pliki txt", "*.txt"), ("Wszystkie pliki", "*.*"))) + Pole3Browse.filename = TKfld.askopenfilename(title = "Wybierz plik tekstowy z danymi", filetypes = (("Pliki txt", "*.txt"), ("Wszystkie pliki", "*.*"))) Pole3.delete(0, 'end') Pole3.insert(0, Pole3Browse.filename) @@ -291,7 +291,7 @@ class Main(TK.Tk): PoleKonta.grid(row = wiersz, column = 1) def PoleKontaBrowseDialog(): - PoleKontaBrowse.filename = TKfld.askopenfilename(initialdir="C:/", title="Wybierz plik .csv dla poczty", filetypes=(("Pliki csv", "*.csv"), ("Wszystkie pliki", "*.*"))) + PoleKontaBrowse.filename = TKfld.askopenfilename(title="Wybierz plik .csv dla poczty", filetypes=(("Pliki csv", "*.csv"), ("Wszystkie pliki", "*.*"))) PoleKonta.delete(0, 'end') PoleKonta.insert(0, PoleKontaBrowse.filename) @@ -324,13 +324,13 @@ class Main(TK.Tk): PoleOffice.grid(row = wiersz, column = 1) def PoleOfficeBrowseDialog(): - PoleOfficeBrowse.filename = TKfld.askopenfilename(initialdir = "C:/", title = "Wybierz plik .csv dla poczty", filetypes = (("Pliki csv", "*.csv"), ("Wszystkie pliki", "*.*"))) + PoleOfficeBrowse.filename = TKfld.askopenfilename(title = "Wybierz plik .csv dla office365", filetypes = (("Pliki csv", "*.csv"), ("Wszystkie pliki", "*.*"))) PoleOffice.delete(0, 'end') PoleOffice.insert(0, PoleOfficeBrowse.filename) PoleOfficeBrowse = TK.Button(Ramka2) PoleOfficeBrowse.config(text = '...') - PoleOfficeBrowse.config(command = PoleKontaBrowseDialog) + PoleOfficeBrowse.config(command = PoleOfficeBrowseDialog) PoleOfficeBrowse.config(bg = M_przycisktlo) PoleOfficeBrowse.config(fg = M_przycisktext) PoleOfficeBrowse.config(relief = 'flat') @@ -366,7 +366,7 @@ class Main(TK.Tk): PoleKontaEksport.grid(row = wiersz, column = 1) def PoleKontaEksportBrowseDialog(): - PoleKontaEksportBrowse.filename = TKfld.saveasfilename(initialdir = "C:/", title = "Zapisz", filetypes = (("Pliki csv", "*.csv"), ("Wszystkie pliki", "*.*"))) + PoleKontaEksportBrowse.filename = TKfld.askdirectory(title = "Zapisz w...") PoleKontaEksport.delete(0, 'end') PoleKontaEksport.insert(0, PoleKontaEksportBrowse.filename) @@ -398,7 +398,7 @@ class Main(TK.Tk): PoleOfficeEksport.grid(row = wiersz, column = 1) def PoleOfficeEksportBrowseDialog(): - PoleOfficeEksportBrowse.filename = TKfld.saveasfilename(initialdir = "C:/", title = "Zapisz", filetypes = (("Pliki csv", "*.csv"), ("Wszystkie pliki", "*.*"))) + PoleOfficeEksportBrowse.filename = TKfld.askdirectory(title = "Zapisz w...",) PoleOfficeEksport.delete(0, 'end') PoleOfficeEksport.insert(0, PoleOfficeEksportBrowse.filename) @@ -443,11 +443,13 @@ class Main(TK.Tk): def InfoOpen(): try: - x = open('.\instruction.txt') + x = open('readme.txt') except FileNotFoundError: MDdlg.err(4) + except: + MDdlg.err(22) else: - OS.system("notepad .\instruction.txt") + OS.system("notepad readme.txt") PrzyciskINFO = TK.Button(PasekDolny) PrzyciskINFO.config(text = 'Instrukcja') diff --git a/modules/__pycache__/__init__.cpython-38.pyc b/modules/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..624116c0380c4bfb2728eb58cd17626ae5135615 GIT binary patch literal 164 zcmWIL<>g`kf=OO~V?p#|5P=LBfgA@QE@lA|DGb33nv8xc8Hzx{2;!?7kn3s{&ryk0@&Ee;!qs2#|P&p^xo0B36{DgXcg literal 0 HcmV?d00001 diff --git a/modules/__pycache__/dialog.cpython-38.pyc b/modules/__pycache__/dialog.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dea199b5a4766aae7e48700fe86c01fcb2a6d5cd GIT binary patch literal 4266 zcmcgv&2QX96u0*y**Hll{h$O2ol-zGg4OP}6oC+>v?&UZG-3;tR;j|oo@^X@J*%;Y zjaTIoHR2!OODagI#D#OO+?xF-_R6V84jhsAjlGH1?JnAt(%P~;@6F76KfjNeH($-p z8XB(MzyH1c%`r{;OA-B74#dZJg5Pybv$dL5*J?V`cQdwbXX=^zx~|1kHbv!9R6a#b z*pq$p3aP%SRG*Qerc=~RikeMPM^e;tDe7p7dOk%ROHs#D)QJ?%lPT(j6m=>^y_llr z_GQ9~S(b$rU+UxJSPq<*W1FhhY1Oh{v0wG_wF!LBc$1AnZ3;YMpS`Qq4Er_v9KO@` z>-HP?&e*S3HST;{iTrfQuMx;_IzfayFbs_Dx!#a0k20Cqzl3I0{RQVeBSj#ZZfw{fIDEF>vDdWxOFZXG?e6g(kQMs!Y5s;JX=woEt&F%#~nLZ zf(odfLk}z*kF?Mst+wwxe9$Ty3q4mO_7u+rleiuYl+&LWPJGPWWI;#}bt+KGJz(V$ zET(cS6>*0SjN8aLicA_4+yztq{M5nuwB2$7myU_;qA7oR>e!ry!+d94Xz!Zx$EOYr z%b??r>(EPPyl9RPZObyiqMa-)13i;uRvBw%QGPkv-wu=MQ{+{I{PLXryg=?3iUKO$?r z0*KpIn+7D=xT#tRM=7ul>M88HpN%7S(8g!(yEop%zZgf{u!=+B>lGMs1p=3OBkJ7q z)F$1gO(&o}KWkh=T?&pHK71e|YlE~K4)sZccm8Cka#fKRHE3N`U`-UBEqtYr)&`Gv z3@`<+3)+zKZ78^kS`fQ_leJKyS{oKac`SIli2`|V2cqh#yyh(!m|d17XP}BIB$flsY+qSF)Kr zQnZ=x>hsoCp0rPV_vZqe4B06CKnUHl2A zYyN|k8$P=xoNw96g734HWx+S$hG^fUsGwF3&Lac|l9dKd3mywrlEXo<8FjPJdlSL} zC05gq>NB{7I-aJ?q%i04#AYg@5o)_SOqJO;RcxMw%ECNvJ!YQVQq5TKlX7p9#wHP2 zh%xg*iLDis2^I*{IN=&L=y*8z0#Wsk4Jb6u_ubaj2+h_!8xTc>_xM$EBj{7uv z`*GjT2%Iphnw`a?oCuVY%TcJJ>*jScn?ReUfy-Ts`WExL`W0X*TnXI4kT8@%Ne|Ld zJ(m$jK%2AO$<;3LhrE{YZH#P~nj~nbVJqbLTJ~kD(jWh5XD+VX9F04~0BJPyhe` literal 0 HcmV?d00001 diff --git a/modules/__pycache__/load_config.cpython-38.pyc b/modules/__pycache__/load_config.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9d50b02de85fa98c1f744470895c81d4111165d9 GIT binary patch literal 2665 zcmb_e&u<$=6rR~#+Z)G8oQ5W8DPpQ3s1XfLMJ0$(g_NYIR1IxiNH8wZW@l>FUhlfI zyLG&BE={W*L5KsW5=zw*XE<^r`3E@ewOsrMAR#0q-WzWcD@|{BwQqjBdGlu9``$PH zY-}t~;CJ@j(sdV zA+6Ativr5yqW6^|3HwwG#VtM`p zc7fGp#A8S7Jya8*~-13-UHb2 zY_x_3j$pB8xgvIa&u0z6JyG{r%v#F*@4s|dTl#frwIX(Yu<=~GTbg5N*z)&&^DH>x z-UEyK9c%aT!Q$v7Y<|t>%Gr-RT42lN@@Ba_KOen{ZY|r{d*V3_w!CtC`4+PvXq|3j z@5%1ts5Lkioa*4LtnG6 z@HS}T*^u-AgPb;XIUPbZY|||Y_@5r&KGb$}uBW&L^u^_n_6Rplfz6i2jhggpv2u0fyfK0%v=ZMvA zBd{I6BcuUa{!HX6(EN23#PUMzQ%@{O=f0?2^*m9#A^nDELK&w80ku&(AO!}jP{W#W zMNo5ni?7?NLZMia(M5E=sK%FI=U@Q@(KK3sCrgVu_GZ(5mJZJsQ~hu&r@`9BE%tvH z9OPkz!JnWL7ee4+z!UQBMT3MCxD&vMkHVjpW}-bbw~xu%8bNDdXYHF$wrG#;7*EMg zh6C2Nv^v?+d)gAYGrMK<^qp+a08REO<$xgE^WH|7>*>h6J$;pEz_mBYE%FSqjV*93 z=G|QRr!ZGb17zQ=4-$`&PpC|2KGqhP#E&YOiQ%^)$CFnaSFHHq6+iO$GNx2cfL<2S z#*o6|NhSbp43n(Y1_Q2wRLV#MN}_|0Bs=gY5l$-ED%AslWF=n%VZj@m20>IB#_2dF zKr29NP?Ju>tJb|h0V80d!aoNf9uNRrQw1nMOaqobra^-=Tuei*?-+bP4Sx^K?Wf_- zH3GrbdKv`S2(wVu8Wzk@SFJw0MD5eDn$jAT+cx5xAfjPUcp0fm|BrD5I%(hISajbY7y=@VE>jM zc01?G@6MM15SW(uqe#N>kBtu&~#S#1^hTu*Zf+BbW1Occa6YN9Kpr?nPY0v-^--30e z$t1VF&V74*9nR1V60K56aw!!_WK(t0WD5UK94VqC)0T=o9j_gQiP5p3f()?<{Fb JQS%*h>OU3fdmR7( literal 0 HcmV?d00001 diff --git a/modules/__pycache__/load_format.cpython-38.pyc b/modules/__pycache__/load_format.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..df0cdcc0b34098f88642af91f77bfa117e5c9379 GIT binary patch literal 3541 zcmb_eO>7&-6`t8$E>{#qQ??UDikxohG&R#ir4&1k4MTNeQMTh)aYZGPSp~w1Gf`F~ zcd6kr6cGb zYg#Em$trPvsS&PMQldl&zC6jaJsOsYxYWbaV97mN8sSPe#ppvAahEvjFn1K@enm+s zO^H80Fx!Y{kMJ>`-lG#FZm0e`+A6s~m{LmrEWL)v${Jy#Ed4PlW!Mkt5AK)SX1!`xY+f@za7E4XnlQ2l>XD@!l{&Mns=L*khW2W^;T_zs+W=4z zZLeMSowm7ocn^@CZ?544o14C4Rk?4wj%#jn=I|}o^vznh?C*cuGV8+K5>~Bgo{NBI z>h0W&i4$w?;qM#^R6D$9F}GzMe9|A>d2v(VAul;oq+;VE5}CD8i=B0luAZi1=Ua#Bq1D0j)ewiA1=KNV{uKqKf??SR*mkS5JVIin0DYfHgOu*Jy;bPla+U+H|N{IzofdFAu>R>*Fmy0bZt$ z7YxBW(Z~DE6X0e0c#nqQjrZ~XaU33So&;`4LJ{^L0s*m^O6W|@$R}sVbKON(#|C4{z@5sLDQ2w9l;~mRC zdzwwKXV@g0V$ZT^c7{F2zQ;`VJo`R7%U)nVU@x*CvU4oQUSdCDFSA$J4Er(5vspH` zb*5wZF9rsj#cALLnb9UlMc-G&=SMc%S9d;#^Ec?{ulDu&`*C)I6RGYP0i4N3Du9^5 z2*i!^ODmEH3b%u#yS-5;7bU&b!;YRF_P*0ghUHSv2+Ny2yi+87yN5Bgmqsy$B7@=t z2uWupos;xcNzY4qLDJVGy(sDHl3tQDFX^hJ1xc4BU6J&zr0+_4N76SWElRo|DXf28 z(i@V#C22|0Ym&Yv>6?r z`YpHWI=tjq75fV!=3&A=Z-OXXUVq2o3&Q@0uitbWzJ5oz+q?oXG>%FTpw{~l*@Te1 zj)AMn8|ziqVjGcu5V7RwHB@Xd441C~a^o)`2-V=#6^*88N>%7dD0P~GzBKkhPiAP3 z-XD{V$9#t#4Oc|`)8BnA%=*@f+B{XL0lH>G?PEQlu2XRpc3H-)9z93~I#w94ehn2r z?Lp9ju!G6joJKqf3D!#k*F1!$F!^!@`obUtX;W#9;A{w6$}etoXh6H#LjpkxLRN=v zk&Y5jIBcgO94cKsP~ngMFihrlHoYWE2FjiSp-ovO3Z!E{CClV9I5b#7I)=x`So$VC zvmVn3{?bc;#yw0fBo*I`rLHu1-3rsO`IMxom7F0px6U0ozGzqZlItykFS7!=p11(B z1adDmf!>>FKo;dm-KxU`7FsH&iOV=ZMYp|#c+!s^Jm`41L)-rmDqtZRU}`Xpmg<5fx)hUL21&tvWs;!fcMMNq zYC!iC@dw~a4O0X)=1O~#-EDo9*a<-Zg?S8NuW^W@)rY9F3{*b(dZei?FZWzu{RF zZ$MSNi2`>V-h!flwdlrct>i|;Eo{Gq;wp;kC~lw_pljQ;9hqne+vB-p$V(}?8#zTp z5Rz6Ji%$27TeLpnYcf696mKIW-YbuJgv*TC-d~}w@d60E3=BF2#0?S}k%`CjG+Y#7 z8OG(tWpZPKx#f)wsW#dTaS=LYGQN0(0Jdd8%I0r&|+DPCfk%9e0FJ28; Oi{y0L_&GUaoca#}@J check > 1: error = int('x') - except ValueError: + except: MDdlg.err(2) # Linia 2 (utf-8) @@ -71,25 +71,25 @@ def CheckConfig(settings): try: if settings[1] not in DostepneKodowanieWyjsciowe: error = int('x') - except ValueError: + except: MDdlg.err(3) # Linia 4 (int) try: x = int(settings[3]) - except ValueError: + except: MDdlg.err(17) # Linia 6 (int) try: x = int(settings[5]) - except ValueError: + except: MDdlg.err(18) # Linia 7 (int) try: x = int(settings[6]) - except ValueError: + except: MDdlg.err(19) @@ -99,6 +99,8 @@ def read(): check = open('.\config.cfg') except FileNotFoundError: MDdlg.err(0) + except: + MDdlg.err(20) else: with open('.\config.cfg', 'r') as cfg: config = cfg.read().split('\n') @@ -117,6 +119,8 @@ def edit(settings): check = open('.\config.cfg') except FileNotFoundError: MDdlg.err(0) + except: + MDdlg.err(20) else: SettingsToSave = [] SettingsToSave.append('Ciemny motyw(0/1): ' + str(settings[0]) + '\n') diff --git a/modules/load_format.py b/modules/load_format.py index 848cfbb..cc65371 100644 --- a/modules/load_format.py +++ b/modules/load_format.py @@ -36,7 +36,7 @@ except ModuleNotFoundError: except Exception as exc: print('Wystąpił krytyczny błąd!') print('Nieznany błąd podczas ładowania jednego z modułów programu (dialog.py). Nie można załadować programu.') - print('Treść błędu: ' + exc) + print('Treść błędu: ' + str(exc)) print('Kod błędu: E00x0010') wait = input('Naciśnij ENTER aby wyjść') SS.exit(0) @@ -55,14 +55,16 @@ def CheckFormat(Read, format): poprawne = True check = True while check: + # Uczniowie uczniowiefmt = '' for x in format[0]: uczniowiefmt += x + # Sprawdzanie pustych linii try: if format[0].count('') > 0: error = int('x') - except ValueError: + except: MDdlg.err(11) if Read: SS.exit(0) @@ -70,10 +72,11 @@ def CheckFormat(Read, format): poprawne = False break + # Sprawdzanie ilości liter K try: if uczniowiefmt.count('K') != 1: error = int('x') - except ValueError: + except: MDdlg.err(6) if Read: SS.exit(0) @@ -81,10 +84,11 @@ def CheckFormat(Read, format): poprawne = False break + # Sprawdzanie ilości liter O try: if uczniowiefmt.count('O') != 1: error = int('x') - except ValueError: + except: MDdlg.err(7) if Read: SS.exit(0) @@ -92,10 +96,11 @@ def CheckFormat(Read, format): poprawne = False break + # Sprawdzanie ilości liter N try: if uczniowiefmt.count('N') != 1: error = int('x') - except ValueError: + except: MDdlg.err(8) if Read: SS.exit(0) @@ -103,10 +108,11 @@ def CheckFormat(Read, format): poprawne = False break + # Sprawdzanie ilości liter I try: if uczniowiefmt.count('I') != 1: error = int('x') - except ValueError: + except: MDdlg.err(9) if Read: SS.exit(0) @@ -114,10 +120,11 @@ def CheckFormat(Read, format): poprawne = False break + # Sprawdzanie ilości liter L try: if uczniowiefmt.count('L') != 1: error = int('x') - except ValueError: + except: MDdlg.err(10) if Read: SS.exit(0) @@ -125,10 +132,17 @@ def CheckFormat(Read, format): poprawne = False break + + # Nauczyciele + nauczycielefmt = '' + for x in format[1]: + nauczycielefmt += x + + # Sprawdzanie pustych linii try: if format[1].count('') > 0: error = int('x') - except ValueError: + except: MDdlg.err(12) if Read: SS.exit(0) @@ -136,14 +150,11 @@ def CheckFormat(Read, format): poprawne = False break - nauczycielefmt = '' - for x in format[1]: - nauczycielefmt += x - + # Sprawdzanie ilości liter N try: if nauczycielefmt.count('N') != 1: error = int('x') - except ValueError: + except: MDdlg.err(13) if Read: SS.exit(0) @@ -151,10 +162,11 @@ def CheckFormat(Read, format): poprawne = False break + # Sprawdzanie ilości liter I try: if nauczycielefmt.count('I') != 1: error = int('x') - except ValueError: + except: MDdlg.err(14) if Read: SS.exit(0) @@ -162,10 +174,11 @@ def CheckFormat(Read, format): poprawne = False break + # Sprawdzanie ilości liter L try: if nauczycielefmt.count('L') != 1: error = int('x') - except ValueError: + except: MDdlg.err(15) if Read: SS.exit(0) @@ -173,12 +186,14 @@ def CheckFormat(Read, format): poprawne = False break + + # Sprawdzanie poprawności znaków NiedozwoloneZnaki = ['1','2','3','4','5','6','7','8','9','0','W','E','R','T','Y','U','P','A','S','D','F','G','H','J','Z','C','V','B','M'] try: for x in NiedozwoloneZnaki: if x in nauczycielefmt+uczniowiefmt: error = int('x') - except ValueError: + except: MDdlg.err(16) if Read: SS.exit(0) @@ -186,7 +201,8 @@ def CheckFormat(Read, format): poprawne = False break check = False - return poprawne + if not Read: + return poprawne @@ -196,6 +212,8 @@ def read(): check = open(r'.\format.fmt') except FileNotFoundError: MDdlg.err(5) + except: + MDdlg.err(21) else: with open(r'.\format.fmt', 'r') as fmt: fmt = fmt.read().split('\n\n') @@ -217,6 +235,8 @@ def edit(format): check = open(r'.\format.fmt') except FileNotFoundError: MDdlg.err(5) + except: + MDdlg.err(21) else: FormatToSaveX = [] for x in xformat: diff --git a/instruction.txt b/readme.txt similarity index 100% rename from instruction.txt rename to readme.txt From bf5540b7adf569ca549ec16c90bf5ac4a22b8c13 Mon Sep 17 00:00:00 2001 From: Mateusz Skoczek Date: Thu, 6 Aug 2020 18:33:32 +0200 Subject: [PATCH 11/29] 4.0 Alpha (Build 20016) --- .idea/GeneratorCSV 3.1.iml | 13 - .../inspectionProfiles/profiles_settings.xml | 6 - .idea/misc.xml | 7 - .idea/modules.xml | 8 - .idea/vcs.xml | 6 - .idea/workspace.xml | 114 -- assets/icon.ico | Bin 0 -> 112027 bytes assets/other_images/icon.png | Bin 0 -> 4157 bytes assets/tab_icons/generate.png | Bin 0 -> 623 bytes assets/tab_icons/icon.png | Bin 0 -> 4157 bytes assets/tab_icons/info.png | Bin 0 -> 1125 bytes assets/tab_icons/link.png | Bin 0 -> 631 bytes assets/tab_icons/merge.png | Bin 0 -> 882 bytes assets/tab_icons/settings.png | Bin 0 -> 1056 bytes changelog-UC.txt | 9 +- config.cfg | 7 - format.fmt | 5 - generator.py | 1052 +++++------------ modules/__init__.py | 0 modules/__pycache__/__init__.cpython-38.pyc | Bin 164 -> 0 bytes modules/__pycache__/dialog.cpython-38.pyc | Bin 4266 -> 0 bytes .../__pycache__/load_config.cpython-38.pyc | Bin 2665 -> 0 bytes .../__pycache__/load_format.cpython-38.pyc | Bin 3541 -> 0 bytes modules/dialog.py | 105 -- modules/load_config.py | 135 --- modules/load_format.py | 249 ---- readme.txt | 107 -- src/__pycache__/vars.cpython-38.pyc | Bin 0 -> 2402 bytes src/vars.py | 102 ++ 29 files changed, 390 insertions(+), 1535 deletions(-) delete mode 100644 .idea/GeneratorCSV 3.1.iml delete mode 100644 .idea/inspectionProfiles/profiles_settings.xml delete mode 100644 .idea/misc.xml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/vcs.xml delete mode 100644 .idea/workspace.xml create mode 100644 assets/icon.ico create mode 100644 assets/other_images/icon.png create mode 100644 assets/tab_icons/generate.png create mode 100644 assets/tab_icons/icon.png create mode 100644 assets/tab_icons/info.png create mode 100644 assets/tab_icons/link.png create mode 100644 assets/tab_icons/merge.png create mode 100644 assets/tab_icons/settings.png delete mode 100644 config.cfg delete mode 100644 format.fmt delete mode 100644 modules/__init__.py delete mode 100644 modules/__pycache__/__init__.cpython-38.pyc delete mode 100644 modules/__pycache__/dialog.cpython-38.pyc delete mode 100644 modules/__pycache__/load_config.cpython-38.pyc delete mode 100644 modules/__pycache__/load_format.cpython-38.pyc delete mode 100644 modules/dialog.py delete mode 100644 modules/load_config.py delete mode 100644 modules/load_format.py delete mode 100644 readme.txt create mode 100644 src/__pycache__/vars.cpython-38.pyc create mode 100644 src/vars.py diff --git a/.idea/GeneratorCSV 3.1.iml b/.idea/GeneratorCSV 3.1.iml deleted file mode 100644 index ed5ea5b..0000000 --- a/.idea/GeneratorCSV 3.1.iml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml deleted file mode 100644 index 20fc29e..0000000 --- a/.idea/inspectionProfiles/profiles_settings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index adbabba..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 84256e7..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 9661ac7..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml deleted file mode 100644 index 27fa3ca..0000000 --- a/.idea/workspace.xml +++ /dev/null @@ -1,114 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1575129057993 - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/icon.ico b/assets/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..11e00aac42bb02dc40b1b21338d40411f1f97227 GIT binary patch literal 112027 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

x$L06^o(PZ#skbr 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 H$W{Lj$*EGf literal 0 HcmV?d00001 diff --git a/assets/other_images/icon.png b/assets/other_images/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..91780fd565e3f45a7ba99abab73ee0c77e6b2a63 GIT binary patch literal 4157 zcmbVP2{@E(+aCK88IxUQ3_@XMEMtt_kR|&Pkuqi;W?~jIBWw1M7E405Y*~7xr107X z6_O=2qIWMCXh!{Gw}Ce#=3Cs7Px%grnplteIux$D>=>}XhkNU{p211{nAuK4f( zydD8&Yy>q3#jpv;022odB?nR%m{3F5H(m^TzOxL6LBBzm0fsQs9fMF0J4Yy%N(Z1i z8Yp!9IL9;hLK2Y=k-^jKai)s#6#WKN-vc2A)o$F-cSk zbcYe=OATfk!q}dE4}nbkMN47)kcllAJQPQRBQ+2^A$8@02;Lk)k{&X3t$&MPT;!dIvs38p1zZ?L}aZJDv zrisu*u=Ayk)O6KEU^I0wT8KkC=|g;n+EEE4zp%fBqA>{Fe}l41hJa(@{v()x$M{j{ zWE?wS5*gkiI# z9=_AC-{XM)&k(=u{iV5o!r3>*&h*z+#eVs9*#Q*x4M%5RWf!%RpdgT7zqPrkYiRyx zegw^RbYHje1_kx3e|Dd3NsK8MzpI_|%}Xh6UZRec=PqC>wRpX%FWIPH{fJB{s&~yV zajQ)!v3gu&AJpF8{b6kr!LJ`q-S^g%zO1{iO$nxVf4!C4s8S}96oY(9 z*?bIMZvZq8INYv=M20=5`TIW|ztGOl=Oa{ZoxeT1$BEzZG&rrK!=${uY)@h~5j3Br zR&Zf2poMbryjXFnDE>Xn-mHf^#v^0SWqP=M6=OJiFxQ!)uf#=m9gp9x<_EmK38o9Xm3IIi>ZcZNx6}V)GF$TT-|&xszxC$DdX=)cFt$SW=T!UVczczmxF3AIB=b?Pq^cml~Oex_@0CskshNDnZ+BuF_)>U{(cXV7W8P(qw>d2pBi=aG$(An z{%RW!3QAD9A}$IA35ES$>FkHgANMuxRCY$0YG6yh45_d1f`cF{`z z9HL%|z?c2JgF}l2n5ckcoqd~94`{%4?=p3aD+GSRYr8ZBLXEmZZu_bFc+Z zOtim?Yi`Gz(z%Dmf{!aW30y8w!cxG+2m6!G5`GVosHpbR=vxh0^SK%D1WU0_xFnp}W4SK(mW7;q?LNy%z9zmho~ z(QIaaNf0cpDv9XTbiO`eqc=BLR1zfaTAGRT;bN!uhGU zv}rp{{b%WU31m7i#h$ z6r3=%cysX1fy`a2sjq7DJM5xIMu}E>_eKy$?UHRoQd4MvOwXx-97)N<;=5^|y8IEl zy%rOg+ih3SW90KMp3)xXIu}I0lCHBifVSp^@Gs1BXr?dfDRidUKFicK!VYXy2&|Gt z3{%hC3!O++9twRuu_kV_aZYsMA!kXi<}hStSJMLq&%hv*qNM>wh~N z_UiHY)YgYC^F9)Ucqe5{u7MyEo7|KL|lEha0Ck9L=(n{B35`!#q;Ks%` zs+Zgt)V-0xo*~ zL-NLrn{sR$k?Ki@t0hIOM82wXQ*=LL7sypta)1CVQvGQcFHP zM%6&69r?-H|9nl^?Vd6&2z$Fd6*2QlQ}XRQJBzG*YEeqO6P=c^C@2v5YU2Sw&- zy-nEamgINn$v75QQL>^@8jH%CT>vRh#oumyq~>YgICP>fSu)NGojZ7K1!AyIkmF-X zaPaG3uJ~eo8BuJM{=nO)y8}%9qP>O^S7t-c9LgQfUMgokI``?JP}`rPQ=Bbs+D;ZO ziWe4$hPeA1CQY&%O97?+9rua*;khr)FWetI99+$xo#Honcv+Cs`~GgBSKE6ECZyI~ zzSKw`Yw6uGc2{1K)Tr1BE7IMBoWu*-Y>9pD9HLXS-PoW6UP)JjS# z_Kn{wotmt)wy2|atgoE@(*bmb+dqx=5c6`VrsVCH_(tcZ2ISGQl4l!;m6cNFk}|>` zEwEjQFjFXCs@4(10zf*y`*YyU<-rZZ^#vy5x3#Jx62z9D4tn)Zyx6CbH~e9SI1gPK zH3f5VHzlo1px=E~%4kT8hA(#I)i=+S%8U0;BnCVT^c)if;S_GmpI2?wwO49yxl*-j zpz99G!dR;Y=Z%%*w_4SsYIF zLlF;>{_GM`dt=w51*yJ#*ZwJNJi2gapEugy3eO)+4pl9+y=0e5gW`yKcr}k5E8;iPTN3ZqQ$61B==7y2U zBT&Ib?h;rrk>9;3iUkdVqyXYq=Df|nUbK+X=rb)Wyu@oz*>4yc64*7i$vI}vxxk~0 z;189;eJOIkx`|ARZFlQD-Yz6HN%-{A##VAheh>jmO&u>Vn`{%FT#|ShQSTElVIOTA mE3&9W+x>cahE-*C{wrT>5+NS%G}U;vjb? zhIQv;UIIA^$sR$z3=CCj3=9n|3=F@3LJcn%7)pVryh>nTu$sZZAYL$MSD+10f-TA0 z-33Sk!B6Mi^+1ZVz$3C4NPB>>+sSM@pz#KtE{-7?_ukIf?Zx6K;OgHwQz9d8kJ_f+ z`c3m6EsQrGzc2J(HQ}?2#BaS4o;&_GgRbT;zP-Mlj}~^>emN-*D)DC;IsG; zcJq+-hK#ihGuJj+ZeW?T{T8oK0jtiLH%Fs=1X%vh^;c@U-ea--f+;W%RZCnWN>UO_ zQmvAUQh^kMk%5tcu7RPhp;3sTrIn$%m5G_Qfq|8Q0jriwE{cZS{FKbJO57Ss%9mUO zYR~}LP+XR7m6TYVs#{u=$&i+rlWL`}uV0c|pr4zVo|&AjTjuK@8f6h^);04YFy$~X Nc)I$ztaD0e0st(H<^2Ev literal 0 HcmV?d00001 diff --git a/assets/tab_icons/icon.png b/assets/tab_icons/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..91780fd565e3f45a7ba99abab73ee0c77e6b2a63 GIT binary patch literal 4157 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 mE3&9W+x>cahE-*C{wrT>5+NS%G}U;vjb? zhIQv;UIIA^$sR$z3=CCj3=9n|3=F@3LJcn%7)pVryh>nTu$sZZAYL$MSD+10f-TA0 z-33Sk!B6Mi^+1ZVz$3C4NPB>>+sSM@1_ov|PZ!6KjC*fq?)4W56gm2yVYgbttcDo^ zc3jeLbp$5#v;C3gafx>M$GnJB_l7cK^7@NmOOD+0NNVC0GF4&>(O|i<^kcf=`iOUX zrdAr~moIwf>9ND$`<>!@XZG}4D}{!9tXKZv$aJO4qrZcv(_?x^jL^g|!Az6sGF>O8 zb*$+;(bb{Ted4qNM_uYGxfcxY7-#QzENsBMC0%h3YXyUhs=&HJmNU%HwN(GHZFugu z23M>GSinC|832#Pdmgk;j`{Oxd^Sm0`3_MuVjB7 zYxpWO=X~ssM?HIdYZzxRNd*=8OX>Bx>X$D%lDwJsOQqz(4Q}=ul2;nfpY~%}Yl-NK z532iCge`w~O~TXb22V|BiuF^0^*%4GH@Z*KoK`ST|Cs;u<8J(mo$oa~ojapg^uA-s z#92j8JW>ukxwp|r;$wKup|_5P+hWpZPhtCXz}nSN*nH=-7x&T*xjIBKtJTK@<}jER z3jGdwWVcUD)SQ|3K;xxLi`Q?qlnVNoB*0>?m8-zf^j>Mxl7BWb#~r43#fL=I2u}0A zkY-vCcyzt7$%5?c+@N&Ntk93`DpS99OS3pmx;%f~+O3iA9TsaEMKVrCH?IUhUsS8`Tnyqd$6^%c4p#h|acTdpA4Jqx}>KWZkJ6zzfXX^Zdjq@L6Shx})D$8MdO`9yN+p1#M7)=$fRiF}9)^w}-2McDR& zPuPV6u~Xw1ZpB!%Y`DAJ^VjKI28k^^_ts3VaK6&=H{kwDAF=(*-UjUVzO+x%z)NKH zQ-j1K`!`v?=(t3u3@W1W6ek3RZ4@9^2VGnSk1HK-rRT_A3e?`Dx-_g?i)lD+D# zLvqzQdwOd)m6cu6)#ZOU-4efc(8buGeXs7jE+zwRlTepEMwi@nyuIO}P{TaqSZQnM zhp#iYy1A_L*zxto~HcG2Wrp&*-%`TZk3c+oT^(|l*y2mnUiXzudiQ{TcDqtn4X!Oty^kb=;Q5Q VVyQJPcQH^8gQu&X%Q~loCIG|{(t7{^ literal 0 HcmV?d00001 diff --git a/assets/tab_icons/link.png b/assets/tab_icons/link.png new file mode 100644 index 0000000000000000000000000000000000000000..8a8667224fc9d4fcf170d60488c3489412a64716 GIT binary patch literal 631 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTCmUKs7M+SzC{oH>NS%G}U;vjb? zhIQv;UIIA^$sR$z3=CCj3=9n|3=F@3LJcn%7)pVryh>nTu$sZZAYL$MSD+10f-TA0 z-33Sk!B6Mi^+1ZVz$3C4NPB>>+sSM@pz#)-E{-7?_ukGp8+h12pmqP*f>aMt71;?~ zlCu;SGM>A@WFn};RS{#sAun|M*T<|2<)3y5w%@)Oxr|x?=$tUY>s&1z>ufS#lVqPHr>17-yMU^KM%*Mf47-EWxDMJ z)-`8+-*OhOU;9k<1=BS(&xZClTYk8!W%BO{)UT^yWHs+k;p7V^)IaxoL zyX)hRgJ|2})eT)^aX@DOvsoS;7hg z29>4gZ3^yU*4uRX(Yc4F)@Q{OUutA(RP3P#Mut|VX4(b@Rt5&KHC$U!H00)|WTsW( z)}W)fi4&+n17t&SS-MqHVsWZ&X;CIaT4qkFmA<}yNp69DZen_7a<*=mhqJ%8YqB%z TwUd{DdKf%i{an^LB{Ts54AtY$ literal 0 HcmV?d00001 diff --git a/assets/tab_icons/merge.png b/assets/tab_icons/merge.png new file mode 100644 index 0000000000000000000000000000000000000000..19d4ac50d9049d7633b54eea26f3e8dcc19e5ca1 GIT binary patch literal 882 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTCmUKs7M+SzC{oH>NS%G}U;vjb? zhIQv;UIIA^$sR$z3=CCj3=9n|3=F@3LJcn%7)pVryh>nTu$sZZAYL$MSD+10f-TA0 z-33Sk!B6Mi^+1ZVz$3C4NPB>>+sSM@1_mZ$PZ!6KjC*fq?)E!uAkiwjAmq$}kP7x2 zznF4z6w=-a3Ay(4tnujav0>zI*s(_8U6X=hS=x~!xeQbL4~li^Sa0+W+8)$*MRDWn zIg|h2_*d`VA*AXx_^G@v}P=ALzCIF*T5wl|HS*u6wRj!+95e zJr{RdmH9T)_HN7*HxLYJFy|5b#bep{x#K{CK$7YdhWvgp3uE9EK;J~W=mIo}v+R91AI z-Q?DztBV?J;`;x;zIs6^OHwJx>MOImlgs{3Pg_4_?u*k6T;$TZ?uh=f%J(U=ANEND z(~)Y4YeY#(Vo9o1a#1RfVlXl=GSD?J)HO5;F|@P-A_FsR0|P4qgZV3!R8TbJ=BH$) zRpQq0?#nD-dS=i7*-%`TZk3c+oT^(|l*y2mnUiXzudiQ{TcDqtn4X!Ots9)@o)PHf VmF)D}^czqQgQu&X%Q~loCIHoeUjzUE literal 0 HcmV?d00001 diff --git a/assets/tab_icons/settings.png b/assets/tab_icons/settings.png new file mode 100644 index 0000000000000000000000000000000000000000..09f4cbb83fb1eaeacbbd89462a773bbaa2aaf100 GIT binary patch literal 1056 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTCmUKs7M+SzC{oH>NS%G}U;vjb? zhIQv;UIIA^$sR$z3=CCj3=9n|3=F@3LJcn%7)pVryh>nTu$sZZAYL$MSD+10f-TA0 z-33Sk!B6Mi^+1ZVz$3C4NPB>>+sSM@1_q{wo-U3d8Ta1KJnt2XK5>4qr1+PZ3m!ixO|W33fEVpT5s2}HTC_L?-Ukz0IQ=3;>>YG=h3O_dS}o51=j z)9~|xw#wr4cV|9p?$NiY*i-zxdf)w-*3a)%&%5Fx>?^{Sz-7KnYVGD}Kaw?%gkKB{ zxo68-Wf8c1mKB)>|=OyJ~=@_@#lP*Lz@eDRWi4* zd~{bm>#enV;}6A`OJ>&gajWe;^1ZyF!c)srYNcpTEtA9cC6S`hOFt!e2hE>()`fX1 zOW#|CkD+suU9=L`Fh1C{q}X!OT2HA-S9D(n{IJx`wCMTysAoy{xj+Z`4G$xI=P@p2 zc_(D>@Z?^`FUvM@ePQU9d!);{y>dnbOO=tW{sP$IR*@fDsoOzS(#;odj zU$`x2#!W|Y|69kM18jBcu>Ru${2Bz8ROC}&#i z(9OP`rG(*fsn+7u2i{)qGB=eOa)dozI)!u9428c=-)xk>3tgD*y61gZ5>rO$ou6wS zt?sz%b>!@NGtYg;cg&u2j+tL&Z$N>qU>u1syH{LMHN?WgLo3-Ut{RacTJzJ{zzQ(3)6B1vu zbHdD)6)06n)%9-^i*9t>v|a4IK;&Ldv7KHsvM26Yd$h-Geu}||9V_;@Zu1j+Wm0yV zr)I_F8_OEEWSkJu5_$7%f8^|pr0I|J>-%Rd%W6y70ZjF(C9V-ADTyViR>?)FK#IZ0 zz{o(?z);uFD8$gx3W$u&v<(cb3=9O-RF9)*$jwj5OsmALA)$J1HBf^F$cEywbgQJq z;#A$zqD+Rg%$!s!eSQ6s+yedF#PrPMY+a9x@Ty>!aEo1juQY*r7(8A5T-G@yGywot C=Bw)f literal 0 HcmV?d00001 diff --git a/changelog-UC.txt b/changelog-UC.txt index def0dfb..601629f 100644 --- a/changelog-UC.txt +++ b/changelog-UC.txt @@ -51,4 +51,11 @@ - Zmiana nazwy pliku instrukcji na 'readme.txt' - Naprawienie przycisków filedialog - Zmiana zmiennej treści błędu na string -- Ulepszona obsługa wyjątków dla ładowania pliku konfiguracyjnego, formatu i 'readme.txt' \ No newline at end of file +- Ulepszona obsługa wyjątków dla ładowania pliku konfiguracyjnego, formatu i 'readme.txt' + +4.0 Alpha (Build 20016) +- Całkowite przeprojektowanie interfejsu +- Całkowite przeprojektowanie struktury plików programu +- Przywrócenie systemu crashlogów +- Ukończenie karty 'informacje' +- Użycie skryptu instalującego automatycznie bibliotekę PIL \ No newline at end of file diff --git a/config.cfg b/config.cfg deleted file mode 100644 index 59688b9..0000000 --- a/config.cfg +++ /dev/null @@ -1,7 +0,0 @@ -Ciemny motyw(0/1): 1 -Kodowanie wyjsciowe: utf-8 -Domena: losobolew.pl -Quota: 500 -Kraj: Rzeczypospolita Polska -Dlugosc liceum: 4 -Dlugosc branzowej: 3 \ No newline at end of file diff --git a/format.fmt b/format.fmt deleted file mode 100644 index 1ac672d..0000000 --- a/format.fmt +++ /dev/null @@ -1,5 +0,0 @@ -K O N, I X L X -Q - -N, I X L X -Q \ No newline at end of file diff --git a/generator.py b/generator.py index 7aec5bc..3a34c22 100644 --- a/generator.py +++ b/generator.py @@ -1,8 +1,8 @@ """ # Generator CSV -# Wersja 4.0 Experimental +# 4.0 Experimental # by Mateusz Skoczek -# luty 2019 - grudzień 2019 +# styczeń 2019 - luty 2020 # dla ZSP Sobolew # @@ -14,784 +14,292 @@ +# -------------------- # Import bibliotek zewnętrznych # -------------------- # - - -# -------------------------------------------- # Informacje o programie # -------------------------------------------- # - -Nazwa = 'Generator CSV' -Wersja = '4.0 Experimental' -LataPracy = '2019' -Autorzy = 'Mateusz Skoczek' - - - - - - - - -# ----------------------------------- # Import bibliotek zewnętrznych i modułów # ------------------------------------ # - -# Biblioteki zewnętrzne +# Biblioteki główne import os as OS import sys as SS +import time as TM -# Moduły składowe programu -try: - from modules import dialog as MDdlg -except ModuleNotFoundError: - print('Wystąpił krytyczny błąd!') - print('Nie znaleziono jednego z modułów programu (dialog.py). Nie można załadować programu') - print('Kod błędu: E00x0011') - wait = input('Naciśnij ENTER aby wyjść') - SS.exit(0) -except Exception as exc: - print('Wystąpił krytyczny błąd!') - print('Nieznany błąd podczas ładowania jednego z modułów programu (dialog.py). Nie można załadować programu.') - print('Treść błędu: ' + str(exc)) - print('Kod błędu: E00x0010') - wait = input('Naciśnij ENTER aby wyjść') - SS.exit(0) - -try: - from modules import load_config as MDlcg -except ModuleNotFoundError: - print('Wystąpił krytyczny błąd!') - print('Nie znaleziono jednego z modułów programu (load_config.py). Nie można załadować programu') - print('Kod błędu: E00x0021') - wait = input('Naciśnij ENTER aby wyjść') - SS.exit(0) -except Exception as exc: - print('Wystąpił krytyczny błąd!') - print('Nieznany błąd podczas ładowania jednego z modułów programu (load_config.py). Nie można załadować programu.') - print('Treść błędu: ' + str(exc)) - print('Kod błędu: E00x0020') - wait = input('Naciśnij ENTER aby wyjść') - SS.exit(0) - -try: - from modules import load_format as MDlfm -except ModuleNotFoundError: - print('Wystąpił krytyczny błąd!') - print('Nie znaleziono jednego z modułów programu (load_format.py). Nie można załadować programu') - print('Kod błędu: E00x0031') - wait = input('Naciśnij ENTER aby wyjść') - SS.exit(0) -except Exception as exc: - print('Wystąpił krytyczny błąd!') - print('Nieznany błąd podczas ładowania jednego z modułów programu (load_format.py). Nie można załadować programu.') - print('Treść błędu: ' + str(exc)) - print('Kod błędu: E00x0030') - wait = input('Naciśnij ENTER aby wyjść') - SS.exit(0) - - -# Biblioteki zewnętrzne interfejsu graficznego +# Framework i inne biblioteki interfejsu graficznego import tkinter as TK - -from tkinter import filedialog as TKfld from tkinter import ttk as TKttk +from tkinter import filedialog as TKfld +try: + from PIL import ImageTk as PLitk + from PIL import Image as PLimg +except ModuleNotFoundError: + OS.system("python -m pip install pip") + OS.system("python -m pip install Pillow") + OS.system("cls") + from PIL import ImageTk as PLitk + from PIL import Image as PLimg + + + + + +# ------------------ # Import plików składowych programu # ------------------ # + +# Funkcja tworząca plik zawierający logi błędu +def excpt(filename, importingFilename, errorcode, exceptInfo): + filepath = './crashlogs/crash_' + str(TM.localtime()[2]) + str(TM.localtime()[1]) + str(TM.localtime()[0]) + str(TM.localtime()[3]) + str(TM.localtime()[4]) + str(TM.localtime()[5]) + '.txt' + try: + OS.mkdir('./crashlogs') + except: + pass + crashfile = open(filepath, 'w') + crashfile.write('CRASH!\n') + crashfile.write('An error occurred while loading the component file: %s\n' % importingFilename) + crashfile.write('In file: %s\n' % filename) + crashfile.write('%s\n' % exceptInfo) + crashfile.write('Errorcode: %s' % errorcode) + crashfile.close() + SS.exit(0) + + + +# vars.py +try: + from src.vars import prgInfo as SCvar_inf + from src.vars import guiVars as SCvar_gui +except Exception as exceptInfo: + excpt('generator.py', 'variables.py', 'E000000', exceptInfo) #TODO Kod + + + + + +# -------------------------------- # Okno # --------------------------------- # + +# Budowa okna +def gui(): + # Ustawienia okna + root = TK.Tk() + root.title(SCvar_inf.name + " " + SCvar_inf.version) + root.resizable(width = SCvar_gui.other.windowWidthResize, height = SCvar_gui.other.windowHeightResize) + root.configure(bg = SCvar_gui.color.mainBG) + root.iconbitmap(SCvar_gui.image.programIcon) + + + + + # Motyw + TKttk.Style().theme_create("main", parent = "alt", settings = { + "mainMenu.TNotebook":{ + "configure": { + "background": SCvar_gui.color.mainBG, + "tabposition": SCvar_gui.other.tabPosition, + "borderwidth": SCvar_gui.dimension.tabWindowBorderWidth, + } + }, + "mainMenu.TNotebook.Tab":{ + "configure": { + "background": SCvar_gui.color.unselectedTabBG, + "borderwidth": SCvar_gui.dimension.borderTab, + "padding": SCvar_gui.dimension.iconPaddingTab, + }, + "map": { + "background": [("selected", SCvar_gui.color.selectedTabBG), ("disabled", SCvar_gui.color.headerBG)], + } + } + }) + TKttk.Style().theme_use("main") - - - - - - -# ------------------------------------- # Uruchomienie interfejsu graficznego # -------------------------------------- # - -# Zmienne globalne środowiska graficznego -if int(MDlcg.read()[0]) == 1: - CiemnyMotyw = True -else: - CiemnyMotyw = False -SzerokoscOpisu = 17 -SzerokoscOpisu2 = 30 -SzerokoscOpisu3 = 10 -SzerokoscPola = 122 -SzerokoscPola2 = 107 -SzerokoscPola3 = 130 - - - -# Zmienne motywu -if CiemnyMotyw: - ZmienneMotywu = ['#1F1F1F', '#191919', '#B8B8B8', '#FFFFFF', '#404040', '#FFFFFF', '#1F1F1F', 1] -else: - ZmienneMotywu = ['#F0F0F0', '#D4D4D4', '#000000', '#000000', '#A6A6A6', '#000000', '#FFFFFF', 2] - -M_tlo = ZmienneMotywu[0] -M_tytultlo = ZmienneMotywu[1] -M_tytultext = ZmienneMotywu[2] -M_text = ZmienneMotywu[3] -M_przycisktlo = ZmienneMotywu[4] -M_przycisktext = ZmienneMotywu[5] -M_entrytlo = ZmienneMotywu[6] -M_framewielkosc = ZmienneMotywu[7] - - - -# Okno główne -class Main(TK.Tk): - def __init__(self): - # Ustawienia okna - TK.Tk.__init__(self) - self.title(Nazwa + " " + Wersja) - self.resizable(width = False, height = False) - self.configure(bg = M_tlo) - - - # Tytuł - Tytul = TK.Label(self) - Tytul.config(text = Nazwa) - Tytul.config(width = 41) - Tytul.config(bg = M_tytultlo) - Tytul.config(fg = M_tytultext) - Tytul.config(font = ('Segoe UI Semilight', 30)) - Tytul.grid(row = 0) - - - # Frame1 - Pliki z danymi - Ramka1 = TK.LabelFrame(self) - Ramka1.config(text=' Pliki tekstowe zawierające dane (wymagany przynajmniej jeden) ') - Ramka1.config(borderwidth = M_framewielkosc) - Ramka1.config(bg = M_tlo) - Ramka1.config(fg = M_text) - Ramka1.grid(row = 1) - - - # Ścieżka pliku txt nr 1 - wiersz = 1 - text1 = TK.StringVar() - - Pole1Label = TK.Label(Ramka1) - Pole1Label.config(text = 'Plik z danymi (1)') - Pole1Label.config(width = SzerokoscOpisu) - Pole1Label.config(bg = M_tlo) - Pole1Label.config(fg = M_text) - Pole1Label.grid(row = wiersz, column = 0) - - Pole1 = TK.Entry(Ramka1) - Pole1.config(textvariable = text1) - Pole1.config(width = SzerokoscPola) - Pole1.config(bg = M_entrytlo) - Pole1.config(fg = M_text) - Pole1.grid(row = wiersz, column = 1) - - def Pole1BrowseDialog(): - Pole1Browse.filename = TKfld.askopenfilename(title = "Wybierz plik tekstowy z danymi", filetypes = (("Pliki txt", "*.txt"), ("Wszystkie pliki", "*.*"))) - Pole1.delete(0, 'end') - Pole1.insert(0, Pole1Browse.filename) - - Pole1Browse = TK.Button(Ramka1) - Pole1Browse.config(text = '...') - Pole1Browse.config(command = Pole1BrowseDialog) - Pole1Browse.config(bg = M_przycisktlo) - Pole1Browse.config(fg = M_przycisktext) - Pole1Browse.config(relief = 'flat') - Pole1Browse.config(activebackground = M_przycisktlo) - Pole1Browse.grid(row = wiersz, column = 2, padx=5, pady=3) - - - # Ścieżka pliku txt nr 2 - wiersz = 2 - text2 = TK.StringVar() - - Pole2Label = TK.Label(Ramka1) - Pole2Label.config(text = 'Plik z danymi (2)') - Pole2Label.config(width = SzerokoscOpisu) - Pole2Label.config(bg = M_tlo) - Pole2Label.config(fg = M_text) - Pole2Label.grid(row = wiersz, column = 0) - - Pole2 = TK.Entry(Ramka1) - Pole2.config(textvariable = text2) - Pole2.config(width = SzerokoscPola) - Pole2.config(bg = M_entrytlo) - Pole2.config(fg = M_text) - Pole2.grid(row = wiersz, column = 1) - - def Pole2BrowseDialog(): - Pole2Browse.filename = TKfld.askopenfilename(title = "Wybierz plik tekstowy z danymi", filetypes = (("Pliki txt", "*.txt"), ("Wszystkie pliki", "*.*"))) - Pole2.delete(0, 'end') - Pole2.insert(0, Pole2Browse.filename) - - Pole2Browse = TK.Button(Ramka1) - Pole2Browse.config(text = '...') - Pole2Browse.config(command = Pole2BrowseDialog) - Pole2Browse.config(bg = M_przycisktlo) - Pole2Browse.config(fg = M_przycisktext) - Pole2Browse.config(relief = 'flat') - Pole2Browse.config(activebackground = M_przycisktlo) - Pole2Browse.grid(row = wiersz, column = 2, padx = 5, pady = 3) - - # Ścieżka pliku txt nr 3 - wiersz = 3 - text3 = TK.StringVar() - - Pole3Label = TK.Label(Ramka1) - Pole3Label.config(text = 'Plik z danymi (3)') - Pole3Label.config(width = SzerokoscOpisu) - Pole3Label.config(bg = M_tlo) - Pole3Label.config(fg = M_text) - Pole3Label.grid(row = wiersz, column = 0) - - Pole3 = TK.Entry(Ramka1) - Pole3.config(textvariable = text3) - Pole3.config(width = SzerokoscPola) - Pole3.config(bg = M_entrytlo) - Pole3.config(fg = M_text) - Pole3.grid(row = wiersz, column = 1) - - def Pole3BrowseDialog(): - Pole3Browse.filename = TKfld.askopenfilename(title = "Wybierz plik tekstowy z danymi", filetypes = (("Pliki txt", "*.txt"), ("Wszystkie pliki", "*.*"))) - Pole3.delete(0, 'end') - Pole3.insert(0, Pole3Browse.filename) - - Pole3Browse = TK.Button(Ramka1) - Pole3Browse.config(text = '...') - Pole3Browse.config(command = Pole3BrowseDialog) - Pole3Browse.config(bg = M_przycisktlo) - Pole3Browse.config(fg = M_przycisktext) - Pole3Browse.config(relief = 'flat') - Pole3Browse.config(activebackground = M_przycisktlo) - Pole3Browse.grid(row = wiersz, column = 2, padx = 5, pady = 3) - - - # Frame2 - Dołącz pliki .csv - Ramka2 = TK.LabelFrame(self) - Ramka2.config(text = ' Dołącz pliki .csv (opcjonalne) ') - Ramka2.config(borderwidth = M_framewielkosc) - Ramka2.config(bg = M_tlo) - Ramka2.config(fg = M_text) - Ramka2.grid(row = 2) - - - # Sciezka do konta.csv - wiersz = 1 - textKonta = TK.StringVar() - - PoleKontaLabel = TK.Label(Ramka2) - PoleKontaLabel.config(text = 'Ściezka do pliku .csv dla poczty') - PoleKontaLabel.config(width = SzerokoscOpisu2) - PoleKontaLabel.config(bg = M_tlo) - PoleKontaLabel.config(fg = M_text) - PoleKontaLabel.grid(row = wiersz, column = 0) - - PoleKonta = TK.Entry(Ramka2) - PoleKonta.config(textvariable = textKonta) - PoleKonta.config(width = SzerokoscPola2) - PoleKonta.config(bg = M_entrytlo) - PoleKonta.config(fg = M_text) - PoleKonta.grid(row = wiersz, column = 1) - - def PoleKontaBrowseDialog(): - PoleKontaBrowse.filename = TKfld.askopenfilename(title="Wybierz plik .csv dla poczty", filetypes=(("Pliki csv", "*.csv"), ("Wszystkie pliki", "*.*"))) - PoleKonta.delete(0, 'end') - PoleKonta.insert(0, PoleKontaBrowse.filename) - - PoleKontaBrowse = TK.Button(Ramka2) - PoleKontaBrowse.config(text = '...') - PoleKontaBrowse.config(command = PoleKontaBrowseDialog) - PoleKontaBrowse.config(bg = M_przycisktlo) - PoleKontaBrowse.config(fg = M_przycisktext) - PoleKontaBrowse.config(relief = 'flat') - PoleKontaBrowse.config(activebackground = M_przycisktlo) - PoleKontaBrowse.grid(row = wiersz, column = 2, padx = 5, pady = 3) - - - # Sciezka do office.csv - wiersz = 2 - textOffice = TK.StringVar() - - PoleOfficeLabel = TK.Label(Ramka2) - PoleOfficeLabel.config(text = 'Ściezka do pliku .csv dla office365') - PoleOfficeLabel.config(width = SzerokoscOpisu2) - PoleOfficeLabel.config(bg = M_tlo) - PoleOfficeLabel.config(fg = M_text) - PoleOfficeLabel.grid(row = wiersz, column = 0) - - PoleOffice = TK.Entry(Ramka2) - PoleOffice.config(textvariable = textOffice) - PoleOffice.config(width = SzerokoscPola2) - PoleOffice.config(bg = M_entrytlo) - PoleOffice.config(fg = M_text) - PoleOffice.grid(row = wiersz, column = 1) - - def PoleOfficeBrowseDialog(): - PoleOfficeBrowse.filename = TKfld.askopenfilename(title = "Wybierz plik .csv dla office365", filetypes = (("Pliki csv", "*.csv"), ("Wszystkie pliki", "*.*"))) - PoleOffice.delete(0, 'end') - PoleOffice.insert(0, PoleOfficeBrowse.filename) - - PoleOfficeBrowse = TK.Button(Ramka2) - PoleOfficeBrowse.config(text = '...') - PoleOfficeBrowse.config(command = PoleOfficeBrowseDialog) - PoleOfficeBrowse.config(bg = M_przycisktlo) - PoleOfficeBrowse.config(fg = M_przycisktext) - PoleOfficeBrowse.config(relief = 'flat') - PoleOfficeBrowse.config(activebackground = M_przycisktlo) - PoleOfficeBrowse.grid(row = wiersz, column = 2, padx = 5, pady = 3) - - - # Frame3 - Eksport - Ramka3 = TK.LabelFrame(self) - Ramka3.config(text=' Eksport ') - Ramka3.config(borderwidth = M_framewielkosc) - Ramka3.config(bg = M_tlo) - Ramka3.config(fg = M_text) - Ramka3.grid(row = 3) - - - # Sciezka do pliku poczty - wiersz = 1 - textKontaEksport = TK.StringVar() - - PoleKontaEksportLabel = TK.Label(Ramka3) - PoleKontaEksportLabel.config(text = 'Poczta') - PoleKontaEksportLabel.config(width = SzerokoscOpisu3) - PoleKontaEksportLabel.config(bg = M_tlo) - PoleKontaEksportLabel.config(fg = M_text) - PoleKontaEksportLabel.grid(row = wiersz, column = 0) - - PoleKontaEksport = TK.Entry(Ramka3) - PoleKontaEksport.config(textvariable = textKontaEksport) - PoleKontaEksport.config(width = SzerokoscPola3) - PoleKontaEksport.config(bg = M_entrytlo) - PoleKontaEksport.config(fg = M_text) - PoleKontaEksport.grid(row = wiersz, column = 1) - - def PoleKontaEksportBrowseDialog(): - PoleKontaEksportBrowse.filename = TKfld.askdirectory(title = "Zapisz w...") - PoleKontaEksport.delete(0, 'end') - PoleKontaEksport.insert(0, PoleKontaEksportBrowse.filename) - - PoleKontaEksportBrowse = TK.Button(Ramka3) - PoleKontaEksportBrowse.config(text = '...') - PoleKontaEksportBrowse.config(command = PoleKontaEksportBrowseDialog) - PoleKontaEksportBrowse.config(bg = M_przycisktlo) - PoleKontaEksportBrowse.config(fg = M_przycisktext) - PoleKontaEksportBrowse.config(relief = 'flat') - PoleKontaEksportBrowse.config(activebackground = M_przycisktlo) - PoleKontaEksportBrowse.grid(row = wiersz, column = 2, padx = 5, pady = 3) - - # Sciezka do pliku office - wiersz = 2 - textOfficeEksport = TK.StringVar() - - PoleOfficeEksportLabel = TK.Label(Ramka3) - PoleOfficeEksportLabel.config(text = 'Office') - PoleOfficeEksportLabel.config(width = SzerokoscOpisu3) - PoleOfficeEksportLabel.config(bg = M_tlo) - PoleOfficeEksportLabel.config(fg = M_text) - PoleOfficeEksportLabel.grid(row = wiersz, column = 0) - - PoleOfficeEksport = TK.Entry(Ramka3) - PoleOfficeEksport.config(textvariable = textOfficeEksport) - PoleOfficeEksport.config(width = SzerokoscPola3) - PoleOfficeEksport.config(bg = M_entrytlo) - PoleOfficeEksport.config(fg = M_text) - PoleOfficeEksport.grid(row = wiersz, column = 1) - - def PoleOfficeEksportBrowseDialog(): - PoleOfficeEksportBrowse.filename = TKfld.askdirectory(title = "Zapisz w...",) - PoleOfficeEksport.delete(0, 'end') - PoleOfficeEksport.insert(0, PoleOfficeEksportBrowse.filename) - - PoleOfficeEksportBrowse = TK.Button(Ramka3) - PoleOfficeEksportBrowse.config(text = '...') - PoleOfficeEksportBrowse.config(command = PoleOfficeEksportBrowseDialog) - PoleOfficeEksportBrowse.config(bg = M_przycisktlo) - PoleOfficeEksportBrowse.config(fg = M_przycisktext) - PoleOfficeEksportBrowse.config(relief = 'flat') - PoleOfficeEksportBrowse.config(activebackground = M_przycisktlo) - PoleOfficeEksportBrowse.grid(row = wiersz, column = 2, padx = 5, pady = 3) - - # Przycisk START - def PathPreprocess(): - pass - - PrzyciskSTART = TK.Button(self) - PrzyciskSTART.config(text = 'START') - PrzyciskSTART.config(command = PathPreprocess) - PrzyciskSTART.config(width = 50) - PrzyciskSTART.config(bg = M_przycisktlo) - PrzyciskSTART.config(fg = M_przycisktext) - PrzyciskSTART.config(relief = 'flat') - PrzyciskSTART.config(activebackground = M_przycisktlo) - PrzyciskSTART.grid(row = 4, pady = 15) - - # Pasek dolny - PasekDolny = TK.LabelFrame(self) - PasekDolny.config(bd = 0) - PasekDolny.config(bg = M_tytultlo) - PasekDolny.config(fg = M_tytultext) - PasekDolny.grid(row=5) - - InfoLabel = TK.Label(PasekDolny) - InfoLabel.config(text = Nazwa + ' ' + Wersja + ' | © ' + Autorzy + ' ' + LataPracy + ' dla ZSP Sobolew') - InfoLabel.config(width = 107) - InfoLabel.config(justify = 'left') - InfoLabel.config(anchor='w') - InfoLabel.config(bg = M_tytultlo) - InfoLabel.config(fg = M_tytultext) - InfoLabel.grid(row = 0, column = 0) - - def InfoOpen(): - try: - x = open('readme.txt') - except FileNotFoundError: - MDdlg.err(4) - except: - MDdlg.err(22) - else: - OS.system("notepad readme.txt") - - PrzyciskINFO = TK.Button(PasekDolny) - PrzyciskINFO.config(text = 'Instrukcja') - PrzyciskINFO.config(command = InfoOpen) - PrzyciskINFO.config(bg = M_przycisktlo) - PrzyciskINFO.config(fg = M_przycisktext) - PrzyciskINFO.config(relief = 'flat') - PrzyciskINFO.config(activebackground = M_przycisktlo) - PrzyciskINFO.grid(row = 0, column = 1, padx = 5, pady = 5) - - PrzyciskUSTAWIENIA = TK.Button(PasekDolny) - PrzyciskUSTAWIENIA.config(text = 'Ustawienia') - PrzyciskUSTAWIENIA.config(command = self.settingsButton) - PrzyciskUSTAWIENIA.config(bg = M_przycisktlo) - PrzyciskUSTAWIENIA.config(fg = M_przycisktext) - PrzyciskUSTAWIENIA.config(relief = 'flat') - PrzyciskUSTAWIENIA.grid(row = 0, column = 2, padx = 5, pady = 5) - - def settingsButton(self): - self.child = Settings(self) - - def run(self): - self.mainloop() - -# Okno ustawień -class Settings(TK.Toplevel): - def __init__(self, parent): - # Ustawienia okna - TK.Toplevel.__init__(self, parent) - self.title('Ustawienia') - self.resizable(width = False, height = False) - self.configure(bg = M_tlo) - - liczbawierszy = 0 - - # Tytuł - Tytul = TK.Label(self) - Tytul.config(text = 'Ustawienia') - Tytul.config(width = 40) - Tytul.config(bg = M_tytultlo) - Tytul.config(fg = M_tytultext) - Tytul.config(font = ('Segoe UI Semilight', 20)) - Tytul.grid(row = 0) - - - # Frame1 - Motyw - liczbawierszy += 1 - Ramka1 = TK.LabelFrame(self) - Ramka1.config(text = ' Motyw programu ') - Ramka1.config(bg = M_tlo) - Ramka1.config(fg = M_text) - Ramka1.config(borderwidth = M_framewielkosc) - Ramka1.grid(row = 1, pady = 5) - - - if int(MDlcg.read()[0]) == 1: - Motyw_list_set = 1 - else: - Motyw_list_set = 0 - Motyw_list = TKttk.Combobox(Ramka1) - Motyw_list.config(textvariable = TK.StringVar()) - Motyw_list.config(state = 'readonly') - Motyw_list.config(width = 93) - Motyw_list.grid(row = 0, pady = 5, padx = 5) - Motyw_list['values'] = ('Jasny', 'Ciemny') - Motyw_list.current(Motyw_list_set) - - - # Frame2 - Kodowanie - liczbawierszy += 1 - Ramka2 = TK.LabelFrame(self) - Ramka2.config(text = ' Kodowanie wyjściowe ') - Ramka2.config(bg = M_tlo) - Ramka2.config(fg = M_text) - Ramka2.config(borderwidth = M_framewielkosc) - Ramka2.grid(row = 2, pady = 5) - - Code_list = TKttk.Combobox(Ramka2) - Code_list.config(textvariable = TK.StringVar()) - Code_list.config(state = 'readonly') - Code_list.config(width = 93) - Code_list.grid(row = 0, pady = 5, padx = 5) - Code_list['values'] = ('utf-8') - Code_list.set(MDlcg.read()[1]) - - - # Frame3 - Format plików wejściowych - SzerokoscPolaWej = 35 - WysokoscPolaWej = 8 - - liczbawierszy += 1 - Ramka3 = TK.LabelFrame(self) - Ramka3.config(text = ' Format plików wejściowych ') - Ramka3.config(bg = M_tlo) - Ramka3.config(fg = M_text) - Ramka3.config(borderwidth = M_framewielkosc) - Ramka3.grid(row = 3, pady = 5) - - UczniowieLabel = TK.Label(Ramka3) - UczniowieLabel.config(text = 'Uczniowie') - UczniowieLabel.config(justify = 'center') - UczniowieLabel.config(bg = M_tlo) - UczniowieLabel.config(fg = M_text) - UczniowieLabel.grid(row = 0, column = 0) - - uczfmt = MDlfm.read()[0] - uczfmt = '\n'.join(uczfmt) - UczniowieFormat = TK.Text(Ramka3) - UczniowieFormat.config(width = SzerokoscPolaWej) - UczniowieFormat.config(height = WysokoscPolaWej) - UczniowieFormat.config(bg = M_entrytlo) - UczniowieFormat.config(fg = M_text) - UczniowieFormat.grid(row = 1, column = 0, padx = 5, pady = 5) - UczniowieFormat.insert(TK.END, uczfmt) - - NauczycieleLabel = TK.Label(Ramka3) - NauczycieleLabel.config(text = 'Nauczyciele') - NauczycieleLabel.config(justify = 'center') - NauczycieleLabel.config(bg = M_tlo) - NauczycieleLabel.config(fg = M_text) - NauczycieleLabel.grid(row = 0, column = 1) - - nczfmt = MDlfm.read()[1] - nczfmt = '\n'.join(nczfmt) - NauczycieleFormat = TK.Text(Ramka3) - NauczycieleFormat.config(width = SzerokoscPolaWej) - NauczycieleFormat.config(height = WysokoscPolaWej) - NauczycieleFormat.config(bg = M_entrytlo) - NauczycieleFormat.config(fg = M_text) - NauczycieleFormat.grid(row = 1, column = 1, padx = 5, pady = 5) - NauczycieleFormat.insert(TK.END, nczfmt) - - OpisFmt = TK.LabelFrame(Ramka3) - OpisFmt.config(bg=M_tlo) - OpisFmt.config(fg=M_text) - OpisFmt.config(borderwidth=0) - OpisFmt.grid(row=2, pady=5, columnspan=4) - - Opis1 = TK.Label(OpisFmt) - Opis1.config(text='Dozwolone znaki:') - Opis1.config(bg=M_tlo) - Opis1.config(fg=M_text) - Opis1.grid(row=0, columnspan=7) - - Opis2_1 = TK.Label(OpisFmt) - Opis2_1.config(text='K - Klasa') - Opis2_1.config(bg=M_tlo) - Opis2_1.config(fg=M_text) - Opis2_1.grid(row=1, column=0) - - Opis2_2 = TK.Label(OpisFmt) - Opis2_2.config(text='O - Oddzial') - Opis2_2.config(bg=M_tlo) - Opis2_2.config(fg=M_text) - Opis2_2.grid(row=1, column=1) - - Opis2_3 = TK.Label(OpisFmt) - Opis2_3.config(text='N - Nazwisko') - Opis2_3.config(bg=M_tlo) - Opis2_3.config(fg=M_text) - Opis2_3.grid(row=1, column=2) - - Opis2_4 = TK.Label(OpisFmt) - Opis2_4.config(text='I - Imię') - Opis2_4.config(bg=M_tlo) - Opis2_4.config(fg=M_text) - Opis2_4.grid(row=1, column=3) - - Opis2_5 = TK.Label(OpisFmt) - Opis2_5.config(text='L - Login') - Opis2_5.config(bg=M_tlo) - Opis2_5.config(fg=M_text) - Opis2_5.grid(row=1, column=4) - - Opis2_6 = TK.Label(OpisFmt) - Opis2_6.config(text = 'X - Dane nieznaczące') - Opis2_6.config(bg = M_tlo) - Opis2_6.config(fg = M_text) - Opis2_6.grid(row = 1, column = 5) - - Opis2_6 = TK.Label(OpisFmt) - Opis2_6.config(text='Q - Pusta linia') - Opis2_6.config(bg=M_tlo) - Opis2_6.config(fg=M_text) - Opis2_6.grid(row=1, column=6) - - Opis3 = TK.Label(OpisFmt) - Opis3.config(text='Pozostałe znaki oprócz cyfr i pozostałych liter') - Opis3.config(bg=M_tlo) - Opis3.config(fg=M_text) - Opis3.grid(row=2, columnspan = 7) - - - # Frame4 - Stałe - liczbawierszy += 1 - Ramka4 = TK.LabelFrame(self) - Ramka4.config(text = ' Ustawienia generowania ') - Ramka4.config(bg = M_tlo) - Ramka4.config(fg = M_text) - Ramka4.config(borderwidth = M_framewielkosc) - Ramka4.grid(row = 4, pady = 5) - - - # Długość liceum i branżowej - RamkaDl = TK.LabelFrame(Ramka4) - RamkaDl.config(bg = M_tlo) - RamkaDl.config(fg = M_text) - RamkaDl.config(borderwidth = 0) - RamkaDl.grid(row = 0, pady = 5, columnspan = 2) - - DlLicLabel = TK.Label(RamkaDl) - DlLicLabel.config(text = 'Lata nauki w liceum') - DlLicLabel.config(width = SzerokoscOpisu + 5) - DlLicLabel.config(bg = M_tlo) - DlLicLabel.config(fg = M_text) - DlLicLabel.grid(row = 0, column = 0) - - DlLicValue = TK.IntVar() - DlLicPole = TK.Spinbox(RamkaDl) - DlLicPole.config(textvariable = DlLicValue) - DlLicPole.config(from_ = 1, to = 10) - DlLicPole.config(width = 18) - DlLicPole.config(bg = M_entrytlo) - DlLicPole.config(fg = M_text) - DlLicPole.grid(row = 0, column = 1, padx = 5, pady = 5) - DlLicPole.delete(0, 'end') - DlLicPole.insert(0, int(MDlcg.read()[5])) - - DlBrLabel = TK.Label(RamkaDl) - DlBrLabel.config(text='Lata nauki w branżowej') - DlBrLabel.config(width = SzerokoscOpisu + 5) - DlBrLabel.config(bg = M_tlo) - DlBrLabel.config(fg = M_text) - DlBrLabel.grid(row = 0, column = 2) - - DlBrValue = TK.IntVar() - DlBrPole = TK.Spinbox(RamkaDl) - DlBrPole.config(textvariable = DlBrValue) - DlBrPole.config(from_ = 1, to=10) - DlBrPole.config(width = 18) - DlBrPole.config(bg = M_entrytlo) - DlBrPole.config(fg = M_text) - DlBrPole.grid(row = 0, column = 3, padx = 5, pady = 5) - DlBrPole.delete(0, 'end') - DlBrPole.insert(0, int(MDlcg.read()[6])) - - - # Domena - - DomenaLabel = TK.Label(Ramka4) - DomenaLabel.config(text = 'Domena') - DomenaLabel.config(width = SzerokoscOpisu + 5) - DomenaLabel.config(bg = M_tlo) - DomenaLabel.config(fg = M_text) - DomenaLabel.grid(row = 2, column = 0) - - text1 = TK.StringVar() - PoleDomena = TK.Entry(Ramka4) - PoleDomena.config(textvariable = text1) - PoleDomena.config(width = 69) - PoleDomena.config(bg = M_entrytlo) - PoleDomena.config(fg = M_text) - PoleDomena.grid(row = 2, column = 1, padx = 5, pady = 5) - PoleDomena.insert(0, MDlcg.read()[2]) - - - # Quota - - QuotaLabel = TK.Label(Ramka4) - QuotaLabel.config(text = 'Quota (MB)') - QuotaLabel.config(width = SzerokoscOpisu) - QuotaLabel.config(bg = M_tlo) - QuotaLabel.config(fg = M_text) - QuotaLabel.grid(row = 3, column = 0) - - value2 = TK.IntVar() - PoleQuota = TK.Spinbox(Ramka4) - PoleQuota.config(textvariable = value2) - PoleQuota.config(from_ = 1, to = 100000) - PoleQuota.config(width = 67) - PoleQuota.config(bg = M_entrytlo) - PoleQuota.config(fg = M_text) - PoleQuota.grid(row = 3, column = 1, padx = 5, pady = 5) - PoleQuota.delete(0, 'end') - PoleQuota.insert(0, int(MDlcg.read()[3])) - - - # Kraj - - KrajLabel = TK.Label(Ramka4) - KrajLabel.config(text = 'Kraj') - KrajLabel.config(width = SzerokoscOpisu + 5) - KrajLabel.config(bg = M_tlo) - KrajLabel.config(fg = M_text) - KrajLabel.grid(row = 4, column = 0) - - KrajValue = TK.StringVar() - KrajPole = TK.Entry(Ramka4) - KrajPole.config(textvariable = KrajValue) - KrajPole.config(width = 69) - KrajPole.config(bg = M_entrytlo) - KrajPole.config(fg = M_text) - KrajPole.grid(row = 4, column = 1, padx = 5, pady = 5) - KrajPole.insert(0, MDlcg.read()[4]) - - - # Przycisk ZAPISZ - def save(): - if MDdlg.ask(1): - motyw = Motyw_list.get() - if motyw == 'Jasny': - motyw = '0' - else: - motyw = '1' - kodowanie = Code_list.get() - uczniowiefmt = UczniowieFormat.get('1.0', 'end') - nauczycielefmt = NauczycieleFormat.get('1.0', 'end') - liclata = DlLicPole.get() - brlata = DlBrPole.get() - domena = PoleDomena.get() - quota = PoleQuota.get() - kraj = KrajPole.get() - SettingsToSave = [motyw, kodowanie, domena, quota, kraj, liclata, brlata] - FormatToSave = [uczniowiefmt, nauczycielefmt] - if MDlfm.edit(FormatToSave): - MDlcg.edit(SettingsToSave) - MDdlg.inf(0) - self.destroy() - else: - pass - PrzyciskZAPISZ = TK.Button(self) - PrzyciskZAPISZ.config(text = 'ZAPISZ') - PrzyciskZAPISZ.config(command = save) - PrzyciskZAPISZ.config(width = 50) - PrzyciskZAPISZ.config(bg = M_przycisktlo) - PrzyciskZAPISZ.config(fg = M_przycisktext) - PrzyciskZAPISZ.config(relief = 'flat') - PrzyciskZAPISZ.config(activebackground = M_przycisktlo) - PrzyciskZAPISZ.grid(row = liczbawierszy + 1, pady = 15) - - - -# Inicjacja okna głównego -OknoGlowne = Main() -OknoGlowne.run() \ No newline at end of file + + + # Menu główne + mainMenu = TKttk.Notebook(root) + mainMenu.config(style = "mainMenu.TNotebook") + mainMenu.grid(row = 1) + + # TAB1 - Ikona + iconTab = TK.Frame(mainMenu) + iconTab.config(background = SCvar_gui.color.mainBG) + iconTabImg = PLimg.open(SCvar_gui.image.iconTab) + iconTabImg = iconTabImg.resize((SCvar_gui.dimension.iconTab, SCvar_gui.dimension.iconTab), PLimg.ANTIALIAS) + iconTabImg = PLitk.PhotoImage(iconTabImg) + mainMenu.add(iconTab, image = iconTabImg, state = TK.DISABLED) + + # TAB2 - Generowanie plików + generateTab = TK.Frame(mainMenu) + generateTab.config(background = SCvar_gui.color.mainBG) + generateTabImg = PLimg.open(SCvar_gui.image.generateTab) + generateTabImg = generateTabImg.resize((SCvar_gui.dimension.iconTab, SCvar_gui.dimension.iconTab), PLimg.ANTIALIAS) + generateTabImg = PLitk.PhotoImage(generateTabImg) + mainMenu.add(generateTab, image = generateTabImg) + + # TAB3 - Dołącz do pliku + linkTab = TK.Frame(mainMenu) + linkTab.config(background = SCvar_gui.color.mainBG) + linkTabImg = PLimg.open(SCvar_gui.image.linkTab) + linkTabImg = linkTabImg.resize((SCvar_gui.dimension.iconTab, SCvar_gui.dimension.iconTab), PLimg.ANTIALIAS) + linkTabImg = PLitk.PhotoImage(linkTabImg) + mainMenu.add(linkTab, image = linkTabImg) + + # TAB4 - Łączenie plików + mergeTab = TK.Frame(mainMenu) + mergeTab.config(background = SCvar_gui.color.mainBG) + mergeTabImg = PLimg.open(SCvar_gui.image.mergeTab) + mergeTabImg = mergeTabImg.resize((SCvar_gui.dimension.iconTab, SCvar_gui.dimension.iconTab), PLimg.ANTIALIAS) + mergeTabImg = PLitk.PhotoImage(mergeTabImg) + mainMenu.add(mergeTab, image = mergeTabImg) + + # TAB5 - Ustawienia + settingsTab = TK.Frame(mainMenu) + settingsTab.config(background = SCvar_gui.color.mainBG) + settingsTabImg = PLimg.open(SCvar_gui.image.settingsTab) + settingsTabImg = settingsTabImg.resize((SCvar_gui.dimension.iconTab, SCvar_gui.dimension.iconTab), PLimg.ANTIALIAS) + settingsTabImg = PLitk.PhotoImage(settingsTabImg) + mainMenu.add(settingsTab, image = settingsTabImg) + + # TAB6 - Informacje + infoTab = TK.Frame(mainMenu) + infoTab.config(background = SCvar_gui.color.mainBG) + infoTabImg = PLimg.open(SCvar_gui.image.infoTab) + infoTabImg = infoTabImg.resize((SCvar_gui.dimension.iconTab, SCvar_gui.dimension.iconTab), PLimg.ANTIALIAS) + infoTabImg = PLitk.PhotoImage(infoTabImg) + mainMenu.add(infoTab, image = infoTabImg) + + + + + # TAB2 + tab2Label = TK.Label(generateTab) + tab2Label.config(text = 'GENEROWANIE PLIKÓW CSV') + tab2Label.config(font = (SCvar_gui.fonts.tabHeader[0], SCvar_gui.fonts.tabHeader[1])) + tab2Label.config(bg = SCvar_gui.color.headerBG) + tab2Label.config(fg = SCvar_gui.color.headerText) + tab2Label.config(bd = SCvar_gui.dimension.tabHeaderHeight) + tab2Label.config(width = SCvar_gui.dimension.tabHeaderWidth) + tab2Label.grid(row = 0) + + + + + # TAB3 + tab3Label = TK.Label(linkTab) + tab3Label.config(text = 'DOŁĄCZANIE DO PLIKU CSV') + tab3Label.config(font = (SCvar_gui.fonts.tabHeader[0], SCvar_gui.fonts.tabHeader[1])) + tab3Label.config(bg = SCvar_gui.color.headerBG) + tab3Label.config(fg = SCvar_gui.color.headerText) + tab3Label.config(bd = SCvar_gui.dimension.tabHeaderHeight) + tab3Label.config(width = SCvar_gui.dimension.tabHeaderWidth) + tab3Label.grid(row = 0) + + + + + # TAB4 + tab4Label = TK.Label(mergeTab) + tab4Label.config(text = 'ŁĄCZENIE PLIKÓW CSV') + tab4Label.config(font = (SCvar_gui.fonts.tabHeader[0], SCvar_gui.fonts.tabHeader[1])) + tab4Label.config(bg = SCvar_gui.color.headerBG) + tab4Label.config(fg = SCvar_gui.color.headerText) + tab4Label.config(bd = SCvar_gui.dimension.tabHeaderHeight) + tab4Label.config(width = SCvar_gui.dimension.tabHeaderWidth) + tab4Label.grid(row = 0) + + + + + # TAB5 + tab5Label = TK.Label(settingsTab) + tab5Label.config(text = 'USTAWIENIA') + tab5Label.config(font = (SCvar_gui.fonts.tabHeader[0], SCvar_gui.fonts.tabHeader[1])) + tab5Label.config(bg = SCvar_gui.color.headerBG) + tab5Label.config(fg = SCvar_gui.color.headerText) + tab5Label.config(bd = SCvar_gui.dimension.tabHeaderHeight) + tab5Label.config(width = SCvar_gui.dimension.tabHeaderWidth) + tab5Label.grid(row = 0) + + + + + # TAB6 + tab6Label = TK.Label(infoTab) + tab6Label.config(text = 'INFORMACJE') + tab6Label.config(font = (SCvar_gui.fonts.tabHeader[0], SCvar_gui.fonts.tabHeader[1])) + tab6Label.config(bg = SCvar_gui.color.headerBG) + tab6Label.config(fg = SCvar_gui.color.headerText) + tab6Label.config(bd = SCvar_gui.dimension.tabHeaderHeight) + tab6Label.config(width = SCvar_gui.dimension.tabHeaderWidth) + tab6Label.grid(row = 0) + + # Separator1 + separator1 = TK.Label(infoTab) + separator1.config(bg = SCvar_gui.color.mainBG) + separator1.config(height = SCvar_gui.dimension.separator1Height) + separator1.grid(row = 1) + + # Ikona + programIcon = PLimg.open(SCvar_gui.image.programIconOther) + programIcon = programIcon.resize((SCvar_gui.dimension.programIconInInfo, SCvar_gui.dimension.programIconInInfo), PLimg.ANTIALIAS) + programIcon = PLitk.PhotoImage(programIcon) + programIconPlace = TK.Label(infoTab) + programIconPlace.config(image = programIcon) + programIconPlace.config(background = SCvar_gui.color.mainBG) + programIconPlace.config(height = SCvar_gui.dimension.programIconInInfoPlace) + programIconPlace.grid(row = 2) + + # Nazwa programu + programName = TK.Label(infoTab) + programName.config(text = SCvar_inf.name) + programName.config(font = (SCvar_gui.fonts.info1[0], SCvar_gui.fonts.info1[1])) + programName.config(background = SCvar_gui.color.mainBG) + programName.config(foreground = SCvar_gui.color.headerText) + programName.grid(row = 3) + + # Wersja programu + programVersion = TK.Label(infoTab) + programVersion.config(text = 'Wersja ' + SCvar_inf.version) + programVersion.config(font = (SCvar_gui.fonts.info1[0], SCvar_gui.fonts.info1[2])) + programVersion.config(background = SCvar_gui.color.mainBG) + programVersion.config(foreground = SCvar_gui.color.headerText) + programVersion.grid(row = 4) + + # Separator2 + separator2 = TK.Label(infoTab) + separator2.config(bg = SCvar_gui.color.mainBG) + separator2.config(height = SCvar_gui.dimension.separator2Height) + separator2.grid(row = 5) + + # Copyright + copyrightInfo = TK.Label(infoTab) + copyrightInfo.config(text = '© ' + SCvar_inf.years) + copyrightInfo.config(font = (SCvar_gui.fonts.info1[0], SCvar_gui.fonts.info1[3])) + copyrightInfo.config(background = SCvar_gui.color.mainBG) + copyrightInfo.config(foreground = SCvar_gui.color.headerText) + copyrightInfo.grid(row = 6) + + # Autorzy + authors = '' + for x in SCvar_inf.authors: + authors += (x + '\n') + authors += ('dla ' + SCvar_inf.school) + authorsInfo = TK.Label(infoTab) + authorsInfo.config(text = authors) + authorsInfo.config(font = (SCvar_gui.fonts.info1[0], SCvar_gui.fonts.info1[4])) + authorsInfo.config(background = SCvar_gui.color.mainBG) + authorsInfo.config(foreground = SCvar_gui.color.headerText) + authorsInfo.grid(row = 7) + + # Separator3 + separator3 = TK.Label(infoTab) + separator3.config(bg = SCvar_gui.color.mainBG) + separator3.config(height = SCvar_gui.dimension.separator3Height) + separator3.grid(row = 8) + + + + + # Mainloop + root.mainloop() + + +# Inicjacja okna +gui() \ No newline at end of file diff --git a/modules/__init__.py b/modules/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/modules/__pycache__/__init__.cpython-38.pyc b/modules/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 624116c0380c4bfb2728eb58cd17626ae5135615..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 164 zcmWIL<>g`kf=OO~V?p#|5P=LBfgA@QE@lA|DGb33nv8xc8Hzx{2;!?7kn3s{&ryk0@&Ee;!qs2#|P&p^xo0B36{DgXcg diff --git a/modules/__pycache__/dialog.cpython-38.pyc b/modules/__pycache__/dialog.cpython-38.pyc deleted file mode 100644 index dea199b5a4766aae7e48700fe86c01fcb2a6d5cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4266 zcmcgv&2QX96u0*y**Hll{h$O2ol-zGg4OP}6oC+>v?&UZG-3;tR;j|oo@^X@J*%;Y zjaTIoHR2!OODagI#D#OO+?xF-_R6V84jhsAjlGH1?JnAt(%P~;@6F76KfjNeH($-p z8XB(MzyH1c%`r{;OA-B74#dZJg5Pybv$dL5*J?V`cQdwbXX=^zx~|1kHbv!9R6a#b z*pq$p3aP%SRG*Qerc=~RikeMPM^e;tDe7p7dOk%ROHs#D)QJ?%lPT(j6m=>^y_llr z_GQ9~S(b$rU+UxJSPq<*W1FhhY1Oh{v0wG_wF!LBc$1AnZ3;YMpS`Qq4Er_v9KO@` z>-HP?&e*S3HST;{iTrfQuMx;_IzfayFbs_Dx!#a0k20Cqzl3I0{RQVeBSj#ZZfw{fIDEF>vDdWxOFZXG?e6g(kQMs!Y5s;JX=woEt&F%#~nLZ zf(odfLk}z*kF?Mst+wwxe9$Ty3q4mO_7u+rleiuYl+&LWPJGPWWI;#}bt+KGJz(V$ zET(cS6>*0SjN8aLicA_4+yztq{M5nuwB2$7myU_;qA7oR>e!ry!+d94Xz!Zx$EOYr z%b??r>(EPPyl9RPZObyiqMa-)13i;uRvBw%QGPkv-wu=MQ{+{I{PLXryg=?3iUKO$?r z0*KpIn+7D=xT#tRM=7ul>M88HpN%7S(8g!(yEop%zZgf{u!=+B>lGMs1p=3OBkJ7q z)F$1gO(&o}KWkh=T?&pHK71e|YlE~K4)sZccm8Cka#fKRHE3N`U`-UBEqtYr)&`Gv z3@`<+3)+zKZ78^kS`fQ_leJKyS{oKac`SIli2`|V2cqh#yyh(!m|d17XP}BIB$flsY+qSF)Kr zQnZ=x>hsoCp0rPV_vZqe4B06CKnUHl2A zYyN|k8$P=xoNw96g734HWx+S$hG^fUsGwF3&Lac|l9dKd3mywrlEXo<8FjPJdlSL} zC05gq>NB{7I-aJ?q%i04#AYg@5o)_SOqJO;RcxMw%ECNvJ!YQVQq5TKlX7p9#wHP2 zh%xg*iLDis2^I*{IN=&L=y*8z0#Wsk4Jb6u_ubaj2+h_!8xTc>_xM$EBj{7uv z`*GjT2%Iphnw`a?oCuVY%TcJJ>*jScn?ReUfy-Ts`WExL`W0X*TnXI4kT8@%Ne|Ld zJ(m$jK%2AO$<;3LhrE{YZH#P~nj~nbVJqbLTJ~kD(jWh5XD+VX9F04~0BJPyhe` diff --git a/modules/__pycache__/load_config.cpython-38.pyc b/modules/__pycache__/load_config.cpython-38.pyc deleted file mode 100644 index 9d50b02de85fa98c1f744470895c81d4111165d9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2665 zcmb_e&u<$=6rR~#+Z)G8oQ5W8DPpQ3s1XfLMJ0$(g_NYIR1IxiNH8wZW@l>FUhlfI zyLG&BE={W*L5KsW5=zw*XE<^r`3E@ewOsrMAR#0q-WzWcD@|{BwQqjBdGlu9``$PH zY-}t~;CJ@j(sdV zA+6Ativr5yqW6^|3HwwG#VtM`p zc7fGp#A8S7Jya8*~-13-UHb2 zY_x_3j$pB8xgvIa&u0z6JyG{r%v#F*@4s|dTl#frwIX(Yu<=~GTbg5N*z)&&^DH>x z-UEyK9c%aT!Q$v7Y<|t>%Gr-RT42lN@@Ba_KOen{ZY|r{d*V3_w!CtC`4+PvXq|3j z@5%1ts5Lkioa*4LtnG6 z@HS}T*^u-AgPb;XIUPbZY|||Y_@5r&KGb$}uBW&L^u^_n_6Rplfz6i2jhggpv2u0fyfK0%v=ZMvA zBd{I6BcuUa{!HX6(EN23#PUMzQ%@{O=f0?2^*m9#A^nDELK&w80ku&(AO!}jP{W#W zMNo5ni?7?NLZMia(M5E=sK%FI=U@Q@(KK3sCrgVu_GZ(5mJZJsQ~hu&r@`9BE%tvH z9OPkz!JnWL7ee4+z!UQBMT3MCxD&vMkHVjpW}-bbw~xu%8bNDdXYHF$wrG#;7*EMg zh6C2Nv^v?+d)gAYGrMK<^qp+a08REO<$xgE^WH|7>*>h6J$;pEz_mBYE%FSqjV*93 z=G|QRr!ZGb17zQ=4-$`&PpC|2KGqhP#E&YOiQ%^)$CFnaSFHHq6+iO$GNx2cfL<2S z#*o6|NhSbp43n(Y1_Q2wRLV#MN}_|0Bs=gY5l$-ED%AslWF=n%VZj@m20>IB#_2dF zKr29NP?Ju>tJb|h0V80d!aoNf9uNRrQw1nMOaqobra^-=Tuei*?-+bP4Sx^K?Wf_- zH3GrbdKv`S2(wVu8Wzk@SFJw0MD5eDn$jAT+cx5xAfjPUcp0fm|BrD5I%(hISajbY7y=@VE>jM zc01?G@6MM15SW(uqe#N>kBtu&~#S#1^hTu*Zf+BbW1Occa6YN9Kpr?nPY0v-^--30e z$t1VF&V74*9nR1V60K56aw!!_WK(t0WD5UK94VqC)0T=o9j_gQiP5p3f()?<{Fb JQS%*h>OU3fdmR7( diff --git a/modules/__pycache__/load_format.cpython-38.pyc b/modules/__pycache__/load_format.cpython-38.pyc deleted file mode 100644 index df0cdcc0b34098f88642af91f77bfa117e5c9379..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3541 zcmb_eO>7&-6`t8$E>{#qQ??UDikxohG&R#ir4&1k4MTNeQMTh)aYZGPSp~w1Gf`F~ zcd6kr6cGb zYg#Em$trPvsS&PMQldl&zC6jaJsOsYxYWbaV97mN8sSPe#ppvAahEvjFn1K@enm+s zO^H80Fx!Y{kMJ>`-lG#FZm0e`+A6s~m{LmrEWL)v${Jy#Ed4PlW!Mkt5AK)SX1!`xY+f@za7E4XnlQ2l>XD@!l{&Mns=L*khW2W^;T_zs+W=4z zZLeMSowm7ocn^@CZ?544o14C4Rk?4wj%#jn=I|}o^vznh?C*cuGV8+K5>~Bgo{NBI z>h0W&i4$w?;qM#^R6D$9F}GzMe9|A>d2v(VAul;oq+;VE5}CD8i=B0luAZi1=Ua#Bq1D0j)ewiA1=KNV{uKqKf??SR*mkS5JVIin0DYfHgOu*Jy;bPla+U+H|N{IzofdFAu>R>*Fmy0bZt$ z7YxBW(Z~DE6X0e0c#nqQjrZ~XaU33So&;`4LJ{^L0s*m^O6W|@$R}sVbKON(#|C4{z@5sLDQ2w9l;~mRC zdzwwKXV@g0V$ZT^c7{F2zQ;`VJo`R7%U)nVU@x*CvU4oQUSdCDFSA$J4Er(5vspH` zb*5wZF9rsj#cALLnb9UlMc-G&=SMc%S9d;#^Ec?{ulDu&`*C)I6RGYP0i4N3Du9^5 z2*i!^ODmEH3b%u#yS-5;7bU&b!;YRF_P*0ghUHSv2+Ny2yi+87yN5Bgmqsy$B7@=t z2uWupos;xcNzY4qLDJVGy(sDHl3tQDFX^hJ1xc4BU6J&zr0+_4N76SWElRo|DXf28 z(i@V#C22|0Ym&Yv>6?r z`YpHWI=tjq75fV!=3&A=Z-OXXUVq2o3&Q@0uitbWzJ5oz+q?oXG>%FTpw{~l*@Te1 zj)AMn8|ziqVjGcu5V7RwHB@Xd441C~a^o)`2-V=#6^*88N>%7dD0P~GzBKkhPiAP3 z-XD{V$9#t#4Oc|`)8BnA%=*@f+B{XL0lH>G?PEQlu2XRpc3H-)9z93~I#w94ehn2r z?Lp9ju!G6joJKqf3D!#k*F1!$F!^!@`obUtX;W#9;A{w6$}etoXh6H#LjpkxLRN=v zk&Y5jIBcgO94cKsP~ngMFihrlHoYWE2FjiSp-ovO3Z!E{CClV9I5b#7I)=x`So$VC zvmVn3{?bc;#yw0fBo*I`rLHu1-3rsO`IMxom7F0px6U0ozGzqZlItykFS7!=p11(B z1adDmf!>>FKo;dm-KxU`7FsH&iOV=ZMYp|#c+!s^Jm`41L)-rmDqtZRU}`Xpmg<5fx)hUL21&tvWs;!fcMMNq zYC!iC@dw~a4O0X)=1O~#-EDo9*a<-Zg?S8NuW^W@)rY9F3{*b(dZei?FZWzu{RF zZ$MSNi2`>V-h!flwdlrct>i|;Eo{Gq;wp;kC~lw_pljQ;9hqne+vB-p$V(}?8#zTp z5Rz6Ji%$27TeLpnYcf696mKIW-YbuJgv*TC-d~}w@d60E3=BF2#0?S}k%`CjG+Y#7 z8OG(tWpZPKx#f)wsW#dTaS=LYGQN0(0Jdd8%I0r&|+DPCfk%9e0FJ28; Oi{y0L_&GUaoca#}@J check > 1: - error = int('x') - except: - MDdlg.err(2) - # Linia 2 (utf-8) - - DostepneKodowanieWyjsciowe = ['utf-8'] - try: - if settings[1] not in DostepneKodowanieWyjsciowe: - error = int('x') - except: - MDdlg.err(3) - - # Linia 4 (int) - try: - x = int(settings[3]) - except: - MDdlg.err(17) - - # Linia 6 (int) - try: - x = int(settings[5]) - except: - MDdlg.err(18) - - # Linia 7 (int) - try: - x = int(settings[6]) - except: - MDdlg.err(19) - - -# Odczytywanie ustawień z pliku konfiguracyjnego -def read(): - try: - check = open('.\config.cfg') - except FileNotFoundError: - MDdlg.err(0) - except: - MDdlg.err(20) - else: - with open('.\config.cfg', 'r') as cfg: - config = cfg.read().split('\n') - settings = [] - for x in config: - settings.append(x.split(': ')[1]) - CheckConfig(settings) - return settings - - - -# Zapis ustawień do pliku konfiguracyjnego -def edit(settings): - CheckConfig(settings) - try: - check = open('.\config.cfg') - except FileNotFoundError: - MDdlg.err(0) - except: - MDdlg.err(20) - else: - SettingsToSave = [] - SettingsToSave.append('Ciemny motyw(0/1): ' + str(settings[0]) + '\n') - SettingsToSave.append('Kodowanie wyjsciowe: ' + str(settings[1]) + '\n') - SettingsToSave.append('Domena: ' + str(settings[2]) + '\n') - SettingsToSave.append('Quota: ' + str(settings[3]) + '\n') - SettingsToSave.append('Kraj: ' + str(settings[4]) + '\n') - SettingsToSave.append('Dlugosc liceum: ' + str(settings[5]) + '\n') - SettingsToSave.append('Dlugosc branzowej: ' + str(settings[6])) - with open('.\config.cfg', 'w') as cfg: - for x in SettingsToSave: - cfg.write(x) \ No newline at end of file diff --git a/modules/load_format.py b/modules/load_format.py deleted file mode 100644 index cc65371..0000000 --- a/modules/load_format.py +++ /dev/null @@ -1,249 +0,0 @@ -""" -# GeneratorCSV -# Wersja 4.0 Experimental -# by Mateusz Skoczek -# luty 2019 - grudzień 2019 -# dla ZSP Sobolew - -# -# Moduł zarządzający plikiem formatu -# -""" - - - - - - - - -# ----------------------------------- # Import bibliotek zewnętrznych i modułów # ------------------------------------ # - -# Biblioteki zewnętrzne -import sys as SS - - - -# Moduły składowe programu -try: - from modules import dialog as MDdlg -except ModuleNotFoundError: - print('Wystąpił krytyczny błąd!') - print('Nie znaleziono jednego z modułów programu (dialog.py). Nie można załadować programu') - print('Kod błędu: E00x0011') - wait = input('Naciśnij ENTER aby wyjść') - SS.exit(0) -except Exception as exc: - print('Wystąpił krytyczny błąd!') - print('Nieznany błąd podczas ładowania jednego z modułów programu (dialog.py). Nie można załadować programu.') - print('Treść błędu: ' + str(exc)) - print('Kod błędu: E00x0010') - wait = input('Naciśnij ENTER aby wyjść') - SS.exit(0) - - - - - - - - -# --------------------------------------------------- # Funkcje # ---------------------------------------------------- # - -# Wewnętrzna funkcja sprawdzająca błędy pliku formatu -def CheckFormat(Read, format): - poprawne = True - check = True - while check: - # Uczniowie - uczniowiefmt = '' - for x in format[0]: - uczniowiefmt += x - - # Sprawdzanie pustych linii - try: - if format[0].count('') > 0: - error = int('x') - except: - MDdlg.err(11) - if Read: - SS.exit(0) - else: - poprawne = False - break - - # Sprawdzanie ilości liter K - try: - if uczniowiefmt.count('K') != 1: - error = int('x') - except: - MDdlg.err(6) - if Read: - SS.exit(0) - else: - poprawne = False - break - - # Sprawdzanie ilości liter O - try: - if uczniowiefmt.count('O') != 1: - error = int('x') - except: - MDdlg.err(7) - if Read: - SS.exit(0) - else: - poprawne = False - break - - # Sprawdzanie ilości liter N - try: - if uczniowiefmt.count('N') != 1: - error = int('x') - except: - MDdlg.err(8) - if Read: - SS.exit(0) - else: - poprawne = False - break - - # Sprawdzanie ilości liter I - try: - if uczniowiefmt.count('I') != 1: - error = int('x') - except: - MDdlg.err(9) - if Read: - SS.exit(0) - else: - poprawne = False - break - - # Sprawdzanie ilości liter L - try: - if uczniowiefmt.count('L') != 1: - error = int('x') - except: - MDdlg.err(10) - if Read: - SS.exit(0) - else: - poprawne = False - break - - - # Nauczyciele - nauczycielefmt = '' - for x in format[1]: - nauczycielefmt += x - - # Sprawdzanie pustych linii - try: - if format[1].count('') > 0: - error = int('x') - except: - MDdlg.err(12) - if Read: - SS.exit(0) - else: - poprawne = False - break - - # Sprawdzanie ilości liter N - try: - if nauczycielefmt.count('N') != 1: - error = int('x') - except: - MDdlg.err(13) - if Read: - SS.exit(0) - else: - poprawne = False - break - - # Sprawdzanie ilości liter I - try: - if nauczycielefmt.count('I') != 1: - error = int('x') - except: - MDdlg.err(14) - if Read: - SS.exit(0) - else: - poprawne = False - break - - # Sprawdzanie ilości liter L - try: - if nauczycielefmt.count('L') != 1: - error = int('x') - except: - MDdlg.err(15) - if Read: - SS.exit(0) - else: - poprawne = False - break - - - # Sprawdzanie poprawności znaków - NiedozwoloneZnaki = ['1','2','3','4','5','6','7','8','9','0','W','E','R','T','Y','U','P','A','S','D','F','G','H','J','Z','C','V','B','M'] - try: - for x in NiedozwoloneZnaki: - if x in nauczycielefmt+uczniowiefmt: - error = int('x') - except: - MDdlg.err(16) - if Read: - SS.exit(0) - else: - poprawne = False - break - check = False - if not Read: - return poprawne - - - -# Odczytywanie ustawień z pliku formatu -def read(): - try: - check = open(r'.\format.fmt') - except FileNotFoundError: - MDdlg.err(5) - except: - MDdlg.err(21) - else: - with open(r'.\format.fmt', 'r') as fmt: - fmt = fmt.read().split('\n\n') - format = [] - for x in fmt: - format.append(x.split('\n')) - CheckFormat(True, format) - return format - - - -# Zapis ustawień do pliku formatu -def edit(format): - xformat = [] - for x in format: - xformat.append(x.split('\n')[:-1]) - if CheckFormat(False, xformat): - try: - check = open(r'.\format.fmt') - except FileNotFoundError: - MDdlg.err(5) - except: - MDdlg.err(21) - else: - FormatToSaveX = [] - for x in xformat: - FormatToSaveX.append('\n'.join(x)) - FormatToSave = FormatToSaveX[0] + '\n\n' + FormatToSaveX[1] - with open(r'.\format.fmt', 'w') as fmt: - fmt.write(FormatToSave) - return True - else: - return False diff --git a/readme.txt b/readme.txt deleted file mode 100644 index ae55ab1..0000000 --- a/readme.txt +++ /dev/null @@ -1,107 +0,0 @@ -Program tworzy pliki .csv potrzebne do stworzenia kont uczniów i nauczycieli na szkolnej poczcie i Office 365. -Obecnie program obsługuje tylko 4 pliki z danymi. Program tworzy pliki 'email.csv' do eksportu dla szkolnej poczty oraz 'office.csv' do eksportu dla kont office. -Obecna wersja: 3.0 -Autorzy: Mateusz Skoczek -dla ZSP Sobolew -luty 2019 - grudzień 2019 - - - - - -Format domyślny plików z danymi: - -Legenda: -X - Dane nieznaczące -Q - Pusta linia - -Uczniowie: -# , -# - -# Przykład: -# 1a BS Nowak, Adam 1234567u -# - -Nauczyciele: -# , -# - -# Przykład: -# Nowak, Adam 1234567 -# - -Format można edytować w pliku 'format.py'. Więcej info na dole. - - - - - -Format domyślny pliku 'office.csv': -Uczniowie: -# ,,,,uczeń,,,,,,,,,,Rzeczpospolita Polska - -# Przykład: -# adam.nowak@losobolew.pl,Adam,Nowak,Adam Nowak,uczeń,1a BS,,,,,,,,,Rzeczpospolita Polska - -Nauczyciele: -# ,,,,nauczyciel,,,,,,,,,,Rzeczpospolita Polska - -# Przykład: -# adam.nowak@losobolew.pl,Adam,Nowak,Adam Nowak,nauczyciel,,,,,,,,,,Rzeczpospolita Polska - - - - - -Format domyślny pliku 'email.csv': -Uczniowie: -# ,,500 -| -v -# .@losobolew.pl,:,500 - -# Przykład: -# adam.nowak2021bs@losobolew.pl,1234567u,500 - -Nauczyciele: -# ,,500 -| -v -# .@losobolew.pl,:,500 - -# Przykład: -# adam.nowak@losobolew.pl,1234567,500 - - - - - -Dalsze pojęcia: -błąd programu - błąd programu objawiający się komunikatem -krytyczny błąd programu - nieoczekiwany błąd programu nieobjawiający się komunikatem - - - - - -Pliki: - -changelog.txt -Informacje o zmianach w poszczególnych wersjach programu. - -generator.py -Główny plik programu. Jakiekolwiek naruszenie jego zawartości może spowodować krytyczny błąd programu. - -instrukcja.txt -Plik z instrukcją użytkowania. Usunięcie tego pliku spowoduje błąd programu. - -config.cfg -Plik zawiera ukryte ustawienia programu. Można go edytować, ale należy robić to z rozwagą. Usunięcie go spowoduje błąd programu. -1: Obsługiwane kodowania: 'utf-8', 'cp1252', 'iso-8859-1' - -moduly.py -Plik zawierający moduły niezbędne do działania programu. Usunięcie pliku spowoduje błąd programu. Naruszenie jego zawartości może spowodować krytyczny błąd programu. - -format.py -Plik ten jest skryptem przetwarzającym dane. W razie zmiany formatu pliku z danymi należy go edytować, lecz nie powinna tego robić osoba początkująca, gdyż błędny kod może spowodować krytyczny błąd programu lub niepożądane wyniki. Usunięcie pliku spowoduje błąd programu. \ No newline at end of file diff --git a/src/__pycache__/vars.cpython-38.pyc b/src/__pycache__/vars.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..38f86696ff4db832fb4e28b470cfff69f1a5ad25 GIT binary patch literal 2402 zcmaJ?+iv4T7`7e9cAO?@mudwFgrP_%5_H?Npe{nIsx5STK{wj$L9!x@o0&SMiJcug z?bf&KGjI);T=NLPOUx}-c>wPDW}IV_CD#1;599ey-~4`Fsn`mBf4y>k_^7NX{|I6J zSAcMcm3#_96jwP_h)T3K3ekSnToqD*=#X?s8Y#Avx-q=QR(s1ir4eO*8Z+m(^#bz# z_Kx%I>j7n9pGK+QgX-ee`QA_IFv*YA58#}O( zp6|G=rqhZq;vT)R?JX$Xen_K;g73id!cT>Ch?RT-LKRm<3K~(}0?}NZ6x<@wU4s+@ z)isHM;7x80Sa23y##v<{{;WLbHe0`&OI98SE8@&*cal10Xs ze$De<4gKEOqKkiSB%N*?_qcgYSrWz(FWyq0CEWCfX*Xuc?+Oz=ljPq=;4Z&zKZ)oG z3$JPW425VnS^N{dLe&N=4w&En^N*YMLN@J$b=ucBbbD~iM2+6TO2ny_s*fM3Un=Mn z4^OdqQqToV6c#X{Ln;yj2r+<=A|#VokSs_Bq!O_q*^o?9Mg!Kk84SZ02yMyrw3i2S zt`~ZVBhgb6J+(;DEfK@DiRqS!<*t#ETOl?al(UVogKZ*!C@MVK#EBy;Wn>A`6od%! ziy)dH!uv=EIj>il&@fR7CN$g(J8|^Pzu={dm=VeZTNS#dPe>RALf%aMiCsG%m9U#rj-%bCbNoGDT zvdW|(owygX%-m}1Huf7QVD|R*4)z;DbI>^6+Xr*JBOY9+yFD=aei$8{^4c&;XpeSM zN>FwvDvP4kmF0%P&bepwb-J<&MH$Xl$RWQOn#>!yv(NrF>(B3Iy)bITpaDEhz|#Ue zOMs+3t3cTI1De(RB%x{YKtO04B>P~jh|?}*p4>FH4x%7?Z)^p0Jn_n$>BA*ckWXd0 zz52~w7+p!z`%7>3DGTz~mQO{vDS(c7vN|5%R+!G~<-2_=&!L&G&k0XNu1sK$mnT7^ zqudlB3dWKF39dYGFYO_mfmz*CS-TU_({&Ju>M5Gh*#}TA;aObtL#*ToWQJ#=1sXmi zDn1@M)qe%5e^XUOa{81jE^_%tX4db^I7*YuYSAF3&hxX;8a%KH$ViU(8o2Yiakplr zbTTz~QDnQz4Sz785m}wHbkf36i8$WUtb(E1@?l>>v6Sz0(7p@nEMJl9=fZty+=#^Ll}so=4)2=gBXk)UG;)Ti@iL^+%$9 MiTE(+_o|iu0DwD8ssI20 literal 0 HcmV?d00001 diff --git a/src/vars.py b/src/vars.py new file mode 100644 index 0000000..7fdd26d --- /dev/null +++ b/src/vars.py @@ -0,0 +1,102 @@ +""" +# Generator CSV +# 4.0 Experimental +# by Mateusz Skoczek +# styczeń 2019 - luty 2020 +# dla ZSP Sobolew + +# +# Zmienne +# +""" + + + + + +# ----------------------- # Informacje o programie # ------------------------ # + +class prgInfo: + name = 'Generator CSV' # Nazwa programu + school = 'ZSP Sobolew' # Nazwa szkoły + version = '4.0 Experimental' # Wersja programu + years = '2019 - 2020' # Lata pracy na programem + authors = ['Mateusz Skoczek'] # Autorzy + + + + + +# ------------------- # Zmienne środowiska graficznego # -------------------- # + +class guiVars: + # Wymiary + class dimension: + # Karty + iconTab = 20 # Wielkość ikon w kartach + borderTab = 0 # Szerokość ramki kart + iconPaddingTab = 8 # Margines kart + tabWindowBorderWidth = 0 # Szerokość ramki okna kart + + # Nagłówki kart + tabHeaderHeight = 8 # Wysokość nagłówka + tabHeaderWidth = 80 # Szerokość nagłówka + + # Informacje + programIconInInfo = 100 # Szerokość/wysokość ikony + programIconInInfoPlace = 150 # Wysokość kontrolki zawierającej ikonę + separator1Height = 2 # Wysokość separator1 + separator2Height = 1 # Wysokość separator2 + separator3Height = 4 # Wysokość separator3 + + + # Kolory + class color: + # Głowne + mainBG = '#21242D' # Głowne tło + + # Karty + unselectedTabBG = '#21242D' # Niewybrana karta + selectedTabBG = '#333842' # Wybrana karta + + # Nagłowki kart + headerBG = '#282C34' # Tło + headerText = '#C0C0C0' # Tekst + + + # Grafika + class image: + # Ikona programu + programIcon = 'assets/icon.ico' + programIconOther = 'assets/other_images/icon.png' + + # Ikony kart + iconTab = 'assets/tab_icons/icon.png' + generateTab = 'assets/tab_icons/generate.png' + linkTab = 'assets/tab_icons/link.png' + mergeTab = 'assets/tab_icons/merge.png' + settingsTab = 'assets/tab_icons/settings.png' + infoTab = 'assets/tab_icons/info.png' + + + # Czcionki + class fonts: + # Główne + tabHeader = ['Segoe UI', 12] # Nagłowki + + # Informacje + info1 = ['Segoe UI'] # Czcionka + info1.append(20) # Wielkość tekstu - Nazwa programu + info1.append(10) # Wielkość tekstu - Wersja programu + info1.append(8) # Wielkość tekstu - Copyright + info1.append(8) # Wielkość tekstu - Autorzy + + + # Inne + class other: + # Ustawienia okna + windowHeightResize = False # Rozszerzanie okna w pionie + windowWidthResize = False # Rozszerzanie okna w poziomie + + # Ustawienia kart + tabPosition = 'wn' # Pozycja kart \ No newline at end of file From cdd5a2f8eab48117b181f2bfa7536a6839220114 Mon Sep 17 00:00:00 2001 From: Mateusz Skoczek Date: Thu, 6 Aug 2020 18:35:18 +0200 Subject: [PATCH 12/29] 4.0 Alpha (Build 20017) --- assets/tab_icons/format.png | Bin 0 -> 426 bytes changelog-UC.txt | 5 +- config/format.cfg | 9 ++ format_readme.txt | 0 generator.py | 193 ++++++++++++++++++++++++---- src/__pycache__/vars.cpython-38.pyc | Bin 2402 -> 0 bytes src/load_cfg.py | 0 src/vars.py | 38 +++++- 8 files changed, 212 insertions(+), 33 deletions(-) create mode 100644 assets/tab_icons/format.png create mode 100644 config/format.cfg create mode 100644 format_readme.txt delete mode 100644 src/__pycache__/vars.cpython-38.pyc create mode 100644 src/load_cfg.py diff --git a/assets/tab_icons/format.png b/assets/tab_icons/format.png new file mode 100644 index 0000000000000000000000000000000000000000..449443debafa5636a248e3e0b16800a38a99e7c2 GIT binary patch literal 426 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTCmUKs7M+SzC{oH>NS%G}U;vjb? zhIQv;UIIA^$sR$z3=CCj3=9n|3=F@3LJcn%7)pVryh>nTu$sZZAYL$MSD+10f-TA0 z-33Sk!B6Mi^+1ZVz$3C4NPB>>+sSM@AS2t;#W5t~-rH-ATnz?1EDo>#|CjM^)#`HE zy3nGSSNEp(q*TTS=TC(yDk<&W8M}DtoNv3*%&&Pj zRdP`(kYX@0Ff!0JFw`|P3o$geGBvU?Hq +N, I X L X +Q + +Q + +Q \ No newline at end of file diff --git a/format_readme.txt b/format_readme.txt new file mode 100644 index 0000000..e69de29 diff --git a/generator.py b/generator.py index 3a34c22..3cc2356 100644 --- a/generator.py +++ b/generator.py @@ -125,7 +125,7 @@ def gui(): iconTabImg = PLitk.PhotoImage(iconTabImg) mainMenu.add(iconTab, image = iconTabImg, state = TK.DISABLED) - # TAB2 - Generowanie plików + # Generowanie plików generateTab = TK.Frame(mainMenu) generateTab.config(background = SCvar_gui.color.mainBG) generateTabImg = PLimg.open(SCvar_gui.image.generateTab) @@ -133,7 +133,7 @@ def gui(): generateTabImg = PLitk.PhotoImage(generateTabImg) mainMenu.add(generateTab, image = generateTabImg) - # TAB3 - Dołącz do pliku + # Dołączanie do pliku linkTab = TK.Frame(mainMenu) linkTab.config(background = SCvar_gui.color.mainBG) linkTabImg = PLimg.open(SCvar_gui.image.linkTab) @@ -141,7 +141,7 @@ def gui(): linkTabImg = PLitk.PhotoImage(linkTabImg) mainMenu.add(linkTab, image = linkTabImg) - # TAB4 - Łączenie plików + # Łączenie plików mergeTab = TK.Frame(mainMenu) mergeTab.config(background = SCvar_gui.color.mainBG) mergeTabImg = PLimg.open(SCvar_gui.image.mergeTab) @@ -149,7 +149,7 @@ def gui(): mergeTabImg = PLitk.PhotoImage(mergeTabImg) mainMenu.add(mergeTab, image = mergeTabImg) - # TAB5 - Ustawienia + # Ustawienia settingsTab = TK.Frame(mainMenu) settingsTab.config(background = SCvar_gui.color.mainBG) settingsTabImg = PLimg.open(SCvar_gui.image.settingsTab) @@ -157,7 +157,15 @@ def gui(): settingsTabImg = PLitk.PhotoImage(settingsTabImg) mainMenu.add(settingsTab, image = settingsTabImg) - # TAB6 - Informacje + # Format danych + formatTab = TK.Frame(mainMenu) + formatTab.config(background = SCvar_gui.color.mainBG) + formatTabImg = PLimg.open(SCvar_gui.image.formatTab) + formatTabImg = formatTabImg.resize((SCvar_gui.dimension.iconTab, SCvar_gui.dimension.iconTab), PLimg.ANTIALIAS) + formatTabImg = PLitk.PhotoImage(formatTabImg) + mainMenu.add(formatTab, image = formatTabImg) + + # Informacje infoTab = TK.Frame(mainMenu) infoTab.config(background = SCvar_gui.color.mainBG) infoTabImg = PLimg.open(SCvar_gui.image.infoTab) @@ -207,28 +215,161 @@ def gui(): - # TAB5 - tab5Label = TK.Label(settingsTab) - tab5Label.config(text = 'USTAWIENIA') - tab5Label.config(font = (SCvar_gui.fonts.tabHeader[0], SCvar_gui.fonts.tabHeader[1])) - tab5Label.config(bg = SCvar_gui.color.headerBG) - tab5Label.config(fg = SCvar_gui.color.headerText) - tab5Label.config(bd = SCvar_gui.dimension.tabHeaderHeight) - tab5Label.config(width = SCvar_gui.dimension.tabHeaderWidth) - tab5Label.grid(row = 0) + # SETTINGSTAB + settingsTabLabel = TK.Label(settingsTab) + settingsTabLabel.config(text = 'USTAWIENIA') + settingsTabLabel.config(font = (SCvar_gui.fonts.tabHeader[0], SCvar_gui.fonts.tabHeader[1])) + settingsTabLabel.config(bg = SCvar_gui.color.headerBG) + settingsTabLabel.config(fg = SCvar_gui.color.headerText) + settingsTabLabel.config(bd = SCvar_gui.dimension.tabHeaderHeight) + settingsTabLabel.config(width = SCvar_gui.dimension.tabHeaderWidth) + settingsTabLabel.grid(row = 0) - # TAB6 - tab6Label = TK.Label(infoTab) - tab6Label.config(text = 'INFORMACJE') - tab6Label.config(font = (SCvar_gui.fonts.tabHeader[0], SCvar_gui.fonts.tabHeader[1])) - tab6Label.config(bg = SCvar_gui.color.headerBG) - tab6Label.config(fg = SCvar_gui.color.headerText) - tab6Label.config(bd = SCvar_gui.dimension.tabHeaderHeight) - tab6Label.config(width = SCvar_gui.dimension.tabHeaderWidth) - tab6Label.grid(row = 0) + # FORMATTAB + formatTabLabel = TK.Label(formatTab) + formatTabLabel.config(text = 'FORMAT DANYCH') + formatTabLabel.config(font = (SCvar_gui.fonts.tabHeader[0], SCvar_gui.fonts.tabHeader[1])) + formatTabLabel.config(bg = SCvar_gui.color.headerBG) + formatTabLabel.config(fg = SCvar_gui.color.headerText) + formatTabLabel.config(bd = SCvar_gui.dimension.tabHeaderHeight) + formatTabLabel.config(width = SCvar_gui.dimension.tabHeaderWidth) + formatTabLabel.grid(row = 0) + + + # Labelframe - Pliki wejściowe + inFilesLabelFrame = TK.LabelFrame(formatTab) + inFilesLabelFrame.config(text = ' Pliki wejściowe ') + inFilesLabelFrame.config(bg = SCvar_gui.color.mainBG) + inFilesLabelFrame.config(fg = SCvar_gui.color.lfText) + inFilesLabelFrame.config(bd = SCvar_gui.dimension.lfBorderwidth) + inFilesLabelFrame.grid(row = 1, pady = SCvar_gui.dimension.framePadY) + + # UczniowieLABEL + inStudentsLABEL = TK.Label(inFilesLabelFrame) + inStudentsLABEL.config(text = 'Uczniowie') + inStudentsLABEL.config(bg = SCvar_gui.color.mainBG) + inStudentsLABEL.config(fg = SCvar_gui.color.label1) + inStudentsLABEL.grid(row = 0, column = 0) + + # Uczniowie inFormatInput + inStudentsFormatInput = TK.Text(inFilesLabelFrame) + inStudentsFormatInput.config(bg = SCvar_gui.color.textboxBG) + inStudentsFormatInput.config(fg = SCvar_gui.color.textboxText) + inStudentsFormatInput.config(bd = SCvar_gui.dimension.tbBorderwidth) + inStudentsFormatInput.config(width = SCvar_gui.dimension.tbWidth) + inStudentsFormatInput.config(height = SCvar_gui.dimension.tbHeight) + inStudentsFormatInput.grid(row = 1, column = 0, padx = SCvar_gui.dimension.tbPad, pady = SCvar_gui.dimension.tbPad) + + # NauczycieleLABEL + inTeachersLABEL = TK.Label(inFilesLabelFrame) + inTeachersLABEL.config(text = 'Nauczyciele') + inTeachersLABEL.config(bg = SCvar_gui.color.mainBG) + inTeachersLABEL.config(fg = SCvar_gui.color.label1) + inTeachersLABEL.grid(row = 0, column = 1) + + # Nauczyciele inFormatInput + inTeachersFormatInput = TK.Text(inFilesLabelFrame) + inTeachersFormatInput.config(bg = SCvar_gui.color.textboxBG) + inTeachersFormatInput.config(fg = SCvar_gui.color.textboxText) + inTeachersFormatInput.config(bd = SCvar_gui.dimension.tbBorderwidth) + inTeachersFormatInput.config(width = SCvar_gui.dimension.tbWidth) + inTeachersFormatInput.config(height = SCvar_gui.dimension.tbHeight) + inTeachersFormatInput.grid(row = 1, column = 1, padx = SCvar_gui.dimension.tbPad, pady = SCvar_gui.dimension.tbPad) + + + # Labelframe - Pliki wejściowe + outFilesLabelFrame = TK.LabelFrame(formatTab) + outFilesLabelFrame.config(text = ' Pliki wyjściowe ') + outFilesLabelFrame.config(bg = SCvar_gui.color.mainBG) + outFilesLabelFrame.config(fg = SCvar_gui.color.lfText) + outFilesLabelFrame.config(bd = SCvar_gui.dimension.lfBorderwidth) + outFilesLabelFrame.grid(row = 2, pady = SCvar_gui.dimension.framePadY) + + # UczniowieLABEL + outStudentsLABEL = TK.Label(outFilesLabelFrame) + outStudentsLABEL.config(text = 'Uczniowie') + outStudentsLABEL.config(bg = SCvar_gui.color.mainBG) + outStudentsLABEL.config(fg = SCvar_gui.color.label1) + outStudentsLABEL.grid(row = 0, column = 0) + + # Uczniowie outFormatInput + outStudentsFormatInput = TK.Entry(outFilesLabelFrame) + outStudentsFormatInput.config(bg = SCvar_gui.color.textboxBG) + outStudentsFormatInput.config(fg = SCvar_gui.color.textboxText) + outStudentsFormatInput.config(bd = SCvar_gui.dimension.tbBorderwidth) + outStudentsFormatInput.config(width = SCvar_gui.dimension.tbWidth2) + outStudentsFormatInput.grid(row = 1, column = 0, padx = SCvar_gui.dimension.tbPad, pady = SCvar_gui.dimension.tbPad) + + # NauczycieleLABEL + outTeachersLABEL = TK.Label(outFilesLabelFrame) + outTeachersLABEL.config(text = 'Nauczyciele') + outTeachersLABEL.config(bg = SCvar_gui.color.mainBG) + outTeachersLABEL.config(fg = SCvar_gui.color.label1) + outTeachersLABEL.grid(row = 0, column = 1) + + # Nauczyciele outFormatInput + outTeachersFormatInput = TK.Entry(outFilesLabelFrame) + outTeachersFormatInput.config(bg = SCvar_gui.color.textboxBG) + outTeachersFormatInput.config(fg = SCvar_gui.color.textboxText) + outTeachersFormatInput.config(bd = SCvar_gui.dimension.tbBorderwidth) + outTeachersFormatInput.config(width = SCvar_gui.dimension.tbWidth2) + outTeachersFormatInput.grid(row = 1, column = 1, padx = SCvar_gui.dimension.tbPad, pady = SCvar_gui.dimension.tbPad) + + + # Frame - Przyciski + formatButtonsFrame = TK.Frame(formatTab) + formatButtonsFrame.config(bg = SCvar_gui.color.mainBG) + formatButtonsFrame.grid(row = 3, pady = SCvar_gui.dimension.framePadY) + + # Zapisz + saveFormatButton = TK.Button(formatButtonsFrame) + saveFormatButton.config(text = 'ZAPISZ') + saveFormatButton.config(bg = SCvar_gui.color.buttonBG) + saveFormatButton.config(fg = SCvar_gui.color.buttonText) + saveFormatButton.config(relief = TK.FLAT) + saveFormatButton.config(activebackground = SCvar_gui.color.buttonBG) + saveFormatButton.config(activeforeground = SCvar_gui.color.buttonText) + saveFormatButton.config(height = SCvar_gui.dimension.bnHeight) + saveFormatButton.config(width = SCvar_gui.dimension.bnWidth) + saveFormatButton.grid(row = 0, column = 0, padx = 5) + + # Pomoc + def saveFormatButtonCommand(): + try: + x = open('format_readme.txt') + except FileNotFoundError: + print('x') + except: + print('x') + else: + OS.system("notepad format_readme.txt") + instructionFormatButton = TK.Button(formatButtonsFrame) + instructionFormatButton.config(text = 'POMOC') + instructionFormatButton.config(bg = SCvar_gui.color.buttonBG) + instructionFormatButton.config(fg = SCvar_gui.color.buttonText) + instructionFormatButton.config(relief = TK.FLAT) + instructionFormatButton.config(activebackground = SCvar_gui.color.buttonBG) + instructionFormatButton.config(activeforeground = SCvar_gui.color.buttonText) + instructionFormatButton.config(height = SCvar_gui.dimension.bnHeight) + instructionFormatButton.config(width = SCvar_gui.dimension.bnWidth2) + instructionFormatButton.config(command = saveFormatButtonCommand) + instructionFormatButton.grid(row = 0, column = 1, padx = 5) + + + + + # INFOTAB + infoTabLabel = TK.Label(infoTab) + infoTabLabel.config(text = 'INFORMACJE') + infoTabLabel.config(font = (SCvar_gui.fonts.tabHeader[0], SCvar_gui.fonts.tabHeader[1])) + infoTabLabel.config(bg = SCvar_gui.color.headerBG) + infoTabLabel.config(fg = SCvar_gui.color.headerText) + infoTabLabel.config(bd = SCvar_gui.dimension.tabHeaderHeight) + infoTabLabel.config(width = SCvar_gui.dimension.tabHeaderWidth) + infoTabLabel.grid(row = 0) # Separator1 separator1 = TK.Label(infoTab) @@ -277,12 +418,8 @@ def gui(): copyrightInfo.grid(row = 6) # Autorzy - authors = '' - for x in SCvar_inf.authors: - authors += (x + '\n') - authors += ('dla ' + SCvar_inf.school) authorsInfo = TK.Label(infoTab) - authorsInfo.config(text = authors) + authorsInfo.config(text = SCvar_inf.authors + '\ndla ' + SCvar_inf.school) authorsInfo.config(font = (SCvar_gui.fonts.info1[0], SCvar_gui.fonts.info1[4])) authorsInfo.config(background = SCvar_gui.color.mainBG) authorsInfo.config(foreground = SCvar_gui.color.headerText) diff --git a/src/__pycache__/vars.cpython-38.pyc b/src/__pycache__/vars.cpython-38.pyc deleted file mode 100644 index 38f86696ff4db832fb4e28b470cfff69f1a5ad25..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2402 zcmaJ?+iv4T7`7e9cAO?@mudwFgrP_%5_H?Npe{nIsx5STK{wj$L9!x@o0&SMiJcug z?bf&KGjI);T=NLPOUx}-c>wPDW}IV_CD#1;599ey-~4`Fsn`mBf4y>k_^7NX{|I6J zSAcMcm3#_96jwP_h)T3K3ekSnToqD*=#X?s8Y#Avx-q=QR(s1ir4eO*8Z+m(^#bz# z_Kx%I>j7n9pGK+QgX-ee`QA_IFv*YA58#}O( zp6|G=rqhZq;vT)R?JX$Xen_K;g73id!cT>Ch?RT-LKRm<3K~(}0?}NZ6x<@wU4s+@ z)isHM;7x80Sa23y##v<{{;WLbHe0`&OI98SE8@&*cal10Xs ze$De<4gKEOqKkiSB%N*?_qcgYSrWz(FWyq0CEWCfX*Xuc?+Oz=ljPq=;4Z&zKZ)oG z3$JPW425VnS^N{dLe&N=4w&En^N*YMLN@J$b=ucBbbD~iM2+6TO2ny_s*fM3Un=Mn z4^OdqQqToV6c#X{Ln;yj2r+<=A|#VokSs_Bq!O_q*^o?9Mg!Kk84SZ02yMyrw3i2S zt`~ZVBhgb6J+(;DEfK@DiRqS!<*t#ETOl?al(UVogKZ*!C@MVK#EBy;Wn>A`6od%! ziy)dH!uv=EIj>il&@fR7CN$g(J8|^Pzu={dm=VeZTNS#dPe>RALf%aMiCsG%m9U#rj-%bCbNoGDT zvdW|(owygX%-m}1Huf7QVD|R*4)z;DbI>^6+Xr*JBOY9+yFD=aei$8{^4c&;XpeSM zN>FwvDvP4kmF0%P&bepwb-J<&MH$Xl$RWQOn#>!yv(NrF>(B3Iy)bITpaDEhz|#Ue zOMs+3t3cTI1De(RB%x{YKtO04B>P~jh|?}*p4>FH4x%7?Z)^p0Jn_n$>BA*ckWXd0 zz52~w7+p!z`%7>3DGTz~mQO{vDS(c7vN|5%R+!G~<-2_=&!L&G&k0XNu1sK$mnT7^ zqudlB3dWKF39dYGFYO_mfmz*CS-TU_({&Ju>M5Gh*#}TA;aObtL#*ToWQJ#=1sXmi zDn1@M)qe%5e^XUOa{81jE^_%tX4db^I7*YuYSAF3&hxX;8a%KH$ViU(8o2Yiakplr zbTTz~QDnQz4Sz785m}wHbkf36i8$WUtb(E1@?l>>v6Sz0(7p@nEMJl9=fZty+=#^Ll}so=4)2=gBXk)UG;)Ti@iL^+%$9 MiTE(+_o|iu0DwD8ssI20 diff --git a/src/load_cfg.py b/src/load_cfg.py new file mode 100644 index 0000000..e69de29 diff --git a/src/vars.py b/src/vars.py index 7fdd26d..3712333 100644 --- a/src/vars.py +++ b/src/vars.py @@ -21,7 +21,7 @@ class prgInfo: school = 'ZSP Sobolew' # Nazwa szkoły version = '4.0 Experimental' # Wersja programu years = '2019 - 2020' # Lata pracy na programem - authors = ['Mateusz Skoczek'] # Autorzy + authors = 'Mateusz Skoczek' # Autorzy @@ -42,7 +42,23 @@ class guiVars: tabHeaderHeight = 8 # Wysokość nagłówka tabHeaderWidth = 80 # Szerokość nagłówka - # Informacje + # Labelframe'y + lfBorderwidth = 1 # Szerokość obramowania + + # Textbox'y + tbBorderwidth = 0 # Szerokość obramowania + + # FORMATTAB + framePadY = 10 + tbPad = 8 + tbWidth = 42 + tbHeight = 9 + tbWidth2 = 56 + bnHeight = 2 + bnWidth = 30 + bnWidth2 = 30 + + # INFOTAB programIconInInfo = 100 # Szerokość/wysokość ikony programIconInInfoPlace = 150 # Wysokość kontrolki zawierającej ikonę separator1Height = 2 # Wysokość separator1 @@ -60,9 +76,22 @@ class guiVars: selectedTabBG = '#333842' # Wybrana karta # Nagłowki kart - headerBG = '#282C34' # Tło + headerBG = '#323741' # Tło headerText = '#C0C0C0' # Tekst + # Labelframe'y + lfText = '#C0C0C0' # Tekst + + # Label'e + label1 = '#C0C0C0' # Tekst + + # Textbox'y + textboxBG = '#282B36' # Tło + textboxText = '#C0C0C0' # Tekst + + # Button'y + buttonBG = '#323741' # Tło + buttonText = '#C0C0C0' # Tekst # Grafika class image: @@ -76,6 +105,7 @@ class guiVars: linkTab = 'assets/tab_icons/link.png' mergeTab = 'assets/tab_icons/merge.png' settingsTab = 'assets/tab_icons/settings.png' + formatTab = 'assets/tab_icons/format.png' infoTab = 'assets/tab_icons/info.png' @@ -84,7 +114,7 @@ class guiVars: # Główne tabHeader = ['Segoe UI', 12] # Nagłowki - # Informacje + # INFOTAB info1 = ['Segoe UI'] # Czcionka info1.append(20) # Wielkość tekstu - Nazwa programu info1.append(10) # Wielkość tekstu - Wersja programu From f22f5522cf7fc2ae110fdc9e4d01078c52090ca9 Mon Sep 17 00:00:00 2001 From: Mateusz Skoczek Date: Thu, 6 Aug 2020 18:38:31 +0200 Subject: [PATCH 13/29] 4.0 Alpha (Build 20181) --- assets/icon.ico | Bin 112027 -> 143535 bytes assets/other_images/icon.png | Bin 4157 -> 0 bytes .../info.png => tab-icons/about.png} | Bin assets/{tab_icons => tab-icons}/format.png | Bin assets/{tab_icons => tab-icons}/generate.png | Bin assets/{tab_icons => tab-icons}/settings.png | Bin assets/tab_icons/icon.png | Bin 4157 -> 0 bytes assets/tab_icons/link.png | Bin 631 -> 0 bytes assets/tab_icons/merge.png | Bin 882 -> 0 bytes changelog-UC.txt | 13 +- config/format.cfg | 9 - default-configs/config.cfg | 1 + default-configs/style.cfg | 72 + format_readme.txt | 0 generator.py | 442 ----- generator.pyw | 1572 +++++++++++++++++ src/load_cfg.py | 0 src/vars.py | 132 -- 18 files changed, 1657 insertions(+), 584 deletions(-) delete mode 100644 assets/other_images/icon.png rename assets/{tab_icons/info.png => tab-icons/about.png} (100%) rename assets/{tab_icons => tab-icons}/format.png (100%) rename assets/{tab_icons => tab-icons}/generate.png (100%) rename assets/{tab_icons => tab-icons}/settings.png (100%) delete mode 100644 assets/tab_icons/icon.png delete mode 100644 assets/tab_icons/link.png delete mode 100644 assets/tab_icons/merge.png delete mode 100644 config/format.cfg create mode 100644 default-configs/config.cfg create mode 100644 default-configs/style.cfg delete mode 100644 format_readme.txt delete mode 100644 generator.py create mode 100644 generator.pyw delete mode 100644 src/load_cfg.py delete mode 100644 src/vars.py diff --git a/assets/icon.ico b/assets/icon.ico index 11e00aac42bb02dc40b1b21338d40411f1f97227..49f2657d956b72b61846b88ec66f59098ecfd126 100644 GIT binary patch literal 143535 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

x$L06^o(PZ#skbr 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 H$W{Lj$*EGf diff --git a/assets/other_images/icon.png b/assets/other_images/icon.png deleted file mode 100644 index 91780fd565e3f45a7ba99abab73ee0c77e6b2a63..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4157 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 mE3&9W+x>cahE-*C{wrT>5+NS%G}U;vjb? zhIQv;UIIA^$sR$z3=CCj3=9n|3=F@3LJcn%7)pVryh>nTu$sZZAYL$MSD+10f-TA0 z-33Sk!B6Mi^+1ZVz$3C4NPB>>+sSM@pz#)-E{-7?_ukGp8+h12pmqP*f>aMt71;?~ zlCu;SGM>A@WFn};RS{#sAun|M*T<|2<)3y5w%@)Oxr|x?=$tUY>s&1z>ufS#lVqPHr>17-yMU^KM%*Mf47-EWxDMJ z)-`8+-*OhOU;9k<1=BS(&xZClTYk8!W%BO{)UT^yWHs+k;p7V^)IaxoL zyX)hRgJ|2})eT)^aX@DOvsoS;7hg z29>4gZ3^yU*4uRX(Yc4F)@Q{OUutA(RP3P#Mut|VX4(b@Rt5&KHC$U!H00)|WTsW( z)}W)fi4&+n17t&SS-MqHVsWZ&X;CIaT4qkFmA<}yNp69DZen_7a<*=mhqJ%8YqB%z TwUd{DdKf%i{an^LB{Ts54AtY$ diff --git a/assets/tab_icons/merge.png b/assets/tab_icons/merge.png deleted file mode 100644 index 19d4ac50d9049d7633b54eea26f3e8dcc19e5ca1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 882 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTCmUKs7M+SzC{oH>NS%G}U;vjb? zhIQv;UIIA^$sR$z3=CCj3=9n|3=F@3LJcn%7)pVryh>nTu$sZZAYL$MSD+10f-TA0 z-33Sk!B6Mi^+1ZVz$3C4NPB>>+sSM@1_mZ$PZ!6KjC*fq?)E!uAkiwjAmq$}kP7x2 zznF4z6w=-a3Ay(4tnujav0>zI*s(_8U6X=hS=x~!xeQbL4~li^Sa0+W+8)$*MRDWn zIg|h2_*d`VA*AXx_^G@v}P=ALzCIF*T5wl|HS*u6wRj!+95e zJr{RdmH9T)_HN7*HxLYJFy|5b#bep{x#K{CK$7YdhWvgp3uE9EK;J~W=mIo}v+R91AI z-Q?DztBV?J;`;x;zIs6^OHwJx>MOImlgs{3Pg_4_?u*k6T;$TZ?uh=f%J(U=ANEND z(~)Y4YeY#(Vo9o1a#1RfVlXl=GSD?J)HO5;F|@P-A_FsR0|P4qgZV3!R8TbJ=BH$) zRpQq0?#nD-dS=i7*-%`TZk3c+oT^(|l*y2mnUiXzudiQ{TcDqtn4X!Ots9)@o)PHf VmF)D}^czqQgQu&X%Q~loCIHoeUjzUE diff --git a/changelog-UC.txt b/changelog-UC.txt index 05acea9..d1e8a5c 100644 --- a/changelog-UC.txt +++ b/changelog-UC.txt @@ -61,4 +61,15 @@ - Użycie skryptu instalującego automatycznie bibliotekę PIL 4.0 Alpha (Build 20017) -- Ukończenie wizualnej części karty 'format' \ No newline at end of file +- Ukończenie wizualnej części karty 'format' + +4.0 Alpha (Build 20181) +- Zmiana ikony +- Zmniejszenie liczby kart +- Wstępne ukończenie karty "FORMAT DANYCH" +- Zintegrowanie wszystkich plików składowych do głównego pliku programu +- Przeprojektowanie sposobu ładowania plików konfiguracyjnych +- Zastosowanie systemu styli do interfejsu +- Zastosowanie systemu zapisywania plików konfiguracyjnych i formatu w folderze %appdata% +- Usunięcie konsoli w tle +- Wiele mniejszych zmian \ No newline at end of file diff --git a/config/format.cfg b/config/format.cfg deleted file mode 100644 index ec3a521..0000000 --- a/config/format.cfg +++ /dev/null @@ -1,9 +0,0 @@ -K O N, I X L X -Q - -N, I X L X -Q - -Q - -Q \ No newline at end of file diff --git a/default-configs/config.cfg b/default-configs/config.cfg new file mode 100644 index 0000000..830a432 --- /dev/null +++ b/default-configs/config.cfg @@ -0,0 +1 @@ +secret = entersecretstringhere \ No newline at end of file diff --git a/default-configs/style.cfg b/default-configs/style.cfg new file mode 100644 index 0000000..f1ff54e --- /dev/null +++ b/default-configs/style.cfg @@ -0,0 +1,72 @@ +windowWidth = 1000 +windowHeight = 600 +windowWidthResizable = 0 +windowHeightResizable = 0 +windowMainBG = #21242D +mainIcon = assets/icon.ico +mainMenuBG = #21242D +mainMenuPosition = wn +tabIconsSize = 30 +generateTabIcon = assets/tab-icons/generate.png +tabFramesBorderWidth = 0 +unselectedTabBG = #21242D +menuTabsBorderWidth = 0 +menuTabsPadding = 10 +selectedTabBG = #333842 +disabledTabBG = #333842 +headerFont = Segoe UI;15 +headerBG = #333842 +headerTextColor = #C0C0C0 +headerPadding = 10 +headerWidth = 84 +tabFrameBG = #21242D +formatTabIcon = assets/tab-icons/format.png +settingsTabIcon = assets/tab-icons/settings.png +aboutTabIcon = assets/tab-icons/about.png +tabFramePadding = 10 +label1BG = #21242D +label1TextColor = #C0C0C0 +headerTextAnchor = center +combobox1ArrowColor = #C0C0C0 +combobox1ButtonColor = #333842 +combobox1BorderColor = #21242D +combobox1FieldBackground = #333842 +combobox1TextColor = #C0C0C0 +combobox1Relief = flat +combobox1BorderWidth = 0 +combobox1Padding = 3 +combobox1ListBoxBackground = #333842 +combobox1ListBoxForeground = #C0C0C0 +combobox1ListBoxSelectBackground = #737373 +combobox1ListBoxSelectForeground = #FFFFFF +button1TextAnchor = center +button1Background = #333842 +button1Foreground = #C0C0C0 +button1Padding = 1 +editingPresetSaveButtonWidth = 25 +editingPresetCancelButtonWidth = 25 +loadingButtonWidth = 15 +loadingListWidth = 85 +label2Width = 15 +label2Anchor = center +spinbox1ArrowColor = #C0C0C0 +spinbox1FieldBackground = #333842 +spinbox1Relief = flat +spinbox1BorderWidth = 0 +spinbox1TextColor = #C0C0C0 +spinbox1ButtonColor = #333842 +radiobutton1Background = #21242D +radiobutton1TextColor = #C0C0C0 +entry1FieldBackground = #333842 +entry1Relief = flat +entry1BorderWidth = 0 +entry1Padding = 3 +text1Background = #333842 +text1TextColor = #C0C0C0 +text1Relief = flat +entry1TextColor = #C0C0C0 +label2BG = #21242D +label2TextColor = #C0C0C0 +label3BG = #21242D +label3TextColor = #C0C0C0 +label3Anchor = w \ No newline at end of file diff --git a/format_readme.txt b/format_readme.txt deleted file mode 100644 index e69de29..0000000 diff --git a/generator.py b/generator.py deleted file mode 100644 index 3cc2356..0000000 --- a/generator.py +++ /dev/null @@ -1,442 +0,0 @@ -""" -# Generator CSV -# 4.0 Experimental -# by Mateusz Skoczek -# styczeń 2019 - luty 2020 -# dla ZSP Sobolew - -# -# Główny plik programu -# -""" - - - - - -# -------------------- # Import bibliotek zewnętrznych # -------------------- # - -# Biblioteki główne -import os as OS -import sys as SS -import time as TM - - - -# Framework i inne biblioteki interfejsu graficznego -import tkinter as TK -from tkinter import ttk as TKttk -from tkinter import filedialog as TKfld - -try: - from PIL import ImageTk as PLitk - from PIL import Image as PLimg -except ModuleNotFoundError: - OS.system("python -m pip install pip") - OS.system("python -m pip install Pillow") - OS.system("cls") - from PIL import ImageTk as PLitk - from PIL import Image as PLimg - - - - - -# ------------------ # Import plików składowych programu # ------------------ # - -# Funkcja tworząca plik zawierający logi błędu -def excpt(filename, importingFilename, errorcode, exceptInfo): - filepath = './crashlogs/crash_' + str(TM.localtime()[2]) + str(TM.localtime()[1]) + str(TM.localtime()[0]) + str(TM.localtime()[3]) + str(TM.localtime()[4]) + str(TM.localtime()[5]) + '.txt' - try: - OS.mkdir('./crashlogs') - except: - pass - crashfile = open(filepath, 'w') - crashfile.write('CRASH!\n') - crashfile.write('An error occurred while loading the component file: %s\n' % importingFilename) - crashfile.write('In file: %s\n' % filename) - crashfile.write('%s\n' % exceptInfo) - crashfile.write('Errorcode: %s' % errorcode) - crashfile.close() - SS.exit(0) - - - -# vars.py -try: - from src.vars import prgInfo as SCvar_inf - from src.vars import guiVars as SCvar_gui -except Exception as exceptInfo: - excpt('generator.py', 'variables.py', 'E000000', exceptInfo) #TODO Kod - - - - - -# -------------------------------- # Okno # --------------------------------- # - -# Budowa okna -def gui(): - # Ustawienia okna - root = TK.Tk() - root.title(SCvar_inf.name + " " + SCvar_inf.version) - root.resizable(width = SCvar_gui.other.windowWidthResize, height = SCvar_gui.other.windowHeightResize) - root.configure(bg = SCvar_gui.color.mainBG) - root.iconbitmap(SCvar_gui.image.programIcon) - - - - - # Motyw - TKttk.Style().theme_create("main", parent = "alt", settings = { - "mainMenu.TNotebook":{ - "configure": { - "background": SCvar_gui.color.mainBG, - "tabposition": SCvar_gui.other.tabPosition, - "borderwidth": SCvar_gui.dimension.tabWindowBorderWidth, - } - }, - "mainMenu.TNotebook.Tab":{ - "configure": { - "background": SCvar_gui.color.unselectedTabBG, - "borderwidth": SCvar_gui.dimension.borderTab, - "padding": SCvar_gui.dimension.iconPaddingTab, - }, - "map": { - "background": [("selected", SCvar_gui.color.selectedTabBG), ("disabled", SCvar_gui.color.headerBG)], - } - } - }) - TKttk.Style().theme_use("main") - - - - - # Menu główne - mainMenu = TKttk.Notebook(root) - mainMenu.config(style = "mainMenu.TNotebook") - mainMenu.grid(row = 1) - - # TAB1 - Ikona - iconTab = TK.Frame(mainMenu) - iconTab.config(background = SCvar_gui.color.mainBG) - iconTabImg = PLimg.open(SCvar_gui.image.iconTab) - iconTabImg = iconTabImg.resize((SCvar_gui.dimension.iconTab, SCvar_gui.dimension.iconTab), PLimg.ANTIALIAS) - iconTabImg = PLitk.PhotoImage(iconTabImg) - mainMenu.add(iconTab, image = iconTabImg, state = TK.DISABLED) - - # Generowanie plików - generateTab = TK.Frame(mainMenu) - generateTab.config(background = SCvar_gui.color.mainBG) - generateTabImg = PLimg.open(SCvar_gui.image.generateTab) - generateTabImg = generateTabImg.resize((SCvar_gui.dimension.iconTab, SCvar_gui.dimension.iconTab), PLimg.ANTIALIAS) - generateTabImg = PLitk.PhotoImage(generateTabImg) - mainMenu.add(generateTab, image = generateTabImg) - - # Dołączanie do pliku - linkTab = TK.Frame(mainMenu) - linkTab.config(background = SCvar_gui.color.mainBG) - linkTabImg = PLimg.open(SCvar_gui.image.linkTab) - linkTabImg = linkTabImg.resize((SCvar_gui.dimension.iconTab, SCvar_gui.dimension.iconTab), PLimg.ANTIALIAS) - linkTabImg = PLitk.PhotoImage(linkTabImg) - mainMenu.add(linkTab, image = linkTabImg) - - # Łączenie plików - mergeTab = TK.Frame(mainMenu) - mergeTab.config(background = SCvar_gui.color.mainBG) - mergeTabImg = PLimg.open(SCvar_gui.image.mergeTab) - mergeTabImg = mergeTabImg.resize((SCvar_gui.dimension.iconTab, SCvar_gui.dimension.iconTab), PLimg.ANTIALIAS) - mergeTabImg = PLitk.PhotoImage(mergeTabImg) - mainMenu.add(mergeTab, image = mergeTabImg) - - # Ustawienia - settingsTab = TK.Frame(mainMenu) - settingsTab.config(background = SCvar_gui.color.mainBG) - settingsTabImg = PLimg.open(SCvar_gui.image.settingsTab) - settingsTabImg = settingsTabImg.resize((SCvar_gui.dimension.iconTab, SCvar_gui.dimension.iconTab), PLimg.ANTIALIAS) - settingsTabImg = PLitk.PhotoImage(settingsTabImg) - mainMenu.add(settingsTab, image = settingsTabImg) - - # Format danych - formatTab = TK.Frame(mainMenu) - formatTab.config(background = SCvar_gui.color.mainBG) - formatTabImg = PLimg.open(SCvar_gui.image.formatTab) - formatTabImg = formatTabImg.resize((SCvar_gui.dimension.iconTab, SCvar_gui.dimension.iconTab), PLimg.ANTIALIAS) - formatTabImg = PLitk.PhotoImage(formatTabImg) - mainMenu.add(formatTab, image = formatTabImg) - - # Informacje - infoTab = TK.Frame(mainMenu) - infoTab.config(background = SCvar_gui.color.mainBG) - infoTabImg = PLimg.open(SCvar_gui.image.infoTab) - infoTabImg = infoTabImg.resize((SCvar_gui.dimension.iconTab, SCvar_gui.dimension.iconTab), PLimg.ANTIALIAS) - infoTabImg = PLitk.PhotoImage(infoTabImg) - mainMenu.add(infoTab, image = infoTabImg) - - - - - # TAB2 - tab2Label = TK.Label(generateTab) - tab2Label.config(text = 'GENEROWANIE PLIKÓW CSV') - tab2Label.config(font = (SCvar_gui.fonts.tabHeader[0], SCvar_gui.fonts.tabHeader[1])) - tab2Label.config(bg = SCvar_gui.color.headerBG) - tab2Label.config(fg = SCvar_gui.color.headerText) - tab2Label.config(bd = SCvar_gui.dimension.tabHeaderHeight) - tab2Label.config(width = SCvar_gui.dimension.tabHeaderWidth) - tab2Label.grid(row = 0) - - - - - # TAB3 - tab3Label = TK.Label(linkTab) - tab3Label.config(text = 'DOŁĄCZANIE DO PLIKU CSV') - tab3Label.config(font = (SCvar_gui.fonts.tabHeader[0], SCvar_gui.fonts.tabHeader[1])) - tab3Label.config(bg = SCvar_gui.color.headerBG) - tab3Label.config(fg = SCvar_gui.color.headerText) - tab3Label.config(bd = SCvar_gui.dimension.tabHeaderHeight) - tab3Label.config(width = SCvar_gui.dimension.tabHeaderWidth) - tab3Label.grid(row = 0) - - - - - # TAB4 - tab4Label = TK.Label(mergeTab) - tab4Label.config(text = 'ŁĄCZENIE PLIKÓW CSV') - tab4Label.config(font = (SCvar_gui.fonts.tabHeader[0], SCvar_gui.fonts.tabHeader[1])) - tab4Label.config(bg = SCvar_gui.color.headerBG) - tab4Label.config(fg = SCvar_gui.color.headerText) - tab4Label.config(bd = SCvar_gui.dimension.tabHeaderHeight) - tab4Label.config(width = SCvar_gui.dimension.tabHeaderWidth) - tab4Label.grid(row = 0) - - - - - # SETTINGSTAB - settingsTabLabel = TK.Label(settingsTab) - settingsTabLabel.config(text = 'USTAWIENIA') - settingsTabLabel.config(font = (SCvar_gui.fonts.tabHeader[0], SCvar_gui.fonts.tabHeader[1])) - settingsTabLabel.config(bg = SCvar_gui.color.headerBG) - settingsTabLabel.config(fg = SCvar_gui.color.headerText) - settingsTabLabel.config(bd = SCvar_gui.dimension.tabHeaderHeight) - settingsTabLabel.config(width = SCvar_gui.dimension.tabHeaderWidth) - settingsTabLabel.grid(row = 0) - - - - - # FORMATTAB - formatTabLabel = TK.Label(formatTab) - formatTabLabel.config(text = 'FORMAT DANYCH') - formatTabLabel.config(font = (SCvar_gui.fonts.tabHeader[0], SCvar_gui.fonts.tabHeader[1])) - formatTabLabel.config(bg = SCvar_gui.color.headerBG) - formatTabLabel.config(fg = SCvar_gui.color.headerText) - formatTabLabel.config(bd = SCvar_gui.dimension.tabHeaderHeight) - formatTabLabel.config(width = SCvar_gui.dimension.tabHeaderWidth) - formatTabLabel.grid(row = 0) - - - # Labelframe - Pliki wejściowe - inFilesLabelFrame = TK.LabelFrame(formatTab) - inFilesLabelFrame.config(text = ' Pliki wejściowe ') - inFilesLabelFrame.config(bg = SCvar_gui.color.mainBG) - inFilesLabelFrame.config(fg = SCvar_gui.color.lfText) - inFilesLabelFrame.config(bd = SCvar_gui.dimension.lfBorderwidth) - inFilesLabelFrame.grid(row = 1, pady = SCvar_gui.dimension.framePadY) - - # UczniowieLABEL - inStudentsLABEL = TK.Label(inFilesLabelFrame) - inStudentsLABEL.config(text = 'Uczniowie') - inStudentsLABEL.config(bg = SCvar_gui.color.mainBG) - inStudentsLABEL.config(fg = SCvar_gui.color.label1) - inStudentsLABEL.grid(row = 0, column = 0) - - # Uczniowie inFormatInput - inStudentsFormatInput = TK.Text(inFilesLabelFrame) - inStudentsFormatInput.config(bg = SCvar_gui.color.textboxBG) - inStudentsFormatInput.config(fg = SCvar_gui.color.textboxText) - inStudentsFormatInput.config(bd = SCvar_gui.dimension.tbBorderwidth) - inStudentsFormatInput.config(width = SCvar_gui.dimension.tbWidth) - inStudentsFormatInput.config(height = SCvar_gui.dimension.tbHeight) - inStudentsFormatInput.grid(row = 1, column = 0, padx = SCvar_gui.dimension.tbPad, pady = SCvar_gui.dimension.tbPad) - - # NauczycieleLABEL - inTeachersLABEL = TK.Label(inFilesLabelFrame) - inTeachersLABEL.config(text = 'Nauczyciele') - inTeachersLABEL.config(bg = SCvar_gui.color.mainBG) - inTeachersLABEL.config(fg = SCvar_gui.color.label1) - inTeachersLABEL.grid(row = 0, column = 1) - - # Nauczyciele inFormatInput - inTeachersFormatInput = TK.Text(inFilesLabelFrame) - inTeachersFormatInput.config(bg = SCvar_gui.color.textboxBG) - inTeachersFormatInput.config(fg = SCvar_gui.color.textboxText) - inTeachersFormatInput.config(bd = SCvar_gui.dimension.tbBorderwidth) - inTeachersFormatInput.config(width = SCvar_gui.dimension.tbWidth) - inTeachersFormatInput.config(height = SCvar_gui.dimension.tbHeight) - inTeachersFormatInput.grid(row = 1, column = 1, padx = SCvar_gui.dimension.tbPad, pady = SCvar_gui.dimension.tbPad) - - - # Labelframe - Pliki wejściowe - outFilesLabelFrame = TK.LabelFrame(formatTab) - outFilesLabelFrame.config(text = ' Pliki wyjściowe ') - outFilesLabelFrame.config(bg = SCvar_gui.color.mainBG) - outFilesLabelFrame.config(fg = SCvar_gui.color.lfText) - outFilesLabelFrame.config(bd = SCvar_gui.dimension.lfBorderwidth) - outFilesLabelFrame.grid(row = 2, pady = SCvar_gui.dimension.framePadY) - - # UczniowieLABEL - outStudentsLABEL = TK.Label(outFilesLabelFrame) - outStudentsLABEL.config(text = 'Uczniowie') - outStudentsLABEL.config(bg = SCvar_gui.color.mainBG) - outStudentsLABEL.config(fg = SCvar_gui.color.label1) - outStudentsLABEL.grid(row = 0, column = 0) - - # Uczniowie outFormatInput - outStudentsFormatInput = TK.Entry(outFilesLabelFrame) - outStudentsFormatInput.config(bg = SCvar_gui.color.textboxBG) - outStudentsFormatInput.config(fg = SCvar_gui.color.textboxText) - outStudentsFormatInput.config(bd = SCvar_gui.dimension.tbBorderwidth) - outStudentsFormatInput.config(width = SCvar_gui.dimension.tbWidth2) - outStudentsFormatInput.grid(row = 1, column = 0, padx = SCvar_gui.dimension.tbPad, pady = SCvar_gui.dimension.tbPad) - - # NauczycieleLABEL - outTeachersLABEL = TK.Label(outFilesLabelFrame) - outTeachersLABEL.config(text = 'Nauczyciele') - outTeachersLABEL.config(bg = SCvar_gui.color.mainBG) - outTeachersLABEL.config(fg = SCvar_gui.color.label1) - outTeachersLABEL.grid(row = 0, column = 1) - - # Nauczyciele outFormatInput - outTeachersFormatInput = TK.Entry(outFilesLabelFrame) - outTeachersFormatInput.config(bg = SCvar_gui.color.textboxBG) - outTeachersFormatInput.config(fg = SCvar_gui.color.textboxText) - outTeachersFormatInput.config(bd = SCvar_gui.dimension.tbBorderwidth) - outTeachersFormatInput.config(width = SCvar_gui.dimension.tbWidth2) - outTeachersFormatInput.grid(row = 1, column = 1, padx = SCvar_gui.dimension.tbPad, pady = SCvar_gui.dimension.tbPad) - - - # Frame - Przyciski - formatButtonsFrame = TK.Frame(formatTab) - formatButtonsFrame.config(bg = SCvar_gui.color.mainBG) - formatButtonsFrame.grid(row = 3, pady = SCvar_gui.dimension.framePadY) - - # Zapisz - saveFormatButton = TK.Button(formatButtonsFrame) - saveFormatButton.config(text = 'ZAPISZ') - saveFormatButton.config(bg = SCvar_gui.color.buttonBG) - saveFormatButton.config(fg = SCvar_gui.color.buttonText) - saveFormatButton.config(relief = TK.FLAT) - saveFormatButton.config(activebackground = SCvar_gui.color.buttonBG) - saveFormatButton.config(activeforeground = SCvar_gui.color.buttonText) - saveFormatButton.config(height = SCvar_gui.dimension.bnHeight) - saveFormatButton.config(width = SCvar_gui.dimension.bnWidth) - saveFormatButton.grid(row = 0, column = 0, padx = 5) - - # Pomoc - def saveFormatButtonCommand(): - try: - x = open('format_readme.txt') - except FileNotFoundError: - print('x') - except: - print('x') - else: - OS.system("notepad format_readme.txt") - instructionFormatButton = TK.Button(formatButtonsFrame) - instructionFormatButton.config(text = 'POMOC') - instructionFormatButton.config(bg = SCvar_gui.color.buttonBG) - instructionFormatButton.config(fg = SCvar_gui.color.buttonText) - instructionFormatButton.config(relief = TK.FLAT) - instructionFormatButton.config(activebackground = SCvar_gui.color.buttonBG) - instructionFormatButton.config(activeforeground = SCvar_gui.color.buttonText) - instructionFormatButton.config(height = SCvar_gui.dimension.bnHeight) - instructionFormatButton.config(width = SCvar_gui.dimension.bnWidth2) - instructionFormatButton.config(command = saveFormatButtonCommand) - instructionFormatButton.grid(row = 0, column = 1, padx = 5) - - - - - # INFOTAB - infoTabLabel = TK.Label(infoTab) - infoTabLabel.config(text = 'INFORMACJE') - infoTabLabel.config(font = (SCvar_gui.fonts.tabHeader[0], SCvar_gui.fonts.tabHeader[1])) - infoTabLabel.config(bg = SCvar_gui.color.headerBG) - infoTabLabel.config(fg = SCvar_gui.color.headerText) - infoTabLabel.config(bd = SCvar_gui.dimension.tabHeaderHeight) - infoTabLabel.config(width = SCvar_gui.dimension.tabHeaderWidth) - infoTabLabel.grid(row = 0) - - # Separator1 - separator1 = TK.Label(infoTab) - separator1.config(bg = SCvar_gui.color.mainBG) - separator1.config(height = SCvar_gui.dimension.separator1Height) - separator1.grid(row = 1) - - # Ikona - programIcon = PLimg.open(SCvar_gui.image.programIconOther) - programIcon = programIcon.resize((SCvar_gui.dimension.programIconInInfo, SCvar_gui.dimension.programIconInInfo), PLimg.ANTIALIAS) - programIcon = PLitk.PhotoImage(programIcon) - programIconPlace = TK.Label(infoTab) - programIconPlace.config(image = programIcon) - programIconPlace.config(background = SCvar_gui.color.mainBG) - programIconPlace.config(height = SCvar_gui.dimension.programIconInInfoPlace) - programIconPlace.grid(row = 2) - - # Nazwa programu - programName = TK.Label(infoTab) - programName.config(text = SCvar_inf.name) - programName.config(font = (SCvar_gui.fonts.info1[0], SCvar_gui.fonts.info1[1])) - programName.config(background = SCvar_gui.color.mainBG) - programName.config(foreground = SCvar_gui.color.headerText) - programName.grid(row = 3) - - # Wersja programu - programVersion = TK.Label(infoTab) - programVersion.config(text = 'Wersja ' + SCvar_inf.version) - programVersion.config(font = (SCvar_gui.fonts.info1[0], SCvar_gui.fonts.info1[2])) - programVersion.config(background = SCvar_gui.color.mainBG) - programVersion.config(foreground = SCvar_gui.color.headerText) - programVersion.grid(row = 4) - - # Separator2 - separator2 = TK.Label(infoTab) - separator2.config(bg = SCvar_gui.color.mainBG) - separator2.config(height = SCvar_gui.dimension.separator2Height) - separator2.grid(row = 5) - - # Copyright - copyrightInfo = TK.Label(infoTab) - copyrightInfo.config(text = '© ' + SCvar_inf.years) - copyrightInfo.config(font = (SCvar_gui.fonts.info1[0], SCvar_gui.fonts.info1[3])) - copyrightInfo.config(background = SCvar_gui.color.mainBG) - copyrightInfo.config(foreground = SCvar_gui.color.headerText) - copyrightInfo.grid(row = 6) - - # Autorzy - authorsInfo = TK.Label(infoTab) - authorsInfo.config(text = SCvar_inf.authors + '\ndla ' + SCvar_inf.school) - authorsInfo.config(font = (SCvar_gui.fonts.info1[0], SCvar_gui.fonts.info1[4])) - authorsInfo.config(background = SCvar_gui.color.mainBG) - authorsInfo.config(foreground = SCvar_gui.color.headerText) - authorsInfo.grid(row = 7) - - # Separator3 - separator3 = TK.Label(infoTab) - separator3.config(bg = SCvar_gui.color.mainBG) - separator3.config(height = SCvar_gui.dimension.separator3Height) - separator3.grid(row = 8) - - - - - # Mainloop - root.mainloop() - - -# Inicjacja okna -gui() \ No newline at end of file diff --git a/generator.pyw b/generator.pyw new file mode 100644 index 0000000..37f2a77 --- /dev/null +++ b/generator.pyw @@ -0,0 +1,1572 @@ +""" +# Generator CSV +# Wersja 4.0 +# Autorzy: Mateusz Skoczek +# Styczeń 2019 - Czerwiec 2020 +# dla ZSP Sobolew +""" + + + + + +# ----------------------------------------- # Zmienne # ----------------------------------------- # + +class VARS: + programName = 'Generator CSV' + programVersion = '4.0' + programCustomer = 'ZSP Sobolew' + programAuthors = ['Mateusz Skoczek'] + programToW = ['styczeń', 2019, 'wrzesień', 2020] + + + + + +# --------------------------- # Import wbudowanych bibliotek Pythona # -------------------------- # + + +# Główne +import sys as SS +import os as OS +import time as TM +import codecs as CD +import pathlib as PT +import shutil as SU + + +# GUI +import tkinter as TK +from tkinter import ttk as TKttk +from tkinter import messagebox as TKmsb + +from PIL import ImageTk as PLitk +from PIL import Image as PLimg + + + + + +# ---------------------------------------- # Komunikaty # --------------------------------------- # + + +MSGlist = { + 'E0000' : 'none', + 'E0001' : 'Wystąpił błąd podczas inicjalizacji katalogu z plikami konfiguracyjnymi programu w katalogu %APPDATA%', + 'E0002' : 'Wystąpił błąd podczas ładowania pliku konfiguracyjnego (config.cfg)', + 'E0003' : 'Niepoprawne dane w pliku konfiguracyjnym (config.cfg)', + 'E0004' : 'Wystąpił błąd podczas ładowania pliku stylu (style.cfg)', + 'E0005' : 'Niepoprawne dane w pliku stylu (style.cfg)', + 'E0006' : 'Niepoprawne dane w pliku formatu', + 'A0001' : 'Czy chcesz zapisać? Zostanie utworzony nowy plik', + 'A0002' : 'Czy chcesz zapisać? Plik zostanie nadpisany', +} + +def MSG(code, terminate, *optionalInfo): + try: + optionalInfo[0] + except: + optionalInfo = ('', '') + + # Błędy + if code[0] == 'E': + TKmsb.showerror('Wystąpił błąd!', '%s\n%s' % (MSGlist[code], optionalInfo[0])) + if terminate: + SS.exit(0) + + # Informacja + elif code[0] == 'I': + TKmsb.showerror('Informacja', '%s\n%s' % (MSGlist[code], optionalInfo[0])) + if terminate: + SS.exit(0) + + # Ostrzeżenie + elif code[0] == 'W': + TKmsb.showwarning('Ostrzeżenie', '%s\n%s' % (MSGlist[code], optionalInfo[0])) + if terminate: + SS.exit(0) + + # Zapytania + elif code[0] == 'A': + if TKmsb.askokcancel('Pytanie', '%s\n%s' % (MSGlist[code], optionalInfo[0])): + return True + else: + return False + + + + + +# ------------------------- # Sprawdzanie katalogu programu w APPDATA # ------------------------- # + +appdata = PT.Path.home() / 'Appdata/Roaming' +""" +#TODO +SU.rmtree(str(appdata) + '/Generator CSV') +#TODO +""" +if 'Generator CSV' not in [x for x in OS.listdir(appdata)]: + try: + OS.mkdir(str(appdata) + '/Generator CSV') + SU.copy('default-configs/config.cfg', str(appdata) + '\Generator CSV\config.cfg') + SU.copy('default-configs/style.cfg', str(appdata) + '\Generator CSV\style.cfg') + OS.mkdir(str(appdata) + '/Generator CSV/format-presets') + except Exception as exceptInfo: + MSG('E0001', True, exceptInfo) + + + + + +# ----------------------------- # Ładowanie pliku konfiguracyjnego # ---------------------------- # + +class CFG: + def __checkInstance(self, write): + if write: + try: + file = open((str(appdata) + '\Generator CSV\config.cfg'), 'a') + except Exception as exceptInfo: + MSG('E0002', False, exceptInfo) + return False + else: + if not file.writable(): + MSG('E0002', False, 'Plik tylko do odczytu') + return False + else: + return True + else: + try: + open(str(appdata) + '\Generator CSV\config.cfg') + except Exception as exceptInfo: + MSG('E0002', True, exceptInfo) + + def __checkContent(self, write, content): + if write: + return [True, content] + else: + class functions: + pass + return [True, content] + + def R(self): + self.__checkInstance(False) + content = {} + for x in CD.open((str(appdata) + '\Generator CSV\config.cfg'), 'r', 'utf-8').read().split('\n'): + x = x.split(' = ') + try: + content[x[0]] = (x[1]).strip('\r') + except: + continue + contentCheckingOutput = self.__checkContent(False, content) + if contentCheckingOutput[0]: + return contentCheckingOutput[1] + else: + MSG('E0003', True, contentCheckingOutput[1]) + + def W(self, changes): + content = self.R() + for x in changes: + content[x] = changes[x] + contentCheckingOutput = self.__checkContent(True, content) + if contentCheckingOutput[0]: + if self.__checkInstance(True): + with CD.open((str(appdata) + '\Generator CSV\config.cfg'), 'w', 'utf-8') as file: + contentToSave = contentCheckingOutput[1] + for x in contentToSave: + file.write('%s = %s\n' % (x, str(contentToSave[x]))) + else: + return False + else: + MSG('E0004', False, contentCheckingOutput[1]) +CFG = CFG() +checkInstance = CFG.R() + + + + + +# ---------------------------------- # Ładowanie pliku stylu # ---------------------------------- # + +class GUI: + def __checkInstance(self): + try: + open(str(appdata) + '\Generator CSV\style.cfg') + except Exception as exceptInfo: + MSG('E0004', True, exceptInfo) + + def __checkContent(self, content): + class functions: + def integer(self, var): + if var in list(content.keys()): + try: + check = int(content[var]) + except: + return [False, 'Niepoprawne dane - klucz: %s' % var] + else: + content[var] = int(content[var]) + return [True] + else: + return [False, 'Brak danych - klucz: %s' % var] + def bool(self, var): + if var in list(content.keys()): + if content[var] != '0' and content[var] != '1': + return [False, 'Niepoprawne dane - klucz: %s' % var] + else: + if content[var] == '0': + content[var] = False + return [True] + else: + content[var] = True + return [True] + else: + return [False, 'Brak danych - klucz: %s' % var] + def color(self, var): + if var in list(content.keys()): + if len(content[var]) != 7: + return [False, 'Niepoprawne dane - klucz: %s' % var] + else: + if content[var][0] != '#': + return [False, 'Niepoprawne dane - klucz: %s' % var] + else: + return [True] + else: + return [False, 'Brak danych - klucz: %s' % var] + def file(self, var): + if var in list(content.keys()): + try: + check = open(content[var]) + except: + return [False, 'Niepoprawne dane - klucz: %s' % var] + else: + return [True] + else: + return [False, 'Brak danych - klucz: %s' % var] + def fromArray(self, var, array): + if var in list(content.keys()): + if content[var] not in array: + return [False, 'Niepoprawne dane - klucz: %s' % var] + else: + return [True] + else: + return [False, 'Brak danych - klucz: %s' % var] + def font(self, var): + if var in list(content.keys()): + try: + check = int(content[var].split(';')[1]) + except: + return [False, 'Niepoprawne dane - klucz: %s' % var] + else: + content[var] = (content[var].split(';')[0], int(content[var].split(';')[1])) + return [True] + else: + return [False, 'Brak danych - klucz: %s' % var] + functions = functions() + check = functions.integer('windowWidth') + if not check[0]: + return check + check = functions.integer('windowHeight') + if not check[0]: + return check + check = functions.bool('windowWidthResizable') + if not check[0]: + return check + check = functions.bool('windowHeightResizable') + if not check[0]: + return check + check = functions.color('windowMainBG') + if not check[0]: + return check + check = functions.file('mainIcon') + if not check[0]: + return check + check = functions.color('mainMenuBG') + if not check[0]: + return check + check = functions.fromArray('mainMenuPosition', ['nw', 'ne', 'en', 'es', 'se', 'sw', 'ws', 'wn']) + if not check[0]: + return check + check = functions.integer('tabIconsSize') + if not check[0]: + return check + check = functions.file('generateTabIcon') + if not check[0]: + return check + check = functions.integer('tabFramesBorderWidth') + if not check[0]: + return check + check = functions.color('unselectedTabBG') + if not check[0]: + return check + check = functions.integer('menuTabsBorderWidth') + if not check[0]: + return check + check = functions.integer('menuTabsPadding') + if not check[0]: + return check + check = functions.color('selectedTabBG') + if not check[0]: + return check + check = functions.color('disabledTabBG') + if not check[0]: + return check + check = functions.font('headerFont') + if not check[0]: + return check + check = functions.color('headerBG') + if not check[0]: + return check + check = functions.color('headerTextColor') + if not check[0]: + return check + check = functions.integer('headerPadding') + if not check[0]: + return check + check = functions.integer('headerWidth') + if not check[0]: + return check + check = functions.color('tabFrameBG') + if not check[0]: + return check + check = functions.file('formatTabIcon') + if not check[0]: + return check + check = functions.integer('tabFramePadding') + if not check[0]: + return check + check = functions.color('label1BG') + if not check[0]: + return check + check = functions.color('label1TextColor') + if not check[0]: + return check + check = functions.fromArray('headerTextAnchor', ['center', 'nw', 'n', 'ne', 'w', 'e', 'sw', 's', 'se']) + if not check[0]: + return check + check = functions.color('combobox1ArrowColor') + if not check[0]: + return check + check = functions.color('combobox1ButtonColor') + if not check[0]: + return check + check = functions.color('combobox1BorderColor') + if not check[0]: + return check + check = functions.color('combobox1FieldBackground') + if not check[0]: + return check + check = functions.color('combobox1TextColor') + if not check[0]: + return check + check = functions.fromArray('combobox1Relief', ['flat', 'raised', 'sunken', 'groove', 'ridge']) + if not check[0]: + return check + check = functions.integer('combobox1BorderWidth') + if not check[0]: + return check + check = functions.integer('combobox1Padding') + if not check[0]: + return check + check = functions.color('combobox1ListBoxBackground') + if not check[0]: + return check + check = functions.color('combobox1ListBoxForeground') + if not check[0]: + return check + check = functions.color('combobox1ListBoxSelectBackground') + if not check[0]: + return check + check = functions.color('combobox1ListBoxSelectForeground') + if not check[0]: + return check + check = functions.fromArray('button1TextAnchor', ['center', 'nw', 'n', 'ne', 'w', 'e', 'sw', 's', 'se']) + if not check[0]: + return check + check = functions.color('button1Background') + if not check[0]: + return check + check = functions.color('button1Foreground') + if not check[0]: + return check + check = functions.integer('button1Padding') + if not check[0]: + return check + check = functions.integer('editingPresetSaveButtonWidth') + if not check[0]: + return check + check = functions.integer('editingPresetCancelButtonWidth') + if not check[0]: + return check + check = functions.integer('loadingButtonWidth') + if not check[0]: + return check + check = functions.integer('loadingListWidth') + if not check[0]: + return check + check = functions.integer('label2Width') + if not check[0]: + return check + check = functions.fromArray('label2Anchor', ['center', 'nw', 'n', 'ne', 'w', 'e', 'sw', 's', 'se']) + if not check[0]: + return check + check = functions.color('spinbox1ArrowColor') + if not check[0]: + return check + check = functions.color('spinbox1FieldBackground') + if not check[0]: + return check + check = functions.fromArray('spinbox1Relief', ['flat', 'raised', 'sunken', 'groove', 'ridge']) + if not check[0]: + return check + check = functions.integer('spinbox1BorderWidth') + if not check[0]: + return check + check = functions.color('spinbox1TextColor') + if not check[0]: + return check + check = functions.color('spinbox1ButtonColor') + if not check[0]: + return check + check = functions.color('radiobutton1Background') + if not check[0]: + return check + check = functions.color('radiobutton1TextColor') + if not check[0]: + return check + check = functions.color('entry1FieldBackground') + if not check[0]: + return check + check = functions.fromArray('entry1Relief', ['flat', 'raised', 'sunken', 'groove', 'ridge']) + if not check[0]: + return check + check = functions.integer('entry1BorderWidth') + if not check[0]: + return check + check = functions.integer('entry1Padding') + if not check[0]: + return check + check = functions.color('text1Background') + if not check[0]: + return check + check = functions.color('text1TextColor') + if not check[0]: + return check + check = functions.fromArray('text1Relief', ['flat', 'raised', 'sunken', 'groove', 'ridge']) + if not check[0]: + return check + check = functions.color('entry1TextColor') + if not check[0]: + return check + check = functions.color('label2BG') + if not check[0]: + return check + check = functions.color('label2TextColor') + if not check[0]: + return check + check = functions.color('label3BG') + if not check[0]: + return check + check = functions.color('label3TextColor') + if not check[0]: + return check + check = functions.fromArray('label3Anchor', ['center', 'nw', 'n', 'ne', 'w', 'e', 'sw', 's', 'se']) + if not check[0]: + return check + return [True, content] + + def R(self): + self.__checkInstance() + content = {} + for x in CD.open((str(appdata) + '\Generator CSV\style.cfg'), 'r', 'utf-8').read().split('\n'): + x = x.split(' = ') + try: + content[x[0]] = (x[1]).strip('\r') + except: + continue + contentCheckingOutput = self.__checkContent(content) + if contentCheckingOutput[0]: + return contentCheckingOutput[1] + else: + MSG('E0005', True, contentCheckingOutput[1]) +GUI = GUI() +checkInstance = GUI.R() + + + + + +# ------------------------------- # Zarządzanie plikami formatu # ------------------------------- # + +class FMT: + def __checkFolderInstance(self): + if 'Generator CSV' not in [x for x in OS.listdir(appdata)]: + OS.mkdir(str(appdata) + '/Generator CSV') + else: + if 'format-presets' not in [x for x in OS.listdir(str(appdata) + '\Generator CSV')]: + OS.mkdir(str(appdata) + '/Generator CSV/format-presets') + + def __checkContent(self, write, content): + if write: + class functions: + def bool(self, var): + if var in list(content.keys()): + if content[var] != True and content[var] != False: + return [False, 'Niepoprawne dane - klucz: %s' % var] + else: + if content[var] == False: + content[var] = '0' + return [True] + else: + content[var] = '1' + return [True] + else: + return [False, 'Brak danych - klucz: %s' % var] + def string(self, var): + if var in list(content.keys()): + return [True] + else: + return [False, 'Brak danych - klucz: %s' % var] + def array(self, var): + if var in list(content.keys()): + content[var] = 'I'.join(content[var]) + return [True] + else: + return [False, 'Brak danych - klucz: %s' % var] + def integer(self, var): + if var in list(content.keys()): + content[var] = str(content[var]) + return [True] + else: + return [False, 'Brak danych - klucz: %s' % var] + functions = functions() + check = functions.bool('student') + if not check[0]: + return check + check = functions.string('personSeparator') + if not check[0]: + return check + check = functions.string('rowSeparator') + if not check[0]: + return check + check = functions.array('dataSeparators') + if not check[0]: + return check + check = functions.integer('loginRow') + if not check[0]: + return check + check = functions.integer('loginPositionInRow') + if not check[0]: + return check + check = functions.integer('fnameRow') + if not check[0]: + return check + check = functions.integer('fnamePositionInRow') + if not check[0]: + return check + check = functions.integer('lnameRow') + if not check[0]: + return check + check = functions.integer('lnamePositionInRow') + if not check[0]: + return check + check = functions.integer('schoolRow') + if not check[0]: + return check + check = functions.integer('schoolPositionInRow') + if not check[0]: + return check + check = functions.integer('classRow') + if not check[0]: + return check + check = functions.integer('classPositionInRow') + if not check[0]: + return check + return [True, content] + else: + class functions: + def bool(self, var): + if var in list(content.keys()): + if content[var] != '0' and content[var] != '1': + return [False, 'Niepoprawne dane - klucz: %s' % var] + else: + if content[var] == '0': + content[var] = False + return [True] + else: + content[var] = True + return [True] + else: + return [False, 'Brak danych - klucz: %s' % var] + def string(self, var): + if var in list(content.keys()): + return [True] + else: + return [False, 'Brak danych - klucz: %s' % var] + def array(self, var): + if var in list(content.keys()): + content[var] = content[var].split('I') + return [True] + else: + return [False, 'Brak danych - klucz: %s' % var] + def integer(self, var): + if var in list(content.keys()): + try: + check = int(content[var]) + except: + return [False, 'Niepoprawne dane - klucz: %s' % var] + else: + content[var] = int(content[var]) + return [True] + else: + return [False, 'Brak danych - klucz: %s' % var] + functions = functions() + check = functions.bool('student') + if not check[0]: + return check + check = functions.string('personSeparator') + if not check[0]: + return check + check = functions.string('rowSeparator') + if not check[0]: + return check + check = functions.array('dataSeparators') + if not check[0]: + return check + check = functions.integer('loginRow') + if not check[0]: + return check + check = functions.integer('loginPositionInRow') + if not check[0]: + return check + check = functions.integer('fnameRow') + if not check[0]: + return check + check = functions.integer('fnamePositionInRow') + if not check[0]: + return check + check = functions.integer('lnameRow') + if not check[0]: + return check + check = functions.integer('lnamePositionInRow') + if not check[0]: + return check + check = functions.integer('schoolRow') + if not check[0]: + return check + check = functions.integer('schoolPositionInRow') + if not check[0]: + return check + check = functions.integer('classRow') + if not check[0]: + return check + check = functions.integer('classPositionInRow') + if not check[0]: + return check + return [True, content] + + def getList(self): + self.__checkFolderInstance() + filesList = OS.listdir(str(appdata) + '/Generator CSV/format-presets') + formatPresetsList = [] + for x in filesList: + if x[-4:] == '.fmt': + formatPresetsList.append(x[:-4]) + else: + continue + return formatPresetsList + + def R(self, preset): + if preset in self.getList(): + path = str(appdata) + '/Generator CSV/format-presets/%s.fmt' % preset + file = CD.open(path, 'r', 'utf-8').read().split('\n') + content = {} + for x in file: + x = x.split(' = ') + try: + content[x[0]] = (x[1]).strip('\r') + except: + continue + contentCheckingOutput = self.__checkContent(False, content) + if contentCheckingOutput[0]: + content = contentCheckingOutput[1] + else: + MSG('E0006', False, contentCheckingOutput[1]) + else: + content = { + "student" : True, + "personSeparator" : '', + "rowSeparator" : '', + "dataSeparators" : [], + "loginRow" : 0, + "loginPositionInRow" : 0, + "fnameRow" : 0, + "fnamePositionInRow" : 0, + "lnameRow" : 0, + "lnamePositionInRow" : 0, + "schoolRow" : 0, + "schoolPositionInRow" : 0, + "classRow" : 0, + "classPositionInRow" : 0, + } + return content + + def W(self, preset, content): + contentCheckingOutput = self.__checkContent(True, content) + if contentCheckingOutput[0]: + contentToSave = contentCheckingOutput[1] + with CD.open(str(appdata) + '/Generator CSV/format-presets/%s.fmt' % preset, 'w', 'utf-8') as file: + for x in contentToSave: + file.write(x + ' = ' + content[x] + '\n') + else: + MSG('E0006', False, contentCheckingOutput[1]) +FMT = FMT() + + + + + +# ------------------------------------------- # GUI # ------------------------------------------- # + +def window(): + # Ustawienia okna + window = TK.Tk() + window.title('%s %s' % (VARS.programName, VARS.programVersion)) + window.geometry('%sx%s' % (str(GUI.R()['windowWidth']), str(GUI.R()['windowHeight']))) + window.resizable(width = GUI.R()['windowWidthResizable'], height = GUI.R()['windowHeightResizable']) + window.configure(bg = GUI.R()['windowMainBG']) + window.iconbitmap(GUI.R()['mainIcon']) + + + + # Theme + TKttk.Style().theme_create("main", parent = "default", settings = { + "mainMenu.TNotebook": { + "configure": { + "background": GUI.R()['mainMenuBG'], + "tabposition": GUI.R()['mainMenuPosition'], + "borderwidth": GUI.R()['tabFramesBorderWidth'], + }, + }, + "mainMenu.TNotebook.Tab": { + "configure": { + "background": GUI.R()['unselectedTabBG'], + "borderwidth": GUI.R()['menuTabsBorderWidth'], + "padding": GUI.R()['menuTabsPadding'], + }, + "map": { + "background": [ + ("selected", GUI.R()['selectedTabBG']), + ("disabled", GUI.R()['disabledTabBG']), + ] + } + }, + "mainMenuTabFrame.TFrame": { + "configure": { + "background": GUI.R()['tabFrameBG'], + }, + }, + "tabHeader.TLabel": { + "configure": { + "font": GUI.R()['headerFont'], + "background": GUI.R()['headerBG'], + "foreground": GUI.R()['headerTextColor'], + "padding": GUI.R()['headerPadding'], + "anchor": GUI.R()['headerTextAnchor'], + }, + }, + "tabFrame.TFrame": { + "configure": { + "background": GUI.R()['tabFrameBG'], + }, + }, + "layoutFrame.TFrame": { + "configure": { + "background": GUI.R()['tabFrameBG'], + }, + }, + "label1.TLabel": { + "configure": { + "background": GUI.R()['label1BG'], + "foreground": GUI.R()['label1TextColor'] + }, + }, + "label2.TLabel": { + "configure": { + "background": GUI.R()['label2BG'], + "foreground": GUI.R()['label2TextColor'], + "anchor": GUI.R()['label2Anchor'], + "width": GUI.R()['label2Width'], + }, + }, + "combobox1.TCombobox": { + "configure": { + "arrowcolor": GUI.R()['combobox1ArrowColor'], + "background": GUI.R()['combobox1ButtonColor'], + "bordercolor": GUI.R()['combobox1BorderColor'], + "fieldbackground": GUI.R()['combobox1FieldBackground'], + "foreground": GUI.R()['combobox1TextColor'], + "relief": GUI.R()['combobox1Relief'], + "borderwidth": GUI.R()['combobox1BorderWidth'], + "padding": GUI.R()['combobox1Padding'], + }, + }, + "button1.TButton": { + "configure": { + "anchor": GUI.R()['button1TextAnchor'], + "background": GUI.R()['button1Background'], + "foreground": GUI.R()['button1Foreground'], + "padding": GUI.R()['button1Padding'], + }, + }, + "separator1.TSeparator": { + "configure": { + "background": GUI.R()['tabFrameBG'], + }, + }, + "spinbox1.TSpinbox": { + "configure": { + "arrowcolor": GUI.R()['spinbox1ArrowColor'], + "fieldbackground": GUI.R()['spinbox1FieldBackground'], + "relief": GUI.R()['spinbox1Relief'], + "borderwidth": GUI.R()['spinbox1BorderWidth'], + "foreground": GUI.R()['spinbox1TextColor'], + "background": GUI.R()['spinbox1ButtonColor'] + }, + }, + 'radiobutton1.TRadiobutton': { + "configure": { + "background": GUI.R()['radiobutton1Background'], + "foreground": GUI.R()['radiobutton1TextColor'], + }, + }, + "entry1.TEntry": { + "configure": { + "fieldbackground": GUI.R()['entry1FieldBackground'], + "relief": GUI.R()['entry1Relief'], + "borderwidth": GUI.R()['entry1BorderWidth'], + "padding": GUI.R()['entry1Padding'], + "foreground": GUI.R()['entry1TextColor'] + } + } + }) + TKttk.Style().theme_use("main") + + + + # Menu główne + mainMenu = TKttk.Notebook(window, width = window.winfo_width() - (2 * GUI.R()['menuTabsPadding'] + GUI.R()['tabIconsSize']), height = window.winfo_height()) + mainMenu.config(style = "mainMenu.TNotebook") + mainMenu.grid(row = 0) + + # Ikona + iconTab = TKttk.Frame(mainMenu) + iconTabImg = PLimg.open(GUI.R()['mainIcon']) + iconTabImg = iconTabImg.resize((GUI.R()['tabIconsSize'], GUI.R()['tabIconsSize']), PLimg.ANTIALIAS) + iconTabImg = PLitk.PhotoImage(iconTabImg) + mainMenu.add(iconTab, image = iconTabImg, state = TK.DISABLED) + + + + # TAB2 - Generator ################################################### + + generateTab = TKttk.Frame(mainMenu) + generateTab.config(style = "mainMenuTabFrame.TFrame") + generateTabImg = PLimg.open(GUI.R()['generateTabIcon']) + generateTabImg = generateTabImg.resize((GUI.R()['tabIconsSize'], GUI.R()['tabIconsSize']), PLimg.ANTIALIAS) + generateTabImg = PLitk.PhotoImage(generateTabImg) + mainMenu.add(generateTab, image = generateTabImg, state = TK.NORMAL) + + + # Nagłówek + generateHeader = TKttk.Label(generateTab) + generateHeader.config(style = 'tabHeader.TLabel') + generateHeader.config(text = 'GENERATOR CSV') + generateHeader.pack(fill = TK.X) + + + # Zawartość + generateFrame = TKttk.Frame(generateTab) + generateFrame.config(style = 'tabFrame.TFrame') + generateFrame.pack(fill = TK.BOTH, expand = 1, padx = GUI.R()['tabFramePadding'], pady = GUI.R()['tabFramePadding']) + + ###################################################################### + + + + # TAB3 - Format ###################################################### + + formatTab = TKttk.Frame(mainMenu) + formatTab.config(style = "mainMenuTabFrame.TFrame") + formatTabImg = PLimg.open(GUI.R()['formatTabIcon']) + formatTabImg = formatTabImg.resize((GUI.R()['tabIconsSize'], GUI.R()['tabIconsSize']), PLimg.ANTIALIAS) + formatTabImg = PLitk.PhotoImage(formatTabImg) + mainMenu.add(formatTab, image = formatTabImg, state = TK.NORMAL) + + + # Nagłówek + formatHeader = TKttk.Label(formatTab) + formatHeader.config(style = 'tabHeader.TLabel') + formatHeader.config(text = 'FORMAT DANYCH') + formatHeader.pack(fill = TK.X) + + + # Zawartość + formatFrame = TKttk.Frame(formatTab) + formatFrame.config(style = 'tabFrame.TFrame') + formatFrame.pack(fill = TK.BOTH, expand = 1, padx = GUI.R()['tabFramePadding'], pady = GUI.R()['tabFramePadding']) + + + # (1) Ładowanie presetu ##################### + + loadingPresetFrame = TKttk.Frame(formatFrame) + loadingPresetFrame.config(style = 'layoutFrame.TFrame') + loadingPresetFrame.pack(fill = TK.X) + + # "Wybierz preset do edycji lub wpisz nazwę nowego" + loadingListLabel = TKttk.Label(loadingPresetFrame) + loadingListLabel.config(style = 'label1.TLabel') + loadingListLabel.config(text = 'Wybierz preset do edycji lub wpisz nazwę nowego') + loadingListLabel.pack(side = 'left') + + # Rozwijana lista presetów + loadingListVar = TK.StringVar() + loadingList = TKttk.Combobox(loadingPresetFrame) + loadingList.config(textvariable = loadingListVar) + loadingList.config(style = 'combobox1.TCombobox') + loadingList.config(width = GUI.R()['loadingListWidth']) + loadingList.option_add("*TCombobox*Listbox.background", GUI.R()['combobox1ListBoxBackground']) + loadingList.option_add("*TCombobox*Listbox.foreground", GUI.R()['combobox1ListBoxForeground']) + loadingList.option_add("*TCombobox*Listbox.selectBackground", GUI.R()['combobox1ListBoxSelectBackground']) + loadingList.option_add("*TCombobox*Listbox.selectForeground", GUI.R()['combobox1ListBoxSelectForeground']) + loadingList.pack(side = 'left', padx = 5) + loadingList['values'] = tuple(FMT.getList()) + + # Przycisk "WCZYTAJ" + formatFileContent = { + "student" : True, + "personSeparator" : '', + "rowSeparator" : '', + "dataSeparators" : [], + "loginRow" : 0, + "loginPositionInRow" : 0, + "fnameRow" : 0, + "fnamePositionInRow" : 0, + "lnameRow" : 0, + "lnamePositionInRow" : 0, + "schoolRow" : 0, + "schoolPositionInRow" : 0, + "classRow" : 0, + "classPositionInRow" : 0, + } + def loadingButtonAction(): + formatFileContent = FMT.R(loadingList.get()) + loadingList['state'] = TK.DISABLED + loadingButton['state'] = TK.DISABLED + EPOSTypeStudentRadiobutton['state'] = TK.NORMAL + EPOSTypeTeacherRadiobutton['state'] = TK.NORMAL + if formatFileContent['student']: + EPOSTypeVar.set('s') + else: + EPOSTypeVar.set('t') + EPOSPersonSeparatorEntry['state'] = TK.NORMAL + EPOSPersonSeparatorVar.set(formatFileContent['personSeparator']) + EPOSRowSeparatorEntry['state'] = TK.NORMAL + EPOSRowSeparatorVar.set(formatFileContent['rowSeparator']) + EPOSDataSeparatorText['state'] = TK.NORMAL + EPOSDataSeparatorText.insert(TK.END, '\n'.join(formatFileContent['dataSeparators'])) + EPDataLocalizationLoginRowSpinbox['state'] = TK.NORMAL + EPDataLocalizationLoginRowVar.set(formatFileContent['loginRow']) + EPDataLocalizationLoginPosInRowSpinbox['state'] = TK.NORMAL + EPDataLocalizationLoginPosInRowVar.set(formatFileContent['loginPositionInRow']) + EPDataLocalizationFnameRowSpinbox['state'] = TK.NORMAL + EPDataLocalizationFnameRowVar.set(formatFileContent['fnameRow']) + EPDataLocalizationFnamePosInRowSpinbox['state'] = TK.NORMAL + EPDataLocalizationFnamePosInRowVar.set(formatFileContent['fnamePositionInRow']) + EPDataLocalizationLnameRowSpinbox['state'] = TK.NORMAL + EPDataLocalizationLnameRowVar.set(formatFileContent['lnameRow']) + EPDataLocalizationLnamePosInRowSpinbox['state'] = TK.NORMAL + EPDataLocalizationLnamePosInRowVar.set(formatFileContent['lnamePositionInRow']) + EPDataLocalizationSchoolRowSpinbox['state'] = TK.NORMAL + EPDataLocalizationSchoolRowVar.set(formatFileContent['schoolRow']) + EPDataLocalizationSchoolPosInRowSpinbox['state'] = TK.NORMAL + EPDataLocalizationSchoolPosInRowVar.set(formatFileContent['schoolPositionInRow']) + EPDataLocalizationClassRowSpinbox['state'] = TK.NORMAL + EPDataLocalizationClassRowVar.set(formatFileContent['classRow']) + EPDataLocalizationClassPosInRowSpinbox['state'] = TK.NORMAL + EPDataLocalizationClassPosInRowVar.set(formatFileContent['classPositionInRow']) + editingPresetSaveButton['state'] = TK.NORMAL + editingPresetCancelButton['state'] = TK.NORMAL + loadingButton = TKttk.Button(loadingPresetFrame) + loadingButton.config(style = 'button1.TButton') + loadingButton.config(command = loadingButtonAction) + loadingButton.config(width = GUI.R()['loadingButtonWidth']) + loadingButton.config(text = 'WCZYTAJ') + loadingButton.pack(side = 'right') + + ############################################# + + # (1) Separator 1 ########################### + + formatSeparator1 = TKttk.Separator(formatFrame) + formatSeparator1.config(style = 'separator1.TSeparator') + formatSeparator1.config(orient = TK.HORIZONTAL) + formatSeparator1.pack(fill = TK.X, pady = 10) + + ############################################# + + # (1) Edycja presetu ######################## + + editingPresetFrame = TKttk.Frame(formatFrame) + editingPresetFrame.config(style = 'layoutFrame.TFrame') + editingPresetFrame.pack(fill = TK.BOTH, expand = 1) + + # (2) Ustawienia ################## + + editingPresetSettingsFrame = TKttk.Frame(editingPresetFrame) + editingPresetSettingsFrame.config(style = 'layoutFrame.TFrame') + editingPresetSettingsFrame.pack(fill = TK.BOTH, expand = 1) + + # (3) Inne ustawienia ### + + editingPresetOtherSettingsFrame = TKttk.Frame(editingPresetSettingsFrame) + editingPresetOtherSettingsFrame.config(style = 'layoutFrame.TFrame') + editingPresetOtherSettingsFrame.pack(fill = TK.BOTH, expand = 1, side = TK.LEFT) + + # (4) Typ osoby + + editingPresetOSFrame = TKttk.Frame(editingPresetOtherSettingsFrame) + editingPresetOSFrame.config(style = 'layoutFrame.TFrame') + editingPresetOSFrame.pack(fill = TK.BOTH, expand = 1, side = TK.BOTTOM, pady = 5) + + # "Typ osoby" + EPOSTypeLabel = TKttk.Label(editingPresetOSFrame) + EPOSTypeLabel.config(style = 'label1.TLabel') + EPOSTypeLabel.config(text = 'Typ osoby') + EPOSTypeLabel.grid(row = 0, column = 0, pady = 5, sticky = 'w') + + # Typ osoby - Radiobutton + EPOSTypeVar = TK.StringVar() + + EPOSTypeStudentRadiobutton = TKttk.Radiobutton(editingPresetOSFrame) + EPOSTypeStudentRadiobutton.config(style = 'radiobutton1.TRadiobutton') + EPOSTypeStudentRadiobutton.config(variable = EPOSTypeVar) + EPOSTypeStudentRadiobutton.config(value = 's') + EPOSTypeStudentRadiobutton.config(state = TK.DISABLED) + EPOSTypeStudentRadiobutton.config(width = 20) + EPOSTypeStudentRadiobutton.config(text = 'Uczniowie') + + EPOSTypeTeacherRadiobutton = TKttk.Radiobutton(editingPresetOSFrame) + EPOSTypeTeacherRadiobutton.config(style = 'radiobutton1.TRadiobutton') + EPOSTypeTeacherRadiobutton.config(variable = EPOSTypeVar) + EPOSTypeTeacherRadiobutton.config(value = 't') + EPOSTypeTeacherRadiobutton.config(state = TK.DISABLED) + EPOSTypeTeacherRadiobutton.config(width = 20) + EPOSTypeTeacherRadiobutton.config(text = 'Nauczyciele') + + EPOSTypeStudentRadiobutton.grid(row = 0, column = 1, pady = 5) + EPOSTypeTeacherRadiobutton.grid(row = 0, column = 2, pady = 5) + + # "Separator pomiędzy osobami" + EPOSPersonSeparatorLabel = TKttk.Label(editingPresetOSFrame) + EPOSPersonSeparatorLabel.config(style = 'label1.TLabel') + EPOSPersonSeparatorLabel.config(text = 'Separator pomiędzy osobami') + EPOSPersonSeparatorLabel.grid(row = 1, column = 0, pady = 5, sticky = 'w') + + # Entry - Separator pomiedzy osobami + EPOSPersonSeparatorVar = TK.StringVar() + EPOSPersonSeparatorEntry = TKttk.Entry(editingPresetOSFrame) + EPOSPersonSeparatorEntry.config(style = 'entry1.TEntry') + EPOSPersonSeparatorEntry.config(textvariable = EPOSPersonSeparatorVar) + EPOSPersonSeparatorEntry.config(state = TK.DISABLED) + EPOSPersonSeparatorEntry.config(width = 56) + EPOSPersonSeparatorEntry.grid(row = 1, column = 1, columnspan = 2, padx = 5, pady = 5) + + # "Separator pomiędzy wierszami" + EPOSRowSeparatorLabel = TKttk.Label(editingPresetOSFrame) + EPOSRowSeparatorLabel.config(style = 'label1.TLabel') + EPOSRowSeparatorLabel.config(text = 'Separator pomiędzy wierszami') + EPOSRowSeparatorLabel.grid(row = 2, column = 0, pady = 5, sticky = 'w') + + # Entry - Separator pomiedzy wierszami + EPOSRowSeparatorVar = TK.StringVar() + EPOSRowSeparatorEntry = TKttk.Entry(editingPresetOSFrame) + EPOSRowSeparatorEntry.config(style = 'entry1.TEntry') + EPOSRowSeparatorEntry.config(textvariable = EPOSRowSeparatorVar) + EPOSRowSeparatorEntry.config(state = TK.DISABLED) + EPOSRowSeparatorEntry.config(width = 56) + EPOSRowSeparatorEntry.grid(row = 2, column = 1, columnspan = 2, padx = 5, pady = 5) + + # "Separatory pomiędzy danymi" + EPOSDataSeparatorLabel = TKttk.Label(editingPresetOSFrame) + EPOSDataSeparatorLabel.config(style = 'label1.TLabel') + EPOSDataSeparatorLabel.config(text = 'Separatory pomiędzy danymi') + EPOSDataSeparatorLabel.grid(row = 3, column = 0, pady = 5, sticky = 'nw') + + # Entry - Separator pomiedzy wierszami + EPOSDataSeparatorText = TK.Text(editingPresetOSFrame) + EPOSDataSeparatorText.config(state = TK.DISABLED) + EPOSDataSeparatorText.config(width = 42) + EPOSDataSeparatorText.config(height = 19) + EPOSDataSeparatorText.config(background = GUI.R()['text1Background']) + EPOSDataSeparatorText.config(foreground = GUI.R()['text1TextColor']) + EPOSDataSeparatorText.config(relief = GUI.R()['text1Relief']) + EPOSDataSeparatorText.grid(row = 3, column = 1, columnspan = 2, padx = 5, pady = 5) + + # " - nowa linia | wciśnięcie przycisku ENTER | \n" + EPOSSeparatorEnterInfoLabel = TKttk.Label(editingPresetOSFrame) + EPOSSeparatorEnterInfoLabel.config(style = 'label1.TLabel') + EPOSSeparatorEnterInfoLabel.config(text = r' - nowa linia | wciśnięcie przycisku ENTER | \n') + EPOSSeparatorEnterInfoLabel.grid(row = 4, column = 1, columnspan = 2) + + ############### + + ######################### + + # (3) Separator 2 ####### + + formatSeparator2 = TKttk.Separator(editingPresetSettingsFrame) + formatSeparator2.config(style = 'separator1.TSeparator') + formatSeparator2.config(orient = TK.VERTICAL) + formatSeparator2.pack(fill = TK.Y, padx = 10, side = TK.LEFT) + + ######################### + + # (3) Lokalizacja danych + + editingPresetDataLocalizationSettingsFrame = TKttk.Frame(editingPresetSettingsFrame) + editingPresetDataLocalizationSettingsFrame.config(style = 'layoutFrame.TFrame') + editingPresetDataLocalizationSettingsFrame.pack(fill = TK.BOTH, side = TK.RIGHT) + + # C1 - "Wiersz" + editingPresetDataLocalizationC1Label = TKttk.Label(editingPresetDataLocalizationSettingsFrame) + editingPresetDataLocalizationC1Label.config(style = 'label1.TLabel') + editingPresetDataLocalizationC1Label.config(text = 'Wiersz') + editingPresetDataLocalizationC1Label.grid(row = 0, column = 1, padx = 5, pady = 5) + + # C2 - "Pozycja w wierszu" + editingPresetDataLocalizationC2Label = TKttk.Label(editingPresetDataLocalizationSettingsFrame) + editingPresetDataLocalizationC2Label.config(style = 'label1.TLabel') + editingPresetDataLocalizationC2Label.config(text = 'Pozycja w wierszu') + editingPresetDataLocalizationC2Label.grid(row = 0, column = 2, padx = 5, pady = 5) + + # W1 - "Login" + editingPresetDataLocalizationW1Label = TKttk.Label(editingPresetDataLocalizationSettingsFrame) + editingPresetDataLocalizationW1Label.config(style = 'label2.TLabel') + editingPresetDataLocalizationW1Label.config(text = 'Login') + editingPresetDataLocalizationW1Label.grid(row = 1, column = 0, padx = 5, pady = 5) + + # Lokalizacja loginu (wiersz) + EPDataLocalizationLoginRowVar = TK.IntVar() + EPDataLocalizationLoginRowSpinbox = TKttk.Spinbox(editingPresetDataLocalizationSettingsFrame) + EPDataLocalizationLoginRowSpinbox.config(textvariable = EPDataLocalizationLoginRowVar) + EPDataLocalizationLoginRowSpinbox.config(from_ = 0) + EPDataLocalizationLoginRowSpinbox.config(to = 1000000) + EPDataLocalizationLoginRowSpinbox.config(style = 'spinbox1.TSpinbox') + EPDataLocalizationLoginRowSpinbox.grid(row = 1, column = 1, padx = 5, pady = 5) + + # Lokalizacja loginu (pozycja w wierszu) + EPDataLocalizationLoginPosInRowVar = TK.IntVar() + EPDataLocalizationLoginPosInRowSpinbox = TKttk.Spinbox(editingPresetDataLocalizationSettingsFrame) + EPDataLocalizationLoginPosInRowSpinbox.config(textvariable = EPDataLocalizationLoginPosInRowVar) + EPDataLocalizationLoginPosInRowSpinbox.config(from_ = 0) + EPDataLocalizationLoginPosInRowSpinbox.config(to = 1000000) + EPDataLocalizationLoginPosInRowSpinbox.config(style = 'spinbox1.TSpinbox') + EPDataLocalizationLoginPosInRowSpinbox.grid(row = 1, column = 2, padx = 5, pady = 5) + + # W2 - "Imię" + editingPresetDataLocalizationW2Label = TKttk.Label(editingPresetDataLocalizationSettingsFrame) + editingPresetDataLocalizationW2Label.config(style = 'label2.TLabel') + editingPresetDataLocalizationW2Label.config(text = 'Imię') + editingPresetDataLocalizationW2Label.grid(row = 2, column = 0, padx = 5, pady = 5) + + # Lokalizacja imienia (wiersz) + EPDataLocalizationFnameRowVar = TK.IntVar() + EPDataLocalizationFnameRowSpinbox = TKttk.Spinbox(editingPresetDataLocalizationSettingsFrame) + EPDataLocalizationFnameRowSpinbox.config(textvariable = EPDataLocalizationFnameRowVar) + EPDataLocalizationFnameRowSpinbox.config(from_ = 0) + EPDataLocalizationFnameRowSpinbox.config(to = 1000000) + EPDataLocalizationFnameRowSpinbox.config(style = 'spinbox1.TSpinbox') + EPDataLocalizationFnameRowSpinbox.grid(row = 2, column = 1, padx = 5, pady = 5) + + # Lokalizacja imienia (pozycja w wierszu) + EPDataLocalizationFnamePosInRowVar = TK.IntVar() + EPDataLocalizationFnamePosInRowSpinbox = TKttk.Spinbox(editingPresetDataLocalizationSettingsFrame) + EPDataLocalizationFnamePosInRowSpinbox.config(textvariable = EPDataLocalizationFnamePosInRowVar) + EPDataLocalizationFnamePosInRowSpinbox.config(from_ = 0) + EPDataLocalizationFnamePosInRowSpinbox.config(to = 1000000) + EPDataLocalizationFnamePosInRowSpinbox.config(style = 'spinbox1.TSpinbox') + EPDataLocalizationFnamePosInRowSpinbox.grid(row = 2, column = 2, padx = 5, pady = 5) + + # W3 - "Nazwisko" + editingPresetDataLocalizationW3Label = TKttk.Label(editingPresetDataLocalizationSettingsFrame) + editingPresetDataLocalizationW3Label.config(style = 'label2.TLabel') + editingPresetDataLocalizationW3Label.config(text = 'Nazwisko') + editingPresetDataLocalizationW3Label.grid(row = 3, column = 0, padx = 5, pady = 5) + + # Lokalizacja nazwiska (wiersz) + EPDataLocalizationLnameRowVar = TK.IntVar() + EPDataLocalizationLnameRowSpinbox = TKttk.Spinbox(editingPresetDataLocalizationSettingsFrame) + EPDataLocalizationLnameRowSpinbox.config(textvariable = EPDataLocalizationLnameRowVar) + EPDataLocalizationLnameRowSpinbox.config(from_ = 0) + EPDataLocalizationLnameRowSpinbox.config(to = 1000000) + EPDataLocalizationLnameRowSpinbox.config(style = 'spinbox1.TSpinbox') + EPDataLocalizationLnameRowSpinbox.grid(row = 3, column = 1, padx = 5, pady = 5) + + # Lokalizacja nazwiska (pozycja w wierszu) + EPDataLocalizationLnamePosInRowVar = TK.IntVar() + EPDataLocalizationLnamePosInRowSpinbox = TKttk.Spinbox(editingPresetDataLocalizationSettingsFrame) + EPDataLocalizationLnamePosInRowSpinbox.config(textvariable = EPDataLocalizationLnamePosInRowVar) + EPDataLocalizationLnamePosInRowSpinbox.config(from_ = 0) + EPDataLocalizationLnamePosInRowSpinbox.config(to = 1000000) + EPDataLocalizationLnamePosInRowSpinbox.config(style = 'spinbox1.TSpinbox') + EPDataLocalizationLnamePosInRowSpinbox.grid(row = 3, column = 2, padx = 5, pady = 5) + + # W4 - "Szkoła" + editingPresetDataLocalizationW4Label = TKttk.Label(editingPresetDataLocalizationSettingsFrame) + editingPresetDataLocalizationW4Label.config(style = 'label2.TLabel') + editingPresetDataLocalizationW4Label.config(text = 'Szkoła') + editingPresetDataLocalizationW4Label.grid(row = 4, column = 0, padx = 5, pady = 5) + + # Lokalizacja nazwiska (wiersz) + EPDataLocalizationSchoolRowVar = TK.IntVar() + EPDataLocalizationSchoolRowSpinbox = TKttk.Spinbox(editingPresetDataLocalizationSettingsFrame) + EPDataLocalizationSchoolRowSpinbox.config(textvariable = EPDataLocalizationSchoolRowVar) + EPDataLocalizationSchoolRowSpinbox.config(from_ = 0) + EPDataLocalizationSchoolRowSpinbox.config(to = 1000000) + EPDataLocalizationSchoolRowSpinbox.config(style = 'spinbox1.TSpinbox') + EPDataLocalizationSchoolRowSpinbox.grid(row = 4, column = 1, padx = 5, pady = 5) + + # Lokalizacja nazwiska (pozycja w wierszu) + EPDataLocalizationSchoolPosInRowVar = TK.IntVar() + EPDataLocalizationSchoolPosInRowSpinbox = TKttk.Spinbox(editingPresetDataLocalizationSettingsFrame) + EPDataLocalizationSchoolPosInRowSpinbox.config(textvariable = EPDataLocalizationSchoolPosInRowVar) + EPDataLocalizationSchoolPosInRowSpinbox.config(from_ = 0) + EPDataLocalizationSchoolPosInRowSpinbox.config(to = 1000000) + EPDataLocalizationSchoolPosInRowSpinbox.config(style = 'spinbox1.TSpinbox') + EPDataLocalizationSchoolPosInRowSpinbox.grid(row = 4, column = 2, padx = 5, pady = 5) + + # W5 - "Klasa" + editingPresetDataLocalizationW5Label = TKttk.Label(editingPresetDataLocalizationSettingsFrame) + editingPresetDataLocalizationW5Label.config(style = 'label2.TLabel') + editingPresetDataLocalizationW5Label.config(text = 'Klasa') + editingPresetDataLocalizationW5Label.grid(row = 5, column = 0, padx = 5, pady = 5) + + # Lokalizacja nazwiska (wiersz) + EPDataLocalizationClassRowVar = TK.IntVar() + EPDataLocalizationClassRowSpinbox = TKttk.Spinbox(editingPresetDataLocalizationSettingsFrame) + EPDataLocalizationClassRowSpinbox.config(textvariable = EPDataLocalizationClassRowVar) + EPDataLocalizationClassRowSpinbox.config(from_ = 0) + EPDataLocalizationClassRowSpinbox.config(to = 1000000) + EPDataLocalizationClassRowSpinbox.config(style = 'spinbox1.TSpinbox') + EPDataLocalizationClassRowSpinbox.grid(row = 5, column = 1, padx = 5, pady = 5) + + # Lokalizacja nazwiska (pozycja w wierszu) + EPDataLocalizationClassPosInRowVar = TK.IntVar() + EPDataLocalizationClassPosInRowSpinbox = TKttk.Spinbox(editingPresetDataLocalizationSettingsFrame) + EPDataLocalizationClassPosInRowSpinbox.config(textvariable = EPDataLocalizationClassPosInRowVar) + EPDataLocalizationClassPosInRowSpinbox.config(from_ = 0) + EPDataLocalizationClassPosInRowSpinbox.config(to = 1000000) + EPDataLocalizationClassPosInRowSpinbox.config(style = 'spinbox1.TSpinbox') + EPDataLocalizationClassPosInRowSpinbox.grid(row = 5, column = 2, padx = 5, pady = 5) + + ######################### + + ################################### + + # (2) Przyciski ################### + + editingPresetButtonsFrame = TKttk.Frame(editingPresetFrame) + editingPresetButtonsFrame.config(style = 'layoutFrame.TFrame') + editingPresetButtonsFrame.pack(fill = TK.X, side = TK.BOTTOM, pady = 10) + + def editingPresetSaveAction(): + if loadingList.get() not in FMT.getList(): + if MSG('A0001', False): + studentVar = EPOSTypeVar.get() + if studentVar == 's': + studentVar = True + else: + studentVar = False + formatFileContentToSave = { + "student" : studentVar, + "personSeparator" : EPOSPersonSeparatorEntry.get(), + "rowSeparator" : EPOSRowSeparatorEntry.get(), + "dataSeparators" : (EPOSDataSeparatorText.get("1.0", TK.END)).split('\n')[:-1], + "loginRow" : int(EPDataLocalizationLoginRowSpinbox.get()), + "loginPositionInRow" : int(EPDataLocalizationLoginPosInRowSpinbox.get()), + "fnameRow" : int(EPDataLocalizationFnameRowSpinbox.get()), + "fnamePositionInRow" : int(EPDataLocalizationFnamePosInRowSpinbox.get()), + "lnameRow" : int(EPDataLocalizationLnameRowSpinbox.get()), + "lnamePositionInRow" : int(EPDataLocalizationLnamePosInRowSpinbox.get()), + "schoolRow" : int(EPDataLocalizationSchoolRowSpinbox.get()), + "schoolPositionInRow" : int(EPDataLocalizationSchoolPosInRowSpinbox.get()), + "classRow" : int(EPDataLocalizationClassRowSpinbox.get()), + "classPositionInRow" : int(EPDataLocalizationClassPosInRowSpinbox.get()), + } + FMT.W(loadingList.get(), formatFileContentToSave) + formatFileContent = { + "student" : True, + "personSeparator" : '', + "rowSeparator" : '', + "dataSeparators" : [], + "loginRow" : 0, + "loginPositionInRow" : 0, + "fnameRow" : 0, + "fnamePositionInRow" : 0, + "lnameRow" : 0, + "lnamePositionInRow" : 0, + "schoolRow" : 0, + "schoolPositionInRow" : 0, + "classRow" : 0, + "classPositionInRow" : 0, + } + loadingList['state'] = TK.NORMAL + loadingButton['state'] = TK.NORMAL + EPOSTypeStudentRadiobutton['state'] = TK.DISABLED + EPOSTypeTeacherRadiobutton['state'] = TK.DISABLED + if formatFileContent['student']: + EPOSTypeVar.set('s') + else: + EPOSTypeVar.set('t') + EPOSPersonSeparatorEntry['state'] = TK.DISABLED + EPOSPersonSeparatorVar.set(formatFileContent['personSeparator']) + EPOSRowSeparatorEntry['state'] = TK.DISABLED + EPOSRowSeparatorVar.set(formatFileContent['rowSeparator']) + EPOSDataSeparatorText.delete('1.0', TK.END) + EPOSDataSeparatorText['state'] = TK.DISABLED + EPDataLocalizationLoginRowSpinbox['state'] = TK.DISABLED + EPDataLocalizationLoginRowVar.set(formatFileContent['loginRow']) + EPDataLocalizationLoginPosInRowSpinbox['state'] = TK.DISABLED + EPDataLocalizationLoginPosInRowVar.set(formatFileContent['loginPositionInRow']) + EPDataLocalizationFnameRowSpinbox['state'] = TK.DISABLED + EPDataLocalizationFnameRowVar.set(formatFileContent['fnameRow']) + EPDataLocalizationFnamePosInRowSpinbox['state'] = TK.DISABLED + EPDataLocalizationFnamePosInRowVar.set(formatFileContent['fnamePositionInRow']) + EPDataLocalizationLnameRowSpinbox['state'] = TK.DISABLED + EPDataLocalizationLnameRowVar.set(formatFileContent['lnameRow']) + EPDataLocalizationLnamePosInRowSpinbox['state'] = TK.DISABLED + EPDataLocalizationLnamePosInRowVar.set(formatFileContent['lnamePositionInRow']) + EPDataLocalizationSchoolRowSpinbox['state'] = TK.DISABLED + EPDataLocalizationSchoolRowVar.set(formatFileContent['schoolRow']) + EPDataLocalizationSchoolPosInRowSpinbox['state'] = TK.DISABLED + EPDataLocalizationSchoolPosInRowVar.set(formatFileContent['schoolPositionInRow']) + EPDataLocalizationClassRowSpinbox['state'] = TK.DISABLED + EPDataLocalizationClassRowVar.set(formatFileContent['classRow']) + EPDataLocalizationClassPosInRowSpinbox['state'] = TK.DISABLED + EPDataLocalizationClassPosInRowVar.set(formatFileContent['classPositionInRow']) + editingPresetSaveButton['state'] = TK.DISABLED + editingPresetCancelButton['state'] = TK.DISABLED + loadingList['values'] = tuple(FMT.getList()) + else: + return + else: + if MSG('A0002', False): + studentVar = EPOSTypeVar.get() + if studentVar == 's': + studentVar = True + else: + studentVar = False + formatFileContentToSave = { + "student" : studentVar, + "personSeparator" : EPOSPersonSeparatorEntry.get(), + "rowSeparator" : EPOSRowSeparatorEntry.get(), + "dataSeparators" : (EPOSDataSeparatorText.get("1.0", TK.END)).split('\n')[:-1], + "loginRow" : int(EPDataLocalizationLoginRowSpinbox.get()), + "loginPositionInRow" : int(EPDataLocalizationLoginPosInRowSpinbox.get()), + "fnameRow" : int(EPDataLocalizationFnameRowSpinbox.get()), + "fnamePositionInRow" : int(EPDataLocalizationFnamePosInRowSpinbox.get()), + "lnameRow" : int(EPDataLocalizationLnameRowSpinbox.get()), + "lnamePositionInRow" : int(EPDataLocalizationLnamePosInRowSpinbox.get()), + "schoolRow" : int(EPDataLocalizationSchoolRowSpinbox.get()), + "schoolPositionInRow" : int(EPDataLocalizationSchoolPosInRowSpinbox.get()), + "classRow" : int(EPDataLocalizationClassRowSpinbox.get()), + "classPositionInRow" : int(EPDataLocalizationClassPosInRowSpinbox.get()), + } + FMT.W(loadingList.get(), formatFileContentToSave) + formatFileContent = { + "student" : True, + "personSeparator" : '', + "rowSeparator" : '', + "dataSeparators" : [], + "loginRow" : 0, + "loginPositionInRow" : 0, + "fnameRow" : 0, + "fnamePositionInRow" : 0, + "lnameRow" : 0, + "lnamePositionInRow" : 0, + "schoolRow" : 0, + "schoolPositionInRow" : 0, + "classRow" : 0, + "classPositionInRow" : 0, + } + loadingList['state'] = TK.NORMAL + loadingButton['state'] = TK.NORMAL + EPOSTypeStudentRadiobutton['state'] = TK.DISABLED + EPOSTypeTeacherRadiobutton['state'] = TK.DISABLED + if formatFileContent['student']: + EPOSTypeVar.set('s') + else: + EPOSTypeVar.set('t') + EPOSPersonSeparatorEntry['state'] = TK.DISABLED + EPOSPersonSeparatorVar.set(formatFileContent['personSeparator']) + EPOSRowSeparatorEntry['state'] = TK.DISABLED + EPOSRowSeparatorVar.set(formatFileContent['rowSeparator']) + EPOSDataSeparatorText.delete('1.0', TK.END) + EPOSDataSeparatorText['state'] = TK.DISABLED + EPDataLocalizationLoginRowSpinbox['state'] = TK.DISABLED + EPDataLocalizationLoginRowVar.set(formatFileContent['loginRow']) + EPDataLocalizationLoginPosInRowSpinbox['state'] = TK.DISABLED + EPDataLocalizationLoginPosInRowVar.set(formatFileContent['loginPositionInRow']) + EPDataLocalizationFnameRowSpinbox['state'] = TK.DISABLED + EPDataLocalizationFnameRowVar.set(formatFileContent['fnameRow']) + EPDataLocalizationFnamePosInRowSpinbox['state'] = TK.DISABLED + EPDataLocalizationFnamePosInRowVar.set(formatFileContent['fnamePositionInRow']) + EPDataLocalizationLnameRowSpinbox['state'] = TK.DISABLED + EPDataLocalizationLnameRowVar.set(formatFileContent['lnameRow']) + EPDataLocalizationLnamePosInRowSpinbox['state'] = TK.DISABLED + EPDataLocalizationLnamePosInRowVar.set(formatFileContent['lnamePositionInRow']) + EPDataLocalizationSchoolRowSpinbox['state'] = TK.DISABLED + EPDataLocalizationSchoolRowVar.set(formatFileContent['schoolRow']) + EPDataLocalizationSchoolPosInRowSpinbox['state'] = TK.DISABLED + EPDataLocalizationSchoolPosInRowVar.set(formatFileContent['schoolPositionInRow']) + EPDataLocalizationClassRowSpinbox['state'] = TK.DISABLED + EPDataLocalizationClassRowVar.set(formatFileContent['classRow']) + EPDataLocalizationClassPosInRowSpinbox['state'] = TK.DISABLED + EPDataLocalizationClassPosInRowVar.set(formatFileContent['classPositionInRow']) + editingPresetSaveButton['state'] = TK.DISABLED + editingPresetCancelButton['state'] = TK.DISABLED + loadingList['values'] = tuple(FMT.getList()) + else: + return + editingPresetSaveButton = TKttk.Button(editingPresetButtonsFrame) + editingPresetSaveButton.config(command = editingPresetSaveAction) + editingPresetSaveButton.config(state = TK.DISABLED) + editingPresetSaveButton.config(style = 'button1.TButton') + editingPresetSaveButton.config(width = GUI.R()['editingPresetSaveButtonWidth']) + editingPresetSaveButton.config(text = 'ZAPISZ') + editingPresetSaveButton.pack(side = TK.LEFT, expand = 1) + + def editingPresetCancelAction(): + formatFileContent = { + "student" : True, + "personSeparator" : '', + "rowSeparator" : '', + "dataSeparators" : [], + "loginRow" : 0, + "loginPositionInRow" : 0, + "fnameRow" : 0, + "fnamePositionInRow" : 0, + "lnameRow" : 0, + "lnamePositionInRow" : 0, + "schoolRow" : 0, + "schoolPositionInRow" : 0, + "classRow" : 0, + "classPositionInRow" : 0, + } + loadingList['state'] = TK.NORMAL + loadingButton['state'] = TK.NORMAL + EPOSTypeStudentRadiobutton['state'] = TK.DISABLED + EPOSTypeTeacherRadiobutton['state'] = TK.DISABLED + if formatFileContent['student']: + EPOSTypeVar.set('s') + else: + EPOSTypeVar.set('t') + EPOSPersonSeparatorEntry['state'] = TK.DISABLED + EPOSPersonSeparatorVar.set(formatFileContent['personSeparator']) + EPOSRowSeparatorEntry['state'] = TK.DISABLED + EPOSRowSeparatorVar.set(formatFileContent['rowSeparator']) + EPOSDataSeparatorText.delete('1.0', TK.END) + EPOSDataSeparatorText['state'] = TK.DISABLED + EPDataLocalizationLoginRowSpinbox['state'] = TK.DISABLED + EPDataLocalizationLoginRowVar.set(formatFileContent['loginRow']) + EPDataLocalizationLoginPosInRowSpinbox['state'] = TK.DISABLED + EPDataLocalizationLoginPosInRowVar.set(formatFileContent['loginPositionInRow']) + EPDataLocalizationFnameRowSpinbox['state'] = TK.DISABLED + EPDataLocalizationFnameRowVar.set(formatFileContent['fnameRow']) + EPDataLocalizationFnamePosInRowSpinbox['state'] = TK.DISABLED + EPDataLocalizationFnamePosInRowVar.set(formatFileContent['fnamePositionInRow']) + EPDataLocalizationLnameRowSpinbox['state'] = TK.DISABLED + EPDataLocalizationLnameRowVar.set(formatFileContent['lnameRow']) + EPDataLocalizationLnamePosInRowSpinbox['state'] = TK.DISABLED + EPDataLocalizationLnamePosInRowVar.set(formatFileContent['lnamePositionInRow']) + EPDataLocalizationSchoolRowSpinbox['state'] = TK.DISABLED + EPDataLocalizationSchoolRowVar.set(formatFileContent['schoolRow']) + EPDataLocalizationSchoolPosInRowSpinbox['state'] = TK.DISABLED + EPDataLocalizationSchoolPosInRowVar.set(formatFileContent['schoolPositionInRow']) + EPDataLocalizationClassRowSpinbox['state'] = TK.DISABLED + EPDataLocalizationClassRowVar.set(formatFileContent['classRow']) + EPDataLocalizationClassPosInRowSpinbox['state'] = TK.DISABLED + EPDataLocalizationClassPosInRowVar.set(formatFileContent['classPositionInRow']) + editingPresetSaveButton['state'] = TK.DISABLED + editingPresetCancelButton['state'] = TK.DISABLED + loadingList['values'] = tuple(FMT.getList()) + editingPresetCancelButton = TKttk.Button(editingPresetButtonsFrame) + editingPresetCancelButton.config(command = editingPresetCancelAction) + editingPresetCancelButton.config(state = TK.DISABLED) + editingPresetCancelButton.config(style = 'button1.TButton') + editingPresetCancelButton.config(width = GUI.R()['editingPresetCancelButtonWidth']) + editingPresetCancelButton.config(text = 'Anuluj') + editingPresetCancelButton.pack(side = TK.RIGHT, expand = 1) + + ################################### + + ############################################# + + ###################################################################### + + + + # TAB4 - Ustawienia ################################################## + + settingsTab = TKttk.Frame(mainMenu) + settingsTab.config(style = "mainMenuTabFrame.TFrame") + settingsTabImg = PLimg.open(GUI.R()['settingsTabIcon']) + settingsTabImg = settingsTabImg.resize((GUI.R()['tabIconsSize'], GUI.R()['tabIconsSize']), PLimg.ANTIALIAS) + settingsTabImg = PLitk.PhotoImage(settingsTabImg) + mainMenu.add(settingsTab, image = settingsTabImg, state = TK.NORMAL) + + + # Nagłówek + settingsHeader = TKttk.Label(settingsTab) + settingsHeader.config(style = 'tabHeader.TLabel') + settingsHeader.config(text = 'USTAWIENIA') + settingsHeader.pack(fill = TK.X) + + + # Zawartość + settingsFrame = TKttk.Frame(settingsTab) + settingsFrame.config(style = 'tabFrame.TFrame') + settingsFrame.pack(fill = TK.BOTH, expand = 1, padx = GUI.R()['tabFramePadding'], pady = GUI.R()['tabFramePadding']) + + ###################################################################### + + + + # TAB5 - O programie ################################################# + + aboutTab = TKttk.Frame(mainMenu) + aboutTab.config(style = "mainMenuTabFrame.TFrame") + aboutTabImg = PLimg.open(GUI.R()['aboutTabIcon']) + aboutTabImg = aboutTabImg.resize((GUI.R()['tabIconsSize'], GUI.R()['tabIconsSize']), PLimg.ANTIALIAS) + aboutTabImg = PLitk.PhotoImage(aboutTabImg) + mainMenu.add(aboutTab, image = aboutTabImg, state = TK.NORMAL) + + + # Nagłówek + aboutHeader = TKttk.Label(aboutTab) + aboutHeader.config(style = 'tabHeader.TLabel') + aboutHeader.config(text = 'O PROGRAMIE') + aboutHeader.pack(fill = TK.X) + + + # Zawartość + aboutFrame = TKttk.Frame(aboutTab) + aboutFrame.config(style = 'tabFrame.TFrame') + aboutFrame.pack(fill = TK.BOTH, expand = 1, padx = GUI.R()['tabFramePadding'], pady = GUI.R()['tabFramePadding']) + + ###################################################################### + + + + # Mainloop + window.mainloop() +window() \ No newline at end of file diff --git a/src/load_cfg.py b/src/load_cfg.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/vars.py b/src/vars.py deleted file mode 100644 index 3712333..0000000 --- a/src/vars.py +++ /dev/null @@ -1,132 +0,0 @@ -""" -# Generator CSV -# 4.0 Experimental -# by Mateusz Skoczek -# styczeń 2019 - luty 2020 -# dla ZSP Sobolew - -# -# Zmienne -# -""" - - - - - -# ----------------------- # Informacje o programie # ------------------------ # - -class prgInfo: - name = 'Generator CSV' # Nazwa programu - school = 'ZSP Sobolew' # Nazwa szkoły - version = '4.0 Experimental' # Wersja programu - years = '2019 - 2020' # Lata pracy na programem - authors = 'Mateusz Skoczek' # Autorzy - - - - - -# ------------------- # Zmienne środowiska graficznego # -------------------- # - -class guiVars: - # Wymiary - class dimension: - # Karty - iconTab = 20 # Wielkość ikon w kartach - borderTab = 0 # Szerokość ramki kart - iconPaddingTab = 8 # Margines kart - tabWindowBorderWidth = 0 # Szerokość ramki okna kart - - # Nagłówki kart - tabHeaderHeight = 8 # Wysokość nagłówka - tabHeaderWidth = 80 # Szerokość nagłówka - - # Labelframe'y - lfBorderwidth = 1 # Szerokość obramowania - - # Textbox'y - tbBorderwidth = 0 # Szerokość obramowania - - # FORMATTAB - framePadY = 10 - tbPad = 8 - tbWidth = 42 - tbHeight = 9 - tbWidth2 = 56 - bnHeight = 2 - bnWidth = 30 - bnWidth2 = 30 - - # INFOTAB - programIconInInfo = 100 # Szerokość/wysokość ikony - programIconInInfoPlace = 150 # Wysokość kontrolki zawierającej ikonę - separator1Height = 2 # Wysokość separator1 - separator2Height = 1 # Wysokość separator2 - separator3Height = 4 # Wysokość separator3 - - - # Kolory - class color: - # Głowne - mainBG = '#21242D' # Głowne tło - - # Karty - unselectedTabBG = '#21242D' # Niewybrana karta - selectedTabBG = '#333842' # Wybrana karta - - # Nagłowki kart - headerBG = '#323741' # Tło - headerText = '#C0C0C0' # Tekst - - # Labelframe'y - lfText = '#C0C0C0' # Tekst - - # Label'e - label1 = '#C0C0C0' # Tekst - - # Textbox'y - textboxBG = '#282B36' # Tło - textboxText = '#C0C0C0' # Tekst - - # Button'y - buttonBG = '#323741' # Tło - buttonText = '#C0C0C0' # Tekst - - # Grafika - class image: - # Ikona programu - programIcon = 'assets/icon.ico' - programIconOther = 'assets/other_images/icon.png' - - # Ikony kart - iconTab = 'assets/tab_icons/icon.png' - generateTab = 'assets/tab_icons/generate.png' - linkTab = 'assets/tab_icons/link.png' - mergeTab = 'assets/tab_icons/merge.png' - settingsTab = 'assets/tab_icons/settings.png' - formatTab = 'assets/tab_icons/format.png' - infoTab = 'assets/tab_icons/info.png' - - - # Czcionki - class fonts: - # Główne - tabHeader = ['Segoe UI', 12] # Nagłowki - - # INFOTAB - info1 = ['Segoe UI'] # Czcionka - info1.append(20) # Wielkość tekstu - Nazwa programu - info1.append(10) # Wielkość tekstu - Wersja programu - info1.append(8) # Wielkość tekstu - Copyright - info1.append(8) # Wielkość tekstu - Autorzy - - - # Inne - class other: - # Ustawienia okna - windowHeightResize = False # Rozszerzanie okna w pionie - windowWidthResize = False # Rozszerzanie okna w poziomie - - # Ustawienia kart - tabPosition = 'wn' # Pozycja kart \ No newline at end of file From 63ea6543beb441d0c2482fda87959d5e1947ec5f Mon Sep 17 00:00:00 2001 From: Mateusz Skoczek Date: Thu, 6 Aug 2020 18:40:38 +0200 Subject: [PATCH 14/29] 4.0 Alpha (Build 20211) --- changelog-UC.txt | 9 +- default-configs/config.cfg | 3 +- default-configs/style.cfg | 17 +- generator.pyw | 470 ++++++++++++++++++++----------------- 4 files changed, 280 insertions(+), 219 deletions(-) diff --git a/changelog-UC.txt b/changelog-UC.txt index d1e8a5c..f2999dc 100644 --- a/changelog-UC.txt +++ b/changelog-UC.txt @@ -72,4 +72,11 @@ - Zastosowanie systemu styli do interfejsu - Zastosowanie systemu zapisywania plików konfiguracyjnych i formatu w folderze %appdata% - Usunięcie konsoli w tle -- Wiele mniejszych zmian \ No newline at end of file +- Wiele mniejszych zmian + +4.0 Alpha (Build 20211) +- Dodanie filtrowania dozwolonych znaków separatorów +- Naprawienie kilku mniejszych bugów: nieresetowanie pól po błędzie podczas zapisu, dodanie do listy dozwolonych znaków +- Dodanie instrukcji w karcie "FORMAT DANYCH" +- Usprawnienia w kodzie +- Naprawiono przyciski radiobutton \ No newline at end of file diff --git a/default-configs/config.cfg b/default-configs/config.cfg index 830a432..c9a0af1 100644 --- a/default-configs/config.cfg +++ b/default-configs/config.cfg @@ -1 +1,2 @@ -secret = entersecretstringhere \ No newline at end of file +secret = entersecretstringhere +allowedCharactersInSeparator = ['`', '~', '!', '@', '#', '$', '%', '^', '&', '(', ')', '-', '_', '=', '+', '[', ']', '\', '|', ';', ':', ''', '"', ',', '<', '.', '>', '/', '?', ' '] \ No newline at end of file diff --git a/default-configs/style.cfg b/default-configs/style.cfg index f1ff54e..ca0a342 100644 --- a/default-configs/style.cfg +++ b/default-configs/style.cfg @@ -69,4 +69,19 @@ label2BG = #21242D label2TextColor = #C0C0C0 label3BG = #21242D label3TextColor = #C0C0C0 -label3Anchor = w \ No newline at end of file +label3Anchor = w +radiobutton1IndicatorBackground = #21242D +loadingListPadX = 5 +EPOSTypeStudentRadiobuttonPadY = 5 +EPOSTypeStudentRadiobuttonWidth = 20 +EPOSTypeTeacherRadiobuttonPadY = 5 +EPOSTypeTeacherRadiobuttonWidth = 20 +EPOSPersonSeparatorEntryWidth = 56 +EPOSRowSeparatorEntryWidth = 56 +EPOSDataSeparatorTextWidth = 42 +EPOSDataSeparatorTextHeight = 17 +EPDataLocalizationPadX = 5 +EPDataLocalizationPadY = 5 +label3BG = #21242D +label3TextColor = #C0C0C0 +label3Font = Segoe UI;6 \ No newline at end of file diff --git a/generator.pyw b/generator.pyw index 37f2a77..ae7ca8c 100644 --- a/generator.pyw +++ b/generator.pyw @@ -100,11 +100,10 @@ def MSG(code, terminate, *optionalInfo): # ------------------------- # Sprawdzanie katalogu programu w APPDATA # ------------------------- # appdata = PT.Path.home() / 'Appdata/Roaming' -""" + #TODO SU.rmtree(str(appdata) + '/Generator CSV') #TODO -""" if 'Generator CSV' not in [x for x in OS.listdir(appdata)]: try: OS.mkdir(str(appdata) + '/Generator CSV') @@ -145,7 +144,23 @@ class CFG: return [True, content] else: class functions: - pass + def string(self, var): + if var in list(content.keys()): + return [True] + else: + return [False, 'Brak danych - klucz: %s' % var] + def array(self, var): + if var in list(content.keys()): + new_contentVar = (content[var])[1:-1].split(', ') + xnew_contentVar = [] + for x in new_contentVar: + xnew_contentVar.append(x[1:-1]) + content[var] = xnew_contentVar + else: + return [False, 'Brak danych - klucz: %s' % var] + functions = functions() + functions.string('secret') + functions.array('allowedCharactersInSeparator') return [True, content] def R(self): @@ -469,6 +484,51 @@ class GUI: if not check[0]: return check check = functions.fromArray('label3Anchor', ['center', 'nw', 'n', 'ne', 'w', 'e', 'sw', 's', 'se']) + if not check[0]: + return check + check = functions.color('radiobutton1IndicatorBackground') + if not check[0]: + return check + check = functions.integer('loadingListPadX') + if not check[0]: + return check + check = functions.integer('EPOSTypeStudentRadiobuttonPadY') + if not check[0]: + return check + check = functions.integer('EPOSTypeStudentRadiobuttonWidth') + if not check[0]: + return check + check = functions.integer('EPOSTypeTeacherRadiobuttonWidth') + if not check[0]: + return check + check = functions.integer('EPOSTypeTeacherRadiobuttonPadY') + if not check[0]: + return check + check = functions.integer('EPOSPersonSeparatorEntryWidth') + if not check[0]: + return check + check = functions.integer('EPOSRowSeparatorEntryWidth') + if not check[0]: + return check + check = functions.integer('EPOSDataSeparatorTextWidth') + if not check[0]: + return check + check = functions.integer('EPOSDataSeparatorTextHeight') + if not check[0]: + return check + check = functions.integer('EPDataLocalizationPadX') + if not check[0]: + return check + check = functions.integer('EPDataLocalizationPadY') + if not check[0]: + return check + check = functions.color('label3BG') + if not check[0]: + return check + check = functions.color('label3TextColor') + if not check[0]: + return check + check = functions.font('label3Font') if not check[0]: return check return [True, content] @@ -520,14 +580,27 @@ class FMT: return [True] else: return [False, 'Brak danych - klucz: %s' % var] - def string(self, var): + def separator_string(self, var): if var in list(content.keys()): + allowedCharactersInSeparator = CFG.R()['allowedCharactersInSeparator'] + check = content[var] + check = check.strip('') + for x in check: + if x not in allowedCharactersInSeparator: + return [False, 'Niepoprawne dane - klucz: %s' % var] return [True] else: return [False, 'Brak danych - klucz: %s' % var] - def array(self, var): + def separator_array(self, var): if var in list(content.keys()): - content[var] = 'I'.join(content[var]) + allowedCharactersInSeparator = CFG.R()['allowedCharactersInSeparator'] + check = content[var] + for x in check: + x = x.strip('') + for y in x: + if y not in allowedCharactersInSeparator: + return [False, 'Niepoprawne dane - klucz: %s' % var] + content[var] = str(content[var]) return [True] else: return [False, 'Brak danych - klucz: %s' % var] @@ -541,13 +614,13 @@ class FMT: check = functions.bool('student') if not check[0]: return check - check = functions.string('personSeparator') + check = functions.separator_string('personSeparator') if not check[0]: return check - check = functions.string('rowSeparator') + check = functions.separator_string('rowSeparator') if not check[0]: return check - check = functions.array('dataSeparators') + check = functions.separator_array('dataSeparators') if not check[0]: return check check = functions.integer('loginRow') @@ -596,14 +669,31 @@ class FMT: return [True] else: return [False, 'Brak danych - klucz: %s' % var] - def string(self, var): + def separator_string(self, var): if var in list(content.keys()): + allowedCharactersInSeparator = CFG.R()['allowedCharactersInSeparator'] + check = content[var] + check = check.strip('') + for x in check: + if x not in allowedCharactersInSeparator: + return [False, 'Niepoprawne dane - klucz: %s' % var] return [True] else: return [False, 'Brak danych - klucz: %s' % var] - def array(self, var): + def separator_array(self, var): if var in list(content.keys()): - content[var] = content[var].split('I') + allowedCharactersInSeparator = CFG.R()['allowedCharactersInSeparator'] + new_contentVar = (content[var])[1:-1].split(', ') + xnew_contentVar = [] + for x in new_contentVar: + xnew_contentVar.append(x[1:-1]) + check = xnew_contentVar + for x in check: + x = x.strip('') + for y in x: + if y not in allowedCharactersInSeparator: + return [False, 'Niepoprawne dane - klucz: %s' % var] + content[var] = xnew_contentVar return [True] else: return [False, 'Brak danych - klucz: %s' % var] @@ -622,13 +712,13 @@ class FMT: check = functions.bool('student') if not check[0]: return check - check = functions.string('personSeparator') + check = functions.separator_string('personSeparator') if not check[0]: return check - check = functions.string('rowSeparator') + check = functions.separator_string('rowSeparator') if not check[0]: return check - check = functions.array('dataSeparators') + check = functions.separator_array('dataSeparators') if not check[0]: return check check = functions.integer('loginRow') @@ -716,8 +806,10 @@ class FMT: with CD.open(str(appdata) + '/Generator CSV/format-presets/%s.fmt' % preset, 'w', 'utf-8') as file: for x in contentToSave: file.write(x + ' = ' + content[x] + '\n') + return True else: MSG('E0006', False, contentCheckingOutput[1]) + return False FMT = FMT() @@ -797,6 +889,13 @@ def window(): "width": GUI.R()['label2Width'], }, }, + "label3.TLabel": { + "configure": { + "background": GUI.R()['label3BG'], + "foreground": GUI.R()['label3TextColor'], + "font" : GUI.R()['label3Font'], + }, + }, "combobox1.TCombobox": { "configure": { "arrowcolor": GUI.R()['combobox1ArrowColor'], @@ -832,12 +931,6 @@ def window(): "background": GUI.R()['spinbox1ButtonColor'] }, }, - 'radiobutton1.TRadiobutton': { - "configure": { - "background": GUI.R()['radiobutton1Background'], - "foreground": GUI.R()['radiobutton1TextColor'], - }, - }, "entry1.TEntry": { "configure": { "fieldbackground": GUI.R()['entry1FieldBackground'], @@ -937,7 +1030,7 @@ def window(): loadingList.option_add("*TCombobox*Listbox.foreground", GUI.R()['combobox1ListBoxForeground']) loadingList.option_add("*TCombobox*Listbox.selectBackground", GUI.R()['combobox1ListBoxSelectBackground']) loadingList.option_add("*TCombobox*Listbox.selectForeground", GUI.R()['combobox1ListBoxSelectForeground']) - loadingList.pack(side = 'left', padx = 5) + loadingList.pack(side = 'left', padx = GUI.R()['loadingListPadX']) loadingList['values'] = tuple(FMT.getList()) # Przycisk "WCZYTAJ" @@ -961,12 +1054,9 @@ def window(): formatFileContent = FMT.R(loadingList.get()) loadingList['state'] = TK.DISABLED loadingButton['state'] = TK.DISABLED + EPOSTypeVar.set(formatFileContent['student']) EPOSTypeStudentRadiobutton['state'] = TK.NORMAL EPOSTypeTeacherRadiobutton['state'] = TK.NORMAL - if formatFileContent['student']: - EPOSTypeVar.set('s') - else: - EPOSTypeVar.set('t') EPOSPersonSeparatorEntry['state'] = TK.NORMAL EPOSPersonSeparatorVar.set(formatFileContent['personSeparator']) EPOSRowSeparatorEntry['state'] = TK.NORMAL @@ -1044,26 +1134,33 @@ def window(): EPOSTypeLabel.grid(row = 0, column = 0, pady = 5, sticky = 'w') # Typ osoby - Radiobutton - EPOSTypeVar = TK.StringVar() + EPOSTypeVar = TK.BooleanVar(value = True) - EPOSTypeStudentRadiobutton = TKttk.Radiobutton(editingPresetOSFrame) - EPOSTypeStudentRadiobutton.config(style = 'radiobutton1.TRadiobutton') + EPOSTypeStudentRadiobutton = TK.Radiobutton(editingPresetOSFrame) + EPOSTypeStudentRadiobutton.config(background = GUI.R()['radiobutton1Background']) + EPOSTypeStudentRadiobutton.config(foreground = GUI.R()['radiobutton1TextColor']) + EPOSTypeStudentRadiobutton.config(selectcolor = GUI.R()['radiobutton1IndicatorBackground']) + EPOSTypeStudentRadiobutton.config(activebackground = GUI.R()['radiobutton1Background']) + EPOSTypeStudentRadiobutton.config(activeforeground = GUI.R()['radiobutton1TextColor']) EPOSTypeStudentRadiobutton.config(variable = EPOSTypeVar) - EPOSTypeStudentRadiobutton.config(value = 's') + EPOSTypeStudentRadiobutton.config(value = True) EPOSTypeStudentRadiobutton.config(state = TK.DISABLED) - EPOSTypeStudentRadiobutton.config(width = 20) + EPOSTypeStudentRadiobutton.config(width = GUI.R()['EPOSTypeStudentRadiobuttonWidth']) EPOSTypeStudentRadiobutton.config(text = 'Uczniowie') + EPOSTypeStudentRadiobutton.grid(row = 0, column = 1, pady = GUI.R()['EPOSTypeStudentRadiobuttonPadY']) - EPOSTypeTeacherRadiobutton = TKttk.Radiobutton(editingPresetOSFrame) - EPOSTypeTeacherRadiobutton.config(style = 'radiobutton1.TRadiobutton') + EPOSTypeTeacherRadiobutton = TK.Radiobutton(editingPresetOSFrame) + EPOSTypeTeacherRadiobutton.config(background = GUI.R()['radiobutton1Background']) + EPOSTypeTeacherRadiobutton.config(foreground = GUI.R()['radiobutton1TextColor']) + EPOSTypeTeacherRadiobutton.config(selectcolor = GUI.R()['radiobutton1IndicatorBackground']) + EPOSTypeTeacherRadiobutton.config(activebackground = GUI.R()['radiobutton1Background']) + EPOSTypeTeacherRadiobutton.config(activeforeground = GUI.R()['radiobutton1TextColor']) EPOSTypeTeacherRadiobutton.config(variable = EPOSTypeVar) - EPOSTypeTeacherRadiobutton.config(value = 't') + EPOSTypeTeacherRadiobutton.config(value = False) EPOSTypeTeacherRadiobutton.config(state = TK.DISABLED) - EPOSTypeTeacherRadiobutton.config(width = 20) + EPOSTypeTeacherRadiobutton.config(width = GUI.R()['EPOSTypeTeacherRadiobuttonWidth']) EPOSTypeTeacherRadiobutton.config(text = 'Nauczyciele') - - EPOSTypeStudentRadiobutton.grid(row = 0, column = 1, pady = 5) - EPOSTypeTeacherRadiobutton.grid(row = 0, column = 2, pady = 5) + EPOSTypeTeacherRadiobutton.grid(row = 0, column = 2, pady = GUI.R()['EPOSTypeTeacherRadiobuttonPadY']) # "Separator pomiędzy osobami" EPOSPersonSeparatorLabel = TKttk.Label(editingPresetOSFrame) @@ -1077,7 +1174,7 @@ def window(): EPOSPersonSeparatorEntry.config(style = 'entry1.TEntry') EPOSPersonSeparatorEntry.config(textvariable = EPOSPersonSeparatorVar) EPOSPersonSeparatorEntry.config(state = TK.DISABLED) - EPOSPersonSeparatorEntry.config(width = 56) + EPOSPersonSeparatorEntry.config(width = GUI.R()['EPOSPersonSeparatorEntryWidth']) EPOSPersonSeparatorEntry.grid(row = 1, column = 1, columnspan = 2, padx = 5, pady = 5) # "Separator pomiędzy wierszami" @@ -1092,7 +1189,7 @@ def window(): EPOSRowSeparatorEntry.config(style = 'entry1.TEntry') EPOSRowSeparatorEntry.config(textvariable = EPOSRowSeparatorVar) EPOSRowSeparatorEntry.config(state = TK.DISABLED) - EPOSRowSeparatorEntry.config(width = 56) + EPOSRowSeparatorEntry.config(width = GUI.R()['EPOSRowSeparatorEntryWidth']) EPOSRowSeparatorEntry.grid(row = 2, column = 1, columnspan = 2, padx = 5, pady = 5) # "Separatory pomiędzy danymi" @@ -1104,8 +1201,8 @@ def window(): # Entry - Separator pomiedzy wierszami EPOSDataSeparatorText = TK.Text(editingPresetOSFrame) EPOSDataSeparatorText.config(state = TK.DISABLED) - EPOSDataSeparatorText.config(width = 42) - EPOSDataSeparatorText.config(height = 19) + EPOSDataSeparatorText.config(width = GUI.R()['EPOSDataSeparatorTextWidth']) + EPOSDataSeparatorText.config(height = GUI.R()['EPOSDataSeparatorTextHeight']) EPOSDataSeparatorText.config(background = GUI.R()['text1Background']) EPOSDataSeparatorText.config(foreground = GUI.R()['text1TextColor']) EPOSDataSeparatorText.config(relief = GUI.R()['text1Relief']) @@ -1114,7 +1211,7 @@ def window(): # " - nowa linia | wciśnięcie przycisku ENTER | \n" EPOSSeparatorEnterInfoLabel = TKttk.Label(editingPresetOSFrame) EPOSSeparatorEnterInfoLabel.config(style = 'label1.TLabel') - EPOSSeparatorEnterInfoLabel.config(text = r' - nowa linia | wciśnięcie przycisku ENTER | \n') + EPOSSeparatorEnterInfoLabel.config(text = (r' - nowa linia | wciśnięcie przycisku ENTER | \n' + '\n' + 'Niedozwolone znaki: litery, cyfry, *')) EPOSSeparatorEnterInfoLabel.grid(row = 4, column = 1, columnspan = 2) ############### @@ -1140,19 +1237,19 @@ def window(): editingPresetDataLocalizationC1Label = TKttk.Label(editingPresetDataLocalizationSettingsFrame) editingPresetDataLocalizationC1Label.config(style = 'label1.TLabel') editingPresetDataLocalizationC1Label.config(text = 'Wiersz') - editingPresetDataLocalizationC1Label.grid(row = 0, column = 1, padx = 5, pady = 5) + editingPresetDataLocalizationC1Label.grid(row = 0, column = 1, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) # C2 - "Pozycja w wierszu" editingPresetDataLocalizationC2Label = TKttk.Label(editingPresetDataLocalizationSettingsFrame) editingPresetDataLocalizationC2Label.config(style = 'label1.TLabel') editingPresetDataLocalizationC2Label.config(text = 'Pozycja w wierszu') - editingPresetDataLocalizationC2Label.grid(row = 0, column = 2, padx = 5, pady = 5) + editingPresetDataLocalizationC2Label.grid(row = 0, column = 2, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) # W1 - "Login" editingPresetDataLocalizationW1Label = TKttk.Label(editingPresetDataLocalizationSettingsFrame) editingPresetDataLocalizationW1Label.config(style = 'label2.TLabel') editingPresetDataLocalizationW1Label.config(text = 'Login') - editingPresetDataLocalizationW1Label.grid(row = 1, column = 0, padx = 5, pady = 5) + editingPresetDataLocalizationW1Label.grid(row = 1, column = 0, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) # Lokalizacja loginu (wiersz) EPDataLocalizationLoginRowVar = TK.IntVar() @@ -1160,8 +1257,9 @@ def window(): EPDataLocalizationLoginRowSpinbox.config(textvariable = EPDataLocalizationLoginRowVar) EPDataLocalizationLoginRowSpinbox.config(from_ = 0) EPDataLocalizationLoginRowSpinbox.config(to = 1000000) + EPDataLocalizationLoginRowSpinbox.config(state = TK.DISABLED) EPDataLocalizationLoginRowSpinbox.config(style = 'spinbox1.TSpinbox') - EPDataLocalizationLoginRowSpinbox.grid(row = 1, column = 1, padx = 5, pady = 5) + EPDataLocalizationLoginRowSpinbox.grid(row = 1, column = 1, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) # Lokalizacja loginu (pozycja w wierszu) EPDataLocalizationLoginPosInRowVar = TK.IntVar() @@ -1169,14 +1267,15 @@ def window(): EPDataLocalizationLoginPosInRowSpinbox.config(textvariable = EPDataLocalizationLoginPosInRowVar) EPDataLocalizationLoginPosInRowSpinbox.config(from_ = 0) EPDataLocalizationLoginPosInRowSpinbox.config(to = 1000000) + EPDataLocalizationLoginPosInRowSpinbox.config(state = TK.DISABLED) EPDataLocalizationLoginPosInRowSpinbox.config(style = 'spinbox1.TSpinbox') - EPDataLocalizationLoginPosInRowSpinbox.grid(row = 1, column = 2, padx = 5, pady = 5) + EPDataLocalizationLoginPosInRowSpinbox.grid(row = 1, column = 2, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) # W2 - "Imię" editingPresetDataLocalizationW2Label = TKttk.Label(editingPresetDataLocalizationSettingsFrame) editingPresetDataLocalizationW2Label.config(style = 'label2.TLabel') editingPresetDataLocalizationW2Label.config(text = 'Imię') - editingPresetDataLocalizationW2Label.grid(row = 2, column = 0, padx = 5, pady = 5) + editingPresetDataLocalizationW2Label.grid(row = 2, column = 0, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) # Lokalizacja imienia (wiersz) EPDataLocalizationFnameRowVar = TK.IntVar() @@ -1184,8 +1283,9 @@ def window(): EPDataLocalizationFnameRowSpinbox.config(textvariable = EPDataLocalizationFnameRowVar) EPDataLocalizationFnameRowSpinbox.config(from_ = 0) EPDataLocalizationFnameRowSpinbox.config(to = 1000000) + EPDataLocalizationFnameRowSpinbox.config(state = TK.DISABLED) EPDataLocalizationFnameRowSpinbox.config(style = 'spinbox1.TSpinbox') - EPDataLocalizationFnameRowSpinbox.grid(row = 2, column = 1, padx = 5, pady = 5) + EPDataLocalizationFnameRowSpinbox.grid(row = 2, column = 1, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) # Lokalizacja imienia (pozycja w wierszu) EPDataLocalizationFnamePosInRowVar = TK.IntVar() @@ -1193,14 +1293,15 @@ def window(): EPDataLocalizationFnamePosInRowSpinbox.config(textvariable = EPDataLocalizationFnamePosInRowVar) EPDataLocalizationFnamePosInRowSpinbox.config(from_ = 0) EPDataLocalizationFnamePosInRowSpinbox.config(to = 1000000) + EPDataLocalizationFnamePosInRowSpinbox.config(state = TK.DISABLED) EPDataLocalizationFnamePosInRowSpinbox.config(style = 'spinbox1.TSpinbox') - EPDataLocalizationFnamePosInRowSpinbox.grid(row = 2, column = 2, padx = 5, pady = 5) + EPDataLocalizationFnamePosInRowSpinbox.grid(row = 2, column = 2, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) # W3 - "Nazwisko" editingPresetDataLocalizationW3Label = TKttk.Label(editingPresetDataLocalizationSettingsFrame) editingPresetDataLocalizationW3Label.config(style = 'label2.TLabel') editingPresetDataLocalizationW3Label.config(text = 'Nazwisko') - editingPresetDataLocalizationW3Label.grid(row = 3, column = 0, padx = 5, pady = 5) + editingPresetDataLocalizationW3Label.grid(row = 3, column = 0, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) # Lokalizacja nazwiska (wiersz) EPDataLocalizationLnameRowVar = TK.IntVar() @@ -1208,8 +1309,9 @@ def window(): EPDataLocalizationLnameRowSpinbox.config(textvariable = EPDataLocalizationLnameRowVar) EPDataLocalizationLnameRowSpinbox.config(from_ = 0) EPDataLocalizationLnameRowSpinbox.config(to = 1000000) + EPDataLocalizationLnameRowSpinbox.config(state = TK.DISABLED) EPDataLocalizationLnameRowSpinbox.config(style = 'spinbox1.TSpinbox') - EPDataLocalizationLnameRowSpinbox.grid(row = 3, column = 1, padx = 5, pady = 5) + EPDataLocalizationLnameRowSpinbox.grid(row = 3, column = 1, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) # Lokalizacja nazwiska (pozycja w wierszu) EPDataLocalizationLnamePosInRowVar = TK.IntVar() @@ -1217,14 +1319,15 @@ def window(): EPDataLocalizationLnamePosInRowSpinbox.config(textvariable = EPDataLocalizationLnamePosInRowVar) EPDataLocalizationLnamePosInRowSpinbox.config(from_ = 0) EPDataLocalizationLnamePosInRowSpinbox.config(to = 1000000) + EPDataLocalizationLnamePosInRowSpinbox.config(state = TK.DISABLED) EPDataLocalizationLnamePosInRowSpinbox.config(style = 'spinbox1.TSpinbox') - EPDataLocalizationLnamePosInRowSpinbox.grid(row = 3, column = 2, padx = 5, pady = 5) + EPDataLocalizationLnamePosInRowSpinbox.grid(row = 3, column = 2, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) # W4 - "Szkoła" editingPresetDataLocalizationW4Label = TKttk.Label(editingPresetDataLocalizationSettingsFrame) editingPresetDataLocalizationW4Label.config(style = 'label2.TLabel') editingPresetDataLocalizationW4Label.config(text = 'Szkoła') - editingPresetDataLocalizationW4Label.grid(row = 4, column = 0, padx = 5, pady = 5) + editingPresetDataLocalizationW4Label.grid(row = 4, column = 0, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) # Lokalizacja nazwiska (wiersz) EPDataLocalizationSchoolRowVar = TK.IntVar() @@ -1232,8 +1335,9 @@ def window(): EPDataLocalizationSchoolRowSpinbox.config(textvariable = EPDataLocalizationSchoolRowVar) EPDataLocalizationSchoolRowSpinbox.config(from_ = 0) EPDataLocalizationSchoolRowSpinbox.config(to = 1000000) + EPDataLocalizationSchoolRowSpinbox.config(state = TK.DISABLED) EPDataLocalizationSchoolRowSpinbox.config(style = 'spinbox1.TSpinbox') - EPDataLocalizationSchoolRowSpinbox.grid(row = 4, column = 1, padx = 5, pady = 5) + EPDataLocalizationSchoolRowSpinbox.grid(row = 4, column = 1, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) # Lokalizacja nazwiska (pozycja w wierszu) EPDataLocalizationSchoolPosInRowVar = TK.IntVar() @@ -1241,14 +1345,15 @@ def window(): EPDataLocalizationSchoolPosInRowSpinbox.config(textvariable = EPDataLocalizationSchoolPosInRowVar) EPDataLocalizationSchoolPosInRowSpinbox.config(from_ = 0) EPDataLocalizationSchoolPosInRowSpinbox.config(to = 1000000) + EPDataLocalizationSchoolPosInRowSpinbox.config(state = TK.DISABLED) EPDataLocalizationSchoolPosInRowSpinbox.config(style = 'spinbox1.TSpinbox') - EPDataLocalizationSchoolPosInRowSpinbox.grid(row = 4, column = 2, padx = 5, pady = 5) + EPDataLocalizationSchoolPosInRowSpinbox.grid(row = 4, column = 2, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) # W5 - "Klasa" editingPresetDataLocalizationW5Label = TKttk.Label(editingPresetDataLocalizationSettingsFrame) editingPresetDataLocalizationW5Label.config(style = 'label2.TLabel') editingPresetDataLocalizationW5Label.config(text = 'Klasa') - editingPresetDataLocalizationW5Label.grid(row = 5, column = 0, padx = 5, pady = 5) + editingPresetDataLocalizationW5Label.grid(row = 5, column = 0, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) # Lokalizacja nazwiska (wiersz) EPDataLocalizationClassRowVar = TK.IntVar() @@ -1256,8 +1361,9 @@ def window(): EPDataLocalizationClassRowSpinbox.config(textvariable = EPDataLocalizationClassRowVar) EPDataLocalizationClassRowSpinbox.config(from_ = 0) EPDataLocalizationClassRowSpinbox.config(to = 1000000) + EPDataLocalizationClassRowSpinbox.config(state = TK.DISABLED) EPDataLocalizationClassRowSpinbox.config(style = 'spinbox1.TSpinbox') - EPDataLocalizationClassRowSpinbox.grid(row = 5, column = 1, padx = 5, pady = 5) + EPDataLocalizationClassRowSpinbox.grid(row = 5, column = 1, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) # Lokalizacja nazwiska (pozycja w wierszu) EPDataLocalizationClassPosInRowVar = TK.IntVar() @@ -1265,8 +1371,16 @@ def window(): EPDataLocalizationClassPosInRowSpinbox.config(textvariable = EPDataLocalizationClassPosInRowVar) EPDataLocalizationClassPosInRowSpinbox.config(from_ = 0) EPDataLocalizationClassPosInRowSpinbox.config(to = 1000000) + EPDataLocalizationClassPosInRowSpinbox.config(state = TK.DISABLED) EPDataLocalizationClassPosInRowSpinbox.config(style = 'spinbox1.TSpinbox') - EPDataLocalizationClassPosInRowSpinbox.grid(row = 5, column = 2, padx = 5, pady = 5) + EPDataLocalizationClassPosInRowSpinbox.grid(row = 5, column = 2, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) + + # Informacje + EPDataLocalizationInfoLabel = TKttk.Label(editingPresetDataLocalizationSettingsFrame) + EPDataLocalizationInfoLabel.config(style = 'label3.TLabel') + EPDataLocalizationInfoLabel.config(justify = 'center') + EPDataLocalizationInfoLabel.config(text = "1234567u\nAdam Nowak, 18\n1a LO\n*******\n\n7654321u\nJan Kowalski, 11\n2a BS\n**********\n\n------------------\n\nTyp osoby: Uczniowie\nSeparator pomiedzy osobami: ''\nSeparator pomiedzy wierszami: ''\nSeparator pomiedzy danymi: ' *enter*, '\nLogin: 1 | 1\nImię: 2 | 1\nNazwisko: 2 | 2\nSzkoła: 3 | 2\nKlasa: 3 | 1") + EPDataLocalizationInfoLabel.grid(row = 6, column = 0, columnspan = 3, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) ######################### @@ -1278,167 +1392,94 @@ def window(): editingPresetButtonsFrame.config(style = 'layoutFrame.TFrame') editingPresetButtonsFrame.pack(fill = TK.X, side = TK.BOTTOM, pady = 10) - def editingPresetSaveAction(): + def editingPresetSave(): + studentVar = EPOSTypeVar.get() + if studentVar == 's': + studentVar = True + else: + studentVar = False + formatFileContentToSave = { + "student" : studentVar, + "personSeparator" : EPOSPersonSeparatorEntry.get(), + "rowSeparator" : EPOSRowSeparatorEntry.get(), + "dataSeparators" : (EPOSDataSeparatorText.get("1.0", TK.END)).split('\n')[:-1], + "loginRow" : int(EPDataLocalizationLoginRowSpinbox.get()), + "loginPositionInRow" : int(EPDataLocalizationLoginPosInRowSpinbox.get()), + "fnameRow" : int(EPDataLocalizationFnameRowSpinbox.get()), + "fnamePositionInRow" : int(EPDataLocalizationFnamePosInRowSpinbox.get()), + "lnameRow" : int(EPDataLocalizationLnameRowSpinbox.get()), + "lnamePositionInRow" : int(EPDataLocalizationLnamePosInRowSpinbox.get()), + "schoolRow" : int(EPDataLocalizationSchoolRowSpinbox.get()), + "schoolPositionInRow" : int(EPDataLocalizationSchoolPosInRowSpinbox.get()), + "classRow" : int(EPDataLocalizationClassRowSpinbox.get()), + "classPositionInRow" : int(EPDataLocalizationClassPosInRowSpinbox.get()), + } + if not FMT.W(loadingList.get(), formatFileContentToSave): + return + formatFileContent = { + "student" : True, + "personSeparator" : '', + "rowSeparator" : '', + "dataSeparators" : [], + "loginRow" : 0, + "loginPositionInRow" : 0, + "fnameRow" : 0, + "fnamePositionInRow" : 0, + "lnameRow" : 0, + "lnamePositionInRow" : 0, + "schoolRow" : 0, + "schoolPositionInRow" : 0, + "classRow" : 0, + "classPositionInRow" : 0, + } + loadingList['state'] = TK.NORMAL + loadingButton['state'] = TK.NORMAL + EPOSTypeVar.set(formatFileContent['student']) + EPOSTypeStudentRadiobutton['state'] = TK.DISABLED + EPOSTypeTeacherRadiobutton['state'] = TK.DISABLED + EPOSPersonSeparatorEntry['state'] = TK.DISABLED + EPOSPersonSeparatorVar.set(formatFileContent['personSeparator']) + EPOSRowSeparatorEntry['state'] = TK.DISABLED + EPOSRowSeparatorVar.set(formatFileContent['rowSeparator']) + EPOSDataSeparatorText.delete('1.0', TK.END) + EPOSDataSeparatorText['state'] = TK.DISABLED + EPDataLocalizationLoginRowSpinbox['state'] = TK.DISABLED + EPDataLocalizationLoginRowVar.set(formatFileContent['loginRow']) + EPDataLocalizationLoginPosInRowSpinbox['state'] = TK.DISABLED + EPDataLocalizationLoginPosInRowVar.set(formatFileContent['loginPositionInRow']) + EPDataLocalizationFnameRowSpinbox['state'] = TK.DISABLED + EPDataLocalizationFnameRowVar.set(formatFileContent['fnameRow']) + EPDataLocalizationFnamePosInRowSpinbox['state'] = TK.DISABLED + EPDataLocalizationFnamePosInRowVar.set(formatFileContent['fnamePositionInRow']) + EPDataLocalizationLnameRowSpinbox['state'] = TK.DISABLED + EPDataLocalizationLnameRowVar.set(formatFileContent['lnameRow']) + EPDataLocalizationLnamePosInRowSpinbox['state'] = TK.DISABLED + EPDataLocalizationLnamePosInRowVar.set(formatFileContent['lnamePositionInRow']) + EPDataLocalizationSchoolRowSpinbox['state'] = TK.DISABLED + EPDataLocalizationSchoolRowVar.set(formatFileContent['schoolRow']) + EPDataLocalizationSchoolPosInRowSpinbox['state'] = TK.DISABLED + EPDataLocalizationSchoolPosInRowVar.set(formatFileContent['schoolPositionInRow']) + EPDataLocalizationClassRowSpinbox['state'] = TK.DISABLED + EPDataLocalizationClassRowVar.set(formatFileContent['classRow']) + EPDataLocalizationClassPosInRowSpinbox['state'] = TK.DISABLED + EPDataLocalizationClassPosInRowVar.set(formatFileContent['classPositionInRow']) + editingPresetSaveButton['state'] = TK.DISABLED + editingPresetCancelButton['state'] = TK.DISABLED + loadingList['values'] = tuple(FMT.getList()) + + def editingPresetSaveButtonAction(): if loadingList.get() not in FMT.getList(): if MSG('A0001', False): - studentVar = EPOSTypeVar.get() - if studentVar == 's': - studentVar = True - else: - studentVar = False - formatFileContentToSave = { - "student" : studentVar, - "personSeparator" : EPOSPersonSeparatorEntry.get(), - "rowSeparator" : EPOSRowSeparatorEntry.get(), - "dataSeparators" : (EPOSDataSeparatorText.get("1.0", TK.END)).split('\n')[:-1], - "loginRow" : int(EPDataLocalizationLoginRowSpinbox.get()), - "loginPositionInRow" : int(EPDataLocalizationLoginPosInRowSpinbox.get()), - "fnameRow" : int(EPDataLocalizationFnameRowSpinbox.get()), - "fnamePositionInRow" : int(EPDataLocalizationFnamePosInRowSpinbox.get()), - "lnameRow" : int(EPDataLocalizationLnameRowSpinbox.get()), - "lnamePositionInRow" : int(EPDataLocalizationLnamePosInRowSpinbox.get()), - "schoolRow" : int(EPDataLocalizationSchoolRowSpinbox.get()), - "schoolPositionInRow" : int(EPDataLocalizationSchoolPosInRowSpinbox.get()), - "classRow" : int(EPDataLocalizationClassRowSpinbox.get()), - "classPositionInRow" : int(EPDataLocalizationClassPosInRowSpinbox.get()), - } - FMT.W(loadingList.get(), formatFileContentToSave) - formatFileContent = { - "student" : True, - "personSeparator" : '', - "rowSeparator" : '', - "dataSeparators" : [], - "loginRow" : 0, - "loginPositionInRow" : 0, - "fnameRow" : 0, - "fnamePositionInRow" : 0, - "lnameRow" : 0, - "lnamePositionInRow" : 0, - "schoolRow" : 0, - "schoolPositionInRow" : 0, - "classRow" : 0, - "classPositionInRow" : 0, - } - loadingList['state'] = TK.NORMAL - loadingButton['state'] = TK.NORMAL - EPOSTypeStudentRadiobutton['state'] = TK.DISABLED - EPOSTypeTeacherRadiobutton['state'] = TK.DISABLED - if formatFileContent['student']: - EPOSTypeVar.set('s') - else: - EPOSTypeVar.set('t') - EPOSPersonSeparatorEntry['state'] = TK.DISABLED - EPOSPersonSeparatorVar.set(formatFileContent['personSeparator']) - EPOSRowSeparatorEntry['state'] = TK.DISABLED - EPOSRowSeparatorVar.set(formatFileContent['rowSeparator']) - EPOSDataSeparatorText.delete('1.0', TK.END) - EPOSDataSeparatorText['state'] = TK.DISABLED - EPDataLocalizationLoginRowSpinbox['state'] = TK.DISABLED - EPDataLocalizationLoginRowVar.set(formatFileContent['loginRow']) - EPDataLocalizationLoginPosInRowSpinbox['state'] = TK.DISABLED - EPDataLocalizationLoginPosInRowVar.set(formatFileContent['loginPositionInRow']) - EPDataLocalizationFnameRowSpinbox['state'] = TK.DISABLED - EPDataLocalizationFnameRowVar.set(formatFileContent['fnameRow']) - EPDataLocalizationFnamePosInRowSpinbox['state'] = TK.DISABLED - EPDataLocalizationFnamePosInRowVar.set(formatFileContent['fnamePositionInRow']) - EPDataLocalizationLnameRowSpinbox['state'] = TK.DISABLED - EPDataLocalizationLnameRowVar.set(formatFileContent['lnameRow']) - EPDataLocalizationLnamePosInRowSpinbox['state'] = TK.DISABLED - EPDataLocalizationLnamePosInRowVar.set(formatFileContent['lnamePositionInRow']) - EPDataLocalizationSchoolRowSpinbox['state'] = TK.DISABLED - EPDataLocalizationSchoolRowVar.set(formatFileContent['schoolRow']) - EPDataLocalizationSchoolPosInRowSpinbox['state'] = TK.DISABLED - EPDataLocalizationSchoolPosInRowVar.set(formatFileContent['schoolPositionInRow']) - EPDataLocalizationClassRowSpinbox['state'] = TK.DISABLED - EPDataLocalizationClassRowVar.set(formatFileContent['classRow']) - EPDataLocalizationClassPosInRowSpinbox['state'] = TK.DISABLED - EPDataLocalizationClassPosInRowVar.set(formatFileContent['classPositionInRow']) - editingPresetSaveButton['state'] = TK.DISABLED - editingPresetCancelButton['state'] = TK.DISABLED - loadingList['values'] = tuple(FMT.getList()) + editingPresetSave() else: return else: if MSG('A0002', False): - studentVar = EPOSTypeVar.get() - if studentVar == 's': - studentVar = True - else: - studentVar = False - formatFileContentToSave = { - "student" : studentVar, - "personSeparator" : EPOSPersonSeparatorEntry.get(), - "rowSeparator" : EPOSRowSeparatorEntry.get(), - "dataSeparators" : (EPOSDataSeparatorText.get("1.0", TK.END)).split('\n')[:-1], - "loginRow" : int(EPDataLocalizationLoginRowSpinbox.get()), - "loginPositionInRow" : int(EPDataLocalizationLoginPosInRowSpinbox.get()), - "fnameRow" : int(EPDataLocalizationFnameRowSpinbox.get()), - "fnamePositionInRow" : int(EPDataLocalizationFnamePosInRowSpinbox.get()), - "lnameRow" : int(EPDataLocalizationLnameRowSpinbox.get()), - "lnamePositionInRow" : int(EPDataLocalizationLnamePosInRowSpinbox.get()), - "schoolRow" : int(EPDataLocalizationSchoolRowSpinbox.get()), - "schoolPositionInRow" : int(EPDataLocalizationSchoolPosInRowSpinbox.get()), - "classRow" : int(EPDataLocalizationClassRowSpinbox.get()), - "classPositionInRow" : int(EPDataLocalizationClassPosInRowSpinbox.get()), - } - FMT.W(loadingList.get(), formatFileContentToSave) - formatFileContent = { - "student" : True, - "personSeparator" : '', - "rowSeparator" : '', - "dataSeparators" : [], - "loginRow" : 0, - "loginPositionInRow" : 0, - "fnameRow" : 0, - "fnamePositionInRow" : 0, - "lnameRow" : 0, - "lnamePositionInRow" : 0, - "schoolRow" : 0, - "schoolPositionInRow" : 0, - "classRow" : 0, - "classPositionInRow" : 0, - } - loadingList['state'] = TK.NORMAL - loadingButton['state'] = TK.NORMAL - EPOSTypeStudentRadiobutton['state'] = TK.DISABLED - EPOSTypeTeacherRadiobutton['state'] = TK.DISABLED - if formatFileContent['student']: - EPOSTypeVar.set('s') - else: - EPOSTypeVar.set('t') - EPOSPersonSeparatorEntry['state'] = TK.DISABLED - EPOSPersonSeparatorVar.set(formatFileContent['personSeparator']) - EPOSRowSeparatorEntry['state'] = TK.DISABLED - EPOSRowSeparatorVar.set(formatFileContent['rowSeparator']) - EPOSDataSeparatorText.delete('1.0', TK.END) - EPOSDataSeparatorText['state'] = TK.DISABLED - EPDataLocalizationLoginRowSpinbox['state'] = TK.DISABLED - EPDataLocalizationLoginRowVar.set(formatFileContent['loginRow']) - EPDataLocalizationLoginPosInRowSpinbox['state'] = TK.DISABLED - EPDataLocalizationLoginPosInRowVar.set(formatFileContent['loginPositionInRow']) - EPDataLocalizationFnameRowSpinbox['state'] = TK.DISABLED - EPDataLocalizationFnameRowVar.set(formatFileContent['fnameRow']) - EPDataLocalizationFnamePosInRowSpinbox['state'] = TK.DISABLED - EPDataLocalizationFnamePosInRowVar.set(formatFileContent['fnamePositionInRow']) - EPDataLocalizationLnameRowSpinbox['state'] = TK.DISABLED - EPDataLocalizationLnameRowVar.set(formatFileContent['lnameRow']) - EPDataLocalizationLnamePosInRowSpinbox['state'] = TK.DISABLED - EPDataLocalizationLnamePosInRowVar.set(formatFileContent['lnamePositionInRow']) - EPDataLocalizationSchoolRowSpinbox['state'] = TK.DISABLED - EPDataLocalizationSchoolRowVar.set(formatFileContent['schoolRow']) - EPDataLocalizationSchoolPosInRowSpinbox['state'] = TK.DISABLED - EPDataLocalizationSchoolPosInRowVar.set(formatFileContent['schoolPositionInRow']) - EPDataLocalizationClassRowSpinbox['state'] = TK.DISABLED - EPDataLocalizationClassRowVar.set(formatFileContent['classRow']) - EPDataLocalizationClassPosInRowSpinbox['state'] = TK.DISABLED - EPDataLocalizationClassPosInRowVar.set(formatFileContent['classPositionInRow']) - editingPresetSaveButton['state'] = TK.DISABLED - editingPresetCancelButton['state'] = TK.DISABLED - loadingList['values'] = tuple(FMT.getList()) + editingPresetSave() else: return editingPresetSaveButton = TKttk.Button(editingPresetButtonsFrame) - editingPresetSaveButton.config(command = editingPresetSaveAction) + editingPresetSaveButton.config(command = editingPresetSaveButtonAction) editingPresetSaveButton.config(state = TK.DISABLED) editingPresetSaveButton.config(style = 'button1.TButton') editingPresetSaveButton.config(width = GUI.R()['editingPresetSaveButtonWidth']) @@ -1466,10 +1507,7 @@ def window(): loadingButton['state'] = TK.NORMAL EPOSTypeStudentRadiobutton['state'] = TK.DISABLED EPOSTypeTeacherRadiobutton['state'] = TK.DISABLED - if formatFileContent['student']: - EPOSTypeVar.set('s') - else: - EPOSTypeVar.set('t') + EPOSTypeVar.set(formatFileContent['student']) EPOSPersonSeparatorEntry['state'] = TK.DISABLED EPOSPersonSeparatorVar.set(formatFileContent['personSeparator']) EPOSRowSeparatorEntry['state'] = TK.DISABLED From 2a9782056b4a14ebc19b5a2f8475fd5ff19af5fb Mon Sep 17 00:00:00 2001 From: Mateusz Skoczek Date: Thu, 6 Aug 2020 18:43:05 +0200 Subject: [PATCH 15/29] 4.0 Alpha (Build 20219) --- changelog-UC.txt | 7 +- default-configs/config.cfg | 10 +- default-configs/style.cfg | 43 +- generator.pyw | 1951 ++++++++++++++++++++++-------------- 4 files changed, 1245 insertions(+), 766 deletions(-) diff --git a/changelog-UC.txt b/changelog-UC.txt index f2999dc..c25a095 100644 --- a/changelog-UC.txt +++ b/changelog-UC.txt @@ -79,4 +79,9 @@ - Naprawienie kilku mniejszych bugów: nieresetowanie pól po błędzie podczas zapisu, dodanie do listy dozwolonych znaków - Dodanie instrukcji w karcie "FORMAT DANYCH" - Usprawnienia w kodzie -- Naprawiono przyciski radiobutton \ No newline at end of file +- Naprawiono przyciski radiobutton + +4.0 Alpha (Build 20219) +- Ukończenie karty "GENERATOR CSV" (poza przyciskiem "START") +- Usunięcie instrukcji w karcie "FORMAT DANYCH" w celu późniejszego umieszczenia ich w pliku readme +- Przekształcenie interfejsu na podstawie funkcji w interfejs na podstawie klasy \ No newline at end of file diff --git a/default-configs/config.cfg b/default-configs/config.cfg index c9a0af1..b387eb7 100644 --- a/default-configs/config.cfg +++ b/default-configs/config.cfg @@ -1,2 +1,10 @@ secret = entersecretstringhere -allowedCharactersInSeparator = ['`', '~', '!', '@', '#', '$', '%', '^', '&', '(', ')', '-', '_', '=', '+', '[', ']', '\', '|', ';', ':', ''', '"', ',', '<', '.', '>', '/', '?', ' '] \ No newline at end of file +allowedCharactersInSeparator = ['`', '~', '!', '@', '#', '$', '%', '^', '&', '(', ')', '-', '_', '=', '+', '[', ']', '\', '|', ';', ':', ''', '"', ',', '<', '.', '>', '/', '?', ' '] +inputCoding = utf-8 +outputCoding = utf-8 +domain = losobolew.pl +quota = 500 +country = Rzeczypospolita Polska +yearsOfLO = 4 +yearsOfBS = 3 +schoolyearStart = \ No newline at end of file diff --git a/default-configs/style.cfg b/default-configs/style.cfg index ca0a342..addf2fd 100644 --- a/default-configs/style.cfg +++ b/default-configs/style.cfg @@ -1,5 +1,5 @@ -windowWidth = 1000 -windowHeight = 600 +windowWidth = 1200 +windowHeight = 720 windowWidthResizable = 0 windowHeightResizable = 0 windowMainBG = #21242D @@ -34,7 +34,7 @@ combobox1FieldBackground = #333842 combobox1TextColor = #C0C0C0 combobox1Relief = flat combobox1BorderWidth = 0 -combobox1Padding = 3 +combobox1Padding = 5 combobox1ListBoxBackground = #333842 combobox1ListBoxForeground = #C0C0C0 combobox1ListBoxSelectBackground = #737373 @@ -42,25 +42,26 @@ combobox1ListBoxSelectForeground = #FFFFFF button1TextAnchor = center button1Background = #333842 button1Foreground = #C0C0C0 -button1Padding = 1 +button1Padding = 4 editingPresetSaveButtonWidth = 25 editingPresetCancelButtonWidth = 25 loadingButtonWidth = 15 loadingListWidth = 85 -label2Width = 15 -label2Anchor = center +generateFilesLabelWidth = 15 +generateFilesLabelAnchor = center spinbox1ArrowColor = #C0C0C0 spinbox1FieldBackground = #333842 spinbox1Relief = flat spinbox1BorderWidth = 0 spinbox1TextColor = #C0C0C0 spinbox1ButtonColor = #333842 +spinbox1Padding = 7 radiobutton1Background = #21242D radiobutton1TextColor = #C0C0C0 entry1FieldBackground = #333842 entry1Relief = flat entry1BorderWidth = 0 -entry1Padding = 3 +entry1Padding = 7 text1Background = #333842 text1TextColor = #C0C0C0 text1Relief = flat @@ -71,7 +72,7 @@ label3BG = #21242D label3TextColor = #C0C0C0 label3Anchor = w radiobutton1IndicatorBackground = #21242D -loadingListPadX = 5 +loadingListPadX = 12 EPOSTypeStudentRadiobuttonPadY = 5 EPOSTypeStudentRadiobuttonWidth = 20 EPOSTypeTeacherRadiobuttonPadY = 5 @@ -80,8 +81,28 @@ EPOSPersonSeparatorEntryWidth = 56 EPOSRowSeparatorEntryWidth = 56 EPOSDataSeparatorTextWidth = 42 EPOSDataSeparatorTextHeight = 17 -EPDataLocalizationPadX = 5 -EPDataLocalizationPadY = 5 +EPDataLocalizationPadX = 6 +EPDataLocalizationPadY = 6 label3BG = #21242D label3TextColor = #C0C0C0 -label3Font = Segoe UI;6 \ No newline at end of file +label3Font = Segoe UI;6 +GIFSLocalizationEntryWidth = 109 +GIFFrameSeparators = 16 +generateInputFilesPlusMinusButtonsWidth = 2 +generateResetButtonWidth = 10 +generateInputFilesPadding = 6 +generateOutputFilesPadding = 6 +combobox2ArrowColor = #C0C0C0 +combobox2ButtonColor = #333842 +combobox2BorderColor = #21242D +combobox2FieldBackground = #333842 +combobox2TextColor = #C0C0C0 +combobox2Relief = flat +combobox2BorderWidth = 0 +combobox2Padding = 7 +combobox2ListBoxBackground = #333842 +combobox2ListBoxForeground = #C0C0C0 +combobox2ListBoxSelectBackground = #737373 +combobox2ListBoxSelectForeground = #FFFFFF +EPOSLabelWidth = 30 +EPOSLabelAnchor = center \ No newline at end of file diff --git a/generator.pyw b/generator.pyw index ae7ca8c..d98d6b1 100644 --- a/generator.pyw +++ b/generator.pyw @@ -39,6 +39,7 @@ import shutil as SU import tkinter as TK from tkinter import ttk as TKttk from tkinter import messagebox as TKmsb +from tkinter import filedialog as TKfld from PIL import ImageTk as PLitk from PIL import Image as PLimg @@ -60,6 +61,7 @@ MSGlist = { 'E0006' : 'Niepoprawne dane w pliku formatu', 'A0001' : 'Czy chcesz zapisać? Zostanie utworzony nowy plik', 'A0002' : 'Czy chcesz zapisać? Plik zostanie nadpisany', + 'A0003' : 'Czy chcesz rozpocząć przetwarzanie plików?' } def MSG(code, terminate, *optionalInfo): @@ -102,7 +104,7 @@ def MSG(code, terminate, *optionalInfo): appdata = PT.Path.home() / 'Appdata/Roaming' #TODO -SU.rmtree(str(appdata) + '/Generator CSV') +#SU.rmtree(str(appdata) + '/Generator CSV') #TODO if 'Generator CSV' not in [x for x in OS.listdir(appdata)]: try: @@ -417,10 +419,10 @@ class GUI: check = functions.integer('loadingListWidth') if not check[0]: return check - check = functions.integer('label2Width') + check = functions.integer('generateFilesLabelWidth') if not check[0]: return check - check = functions.fromArray('label2Anchor', ['center', 'nw', 'n', 'ne', 'w', 'e', 'sw', 's', 'se']) + check = functions.fromArray('generateFilesLabelAnchor', ['center', 'nw', 'n', 'ne', 'w', 'e', 'sw', 's', 'se']) if not check[0]: return check check = functions.color('spinbox1ArrowColor') @@ -529,6 +531,24 @@ class GUI: if not check[0]: return check check = functions.font('label3Font') + if not check[0]: + return check + check = functions.integer('GIFSLocalizationEntryWidth') + if not check[0]: + return check + check = functions.integer('GIFFrameSeparators') + if not check[0]: + return check + check = functions.integer('generateInputFilesPlusMinusButtonsWidth') + if not check[0]: + return check + check = functions.integer('generateResetButtonWidth') + if not check[0]: + return check + check = functions.integer('generateInputFilesPadding') + if not check[0]: + return check + check = functions.integer('generateOutputFilesPadding') if not check[0]: return check return [True, content] @@ -683,17 +703,14 @@ class FMT: def separator_array(self, var): if var in list(content.keys()): allowedCharactersInSeparator = CFG.R()['allowedCharactersInSeparator'] - new_contentVar = (content[var])[1:-1].split(', ') - xnew_contentVar = [] - for x in new_contentVar: - xnew_contentVar.append(x[1:-1]) - check = xnew_contentVar + new_contentVar = (content[var])[2:-2].split("', '") + check = new_contentVar for x in check: x = x.strip('') for y in x: if y not in allowedCharactersInSeparator: return [False, 'Niepoprawne dane - klucz: %s' % var] - content[var] = xnew_contentVar + content[var] = new_contentVar return [True] else: return [False, 'Brak danych - klucz: %s' % var] @@ -816,606 +833,1132 @@ FMT = FMT() +# ---------------------------------- # Przetwarzanie plików # ----------------------------------- # + +class dataProcess: + def start(self, files): + pass +dataProcess = dataProcess() + + + + + # ------------------------------------------- # GUI # ------------------------------------------- # -def window(): - # Ustawienia okna - window = TK.Tk() - window.title('%s %s' % (VARS.programName, VARS.programVersion)) - window.geometry('%sx%s' % (str(GUI.R()['windowWidth']), str(GUI.R()['windowHeight']))) - window.resizable(width = GUI.R()['windowWidthResizable'], height = GUI.R()['windowHeightResizable']) - window.configure(bg = GUI.R()['windowMainBG']) - window.iconbitmap(GUI.R()['mainIcon']) +class mainWindow: + def __init__(self, master): + # Okno + self.master = master + master.title('%s %s' % (VARS.programName, VARS.programVersion)) + master.geometry('%sx%s' % (str(GUI.R()['windowWidth']), str(GUI.R()['windowHeight']))) + master.resizable(width = GUI.R()['windowWidthResizable'], height = GUI.R()['windowHeightResizable']) + master.configure(bg = GUI.R()['windowMainBG']) + master.iconbitmap(GUI.R()['mainIcon']) - # Theme - TKttk.Style().theme_create("main", parent = "default", settings = { - "mainMenu.TNotebook": { - "configure": { - "background": GUI.R()['mainMenuBG'], - "tabposition": GUI.R()['mainMenuPosition'], - "borderwidth": GUI.R()['tabFramesBorderWidth'], + + # Theme + TKttk.Style().theme_create("main", parent = "default", settings = { + "mainMenu.TNotebook": { + "configure": { + "background": GUI.R()['mainMenuBG'], + "tabposition": GUI.R()['mainMenuPosition'], + "borderwidth": GUI.R()['tabFramesBorderWidth'], + }, }, - }, - "mainMenu.TNotebook.Tab": { - "configure": { - "background": GUI.R()['unselectedTabBG'], - "borderwidth": GUI.R()['menuTabsBorderWidth'], - "padding": GUI.R()['menuTabsPadding'], + "mainMenu.TNotebook.Tab": { + "configure": { + "background": GUI.R()['unselectedTabBG'], + "borderwidth": GUI.R()['menuTabsBorderWidth'], + "padding": GUI.R()['menuTabsPadding'], + }, + "map": { + "background": [ + ("selected", GUI.R()['selectedTabBG']), + ("disabled", GUI.R()['disabledTabBG']), + ] + } }, - "map": { - "background": [ - ("selected", GUI.R()['selectedTabBG']), - ("disabled", GUI.R()['disabledTabBG']), - ] - } - }, - "mainMenuTabFrame.TFrame": { - "configure": { - "background": GUI.R()['tabFrameBG'], + "mainMenuTabFrame.TFrame": { + "configure": { + "background": GUI.R()['tabFrameBG'], + }, }, - }, - "tabHeader.TLabel": { - "configure": { - "font": GUI.R()['headerFont'], - "background": GUI.R()['headerBG'], - "foreground": GUI.R()['headerTextColor'], - "padding": GUI.R()['headerPadding'], - "anchor": GUI.R()['headerTextAnchor'], + "tabHeader.TLabel": { + "configure": { + "font": GUI.R()['headerFont'], + "background": GUI.R()['headerBG'], + "foreground": GUI.R()['headerTextColor'], + "padding": GUI.R()['headerPadding'], + "anchor": GUI.R()['headerTextAnchor'], + }, }, - }, - "tabFrame.TFrame": { - "configure": { - "background": GUI.R()['tabFrameBG'], + "tabFrame.TFrame": { + "configure": { + "background": GUI.R()['tabFrameBG'], + }, }, - }, - "layoutFrame.TFrame": { - "configure": { - "background": GUI.R()['tabFrameBG'], + "layoutFrame.TFrame": { + "configure": { + "background": GUI.R()['tabFrameBG'], + }, }, - }, - "label1.TLabel": { - "configure": { - "background": GUI.R()['label1BG'], - "foreground": GUI.R()['label1TextColor'] + "label1.TLabel": { + "configure": { + "background": GUI.R()['label1BG'], + "foreground": GUI.R()['label1TextColor'], + "font": ('Segoe UI', 10) + }, }, - }, - "label2.TLabel": { - "configure": { - "background": GUI.R()['label2BG'], - "foreground": GUI.R()['label2TextColor'], - "anchor": GUI.R()['label2Anchor'], - "width": GUI.R()['label2Width'], + "label2.TLabel": { + "configure": { + "background": GUI.R()['label2BG'], + "foreground": GUI.R()['label2TextColor'], + }, }, - }, - "label3.TLabel": { - "configure": { - "background": GUI.R()['label3BG'], - "foreground": GUI.R()['label3TextColor'], - "font" : GUI.R()['label3Font'], + "combobox1.TCombobox": { + "configure": { + "arrowcolor": GUI.R()['combobox1ArrowColor'], + "background": GUI.R()['combobox1ButtonColor'], + "bordercolor": GUI.R()['combobox1BorderColor'], + "fieldbackground": GUI.R()['combobox1FieldBackground'], + "foreground": GUI.R()['combobox1TextColor'], + "relief": GUI.R()['combobox1Relief'], + "borderwidth": GUI.R()['combobox1BorderWidth'], + "padding": GUI.R()['combobox1Padding'], + }, }, - }, - "combobox1.TCombobox": { - "configure": { - "arrowcolor": GUI.R()['combobox1ArrowColor'], - "background": GUI.R()['combobox1ButtonColor'], - "bordercolor": GUI.R()['combobox1BorderColor'], - "fieldbackground": GUI.R()['combobox1FieldBackground'], - "foreground": GUI.R()['combobox1TextColor'], - "relief": GUI.R()['combobox1Relief'], - "borderwidth": GUI.R()['combobox1BorderWidth'], - "padding": GUI.R()['combobox1Padding'], + "combobox2.TCombobox": { + "configure": { + "arrowcolor": GUI.R()['combobox2ArrowColor'], + "background": GUI.R()['combobox2ButtonColor'], + "bordercolor": GUI.R()['combobox2BorderColor'], + "fieldbackground": GUI.R()['combobox2FieldBackground'], + "foreground": GUI.R()['combobox2TextColor'], + "relief": GUI.R()['combobox2Relief'], + "borderwidth": GUI.R()['combobox2BorderWidth'], + "padding": GUI.R()['combobox2Padding'], + }, }, - }, - "button1.TButton": { - "configure": { - "anchor": GUI.R()['button1TextAnchor'], - "background": GUI.R()['button1Background'], - "foreground": GUI.R()['button1Foreground'], - "padding": GUI.R()['button1Padding'], + "button1.TButton": { + "configure": { + "anchor": GUI.R()['button1TextAnchor'], + "background": GUI.R()['button1Background'], + "foreground": GUI.R()['button1Foreground'], + "padding": GUI.R()['button1Padding'], + }, }, - }, - "separator1.TSeparator": { - "configure": { - "background": GUI.R()['tabFrameBG'], + "separator1.TSeparator": { + "configure": { + "background": GUI.R()['tabFrameBG'], + }, }, - }, - "spinbox1.TSpinbox": { - "configure": { - "arrowcolor": GUI.R()['spinbox1ArrowColor'], - "fieldbackground": GUI.R()['spinbox1FieldBackground'], - "relief": GUI.R()['spinbox1Relief'], - "borderwidth": GUI.R()['spinbox1BorderWidth'], - "foreground": GUI.R()['spinbox1TextColor'], - "background": GUI.R()['spinbox1ButtonColor'] + "spinbox1.TSpinbox": { + "configure": { + "arrowcolor": GUI.R()['spinbox1ArrowColor'], + "fieldbackground": GUI.R()['spinbox1FieldBackground'], + "relief": GUI.R()['spinbox1Relief'], + "borderwidth": GUI.R()['spinbox1BorderWidth'], + "foreground": GUI.R()['spinbox1TextColor'], + "background": GUI.R()['spinbox1ButtonColor'], + "padding" : GUI.R()['spinbox1Padding'], + }, }, - }, - "entry1.TEntry": { - "configure": { - "fieldbackground": GUI.R()['entry1FieldBackground'], - "relief": GUI.R()['entry1Relief'], - "borderwidth": GUI.R()['entry1BorderWidth'], - "padding": GUI.R()['entry1Padding'], - "foreground": GUI.R()['entry1TextColor'] - } - } - }) - TKttk.Style().theme_use("main") - - - - # Menu główne - mainMenu = TKttk.Notebook(window, width = window.winfo_width() - (2 * GUI.R()['menuTabsPadding'] + GUI.R()['tabIconsSize']), height = window.winfo_height()) - mainMenu.config(style = "mainMenu.TNotebook") - mainMenu.grid(row = 0) - - # Ikona - iconTab = TKttk.Frame(mainMenu) - iconTabImg = PLimg.open(GUI.R()['mainIcon']) - iconTabImg = iconTabImg.resize((GUI.R()['tabIconsSize'], GUI.R()['tabIconsSize']), PLimg.ANTIALIAS) - iconTabImg = PLitk.PhotoImage(iconTabImg) - mainMenu.add(iconTab, image = iconTabImg, state = TK.DISABLED) - - - - # TAB2 - Generator ################################################### - - generateTab = TKttk.Frame(mainMenu) - generateTab.config(style = "mainMenuTabFrame.TFrame") - generateTabImg = PLimg.open(GUI.R()['generateTabIcon']) - generateTabImg = generateTabImg.resize((GUI.R()['tabIconsSize'], GUI.R()['tabIconsSize']), PLimg.ANTIALIAS) - generateTabImg = PLitk.PhotoImage(generateTabImg) - mainMenu.add(generateTab, image = generateTabImg, state = TK.NORMAL) - - - # Nagłówek - generateHeader = TKttk.Label(generateTab) - generateHeader.config(style = 'tabHeader.TLabel') - generateHeader.config(text = 'GENERATOR CSV') - generateHeader.pack(fill = TK.X) - - - # Zawartość - generateFrame = TKttk.Frame(generateTab) - generateFrame.config(style = 'tabFrame.TFrame') - generateFrame.pack(fill = TK.BOTH, expand = 1, padx = GUI.R()['tabFramePadding'], pady = GUI.R()['tabFramePadding']) - - ###################################################################### - - - - # TAB3 - Format ###################################################### - - formatTab = TKttk.Frame(mainMenu) - formatTab.config(style = "mainMenuTabFrame.TFrame") - formatTabImg = PLimg.open(GUI.R()['formatTabIcon']) - formatTabImg = formatTabImg.resize((GUI.R()['tabIconsSize'], GUI.R()['tabIconsSize']), PLimg.ANTIALIAS) - formatTabImg = PLitk.PhotoImage(formatTabImg) - mainMenu.add(formatTab, image = formatTabImg, state = TK.NORMAL) - - - # Nagłówek - formatHeader = TKttk.Label(formatTab) - formatHeader.config(style = 'tabHeader.TLabel') - formatHeader.config(text = 'FORMAT DANYCH') - formatHeader.pack(fill = TK.X) - - - # Zawartość - formatFrame = TKttk.Frame(formatTab) - formatFrame.config(style = 'tabFrame.TFrame') - formatFrame.pack(fill = TK.BOTH, expand = 1, padx = GUI.R()['tabFramePadding'], pady = GUI.R()['tabFramePadding']) - - - # (1) Ładowanie presetu ##################### - - loadingPresetFrame = TKttk.Frame(formatFrame) - loadingPresetFrame.config(style = 'layoutFrame.TFrame') - loadingPresetFrame.pack(fill = TK.X) - - # "Wybierz preset do edycji lub wpisz nazwę nowego" - loadingListLabel = TKttk.Label(loadingPresetFrame) - loadingListLabel.config(style = 'label1.TLabel') - loadingListLabel.config(text = 'Wybierz preset do edycji lub wpisz nazwę nowego') - loadingListLabel.pack(side = 'left') - - # Rozwijana lista presetów - loadingListVar = TK.StringVar() - loadingList = TKttk.Combobox(loadingPresetFrame) - loadingList.config(textvariable = loadingListVar) - loadingList.config(style = 'combobox1.TCombobox') - loadingList.config(width = GUI.R()['loadingListWidth']) - loadingList.option_add("*TCombobox*Listbox.background", GUI.R()['combobox1ListBoxBackground']) - loadingList.option_add("*TCombobox*Listbox.foreground", GUI.R()['combobox1ListBoxForeground']) - loadingList.option_add("*TCombobox*Listbox.selectBackground", GUI.R()['combobox1ListBoxSelectBackground']) - loadingList.option_add("*TCombobox*Listbox.selectForeground", GUI.R()['combobox1ListBoxSelectForeground']) - loadingList.pack(side = 'left', padx = GUI.R()['loadingListPadX']) - loadingList['values'] = tuple(FMT.getList()) - - # Przycisk "WCZYTAJ" - formatFileContent = { - "student" : True, - "personSeparator" : '', - "rowSeparator" : '', - "dataSeparators" : [], - "loginRow" : 0, - "loginPositionInRow" : 0, - "fnameRow" : 0, - "fnamePositionInRow" : 0, - "lnameRow" : 0, - "lnamePositionInRow" : 0, - "schoolRow" : 0, - "schoolPositionInRow" : 0, - "classRow" : 0, - "classPositionInRow" : 0, - } - def loadingButtonAction(): - formatFileContent = FMT.R(loadingList.get()) - loadingList['state'] = TK.DISABLED - loadingButton['state'] = TK.DISABLED - EPOSTypeVar.set(formatFileContent['student']) - EPOSTypeStudentRadiobutton['state'] = TK.NORMAL - EPOSTypeTeacherRadiobutton['state'] = TK.NORMAL - EPOSPersonSeparatorEntry['state'] = TK.NORMAL - EPOSPersonSeparatorVar.set(formatFileContent['personSeparator']) - EPOSRowSeparatorEntry['state'] = TK.NORMAL - EPOSRowSeparatorVar.set(formatFileContent['rowSeparator']) - EPOSDataSeparatorText['state'] = TK.NORMAL - EPOSDataSeparatorText.insert(TK.END, '\n'.join(formatFileContent['dataSeparators'])) - EPDataLocalizationLoginRowSpinbox['state'] = TK.NORMAL - EPDataLocalizationLoginRowVar.set(formatFileContent['loginRow']) - EPDataLocalizationLoginPosInRowSpinbox['state'] = TK.NORMAL - EPDataLocalizationLoginPosInRowVar.set(formatFileContent['loginPositionInRow']) - EPDataLocalizationFnameRowSpinbox['state'] = TK.NORMAL - EPDataLocalizationFnameRowVar.set(formatFileContent['fnameRow']) - EPDataLocalizationFnamePosInRowSpinbox['state'] = TK.NORMAL - EPDataLocalizationFnamePosInRowVar.set(formatFileContent['fnamePositionInRow']) - EPDataLocalizationLnameRowSpinbox['state'] = TK.NORMAL - EPDataLocalizationLnameRowVar.set(formatFileContent['lnameRow']) - EPDataLocalizationLnamePosInRowSpinbox['state'] = TK.NORMAL - EPDataLocalizationLnamePosInRowVar.set(formatFileContent['lnamePositionInRow']) - EPDataLocalizationSchoolRowSpinbox['state'] = TK.NORMAL - EPDataLocalizationSchoolRowVar.set(formatFileContent['schoolRow']) - EPDataLocalizationSchoolPosInRowSpinbox['state'] = TK.NORMAL - EPDataLocalizationSchoolPosInRowVar.set(formatFileContent['schoolPositionInRow']) - EPDataLocalizationClassRowSpinbox['state'] = TK.NORMAL - EPDataLocalizationClassRowVar.set(formatFileContent['classRow']) - EPDataLocalizationClassPosInRowSpinbox['state'] = TK.NORMAL - EPDataLocalizationClassPosInRowVar.set(formatFileContent['classPositionInRow']) - editingPresetSaveButton['state'] = TK.NORMAL - editingPresetCancelButton['state'] = TK.NORMAL - loadingButton = TKttk.Button(loadingPresetFrame) - loadingButton.config(style = 'button1.TButton') - loadingButton.config(command = loadingButtonAction) - loadingButton.config(width = GUI.R()['loadingButtonWidth']) - loadingButton.config(text = 'WCZYTAJ') - loadingButton.pack(side = 'right') - - ############################################# - - # (1) Separator 1 ########################### - - formatSeparator1 = TKttk.Separator(formatFrame) - formatSeparator1.config(style = 'separator1.TSeparator') - formatSeparator1.config(orient = TK.HORIZONTAL) - formatSeparator1.pack(fill = TK.X, pady = 10) - - ############################################# - - # (1) Edycja presetu ######################## - - editingPresetFrame = TKttk.Frame(formatFrame) - editingPresetFrame.config(style = 'layoutFrame.TFrame') - editingPresetFrame.pack(fill = TK.BOTH, expand = 1) - - # (2) Ustawienia ################## - - editingPresetSettingsFrame = TKttk.Frame(editingPresetFrame) - editingPresetSettingsFrame.config(style = 'layoutFrame.TFrame') - editingPresetSettingsFrame.pack(fill = TK.BOTH, expand = 1) - - # (3) Inne ustawienia ### - - editingPresetOtherSettingsFrame = TKttk.Frame(editingPresetSettingsFrame) - editingPresetOtherSettingsFrame.config(style = 'layoutFrame.TFrame') - editingPresetOtherSettingsFrame.pack(fill = TK.BOTH, expand = 1, side = TK.LEFT) - - # (4) Typ osoby - - editingPresetOSFrame = TKttk.Frame(editingPresetOtherSettingsFrame) - editingPresetOSFrame.config(style = 'layoutFrame.TFrame') - editingPresetOSFrame.pack(fill = TK.BOTH, expand = 1, side = TK.BOTTOM, pady = 5) - - # "Typ osoby" - EPOSTypeLabel = TKttk.Label(editingPresetOSFrame) - EPOSTypeLabel.config(style = 'label1.TLabel') - EPOSTypeLabel.config(text = 'Typ osoby') - EPOSTypeLabel.grid(row = 0, column = 0, pady = 5, sticky = 'w') - - # Typ osoby - Radiobutton - EPOSTypeVar = TK.BooleanVar(value = True) - - EPOSTypeStudentRadiobutton = TK.Radiobutton(editingPresetOSFrame) - EPOSTypeStudentRadiobutton.config(background = GUI.R()['radiobutton1Background']) - EPOSTypeStudentRadiobutton.config(foreground = GUI.R()['radiobutton1TextColor']) - EPOSTypeStudentRadiobutton.config(selectcolor = GUI.R()['radiobutton1IndicatorBackground']) - EPOSTypeStudentRadiobutton.config(activebackground = GUI.R()['radiobutton1Background']) - EPOSTypeStudentRadiobutton.config(activeforeground = GUI.R()['radiobutton1TextColor']) - EPOSTypeStudentRadiobutton.config(variable = EPOSTypeVar) - EPOSTypeStudentRadiobutton.config(value = True) - EPOSTypeStudentRadiobutton.config(state = TK.DISABLED) - EPOSTypeStudentRadiobutton.config(width = GUI.R()['EPOSTypeStudentRadiobuttonWidth']) - EPOSTypeStudentRadiobutton.config(text = 'Uczniowie') - EPOSTypeStudentRadiobutton.grid(row = 0, column = 1, pady = GUI.R()['EPOSTypeStudentRadiobuttonPadY']) - - EPOSTypeTeacherRadiobutton = TK.Radiobutton(editingPresetOSFrame) - EPOSTypeTeacherRadiobutton.config(background = GUI.R()['radiobutton1Background']) - EPOSTypeTeacherRadiobutton.config(foreground = GUI.R()['radiobutton1TextColor']) - EPOSTypeTeacherRadiobutton.config(selectcolor = GUI.R()['radiobutton1IndicatorBackground']) - EPOSTypeTeacherRadiobutton.config(activebackground = GUI.R()['radiobutton1Background']) - EPOSTypeTeacherRadiobutton.config(activeforeground = GUI.R()['radiobutton1TextColor']) - EPOSTypeTeacherRadiobutton.config(variable = EPOSTypeVar) - EPOSTypeTeacherRadiobutton.config(value = False) - EPOSTypeTeacherRadiobutton.config(state = TK.DISABLED) - EPOSTypeTeacherRadiobutton.config(width = GUI.R()['EPOSTypeTeacherRadiobuttonWidth']) - EPOSTypeTeacherRadiobutton.config(text = 'Nauczyciele') - EPOSTypeTeacherRadiobutton.grid(row = 0, column = 2, pady = GUI.R()['EPOSTypeTeacherRadiobuttonPadY']) - - # "Separator pomiędzy osobami" - EPOSPersonSeparatorLabel = TKttk.Label(editingPresetOSFrame) - EPOSPersonSeparatorLabel.config(style = 'label1.TLabel') - EPOSPersonSeparatorLabel.config(text = 'Separator pomiędzy osobami') - EPOSPersonSeparatorLabel.grid(row = 1, column = 0, pady = 5, sticky = 'w') - - # Entry - Separator pomiedzy osobami - EPOSPersonSeparatorVar = TK.StringVar() - EPOSPersonSeparatorEntry = TKttk.Entry(editingPresetOSFrame) - EPOSPersonSeparatorEntry.config(style = 'entry1.TEntry') - EPOSPersonSeparatorEntry.config(textvariable = EPOSPersonSeparatorVar) - EPOSPersonSeparatorEntry.config(state = TK.DISABLED) - EPOSPersonSeparatorEntry.config(width = GUI.R()['EPOSPersonSeparatorEntryWidth']) - EPOSPersonSeparatorEntry.grid(row = 1, column = 1, columnspan = 2, padx = 5, pady = 5) - - # "Separator pomiędzy wierszami" - EPOSRowSeparatorLabel = TKttk.Label(editingPresetOSFrame) - EPOSRowSeparatorLabel.config(style = 'label1.TLabel') - EPOSRowSeparatorLabel.config(text = 'Separator pomiędzy wierszami') - EPOSRowSeparatorLabel.grid(row = 2, column = 0, pady = 5, sticky = 'w') - - # Entry - Separator pomiedzy wierszami - EPOSRowSeparatorVar = TK.StringVar() - EPOSRowSeparatorEntry = TKttk.Entry(editingPresetOSFrame) - EPOSRowSeparatorEntry.config(style = 'entry1.TEntry') - EPOSRowSeparatorEntry.config(textvariable = EPOSRowSeparatorVar) - EPOSRowSeparatorEntry.config(state = TK.DISABLED) - EPOSRowSeparatorEntry.config(width = GUI.R()['EPOSRowSeparatorEntryWidth']) - EPOSRowSeparatorEntry.grid(row = 2, column = 1, columnspan = 2, padx = 5, pady = 5) - - # "Separatory pomiędzy danymi" - EPOSDataSeparatorLabel = TKttk.Label(editingPresetOSFrame) - EPOSDataSeparatorLabel.config(style = 'label1.TLabel') - EPOSDataSeparatorLabel.config(text = 'Separatory pomiędzy danymi') - EPOSDataSeparatorLabel.grid(row = 3, column = 0, pady = 5, sticky = 'nw') - - # Entry - Separator pomiedzy wierszami - EPOSDataSeparatorText = TK.Text(editingPresetOSFrame) - EPOSDataSeparatorText.config(state = TK.DISABLED) - EPOSDataSeparatorText.config(width = GUI.R()['EPOSDataSeparatorTextWidth']) - EPOSDataSeparatorText.config(height = GUI.R()['EPOSDataSeparatorTextHeight']) - EPOSDataSeparatorText.config(background = GUI.R()['text1Background']) - EPOSDataSeparatorText.config(foreground = GUI.R()['text1TextColor']) - EPOSDataSeparatorText.config(relief = GUI.R()['text1Relief']) - EPOSDataSeparatorText.grid(row = 3, column = 1, columnspan = 2, padx = 5, pady = 5) - - # " - nowa linia | wciśnięcie przycisku ENTER | \n" - EPOSSeparatorEnterInfoLabel = TKttk.Label(editingPresetOSFrame) - EPOSSeparatorEnterInfoLabel.config(style = 'label1.TLabel') - EPOSSeparatorEnterInfoLabel.config(text = (r' - nowa linia | wciśnięcie przycisku ENTER | \n' + '\n' + 'Niedozwolone znaki: litery, cyfry, *')) - EPOSSeparatorEnterInfoLabel.grid(row = 4, column = 1, columnspan = 2) - - ############### - - ######################### - - # (3) Separator 2 ####### - - formatSeparator2 = TKttk.Separator(editingPresetSettingsFrame) - formatSeparator2.config(style = 'separator1.TSeparator') - formatSeparator2.config(orient = TK.VERTICAL) - formatSeparator2.pack(fill = TK.Y, padx = 10, side = TK.LEFT) - - ######################### - - # (3) Lokalizacja danych - - editingPresetDataLocalizationSettingsFrame = TKttk.Frame(editingPresetSettingsFrame) - editingPresetDataLocalizationSettingsFrame.config(style = 'layoutFrame.TFrame') - editingPresetDataLocalizationSettingsFrame.pack(fill = TK.BOTH, side = TK.RIGHT) - - # C1 - "Wiersz" - editingPresetDataLocalizationC1Label = TKttk.Label(editingPresetDataLocalizationSettingsFrame) - editingPresetDataLocalizationC1Label.config(style = 'label1.TLabel') - editingPresetDataLocalizationC1Label.config(text = 'Wiersz') - editingPresetDataLocalizationC1Label.grid(row = 0, column = 1, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) - - # C2 - "Pozycja w wierszu" - editingPresetDataLocalizationC2Label = TKttk.Label(editingPresetDataLocalizationSettingsFrame) - editingPresetDataLocalizationC2Label.config(style = 'label1.TLabel') - editingPresetDataLocalizationC2Label.config(text = 'Pozycja w wierszu') - editingPresetDataLocalizationC2Label.grid(row = 0, column = 2, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) - - # W1 - "Login" - editingPresetDataLocalizationW1Label = TKttk.Label(editingPresetDataLocalizationSettingsFrame) - editingPresetDataLocalizationW1Label.config(style = 'label2.TLabel') - editingPresetDataLocalizationW1Label.config(text = 'Login') - editingPresetDataLocalizationW1Label.grid(row = 1, column = 0, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) - - # Lokalizacja loginu (wiersz) - EPDataLocalizationLoginRowVar = TK.IntVar() - EPDataLocalizationLoginRowSpinbox = TKttk.Spinbox(editingPresetDataLocalizationSettingsFrame) - EPDataLocalizationLoginRowSpinbox.config(textvariable = EPDataLocalizationLoginRowVar) - EPDataLocalizationLoginRowSpinbox.config(from_ = 0) - EPDataLocalizationLoginRowSpinbox.config(to = 1000000) - EPDataLocalizationLoginRowSpinbox.config(state = TK.DISABLED) - EPDataLocalizationLoginRowSpinbox.config(style = 'spinbox1.TSpinbox') - EPDataLocalizationLoginRowSpinbox.grid(row = 1, column = 1, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) - - # Lokalizacja loginu (pozycja w wierszu) - EPDataLocalizationLoginPosInRowVar = TK.IntVar() - EPDataLocalizationLoginPosInRowSpinbox = TKttk.Spinbox(editingPresetDataLocalizationSettingsFrame) - EPDataLocalizationLoginPosInRowSpinbox.config(textvariable = EPDataLocalizationLoginPosInRowVar) - EPDataLocalizationLoginPosInRowSpinbox.config(from_ = 0) - EPDataLocalizationLoginPosInRowSpinbox.config(to = 1000000) - EPDataLocalizationLoginPosInRowSpinbox.config(state = TK.DISABLED) - EPDataLocalizationLoginPosInRowSpinbox.config(style = 'spinbox1.TSpinbox') - EPDataLocalizationLoginPosInRowSpinbox.grid(row = 1, column = 2, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) - - # W2 - "Imię" - editingPresetDataLocalizationW2Label = TKttk.Label(editingPresetDataLocalizationSettingsFrame) - editingPresetDataLocalizationW2Label.config(style = 'label2.TLabel') - editingPresetDataLocalizationW2Label.config(text = 'Imię') - editingPresetDataLocalizationW2Label.grid(row = 2, column = 0, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) - - # Lokalizacja imienia (wiersz) - EPDataLocalizationFnameRowVar = TK.IntVar() - EPDataLocalizationFnameRowSpinbox = TKttk.Spinbox(editingPresetDataLocalizationSettingsFrame) - EPDataLocalizationFnameRowSpinbox.config(textvariable = EPDataLocalizationFnameRowVar) - EPDataLocalizationFnameRowSpinbox.config(from_ = 0) - EPDataLocalizationFnameRowSpinbox.config(to = 1000000) - EPDataLocalizationFnameRowSpinbox.config(state = TK.DISABLED) - EPDataLocalizationFnameRowSpinbox.config(style = 'spinbox1.TSpinbox') - EPDataLocalizationFnameRowSpinbox.grid(row = 2, column = 1, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) - - # Lokalizacja imienia (pozycja w wierszu) - EPDataLocalizationFnamePosInRowVar = TK.IntVar() - EPDataLocalizationFnamePosInRowSpinbox = TKttk.Spinbox(editingPresetDataLocalizationSettingsFrame) - EPDataLocalizationFnamePosInRowSpinbox.config(textvariable = EPDataLocalizationFnamePosInRowVar) - EPDataLocalizationFnamePosInRowSpinbox.config(from_ = 0) - EPDataLocalizationFnamePosInRowSpinbox.config(to = 1000000) - EPDataLocalizationFnamePosInRowSpinbox.config(state = TK.DISABLED) - EPDataLocalizationFnamePosInRowSpinbox.config(style = 'spinbox1.TSpinbox') - EPDataLocalizationFnamePosInRowSpinbox.grid(row = 2, column = 2, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) - - # W3 - "Nazwisko" - editingPresetDataLocalizationW3Label = TKttk.Label(editingPresetDataLocalizationSettingsFrame) - editingPresetDataLocalizationW3Label.config(style = 'label2.TLabel') - editingPresetDataLocalizationW3Label.config(text = 'Nazwisko') - editingPresetDataLocalizationW3Label.grid(row = 3, column = 0, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) - - # Lokalizacja nazwiska (wiersz) - EPDataLocalizationLnameRowVar = TK.IntVar() - EPDataLocalizationLnameRowSpinbox = TKttk.Spinbox(editingPresetDataLocalizationSettingsFrame) - EPDataLocalizationLnameRowSpinbox.config(textvariable = EPDataLocalizationLnameRowVar) - EPDataLocalizationLnameRowSpinbox.config(from_ = 0) - EPDataLocalizationLnameRowSpinbox.config(to = 1000000) - EPDataLocalizationLnameRowSpinbox.config(state = TK.DISABLED) - EPDataLocalizationLnameRowSpinbox.config(style = 'spinbox1.TSpinbox') - EPDataLocalizationLnameRowSpinbox.grid(row = 3, column = 1, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) - - # Lokalizacja nazwiska (pozycja w wierszu) - EPDataLocalizationLnamePosInRowVar = TK.IntVar() - EPDataLocalizationLnamePosInRowSpinbox = TKttk.Spinbox(editingPresetDataLocalizationSettingsFrame) - EPDataLocalizationLnamePosInRowSpinbox.config(textvariable = EPDataLocalizationLnamePosInRowVar) - EPDataLocalizationLnamePosInRowSpinbox.config(from_ = 0) - EPDataLocalizationLnamePosInRowSpinbox.config(to = 1000000) - EPDataLocalizationLnamePosInRowSpinbox.config(state = TK.DISABLED) - EPDataLocalizationLnamePosInRowSpinbox.config(style = 'spinbox1.TSpinbox') - EPDataLocalizationLnamePosInRowSpinbox.grid(row = 3, column = 2, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) - - # W4 - "Szkoła" - editingPresetDataLocalizationW4Label = TKttk.Label(editingPresetDataLocalizationSettingsFrame) - editingPresetDataLocalizationW4Label.config(style = 'label2.TLabel') - editingPresetDataLocalizationW4Label.config(text = 'Szkoła') - editingPresetDataLocalizationW4Label.grid(row = 4, column = 0, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) - - # Lokalizacja nazwiska (wiersz) - EPDataLocalizationSchoolRowVar = TK.IntVar() - EPDataLocalizationSchoolRowSpinbox = TKttk.Spinbox(editingPresetDataLocalizationSettingsFrame) - EPDataLocalizationSchoolRowSpinbox.config(textvariable = EPDataLocalizationSchoolRowVar) - EPDataLocalizationSchoolRowSpinbox.config(from_ = 0) - EPDataLocalizationSchoolRowSpinbox.config(to = 1000000) - EPDataLocalizationSchoolRowSpinbox.config(state = TK.DISABLED) - EPDataLocalizationSchoolRowSpinbox.config(style = 'spinbox1.TSpinbox') - EPDataLocalizationSchoolRowSpinbox.grid(row = 4, column = 1, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) - - # Lokalizacja nazwiska (pozycja w wierszu) - EPDataLocalizationSchoolPosInRowVar = TK.IntVar() - EPDataLocalizationSchoolPosInRowSpinbox = TKttk.Spinbox(editingPresetDataLocalizationSettingsFrame) - EPDataLocalizationSchoolPosInRowSpinbox.config(textvariable = EPDataLocalizationSchoolPosInRowVar) - EPDataLocalizationSchoolPosInRowSpinbox.config(from_ = 0) - EPDataLocalizationSchoolPosInRowSpinbox.config(to = 1000000) - EPDataLocalizationSchoolPosInRowSpinbox.config(state = TK.DISABLED) - EPDataLocalizationSchoolPosInRowSpinbox.config(style = 'spinbox1.TSpinbox') - EPDataLocalizationSchoolPosInRowSpinbox.grid(row = 4, column = 2, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) - - # W5 - "Klasa" - editingPresetDataLocalizationW5Label = TKttk.Label(editingPresetDataLocalizationSettingsFrame) - editingPresetDataLocalizationW5Label.config(style = 'label2.TLabel') - editingPresetDataLocalizationW5Label.config(text = 'Klasa') - editingPresetDataLocalizationW5Label.grid(row = 5, column = 0, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) - - # Lokalizacja nazwiska (wiersz) - EPDataLocalizationClassRowVar = TK.IntVar() - EPDataLocalizationClassRowSpinbox = TKttk.Spinbox(editingPresetDataLocalizationSettingsFrame) - EPDataLocalizationClassRowSpinbox.config(textvariable = EPDataLocalizationClassRowVar) - EPDataLocalizationClassRowSpinbox.config(from_ = 0) - EPDataLocalizationClassRowSpinbox.config(to = 1000000) - EPDataLocalizationClassRowSpinbox.config(state = TK.DISABLED) - EPDataLocalizationClassRowSpinbox.config(style = 'spinbox1.TSpinbox') - EPDataLocalizationClassRowSpinbox.grid(row = 5, column = 1, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) - - # Lokalizacja nazwiska (pozycja w wierszu) - EPDataLocalizationClassPosInRowVar = TK.IntVar() - EPDataLocalizationClassPosInRowSpinbox = TKttk.Spinbox(editingPresetDataLocalizationSettingsFrame) - EPDataLocalizationClassPosInRowSpinbox.config(textvariable = EPDataLocalizationClassPosInRowVar) - EPDataLocalizationClassPosInRowSpinbox.config(from_ = 0) - EPDataLocalizationClassPosInRowSpinbox.config(to = 1000000) - EPDataLocalizationClassPosInRowSpinbox.config(state = TK.DISABLED) - EPDataLocalizationClassPosInRowSpinbox.config(style = 'spinbox1.TSpinbox') - EPDataLocalizationClassPosInRowSpinbox.grid(row = 5, column = 2, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) - - # Informacje - EPDataLocalizationInfoLabel = TKttk.Label(editingPresetDataLocalizationSettingsFrame) - EPDataLocalizationInfoLabel.config(style = 'label3.TLabel') - EPDataLocalizationInfoLabel.config(justify = 'center') - EPDataLocalizationInfoLabel.config(text = "1234567u\nAdam Nowak, 18\n1a LO\n*******\n\n7654321u\nJan Kowalski, 11\n2a BS\n**********\n\n------------------\n\nTyp osoby: Uczniowie\nSeparator pomiedzy osobami: ''\nSeparator pomiedzy wierszami: ''\nSeparator pomiedzy danymi: ' *enter*, '\nLogin: 1 | 1\nImię: 2 | 1\nNazwisko: 2 | 2\nSzkoła: 3 | 2\nKlasa: 3 | 1") - EPDataLocalizationInfoLabel.grid(row = 6, column = 0, columnspan = 3, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) - - ######################### - - ################################### - - # (2) Przyciski ################### - - editingPresetButtonsFrame = TKttk.Frame(editingPresetFrame) - editingPresetButtonsFrame.config(style = 'layoutFrame.TFrame') - editingPresetButtonsFrame.pack(fill = TK.X, side = TK.BOTTOM, pady = 10) - - def editingPresetSave(): - studentVar = EPOSTypeVar.get() - if studentVar == 's': - studentVar = True - else: - studentVar = False - formatFileContentToSave = { - "student" : studentVar, - "personSeparator" : EPOSPersonSeparatorEntry.get(), - "rowSeparator" : EPOSRowSeparatorEntry.get(), - "dataSeparators" : (EPOSDataSeparatorText.get("1.0", TK.END)).split('\n')[:-1], - "loginRow" : int(EPDataLocalizationLoginRowSpinbox.get()), - "loginPositionInRow" : int(EPDataLocalizationLoginPosInRowSpinbox.get()), - "fnameRow" : int(EPDataLocalizationFnameRowSpinbox.get()), - "fnamePositionInRow" : int(EPDataLocalizationFnamePosInRowSpinbox.get()), - "lnameRow" : int(EPDataLocalizationLnameRowSpinbox.get()), - "lnamePositionInRow" : int(EPDataLocalizationLnamePosInRowSpinbox.get()), - "schoolRow" : int(EPDataLocalizationSchoolRowSpinbox.get()), - "schoolPositionInRow" : int(EPDataLocalizationSchoolPosInRowSpinbox.get()), - "classRow" : int(EPDataLocalizationClassRowSpinbox.get()), - "classPositionInRow" : int(EPDataLocalizationClassPosInRowSpinbox.get()), - } - if not FMT.W(loadingList.get(), formatFileContentToSave): + "entry1.TEntry": { + "configure": { + "fieldbackground": GUI.R()['entry1FieldBackground'], + "relief": GUI.R()['entry1Relief'], + "borderwidth": GUI.R()['entry1BorderWidth'], + "padding": GUI.R()['entry1Padding'], + "foreground": GUI.R()['entry1TextColor'], + }, + }, + }) + TKttk.Style().theme_use("main") + + + + + # Menu główne + self.mainMenu = TKttk.Notebook(master, width = master.winfo_width() - (2 * GUI.R()['menuTabsPadding'] + GUI.R()['tabIconsSize']), height = master.winfo_height()) + self.mainMenu.config(style = "mainMenu.TNotebook") + self.mainMenu.grid(row = 0) + + # Ikona + self.iconTab = TKttk.Frame(self.mainMenu) + self.iconTabImg = PLimg.open(GUI.R()['mainIcon']) + self.iconTabImg = self.iconTabImg.resize((GUI.R()['tabIconsSize'], GUI.R()['tabIconsSize']), PLimg.ANTIALIAS) + self.iconTabImg = PLitk.PhotoImage(self.iconTabImg) + self.mainMenu.add(self.iconTab, image = self.iconTabImg, state = TK.DISABLED) + + + + + # TAB1 - Generator #################################################### + + self.generateTab = TKttk.Frame(self.mainMenu) + self.generateTab.config(style = "mainMenuTabFrame.TFrame") + self.generateTabImg = PLimg.open(GUI.R()['generateTabIcon']) + self.generateTabImg = self.generateTabImg.resize((GUI.R()['tabIconsSize'], GUI.R()['tabIconsSize']), PLimg.ANTIALIAS) + self.generateTabImg = PLitk.PhotoImage(self.generateTabImg) + self.mainMenu.add(self.generateTab, image = self.generateTabImg, state = TK.NORMAL) + + + # Nagłówek + self.generateHeader = TKttk.Label(self.generateTab) + self.generateHeader.config(style = 'tabHeader.TLabel') + self.generateHeader.config(text = 'GENERATOR CSV') + self.generateHeader.pack(fill = TK.X) + + + # Zawartość + self.generateFrame = TKttk.Frame(self.generateTab) + self.generateFrame.config(style = 'tabFrame.TFrame') + self.generateFrame.pack(fill = TK.BOTH, expand = 1, padx = GUI.R()['tabFramePadding'], pady = GUI.R()['tabFramePadding']) + + + # (1) Pliki ################################################# + + self.generateFilesFrame = TKttk.Frame(self.generateFrame) + self.generateFilesFrame.config(style = 'layoutFrame.TFrame') + self.generateFilesFrame.pack(fill = TK.BOTH, expand = 1) + + # (2) Pliki wejściowe ############################# + + self.generateInputFilesFrame = TKttk.Frame(self.generateFilesFrame) + self.generateInputFilesFrame.config(style = 'layoutFrame.TFrame') + self.generateInputFilesFrame.pack(fill = TK.BOTH, expand = 1, padx = 6) + + # (3) Plik źródłowy 1 ################### + + self.GIF1Frame = TKttk.Frame(self.generateInputFilesFrame) + self.GIF1Frame.config(style = 'layoutFrame.TFrame') + self.GIF1Frame.pack(fill = TK.X, expand = 1, pady = int((GUI.R()['GIFFrameSeparators'])/2)) + + # "Plik źródłowy (1)" + self.GIF1Label = TKttk.Label(self.GIF1Frame) + self.GIF1Label.config(style = 'label1.TLabel') + self.GIF1Label.config(width = GUI.R()['generateFilesLabelWidth']) + self.GIF1Label.config(anchor = GUI.R()['generateFilesLabelAnchor']) + self.GIF1Label.config(padding = ('0 0 %s 0' % str(2 * GUI.R()['generateInputFilesPadding']))) + self.GIF1Label.config(text = 'Plik źródłowy (1)') + self.GIF1Label.pack(side = TK.LEFT) + + # Plik żródłowy (1) - Ustawienia + self.GIF1SFrame = TKttk.Frame(self.GIF1Frame) + self.GIF1SFrame.config(style = 'layoutFrame.TFrame') + self.GIF1SFrame.pack(side = TK.RIGHT, fill = TK.X, expand = 1) + + # Lokalizacja + self.GIF1SLocalizationFrame = TKttk.Frame(self.GIF1SFrame) + self.GIF1SLocalizationFrame.config(style = 'layoutFrame.TFrame') + self.GIF1SLocalizationFrame.pack(side = TK.TOP, fill = TK.X, expand = 1, pady = GUI.R()['generateInputFilesPadding']) + + # Lokalizacja - Entry + self.GIF1SLocalizationEntryVar = TK.StringVar() + self.GIF1SLocalizationEntry = TKttk.Entry(self.GIF1SLocalizationFrame) + self.GIF1SLocalizationEntry.config(style = 'entry1.TEntry') + self.GIF1SLocalizationEntry.config(textvariable = self.GIF1SLocalizationEntryVar) + self.GIF1SLocalizationEntry.pack(side = TK.LEFT, expand = 1, fill = TK.X, padx = GUI.R()['generateInputFilesPadding']) + + # Lokalizacja - Button + self.GIF1SLocalizationButton = TKttk.Button(self.GIF1SLocalizationFrame) + self.GIF1SLocalizationButton.config(style = 'button1.TButton') + self.GIF1SLocalizationButton.config(text = 'Przeglądaj') + self.GIF1SLocalizationButton.config(command = self.GIF1SLocalizationButtonAction) + self.GIF1SLocalizationButton.pack(side = TK.RIGHT, padx = GUI.R()['generateInputFilesPadding']) + + # Format + self.GIF1SFormatFrame = TKttk.Frame(self.GIF1SFrame) + self.GIF1SFormatFrame.config(style = 'layoutFrame.TFrame') + self.GIF1SFormatFrame.pack(side = TK.BOTTOM, fill = TK.X, expand = 1, pady = GUI.R()['generateInputFilesPadding']) + + # Format - Label + self.GIF1SFormatLabel = TKttk.Label(self.GIF1SFormatFrame) + self.GIF1SFormatLabel.config(style = 'label2.TLabel') + self.GIF1SFormatLabel.config(text = 'Format') + self.GIF1SFormatLabel.pack(side = TK.LEFT, padx = GUI.R()['generateInputFilesPadding']) + + # Format - Combobox + self.GIF1SFormatComboboxVar = TK.StringVar() + self.GIF1SFormatCombobox = TKttk.Combobox(self.GIF1SFormatFrame) + self.GIF1SFormatCombobox.config(style = 'combobox1.TCombobox') + self.GIF1SFormatCombobox.option_add("*TCombobox*Listbox.background", GUI.R()['combobox1ListBoxBackground']) + self.GIF1SFormatCombobox.option_add("*TCombobox*Listbox.foreground", GUI.R()['combobox1ListBoxForeground']) + self.GIF1SFormatCombobox.option_add("*TCombobox*Listbox.selectBackground", GUI.R()['combobox1ListBoxSelectBackground']) + self.GIF1SFormatCombobox.option_add("*TCombobox*Listbox.selectForeground", GUI.R()['combobox1ListBoxSelectForeground']) + self.GIF1SFormatCombobox.config(state = 'readonly') + self.GIF1SFormatCombobox.config(textvariable = self.GIF1SFormatComboboxVar) + self.GIF1SFormatCombobox['values'] = tuple(FMT.getList()) + self.GIF1SFormatCombobox.pack(side = TK.LEFT, expand = 1, fill = TK.X, padx = GUI.R()['generateInputFilesPadding']) + + ######################################### + + # (3) Plik źródłowy 2 ################### + + self.GIF2Frame = TKttk.Frame(self.generateInputFilesFrame) + self.GIF2Frame.config(style = 'layoutFrame.TFrame') + self.GIF2Frame.pack(fill = TK.X, expand = 1, pady = int((GUI.R()['GIFFrameSeparators'])/2)) + + # "Plik źródłowy (2)" + self.GIF2Label = TKttk.Label(self.GIF2Frame) + self.GIF2Label.config(style = 'label1.TLabel') + self.GIF2Label.config(width = GUI.R()['generateFilesLabelWidth']) + self.GIF2Label.config(anchor = GUI.R()['generateFilesLabelAnchor']) + self.GIF2Label.config(padding = ('0 0 %s 0' % str(2 * GUI.R()['generateInputFilesPadding']))) + self.GIF2Label.config(text = 'Plik źródłowy (2)') + self.GIF2Label.pack(side = TK.LEFT) + + # Plik żródłowy (1) - Ustawienia + self.GIF2SFrame = TKttk.Frame(self.GIF2Frame) + self.GIF2SFrame.config(style = 'layoutFrame.TFrame') + self.GIF2SFrame.pack(side = TK.RIGHT, fill = TK.X, expand = 1) + + # Lokalizacja + self.GIF2SLocalizationFrame = TKttk.Frame(self.GIF2SFrame) + self.GIF2SLocalizationFrame.config(style = 'layoutFrame.TFrame') + self.GIF2SLocalizationFrame.pack(side = TK.TOP, fill = TK.X, expand = 1, pady = GUI.R()['generateInputFilesPadding']) + + # Lokalizacja - Entry + self.GIF2SLocalizationEntryVar = TK.StringVar() + self.GIF2SLocalizationEntry = TKttk.Entry(self.GIF2SLocalizationFrame) + self.GIF2SLocalizationEntry.config(style = 'entry1.TEntry') + self.GIF2SLocalizationEntry.config(textvariable = self.GIF2SLocalizationEntryVar) + self.GIF2SLocalizationEntry.pack(side = TK.LEFT, expand = 1, fill = TK.X, padx = GUI.R()['generateInputFilesPadding']) + + # Lokalizacja - Button + self.GIF2SLocalizationButton = TKttk.Button(self.GIF2SLocalizationFrame) + self.GIF2SLocalizationButton.config(style = 'button1.TButton') + self.GIF2SLocalizationButton.config(text = 'Przeglądaj') + self.GIF2SLocalizationButton.config(command = self.GIF2SLocalizationButtonAction) + self.GIF2SLocalizationButton.pack(side = TK.RIGHT, padx = GUI.R()['generateInputFilesPadding']) + + # Format + self.GIF2SFormatFrame = TKttk.Frame(self.GIF2SFrame) + self.GIF2SFormatFrame.config(style = 'layoutFrame.TFrame') + self.GIF2SFormatFrame.pack(side = TK.BOTTOM, fill = TK.X, expand = 1, pady = GUI.R()['generateInputFilesPadding']) + + # Format - Label + self.GIF2SFormatLabel = TKttk.Label(self.GIF2SFormatFrame) + self.GIF2SFormatLabel.config(style = 'label2.TLabel') + self.GIF2SFormatLabel.config(text = 'Format') + self.GIF2SFormatLabel.pack(side = TK.LEFT, padx = GUI.R()['generateInputFilesPadding']) + + # Format - Combobox + self.GIF2SFormatComboboxVar = TK.StringVar() + self.GIF2SFormatCombobox = TKttk.Combobox(self.GIF2SFormatFrame) + self.GIF2SFormatCombobox.config(style = 'combobox1.TCombobox') + self.GIF2SFormatCombobox.option_add("*TCombobox*Listbox.background", GUI.R()['combobox1ListBoxBackground']) + self.GIF2SFormatCombobox.option_add("*TCombobox*Listbox.foreground", GUI.R()['combobox1ListBoxForeground']) + self.GIF2SFormatCombobox.option_add("*TCombobox*Listbox.selectBackground", GUI.R()['combobox1ListBoxSelectBackground']) + self.GIF2SFormatCombobox.option_add("*TCombobox*Listbox.selectForeground", GUI.R()['combobox1ListBoxSelectForeground']) + self.GIF2SFormatCombobox.config(state = 'readonly') + self.GIF2SFormatCombobox.config(textvariable = self.GIF2SFormatComboboxVar) + self.GIF2SFormatCombobox['values'] = tuple(FMT.getList()) + self.GIF2SFormatCombobox.pack(side = TK.LEFT, expand = 1, fill = TK.X, padx = GUI.R()['generateInputFilesPadding']) + + ######################################### + + # (3) Plik źródłowy 3 ################### + + self.GIF3Frame = TKttk.Frame(self.generateInputFilesFrame) + self.GIF3Frame.config(style = 'layoutFrame.TFrame') + self.GIF3Frame.pack(fill = TK.X, expand = 1, pady = int((GUI.R()['GIFFrameSeparators'])/2)) + + # "Plik źródłowy (3)" + self.GIF3Label = TKttk.Label(self.GIF3Frame) + self.GIF3Label.config(style = 'label1.TLabel') + self.GIF3Label.config(width = GUI.R()['generateFilesLabelWidth']) + self.GIF3Label.config(anchor = GUI.R()['generateFilesLabelAnchor']) + self.GIF3Label.config(padding = ('0 0 %s 0' % str(2 * GUI.R()['generateInputFilesPadding']))) + self.GIF3Label.config(text = 'Plik źródłowy (3)') + self.GIF3Label.pack(side = TK.LEFT) + + # Plik żródłowy (1) - Ustawienia + self.GIF3SFrame = TKttk.Frame(self.GIF3Frame) + self.GIF3SFrame.config(style = 'layoutFrame.TFrame') + self.GIF3SFrame.pack(side = TK.RIGHT, fill = TK.X, expand = 1) + + # Lokalizacja + self.GIF3SLocalizationFrame = TKttk.Frame(self.GIF3SFrame) + self.GIF3SLocalizationFrame.config(style = 'layoutFrame.TFrame') + self.GIF3SLocalizationFrame.pack(side = TK.TOP, fill = TK.X, expand = 1, pady = GUI.R()['generateInputFilesPadding']) + + # Lokalizacja - Entry + self.GIF3SLocalizationEntryVar = TK.StringVar() + self.GIF3SLocalizationEntry = TKttk.Entry(self.GIF3SLocalizationFrame) + self.GIF3SLocalizationEntry.config(style = 'entry1.TEntry') + self.GIF3SLocalizationEntry.config(textvariable = self.GIF3SLocalizationEntryVar) + self.GIF3SLocalizationEntry.pack(side = TK.LEFT, expand = 1, fill = TK.X, padx = GUI.R()['generateInputFilesPadding']) + + # Lokalizacja - Button + self.GIF3SLocalizationButton = TKttk.Button(self.GIF3SLocalizationFrame) + self.GIF3SLocalizationButton.config(style = 'button1.TButton') + self.GIF3SLocalizationButton.config(text = 'Przeglądaj') + self.GIF3SLocalizationButton.config(command = self.GIF3SLocalizationButtonAction) + self.GIF3SLocalizationButton.pack(side = TK.RIGHT, padx = GUI.R()['generateInputFilesPadding']) + + # Format + self.GIF3SFormatFrame = TKttk.Frame(self.GIF3SFrame) + self.GIF3SFormatFrame.config(style = 'layoutFrame.TFrame') + self.GIF3SFormatFrame.pack(side = TK.BOTTOM, fill = TK.X, expand = 1, pady = GUI.R()['generateInputFilesPadding']) + + # Format - Label + self.GIF3SFormatLabel = TKttk.Label(self.GIF3SFormatFrame) + self.GIF3SFormatLabel.config(style = 'label2.TLabel') + self.GIF3SFormatLabel.config(text = 'Format') + self.GIF3SFormatLabel.pack(side = TK.LEFT, padx = GUI.R()['generateInputFilesPadding']) + + # Format - Combobox + self.GIF3SFormatComboboxVar = TK.StringVar() + self.GIF3SFormatCombobox = TKttk.Combobox(self.GIF3SFormatFrame) + self.GIF3SFormatCombobox.config(style = 'combobox1.TCombobox') + self.GIF3SFormatCombobox.option_add("*TCombobox*Listbox.background", GUI.R()['combobox1ListBoxBackground']) + self.GIF3SFormatCombobox.option_add("*TCombobox*Listbox.foreground", GUI.R()['combobox1ListBoxForeground']) + self.GIF3SFormatCombobox.option_add("*TCombobox*Listbox.selectBackground", GUI.R()['combobox1ListBoxSelectBackground']) + self.GIF3SFormatCombobox.option_add("*TCombobox*Listbox.selectForeground", GUI.R()['combobox1ListBoxSelectForeground']) + self.GIF3SFormatCombobox.config(state = 'readonly') + self.GIF3SFormatCombobox.config(textvariable = self.GIF3SFormatComboboxVar) + self.GIF3SFormatCombobox['values'] = tuple(FMT.getList()) + self.GIF3SFormatCombobox.pack(side = TK.LEFT, expand = 1, fill = TK.X, padx = GUI.R()['generateInputFilesPadding']) + + ######################################### + + # (3) Plik źródłowy 4 ################### + + self.GIF4Frame = TKttk.Frame(self.generateInputFilesFrame) + self.GIF4Frame.config(style = 'layoutFrame.TFrame') + self.GIF4Frame.pack(fill = TK.X, expand = 1, pady = int((GUI.R()['GIFFrameSeparators'])/2)) + + # "Plik źródłowy (4)" + self.GIF4Label = TKttk.Label(self.GIF4Frame) + self.GIF4Label.config(style = 'label1.TLabel') + self.GIF4Label.config(width = GUI.R()['generateFilesLabelWidth']) + self.GIF4Label.config(anchor = GUI.R()['generateFilesLabelAnchor']) + self.GIF4Label.config(padding = ('0 0 %s 0' % str(2 * GUI.R()['generateInputFilesPadding']))) + self.GIF4Label.config(text = 'Plik źródłowy (4)') + self.GIF4Label.pack(side = TK.LEFT) + + # Plik żródłowy (1) - Ustawienia + self.GIF4SFrame = TKttk.Frame(self.GIF4Frame) + self.GIF4SFrame.config(style = 'layoutFrame.TFrame') + self.GIF4SFrame.pack(side = TK.RIGHT, fill = TK.X, expand = 1) + + # Lokalizacja + self.GIF4SLocalizationFrame = TKttk.Frame(self.GIF4SFrame) + self.GIF4SLocalizationFrame.config(style = 'layoutFrame.TFrame') + self.GIF4SLocalizationFrame.pack(side = TK.TOP, fill = TK.X, expand = 1, pady = GUI.R()['generateInputFilesPadding']) + + # Lokalizacja - Entry + self.GIF4SLocalizationEntryVar = TK.StringVar() + self.GIF4SLocalizationEntry = TKttk.Entry(self.GIF4SLocalizationFrame) + self.GIF4SLocalizationEntry.config(style = 'entry1.TEntry') + self.GIF4SLocalizationEntry.config(textvariable = self.GIF4SLocalizationEntryVar) + self.GIF4SLocalizationEntry.pack(side = TK.LEFT, expand = 1, fill = TK.X, padx = GUI.R()['generateInputFilesPadding']) + + # Lokalizacja - Button + self.GIF4SLocalizationButton = TKttk.Button(self.GIF4SLocalizationFrame) + self.GIF4SLocalizationButton.config(style = 'button1.TButton') + self.GIF4SLocalizationButton.config(text = 'Przeglądaj') + self.GIF4SLocalizationButton.config(command = self.GIF4SLocalizationButtonAction) + self.GIF4SLocalizationButton.pack(side = TK.RIGHT, padx = GUI.R()['generateInputFilesPadding']) + + # Format + self.GIF4SFormatFrame = TKttk.Frame(self.GIF4SFrame) + self.GIF4SFormatFrame.config(style = 'layoutFrame.TFrame') + self.GIF4SFormatFrame.pack(side = TK.BOTTOM, fill = TK.X, expand = 1, pady = GUI.R()['generateInputFilesPadding']) + + # Format - Label + self.GIF4SFormatLabel = TKttk.Label(self.GIF4SFormatFrame) + self.GIF4SFormatLabel.config(style = 'label2.TLabel') + self.GIF4SFormatLabel.config(text = 'Format') + self.GIF4SFormatLabel.pack(side = TK.LEFT, padx = GUI.R()['generateInputFilesPadding']) + + # Format - Combobox + self.GIF4SFormatComboboxVar = TK.StringVar() + self.GIF4SFormatCombobox = TKttk.Combobox(self.GIF4SFormatFrame) + self.GIF4SFormatCombobox.config(style = 'combobox1.TCombobox') + self.GIF4SFormatCombobox.option_add("*TCombobox*Listbox.background", GUI.R()['combobox1ListBoxBackground']) + self.GIF4SFormatCombobox.option_add("*TCombobox*Listbox.foreground", GUI.R()['combobox1ListBoxForeground']) + self.GIF4SFormatCombobox.option_add("*TCombobox*Listbox.selectBackground", GUI.R()['combobox1ListBoxSelectBackground']) + self.GIF4SFormatCombobox.option_add("*TCombobox*Listbox.selectForeground", GUI.R()['combobox1ListBoxSelectForeground']) + self.GIF4SFormatCombobox.config(state = 'readonly') + self.GIF4SFormatCombobox.config(textvariable = self.GIF4SFormatComboboxVar) + self.GIF4SFormatCombobox['values'] = tuple(FMT.getList()) + self.GIF4SFormatCombobox.pack(side = TK.LEFT, expand = 1, fill = TK.X, padx = GUI.R()['generateInputFilesPadding']) + + ######################################### + + ################################################### + + # (2) Separator1 ################################## + + self.generateSeparator1 = TKttk.Separator(self.generateFilesFrame) + self.generateSeparator1.config(style = 'separator1.TSeparator') + self.generateSeparator1.pack(fill = TK.X, pady = 10) + + ################################################### + + # (2) Pliki wyjściowe ############################# + + self.generateOutputFilesFrame = TKttk.Frame(self.generateFilesFrame) + self.generateOutputFilesFrame.config(style = 'layoutFrame.TFrame') + self.generateOutputFilesFrame.pack(fill = TK.X, pady = 10, padx = 12) + + # (3) Poczta ############################ + + self.GOFMailFrame = TKttk.Frame(self.generateOutputFilesFrame) + self.GOFMailFrame.config(style = 'layoutFrame.TFrame') + self.GOFMailFrame.pack(pady = GUI.R()['generateOutputFilesPadding'], fill = TK.X, expand = 1) + + # "Poczta" + self.GOFMailLabel = TKttk.Label(self.GOFMailFrame) + self.GOFMailLabel.config(style = 'label1.TLabel') + self.GOFMailLabel.config(width = GUI.R()['generateFilesLabelWidth']) + self.GOFMailLabel.config(anchor = GUI.R()['generateFilesLabelAnchor']) + self.GOFMailLabel.config(text = 'Poczta') + self.GOFMailLabel.pack(side = TK.LEFT) + + # Plik poczty - Lokalizacja (Entry) + self.GOFMailEntryVar = TK.StringVar() + self.GOFMailEntry = TKttk.Entry(self.GOFMailFrame) + self.GOFMailEntry.config(style = 'entry1.TEntry') + self.GOFMailEntry.config(textvariable = self.GOFMailEntryVar) + self.GOFMailEntry.pack(padx = 2 * GUI.R()['generateOutputFilesPadding'], side = TK.LEFT, fill = TK.X, expand = 1) + + # Plik poczty - Lokalizacja (Button) + self.GOFMailButton = TKttk.Button(self.GOFMailFrame) + self.GOFMailButton.config(style = 'button1.TButton') + self.GOFMailButton.config(text = 'Przeglądaj') + self.GOFMailButton.config(command = self.GOFMailButtonAction) + self.GOFMailButton.pack(side = TK.LEFT) + + ######################################### + + # (3) Office ############################ + + self.GOFOfficeFrame = TKttk.Frame(self.generateOutputFilesFrame) + self.GOFOfficeFrame.config(style = 'layoutFrame.TFrame') + self.GOFOfficeFrame.pack(pady = GUI.R()['generateOutputFilesPadding'], fill = TK.X, expand = 1) + + # "Office" + self.GOFOfficeLabel = TKttk.Label(self.GOFOfficeFrame) + self.GOFOfficeLabel.config(style = 'label1.TLabel') + self.GOFOfficeLabel.config(width = GUI.R()['generateFilesLabelWidth']) + self.GOFOfficeLabel.config(anchor = GUI.R()['generateFilesLabelAnchor']) + self.GOFOfficeLabel.config(text = 'Office') + self.GOFOfficeLabel.pack(side = TK.LEFT) + + # Plik office - Lokalizacja (Entry) + self.GOFOfficeEntryVar = TK.StringVar() + self.GOFOfficeEntry = TKttk.Entry(self.GOFOfficeFrame) + self.GOFOfficeEntry.config(style = 'entry1.TEntry') + self.GOFOfficeEntry.config(textvariable = self.GOFOfficeEntryVar) + self.GOFOfficeEntry.pack(padx = 2 * GUI.R()['generateOutputFilesPadding'], side = TK.LEFT, fill = TK.X, expand = 1) + + # Plik office - Lokalizacja (Button) + self.GOFOfficeButton = TKttk.Button(self.GOFOfficeFrame) + self.GOFOfficeButton.config(style = 'button1.TButton') + self.GOFOfficeButton.config(text = 'Przeglądaj') + self.GOFOfficeButton.config(command = self.GOFOfficeButtonAction) + self.GOFOfficeButton.pack(side = TK.LEFT) + + ######################################### + + ################################################### + + ############################################################# + + # (1) Separator2 ########################################### + + self.generateSeparator2 = TKttk.Separator(self.generateFrame) + self.generateSeparator2.config(style = 'separator1.TSeparator') + self.generateSeparator2.pack(fill = TK.X, pady = 10) + + ############################################################# + + # (1) Przyciski ############################################# + + self.generateButtonsFrame = TKttk.Frame(self.generateFrame) + self.generateButtonsFrame.config(style = 'layoutFrame.TFrame') + self.generateButtonsFrame.pack(fill = TK.X, pady = 10, padx = 12) + + # Przycisk "START" + self.generateStartButton = TKttk.Button(self.generateButtonsFrame) + self.generateStartButton.config(style = 'button1.TButton') + self.generateStartButton.config(padding = 10) + self.generateStartButton.config(text = 'START') + self.generateStartButton.config(command = self.generateStartButtonAction) + self.generateStartButton.pack(side = TK.LEFT, fill = TK.X, expand = 1) + + ############################################################## + + ####################################################################### + + + + + # TAB3 - Format ####################################################### + + self.formatTab = TKttk.Frame(self.mainMenu) + self.formatTab.config(style = "mainMenuTabFrame.TFrame") + self.formatTabImg = PLimg.open(GUI.R()['formatTabIcon']) + self.formatTabImg = self.formatTabImg.resize((GUI.R()['tabIconsSize'], GUI.R()['tabIconsSize']), PLimg.ANTIALIAS) + self.formatTabImg = PLitk.PhotoImage(self.formatTabImg) + self.mainMenu.add(self.formatTab, image = self.formatTabImg, state = TK.NORMAL) + + + # Nagłówek + self.formatHeader = TKttk.Label(self.formatTab) + self.formatHeader.config(style = 'tabHeader.TLabel') + self.formatHeader.config(text = 'FORMAT DANYCH') + self.formatHeader.pack(fill = TK.X) + + + # Zawartość + self.formatFrame = TKttk.Frame(self.formatTab) + self.formatFrame.config(style = 'tabFrame.TFrame') + self.formatFrame.pack(fill = TK.BOTH, expand = 1, padx = GUI.R()['tabFramePadding'], pady = GUI.R()['tabFramePadding']) + + + # (1) Ładowanie presetu ##################################### + + self.loadingPresetFrame = TKttk.Frame(self.formatFrame) + self.loadingPresetFrame.config(style = 'layoutFrame.TFrame') + self.loadingPresetFrame.pack(fill = TK.X, side = TK.TOP, pady = 5, padx = 10) + + # "Wybierz preset do edycji lub wpisz nazwę nowego" + self.loadingListLabel = TKttk.Label(self.loadingPresetFrame) + self.loadingListLabel.config(style = 'label1.TLabel') + self.loadingListLabel.config(text = 'Wybierz preset do edycji lub wpisz nazwę nowego') + self.loadingListLabel.pack(side = TK.LEFT) + + # Rozwijana lista presetów + self.loadingListVar = TK.StringVar() + self.loadingList = TKttk.Combobox(self.loadingPresetFrame) + self.loadingList.config(textvariable = self.loadingListVar) + self.loadingList.config(style = 'combobox2.TCombobox') + self.loadingList.option_add("*TCombobox*Listbox.background", GUI.R()['combobox2ListBoxBackground']) + self.loadingList.option_add("*TCombobox*Listbox.foreground", GUI.R()['combobox2ListBoxForeground']) + self.loadingList.option_add("*TCombobox*Listbox.selectBackground", GUI.R()['combobox2ListBoxSelectBackground']) + self.loadingList.option_add("*TCombobox*Listbox.selectForeground", GUI.R()['combobox2ListBoxSelectForeground']) + self.loadingList.pack(side = TK.LEFT, padx = GUI.R()['loadingListPadX'], fill = TK.X, expand = 1) + self.loadingList['values'] = tuple(FMT.getList()) + + # Przycisk "WCZYTAJ" + self.loadingButton = TKttk.Button(self.loadingPresetFrame) + self.loadingButton.config(style = 'button1.TButton') + self.loadingButton.config(command = self.loadingButtonAction) + self.loadingButton.config(width = GUI.R()['loadingButtonWidth']) + self.loadingButton.config(text = 'WCZYTAJ') + self.loadingButton.pack(side = TK.RIGHT) + + ############################################################# + + # (1) Separator 1 ########################################### + + self.formatSeparator1 = TKttk.Separator(self.formatFrame) + self.formatSeparator1.config(style = 'separator1.TSeparator') + self.formatSeparator1.config(orient = TK.HORIZONTAL) + self.formatSeparator1.pack(fill = TK.X, pady = 10) + + ############################################################# + + # (1) Edycja presetu ######################################## + + self.editingPresetFrame = TKttk.Frame(self.formatFrame) + self.editingPresetFrame.config(style = 'layoutFrame.TFrame') + self.editingPresetFrame.pack(fill = TK.BOTH, expand = 1, padx = 10) + + # (2) Ustawienia ################################## + + self.editingPresetSettingsFrame = TKttk.Frame(self.editingPresetFrame) + self.editingPresetSettingsFrame.config(style = 'layoutFrame.TFrame') + self.editingPresetSettingsFrame.pack(fill = TK.BOTH, expand = 1) + + # (3) Inne ustawienia ################### + + self.editingPresetOSFrame = TKttk.Frame(self.editingPresetSettingsFrame) + self.editingPresetOSFrame.config(style = 'layoutFrame.TFrame') + self.editingPresetOSFrame.pack(fill = TK.BOTH, expand = 1, side = TK.LEFT) + + # (5) Typ osoby ############### + + self.EPOSTypeFrame = TKttk.Frame(self.editingPresetOSFrame) + self.EPOSTypeFrame.config(style = 'layoutFrame.TFrame') + self.EPOSTypeFrame.pack(fill = TK.X, expand = 1, pady = 5) + + # "Typ osoby" + self.EPOSTypeLabel = TKttk.Label(self.EPOSTypeFrame) + self.EPOSTypeLabel.config(style = 'label1.TLabel') + self.EPOSTypeLabel.config(width = GUI.R()['EPOSLabelWidth']) + self.EPOSTypeLabel.config(anchor = GUI.R()['EPOSLabelAnchor']) + self.EPOSTypeLabel.config(text = 'Typ osoby') + self.EPOSTypeLabel.pack(side = TK.LEFT) + + # Radiobutton + self.EPOSTypeVar = TK.BooleanVar(value = True) + + self.EPOSTypeStudentRadiobutton = TK.Radiobutton(self.EPOSTypeFrame) + self.EPOSTypeStudentRadiobutton.config(background = GUI.R()['radiobutton1Background']) + self.EPOSTypeStudentRadiobutton.config(foreground = GUI.R()['radiobutton1TextColor']) + self.EPOSTypeStudentRadiobutton.config(selectcolor = GUI.R()['radiobutton1IndicatorBackground']) + self.EPOSTypeStudentRadiobutton.config(activebackground = GUI.R()['radiobutton1Background']) + self.EPOSTypeStudentRadiobutton.config(activeforeground = GUI.R()['radiobutton1TextColor']) + self.EPOSTypeStudentRadiobutton.config(variable = self.EPOSTypeVar) + self.EPOSTypeStudentRadiobutton.config(value = True) + self.EPOSTypeStudentRadiobutton.config(state = TK.DISABLED) + self.EPOSTypeStudentRadiobutton.config(text = 'Uczniowie') + self.EPOSTypeStudentRadiobutton.pack(side = TK.RIGHT, fill = TK.X, expand = 1) + + self.EPOSTypeTeacherRadiobutton = TK.Radiobutton(self.EPOSTypeFrame) + self.EPOSTypeTeacherRadiobutton.config(background = GUI.R()['radiobutton1Background']) + self.EPOSTypeTeacherRadiobutton.config(foreground = GUI.R()['radiobutton1TextColor']) + self.EPOSTypeTeacherRadiobutton.config(selectcolor = GUI.R()['radiobutton1IndicatorBackground']) + self.EPOSTypeTeacherRadiobutton.config(activebackground = GUI.R()['radiobutton1Background']) + self.EPOSTypeTeacherRadiobutton.config(activeforeground = GUI.R()['radiobutton1TextColor']) + self.EPOSTypeTeacherRadiobutton.config(variable = self.EPOSTypeVar) + self.EPOSTypeTeacherRadiobutton.config(value = False) + self.EPOSTypeTeacherRadiobutton.config(state = TK.DISABLED) + self.EPOSTypeTeacherRadiobutton.config(text = 'Nauczyciele') + self.EPOSTypeTeacherRadiobutton.pack(side = TK.RIGHT, fill = TK.X, expand = 1) + + ##################### + + # (5) Separator pomiedzy osobami + + self.EPOSPersonSeparatorFrame = TKttk.Frame(self.editingPresetOSFrame) + self.EPOSPersonSeparatorFrame.config(style = 'layoutFrame.TFrame') + self.EPOSPersonSeparatorFrame.pack(fill = TK.X, expand = 1, pady = 5) + + # "Separator pomiędzy osobami" + self.EPOSPersonSeparatorLabel = TKttk.Label(self.EPOSPersonSeparatorFrame) + self.EPOSPersonSeparatorLabel.config(style = 'label1.TLabel') + self.EPOSPersonSeparatorLabel.config(width = GUI.R()['EPOSLabelWidth']) + self.EPOSPersonSeparatorLabel.config(anchor = GUI.R()['EPOSLabelAnchor']) + self.EPOSPersonSeparatorLabel.config(text = 'Separator pomiędzy osobami') + self.EPOSPersonSeparatorLabel.pack(side = TK.LEFT) + + # Entry - Separator pomiedzy osobami + self.EPOSPersonSeparatorVar = TK.StringVar() + self.EPOSPersonSeparatorEntry = TKttk.Entry(self.EPOSPersonSeparatorFrame) + self.EPOSPersonSeparatorEntry.config(style = 'entry1.TEntry') + self.EPOSPersonSeparatorEntry.config(textvariable = self.EPOSPersonSeparatorVar) + self.EPOSPersonSeparatorEntry.config(state = TK.DISABLED) + self.EPOSPersonSeparatorEntry.config(width = GUI.R()['EPOSPersonSeparatorEntryWidth']) + self.EPOSPersonSeparatorEntry.pack(side = TK.RIGHT, fill = TK.X, expand = 1) + + ##################### + + # (5) Separator pomiedzy wierszami + + self.EPOSRowSeparatorFrame = TKttk.Frame(self.editingPresetOSFrame) + self.EPOSRowSeparatorFrame.config(style = 'layoutFrame.TFrame') + self.EPOSRowSeparatorFrame.pack(fill = TK.X, expand = 1, pady = 5) + + # "Separator pomiędzy wierszami" + self.EPOSRowSeparatorLabel = TKttk.Label(self.EPOSRowSeparatorFrame) + self.EPOSRowSeparatorLabel.config(style = 'label1.TLabel') + self.EPOSRowSeparatorLabel.config(width = GUI.R()['EPOSLabelWidth']) + self.EPOSRowSeparatorLabel.config(anchor = GUI.R()['EPOSLabelAnchor']) + self.EPOSRowSeparatorLabel.config(text = 'Separator pomiędzy wierszami') + self.EPOSRowSeparatorLabel.pack(side = TK.LEFT) + + # Entry - Separator pomiedzy wierszami + self.EPOSRowSeparatorVar = TK.StringVar() + self.EPOSRowSeparatorEntry = TKttk.Entry(self.EPOSRowSeparatorFrame) + self.EPOSRowSeparatorEntry.config(style = 'entry1.TEntry') + self.EPOSRowSeparatorEntry.config(textvariable = self.EPOSRowSeparatorVar) + self.EPOSRowSeparatorEntry.config(state = TK.DISABLED) + self.EPOSRowSeparatorEntry.config(width = GUI.R()['EPOSRowSeparatorEntryWidth']) + self.EPOSRowSeparatorEntry.pack(side = TK.RIGHT, fill = TK.X, expand = 1) + + ##################### + + # (5) Separatory pomiedzy danymi + + self.EPOSDataSeparatorFrame = TKttk.Frame(self.editingPresetOSFrame) + self.EPOSDataSeparatorFrame.config(style = 'layoutFrame.TFrame') + self.EPOSDataSeparatorFrame.pack(fill = TK.BOTH, expand = 1, pady = 5) + + # "Separatory pomiędzy danymi" + self.EPOSDataSeparatorLabel = TKttk.Label(self.EPOSDataSeparatorFrame) + self.EPOSDataSeparatorLabel.config(style = 'label1.TLabel') + self.EPOSDataSeparatorLabel.config(width = GUI.R()['EPOSLabelWidth']) + self.EPOSDataSeparatorLabel.config(anchor = GUI.R()['EPOSLabelAnchor']) + self.EPOSDataSeparatorLabel.config(text = 'Separatory pomiędzy danymi') + self.EPOSDataSeparatorLabel.pack(side = TK.LEFT) + + # Entry - Separator pomiedzy wierszami + self.EPOSDataSeparatorText = TK.Text(self.EPOSDataSeparatorFrame) + self.EPOSDataSeparatorText.config(state = TK.DISABLED) + self.EPOSDataSeparatorText.config(background = GUI.R()['text1Background']) + self.EPOSDataSeparatorText.config(foreground = GUI.R()['text1TextColor']) + self.EPOSDataSeparatorText.config(relief = GUI.R()['text1Relief']) + self.EPOSDataSeparatorText.pack(side = TK.TOP, fill = TK.BOTH) + + ##################### + + ############################### + + # (4) Separator 2 ############# + + self.formatSeparator2 = TKttk.Separator(self.editingPresetSettingsFrame) + self.formatSeparator2.config(style = 'separator1.TSeparator') + self.formatSeparator2.config(orient = TK.VERTICAL) + self.formatSeparator2.pack(fill = TK.Y, padx = 12, expand = 1, side = TK.LEFT) + + ############################### + + # (4) Lokalizacja danych ###### + + self.editingPresetDLFrame = TKttk.Frame(self.editingPresetSettingsFrame) + self.editingPresetDLFrame.config(style = 'layoutFrame.TFrame') + self.editingPresetDLFrame.pack(fill = TK.BOTH, side = TK.RIGHT) + self.editingPresetDLFrame.grid_columnconfigure(1, weight = 1) + self.editingPresetDLFrame.grid_columnconfigure(2, weight = 1) + + # C1 - "Wiersz" + self.EPDLC1Label = TKttk.Label(self.editingPresetDLFrame) + self.EPDLC1Label.config(style = 'label1.TLabel') + self.EPDLC1Label.config(text = 'Wiersz') + self.EPDLC1Label.grid(row = 0, column = 1, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) + + # C2 - "Pozycja w wierszu" + self.EPDLC2Label = TKttk.Label(self.editingPresetDLFrame) + self.EPDLC2Label.config(style = 'label1.TLabel') + self.EPDLC2Label.config(justify = TK.CENTER) + self.EPDLC2Label.config(text = 'Pozycja\nw wierszu') + self.EPDLC2Label.grid(row = 0, column = 2, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) + + # W1 - "Login" + self.EPDLW1Label = TKttk.Label(self.editingPresetDLFrame) + self.EPDLW1Label.config(style = 'label1.TLabel') + self.EPDLW1Label.config(text = 'Login') + self.EPDLW1Label.grid(row = 1, column = 0, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) + + # Lokalizacja loginu (wiersz) + self.EPDLLoginRowVar = TK.IntVar() + self.EPDLLoginRowSpinbox = TKttk.Spinbox(self.editingPresetDLFrame) + self.EPDLLoginRowSpinbox.config(textvariable = self.EPDLLoginRowVar) + self.EPDLLoginRowSpinbox.config(from_ = 0) + self.EPDLLoginRowSpinbox.config(to = 1000000) + self.EPDLLoginRowSpinbox.config(state = TK.DISABLED) + self.EPDLLoginRowSpinbox.config(style = 'spinbox1.TSpinbox') + self.EPDLLoginRowSpinbox.grid(row = 1, column = 1, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) + + # Lokalizacja loginu (pozycja w wierszu) + self.EPDLLoginPosInRowVar = TK.IntVar() + self.EPDLLoginPosInRowSpinbox = TKttk.Spinbox(self.editingPresetDLFrame) + self.EPDLLoginPosInRowSpinbox.config(textvariable = self.EPDLLoginPosInRowVar) + self.EPDLLoginPosInRowSpinbox.config(from_ = 0) + self.EPDLLoginPosInRowSpinbox.config(to = 1000000) + self.EPDLLoginPosInRowSpinbox.config(state = TK.DISABLED) + self.EPDLLoginPosInRowSpinbox.config(style = 'spinbox1.TSpinbox') + self.EPDLLoginPosInRowSpinbox.grid(row = 1, column = 2, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) + + # W2 - "Imię" + self.EPDLW2Label = TKttk.Label(self.editingPresetDLFrame) + self.EPDLW2Label.config(style = 'label1.TLabel') + self.EPDLW2Label.config(text = 'Imię') + self.EPDLW2Label.grid(row = 2, column = 0, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) + + # Lokalizacja imienia (wiersz) + self.EPDLFnameRowVar = TK.IntVar() + self.EPDLFnameRowSpinbox = TKttk.Spinbox(self.editingPresetDLFrame) + self.EPDLFnameRowSpinbox.config(textvariable = self.EPDLFnameRowVar) + self.EPDLFnameRowSpinbox.config(from_ = 0) + self.EPDLFnameRowSpinbox.config(to = 1000000) + self.EPDLFnameRowSpinbox.config(state = TK.DISABLED) + self.EPDLFnameRowSpinbox.config(style = 'spinbox1.TSpinbox') + self.EPDLFnameRowSpinbox.grid(row = 2, column = 1, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) + + # Lokalizacja imienia (pozycja w wierszu) + self.EPDLFnamePosInRowVar = TK.IntVar() + self.EPDLFnamePosInRowSpinbox = TKttk.Spinbox(self.editingPresetDLFrame) + self.EPDLFnamePosInRowSpinbox.config(textvariable = self.EPDLFnamePosInRowVar) + self.EPDLFnamePosInRowSpinbox.config(from_ = 0) + self.EPDLFnamePosInRowSpinbox.config(to = 1000000) + self.EPDLFnamePosInRowSpinbox.config(state = TK.DISABLED) + self.EPDLFnamePosInRowSpinbox.config(style = 'spinbox1.TSpinbox') + self.EPDLFnamePosInRowSpinbox.grid(row = 2, column = 2, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) + + # W3 - "Nazwisko" + self.EPDLW3Label = TKttk.Label(self.editingPresetDLFrame) + self.EPDLW3Label.config(style = 'label1.TLabel') + self.EPDLW3Label.config(text = 'Nazwisko') + self.EPDLW3Label.grid(row = 3, column = 0, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) + + # Lokalizacja nazwiska (wiersz) + self.EPDLLnameRowVar = TK.IntVar() + self.EPDLLnameRowSpinbox = TKttk.Spinbox(self.editingPresetDLFrame) + self.EPDLLnameRowSpinbox.config(textvariable = self.EPDLLnameRowVar) + self.EPDLLnameRowSpinbox.config(from_ = 0) + self.EPDLLnameRowSpinbox.config(to = 1000000) + self.EPDLLnameRowSpinbox.config(state = TK.DISABLED) + self.EPDLLnameRowSpinbox.config(style = 'spinbox1.TSpinbox') + self.EPDLLnameRowSpinbox.grid(row = 3, column = 1, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) + + # Lokalizacja nazwiska (pozycja w wierszu) + self.EPDLLnamePosInRowVar = TK.IntVar() + self.EPDLLnamePosInRowSpinbox = TKttk.Spinbox(self.editingPresetDLFrame) + self.EPDLLnamePosInRowSpinbox.config(textvariable = self.EPDLLnamePosInRowVar) + self.EPDLLnamePosInRowSpinbox.config(from_ = 0) + self.EPDLLnamePosInRowSpinbox.config(to = 1000000) + self.EPDLLnamePosInRowSpinbox.config(state = TK.DISABLED) + self.EPDLLnamePosInRowSpinbox.config(style = 'spinbox1.TSpinbox') + self.EPDLLnamePosInRowSpinbox.grid(row = 3, column = 2, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) + + # W4 - "Szkoła" + self.EPDLW4Label = TKttk.Label(self.editingPresetDLFrame) + self.EPDLW4Label.config(style = 'label1.TLabel') + self.EPDLW4Label.config(text = 'Szkoła') + self.EPDLW4Label.grid(row = 4, column = 0, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) + + # Lokalizacja nazwiska (wiersz) + self.EPDLSchoolRowVar = TK.IntVar() + self.EPDLSchoolRowSpinbox = TKttk.Spinbox(self.editingPresetDLFrame) + self.EPDLSchoolRowSpinbox.config(textvariable = self.EPDLSchoolRowVar) + self.EPDLSchoolRowSpinbox.config(from_ = 0) + self.EPDLSchoolRowSpinbox.config(to = 1000000) + self.EPDLSchoolRowSpinbox.config(state = TK.DISABLED) + self.EPDLSchoolRowSpinbox.config(style = 'spinbox1.TSpinbox') + self.EPDLSchoolRowSpinbox.grid(row = 4, column = 1, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) + + # Lokalizacja nazwiska (pozycja w wierszu) + self.EPDLSchoolPosInRowVar = TK.IntVar() + self.EPDLSchoolPosInRowSpinbox = TKttk.Spinbox(self.editingPresetDLFrame) + self.EPDLSchoolPosInRowSpinbox.config(textvariable = self.EPDLSchoolPosInRowVar) + self.EPDLSchoolPosInRowSpinbox.config(from_ = 0) + self.EPDLSchoolPosInRowSpinbox.config(to = 1000000) + self.EPDLSchoolPosInRowSpinbox.config(state = TK.DISABLED) + self.EPDLSchoolPosInRowSpinbox.config(style = 'spinbox1.TSpinbox') + self.EPDLSchoolPosInRowSpinbox.grid(row = 4, column = 2, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) + + # W5 - "Klasa" + self.EPDLW5Label = TKttk.Label(self.editingPresetDLFrame) + self.EPDLW5Label.config(style = 'label1.TLabel') + self.EPDLW5Label.config(text = 'Klasa') + self.EPDLW5Label.grid(row = 5, column = 0, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) + + # Lokalizacja nazwiska (wiersz) + self.EPDLClassRowVar = TK.IntVar() + self.EPDLClassRowSpinbox = TKttk.Spinbox(self.editingPresetDLFrame) + self.EPDLClassRowSpinbox.config(textvariable = self.EPDLClassRowVar) + self.EPDLClassRowSpinbox.config(from_ = 0) + self.EPDLClassRowSpinbox.config(to = 1000000) + self.EPDLClassRowSpinbox.config(state = TK.DISABLED) + self.EPDLClassRowSpinbox.config(style = 'spinbox1.TSpinbox') + self.EPDLClassRowSpinbox.grid(row = 5, column = 1, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) + + # Lokalizacja nazwiska (pozycja w wierszu) + self.EPDLClassPosInRowVar = TK.IntVar() + self.EPDLClassPosInRowSpinbox = TKttk.Spinbox(self.editingPresetDLFrame) + self.EPDLClassPosInRowSpinbox.config(textvariable = self.EPDLClassPosInRowVar) + self.EPDLClassPosInRowSpinbox.config(from_ = 0) + self.EPDLClassPosInRowSpinbox.config(to = 1000000) + self.EPDLClassPosInRowSpinbox.config(state = TK.DISABLED) + self.EPDLClassPosInRowSpinbox.config(style = 'spinbox1.TSpinbox') + self.EPDLClassPosInRowSpinbox.grid(row = 5, column = 2, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) + + ############################### + + ######################################### + + ################################################### + + # (1) Separator 3 ########################################### + + self.formatSeparator3 = TKttk.Separator(self.formatFrame) + self.formatSeparator3.config(style = 'separator1.TSeparator') + self.formatSeparator3.config(orient = TK.HORIZONTAL) + self.formatSeparator3.pack(fill = TK.X, expand = 1, pady = 6) + + ############################################################# + + # (2) Przyciski ############################################# + + self.editingPresetButtonsFrame = TKttk.Frame(self.formatFrame) + self.editingPresetButtonsFrame.config(style = 'layoutFrame.TFrame') + self.editingPresetButtonsFrame.pack(fill = TK.X, expand = 1, side = TK.BOTTOM) + + # Przycisk 'ZAPISZ' + self.editingPresetSaveButton = TKttk.Button(self.editingPresetButtonsFrame) + self.editingPresetSaveButton.config(command = self.editingPresetSaveButtonAction) + self.editingPresetSaveButton.config(state = TK.DISABLED) + self.editingPresetSaveButton.config(style = 'button1.TButton') + self.editingPresetSaveButton.config(width = GUI.R()['editingPresetSaveButtonWidth']) + self.editingPresetSaveButton.config(text = 'ZAPISZ') + self.editingPresetSaveButton.pack(side = TK.LEFT, expand = 1) + + # Przycisk 'Anuluj' + self.editingPresetCancelButton = TKttk.Button(self.editingPresetButtonsFrame) + self.editingPresetCancelButton.config(command = self.editingPresetCancelButtonAction) + self.editingPresetCancelButton.config(state = TK.DISABLED) + self.editingPresetCancelButton.config(style = 'button1.TButton') + self.editingPresetCancelButton.config(width = GUI.R()['editingPresetCancelButtonWidth']) + self.editingPresetCancelButton.config(text = 'Anuluj') + self.editingPresetCancelButton.pack(side = TK.RIGHT, expand = 1) + + ############################################################# + + ###################################################################### + + + + + # TAB3 - Ustawienia ################################################## + + self.settingsTab = TKttk.Frame(self.mainMenu) + self.settingsTab.config(style = "mainMenuTabFrame.TFrame") + self.settingsTabImg = PLimg.open(GUI.R()['settingsTabIcon']) + self.settingsTabImg = self.settingsTabImg.resize((GUI.R()['tabIconsSize'], GUI.R()['tabIconsSize']), PLimg.ANTIALIAS) + self.settingsTabImg = PLitk.PhotoImage(self.settingsTabImg) + self.mainMenu.add(self.settingsTab, image = self.settingsTabImg, state = TK.NORMAL) + + + # Nagłówek + self.settingsHeader = TKttk.Label(self.settingsTab) + self.settingsHeader.config(style = 'tabHeader.TLabel') + self.settingsHeader.config(text = 'USTAWIENIA') + self.settingsHeader.pack(fill = TK.X) + + + # Zawartość + self.settingsFrame = TKttk.Frame(self.settingsTab) + self.settingsFrame.config(style = 'tabFrame.TFrame') + self.settingsFrame.pack(fill = TK.BOTH, expand = 1, padx = GUI.R()['tabFramePadding'], pady = GUI.R()['tabFramePadding']) + + ###################################################################### + + + + + # TAB4 - O programie ################################################# + + self.aboutTab = TKttk.Frame(self.mainMenu) + self.aboutTab.config(style = "mainMenuTabFrame.TFrame") + self.aboutTabImg = PLimg.open(GUI.R()['aboutTabIcon']) + self.aboutTabImg = self.aboutTabImg.resize((GUI.R()['tabIconsSize'], GUI.R()['tabIconsSize']), PLimg.ANTIALIAS) + self.aboutTabImg = PLitk.PhotoImage(self.aboutTabImg) + self.mainMenu.add(self.aboutTab, image = self.aboutTabImg, state = TK.NORMAL) + + + # Nagłówek + self.aboutHeader = TKttk.Label(self.aboutTab) + self.aboutHeader.config(style = 'tabHeader.TLabel') + self.aboutHeader.config(text = 'O PROGRAMIE') + self.aboutHeader.pack(fill = TK.X) + + + # Zawartość + self.aboutFrame = TKttk.Frame(self.aboutTab) + self.aboutFrame.config(style = 'tabFrame.TFrame') + self.aboutFrame.pack(fill = TK.BOTH, expand = 1, padx = GUI.R()['tabFramePadding'], pady = GUI.R()['tabFramePadding']) + + ###################################################################### + + + + # Akcje przycisków - TAB1 + + def GIF1SLocalizationButtonAction(self): + filename = str(TKfld.askopenfilename(initialdir = '/', title = "Wybierz plik z danymi")) + self.GIF1SLocalizationEntryVar.set(filename) + + def GIF2SLocalizationButtonAction(self): + filename = str(TKfld.askopenfilename(initialdir = '/', title = "Wybierz plik z danymi")) + self.GIF2SLocalizationEntryVar.set(filename) + + def GIF3SLocalizationButtonAction(self): + filename = str(TKfld.askopenfilename(initialdir = '/', title = "Wybierz plik z danymi")) + self.GIF3SLocalizationEntryVar.set(filename) + + def GIF4SLocalizationButtonAction(self): + filename = str(TKfld.askopenfilename(initialdir = '/', title = "Wybierz plik z danymi")) + self.GIF4SLocalizationEntryVar.set(filename) + + def GOFMailButtonAction(self): + filename = str(TKfld.asksaveasfilename(initialdir = '/', title = "Wybierz miejsce zapisu pliku csv dla poczty", filetypes = [('Plik CSV', '*.csv')])) + if not filename: return + if not filename.endswith('.csv'): + filename += '.csv' + self.GOFMailEntryVar.set(filename) + + def GOFOfficeButtonAction(self): + filename = str(TKfld.asksaveasfilename(initialdir = '/', title = "Wybierz miejsce zapisu pliku csv dla Office", filetypes = [('Plik CSV', '*.csv')])) + if not filename: + return + if not filename.endswith('.csv'): + filename += '.csv' + self.GOFOfficeEntryVar.set(filename) + + def generateStartButtonAction(self): + if MSG('A0003', False): + GIF1SFilename = self.GIF1SLocalizationEntryVar.get() + GIF1SFormat = self.GIF1SFormatComboboxVar.get() + GIF2SFilename = self.GIF2SLocalizationEntryVar.get() + GIF2SFormat = self.GIF2SFormatComboboxVar.get() + GIF3SFilename = self.GIF3SLocalizationEntryVar.get() + GIF3SFormat = self.GIF3SFormatComboboxVar.get() + GIF4SFilename = self.GIF4SLocalizationEntryVar.get() + GIF4SFormat = self.GIF4SFormatComboboxVar.get() + GOFMailFilename = self.GOFMailEntryVar.get() + GOFOfficeFilename = self.GOFOfficeEntryVar.get() + GIF1 = (GIF1SFilename, GIF1SFormat) + GIF2 = (GIF2SFilename, GIF2SFormat) + GIF3 = (GIF3SFilename, GIF3SFormat) + GIF4 = (GIF4SFilename, GIF4SFormat) + GOF = (GOFMailFilename, GOFOfficeFilename) + filesList = (GIF1, GIF2, GIF3, GIF4, GOF) + output = dataProcess.start(filesList) + if not output[0]: + print('x') + else: + return + + # Akcje przycisków - TAB2 + + def loadingButtonAction(self): + formatFileContent = FMT.R(self.loadingList.get()) + self.loadingList['state'] = TK.DISABLED + self.loadingButton['state'] = TK.DISABLED + self.EPOSTypeVar.set(formatFileContent['student']) + self.EPOSTypeStudentRadiobutton['state'] = TK.NORMAL + self.EPOSTypeTeacherRadiobutton['state'] = TK.NORMAL + self.EPOSPersonSeparatorEntry['state'] = TK.NORMAL + self.EPOSPersonSeparatorVar.set(formatFileContent['personSeparator']) + self.EPOSRowSeparatorEntry['state'] = TK.NORMAL + self.EPOSRowSeparatorVar.set(formatFileContent['rowSeparator']) + self.EPOSDataSeparatorText['state'] = TK.NORMAL + self.EPOSDataSeparatorText.insert(TK.END, '\n'.join(formatFileContent['dataSeparators'])) + self.EPDLLoginRowSpinbox['state'] = TK.NORMAL + self.EPDLLoginRowVar.set(formatFileContent['loginRow']) + self.EPDLLoginPosInRowSpinbox['state'] = TK.NORMAL + self.EPDLLoginPosInRowVar.set(formatFileContent['loginPositionInRow']) + self.EPDLFnameRowSpinbox['state'] = TK.NORMAL + self.EPDLFnameRowVar.set(formatFileContent['fnameRow']) + self.EPDLFnamePosInRowSpinbox['state'] = TK.NORMAL + self.EPDLFnamePosInRowVar.set(formatFileContent['fnamePositionInRow']) + self.EPDLLnameRowSpinbox['state'] = TK.NORMAL + self.EPDLLnameRowVar.set(formatFileContent['lnameRow']) + self.EPDLLnamePosInRowSpinbox['state'] = TK.NORMAL + self.EPDLLnamePosInRowVar.set(formatFileContent['lnamePositionInRow']) + self.EPDLSchoolRowSpinbox['state'] = TK.NORMAL + self.EPDLSchoolRowVar.set(formatFileContent['schoolRow']) + self.EPDLSchoolPosInRowSpinbox['state'] = TK.NORMAL + self.EPDLSchoolPosInRowVar.set(formatFileContent['schoolPositionInRow']) + self.EPDLClassRowSpinbox['state'] = TK.NORMAL + self.EPDLClassRowVar.set(formatFileContent['classRow']) + self.EPDLClassPosInRowSpinbox['state'] = TK.NORMAL + self.EPDLClassPosInRowVar.set(formatFileContent['classPositionInRow']) + self.editingPresetSaveButton['state'] = TK.NORMAL + self.editingPresetCancelButton['state'] = TK.NORMAL + + def editingPresetClear(self): formatFileContent = { "student" : True, "personSeparator" : '', @@ -1432,179 +1975,81 @@ def window(): "classRow" : 0, "classPositionInRow" : 0, } - loadingList['state'] = TK.NORMAL - loadingButton['state'] = TK.NORMAL - EPOSTypeVar.set(formatFileContent['student']) - EPOSTypeStudentRadiobutton['state'] = TK.DISABLED - EPOSTypeTeacherRadiobutton['state'] = TK.DISABLED - EPOSPersonSeparatorEntry['state'] = TK.DISABLED - EPOSPersonSeparatorVar.set(formatFileContent['personSeparator']) - EPOSRowSeparatorEntry['state'] = TK.DISABLED - EPOSRowSeparatorVar.set(formatFileContent['rowSeparator']) - EPOSDataSeparatorText.delete('1.0', TK.END) - EPOSDataSeparatorText['state'] = TK.DISABLED - EPDataLocalizationLoginRowSpinbox['state'] = TK.DISABLED - EPDataLocalizationLoginRowVar.set(formatFileContent['loginRow']) - EPDataLocalizationLoginPosInRowSpinbox['state'] = TK.DISABLED - EPDataLocalizationLoginPosInRowVar.set(formatFileContent['loginPositionInRow']) - EPDataLocalizationFnameRowSpinbox['state'] = TK.DISABLED - EPDataLocalizationFnameRowVar.set(formatFileContent['fnameRow']) - EPDataLocalizationFnamePosInRowSpinbox['state'] = TK.DISABLED - EPDataLocalizationFnamePosInRowVar.set(formatFileContent['fnamePositionInRow']) - EPDataLocalizationLnameRowSpinbox['state'] = TK.DISABLED - EPDataLocalizationLnameRowVar.set(formatFileContent['lnameRow']) - EPDataLocalizationLnamePosInRowSpinbox['state'] = TK.DISABLED - EPDataLocalizationLnamePosInRowVar.set(formatFileContent['lnamePositionInRow']) - EPDataLocalizationSchoolRowSpinbox['state'] = TK.DISABLED - EPDataLocalizationSchoolRowVar.set(formatFileContent['schoolRow']) - EPDataLocalizationSchoolPosInRowSpinbox['state'] = TK.DISABLED - EPDataLocalizationSchoolPosInRowVar.set(formatFileContent['schoolPositionInRow']) - EPDataLocalizationClassRowSpinbox['state'] = TK.DISABLED - EPDataLocalizationClassRowVar.set(formatFileContent['classRow']) - EPDataLocalizationClassPosInRowSpinbox['state'] = TK.DISABLED - EPDataLocalizationClassPosInRowVar.set(formatFileContent['classPositionInRow']) - editingPresetSaveButton['state'] = TK.DISABLED - editingPresetCancelButton['state'] = TK.DISABLED - loadingList['values'] = tuple(FMT.getList()) + self.loadingList['state'] = TK.NORMAL + self.loadingButton['state'] = TK.NORMAL + self.EPOSTypeVar.set(formatFileContent['student']) + self.EPOSTypeStudentRadiobutton['state'] = TK.DISABLED + self.EPOSTypeTeacherRadiobutton['state'] = TK.DISABLED + self.EPOSPersonSeparatorEntry['state'] = TK.DISABLED + self.EPOSPersonSeparatorVar.set(formatFileContent['personSeparator']) + self.EPOSRowSeparatorEntry['state'] = TK.DISABLED + self.EPOSRowSeparatorVar.set(formatFileContent['rowSeparator']) + self.EPOSDataSeparatorText.delete('1.0', TK.END) + self.EPOSDataSeparatorText['state'] = TK.DISABLED + self.EPDLLoginRowSpinbox['state'] = TK.DISABLED + self.EPDLLoginRowVar.set(formatFileContent['loginRow']) + self.EPDLLoginPosInRowSpinbox['state'] = TK.DISABLED + self.EPDLLoginPosInRowVar.set(formatFileContent['loginPositionInRow']) + self.EPDLFnameRowSpinbox['state'] = TK.DISABLED + self.EPDLFnameRowVar.set(formatFileContent['fnameRow']) + self.EPDLFnamePosInRowSpinbox['state'] = TK.DISABLED + self.EPDLFnamePosInRowVar.set(formatFileContent['fnamePositionInRow']) + self.EPDLLnameRowSpinbox['state'] = TK.DISABLED + self.EPDLLnameRowVar.set(formatFileContent['lnameRow']) + self.EPDLLnamePosInRowSpinbox['state'] = TK.DISABLED + self.EPDLLnamePosInRowVar.set(formatFileContent['lnamePositionInRow']) + self.EPDLSchoolRowSpinbox['state'] = TK.DISABLED + self.EPDLSchoolRowVar.set(formatFileContent['schoolRow']) + self.EPDLSchoolPosInRowSpinbox['state'] = TK.DISABLED + self.EPDLSchoolPosInRowVar.set(formatFileContent['schoolPositionInRow']) + self.EPDLClassRowSpinbox['state'] = TK.DISABLED + self.EPDLClassRowVar.set(formatFileContent['classRow']) + self.EPDLClassPosInRowSpinbox['state'] = TK.DISABLED + self.EPDLClassPosInRowVar.set(formatFileContent['classPositionInRow']) + self.editingPresetSaveButton['state'] = TK.DISABLED + self.editingPresetCancelButton['state'] = TK.DISABLED + self.loadingList['values'] = tuple(FMT.getList()) - def editingPresetSaveButtonAction(): - if loadingList.get() not in FMT.getList(): + def editingPresetSave(self): + formatFileContentToSave = { + "student" : self.EPOSTypeVar.get(), + "personSeparator" : self.EPOSPersonSeparatorEntry.get(), + "rowSeparator" : self.EPOSRowSeparatorEntry.get(), + "dataSeparators" : (self.EPOSDataSeparatorText.get("1.0", TK.END)).split('\n')[:-1], + "loginRow" : int(self.EPDLLoginRowSpinbox.get()), + "loginPositionInRow" : int(self.EPDLLoginPosInRowSpinbox.get()), + "fnameRow" : int(self.EPDLFnameRowSpinbox.get()), + "fnamePositionInRow" : int(self.EPDLFnamePosInRowSpinbox.get()), + "lnameRow" : int(self.EPDLLnameRowSpinbox.get()), + "lnamePositionInRow" : int(self.EPDLLnamePosInRowSpinbox.get()), + "schoolRow" : int(self.EPDLSchoolRowSpinbox.get()), + "schoolPositionInRow" : int(self.EPDLSchoolPosInRowSpinbox.get()), + "classRow" : int(self.EPDLClassRowSpinbox.get()), + "classPositionInRow" : int(self.EPDLClassPosInRowSpinbox.get()), + } + if not FMT.W(self.loadingList.get(), formatFileContentToSave): + return + self.editingPresetClear() + + def editingPresetSaveButtonAction(self): + if self.loadingList.get() not in FMT.getList(): if MSG('A0001', False): - editingPresetSave() + self.editingPresetSave() else: return else: if MSG('A0002', False): - editingPresetSave() + self.editingPresetSave() else: return - editingPresetSaveButton = TKttk.Button(editingPresetButtonsFrame) - editingPresetSaveButton.config(command = editingPresetSaveButtonAction) - editingPresetSaveButton.config(state = TK.DISABLED) - editingPresetSaveButton.config(style = 'button1.TButton') - editingPresetSaveButton.config(width = GUI.R()['editingPresetSaveButtonWidth']) - editingPresetSaveButton.config(text = 'ZAPISZ') - editingPresetSaveButton.pack(side = TK.LEFT, expand = 1) - def editingPresetCancelAction(): - formatFileContent = { - "student" : True, - "personSeparator" : '', - "rowSeparator" : '', - "dataSeparators" : [], - "loginRow" : 0, - "loginPositionInRow" : 0, - "fnameRow" : 0, - "fnamePositionInRow" : 0, - "lnameRow" : 0, - "lnamePositionInRow" : 0, - "schoolRow" : 0, - "schoolPositionInRow" : 0, - "classRow" : 0, - "classPositionInRow" : 0, - } - loadingList['state'] = TK.NORMAL - loadingButton['state'] = TK.NORMAL - EPOSTypeStudentRadiobutton['state'] = TK.DISABLED - EPOSTypeTeacherRadiobutton['state'] = TK.DISABLED - EPOSTypeVar.set(formatFileContent['student']) - EPOSPersonSeparatorEntry['state'] = TK.DISABLED - EPOSPersonSeparatorVar.set(formatFileContent['personSeparator']) - EPOSRowSeparatorEntry['state'] = TK.DISABLED - EPOSRowSeparatorVar.set(formatFileContent['rowSeparator']) - EPOSDataSeparatorText.delete('1.0', TK.END) - EPOSDataSeparatorText['state'] = TK.DISABLED - EPDataLocalizationLoginRowSpinbox['state'] = TK.DISABLED - EPDataLocalizationLoginRowVar.set(formatFileContent['loginRow']) - EPDataLocalizationLoginPosInRowSpinbox['state'] = TK.DISABLED - EPDataLocalizationLoginPosInRowVar.set(formatFileContent['loginPositionInRow']) - EPDataLocalizationFnameRowSpinbox['state'] = TK.DISABLED - EPDataLocalizationFnameRowVar.set(formatFileContent['fnameRow']) - EPDataLocalizationFnamePosInRowSpinbox['state'] = TK.DISABLED - EPDataLocalizationFnamePosInRowVar.set(formatFileContent['fnamePositionInRow']) - EPDataLocalizationLnameRowSpinbox['state'] = TK.DISABLED - EPDataLocalizationLnameRowVar.set(formatFileContent['lnameRow']) - EPDataLocalizationLnamePosInRowSpinbox['state'] = TK.DISABLED - EPDataLocalizationLnamePosInRowVar.set(formatFileContent['lnamePositionInRow']) - EPDataLocalizationSchoolRowSpinbox['state'] = TK.DISABLED - EPDataLocalizationSchoolRowVar.set(formatFileContent['schoolRow']) - EPDataLocalizationSchoolPosInRowSpinbox['state'] = TK.DISABLED - EPDataLocalizationSchoolPosInRowVar.set(formatFileContent['schoolPositionInRow']) - EPDataLocalizationClassRowSpinbox['state'] = TK.DISABLED - EPDataLocalizationClassRowVar.set(formatFileContent['classRow']) - EPDataLocalizationClassPosInRowSpinbox['state'] = TK.DISABLED - EPDataLocalizationClassPosInRowVar.set(formatFileContent['classPositionInRow']) - editingPresetSaveButton['state'] = TK.DISABLED - editingPresetCancelButton['state'] = TK.DISABLED - loadingList['values'] = tuple(FMT.getList()) - editingPresetCancelButton = TKttk.Button(editingPresetButtonsFrame) - editingPresetCancelButton.config(command = editingPresetCancelAction) - editingPresetCancelButton.config(state = TK.DISABLED) - editingPresetCancelButton.config(style = 'button1.TButton') - editingPresetCancelButton.config(width = GUI.R()['editingPresetCancelButtonWidth']) - editingPresetCancelButton.config(text = 'Anuluj') - editingPresetCancelButton.pack(side = TK.RIGHT, expand = 1) - - ################################### - - ############################################# - - ###################################################################### + def editingPresetCancelButtonAction(self): + self.editingPresetClear() - # TAB4 - Ustawienia ################################################## - settingsTab = TKttk.Frame(mainMenu) - settingsTab.config(style = "mainMenuTabFrame.TFrame") - settingsTabImg = PLimg.open(GUI.R()['settingsTabIcon']) - settingsTabImg = settingsTabImg.resize((GUI.R()['tabIconsSize'], GUI.R()['tabIconsSize']), PLimg.ANTIALIAS) - settingsTabImg = PLitk.PhotoImage(settingsTabImg) - mainMenu.add(settingsTab, image = settingsTabImg, state = TK.NORMAL) - - - # Nagłówek - settingsHeader = TKttk.Label(settingsTab) - settingsHeader.config(style = 'tabHeader.TLabel') - settingsHeader.config(text = 'USTAWIENIA') - settingsHeader.pack(fill = TK.X) - - - # Zawartość - settingsFrame = TKttk.Frame(settingsTab) - settingsFrame.config(style = 'tabFrame.TFrame') - settingsFrame.pack(fill = TK.BOTH, expand = 1, padx = GUI.R()['tabFramePadding'], pady = GUI.R()['tabFramePadding']) - - ###################################################################### - - - - # TAB5 - O programie ################################################# - - aboutTab = TKttk.Frame(mainMenu) - aboutTab.config(style = "mainMenuTabFrame.TFrame") - aboutTabImg = PLimg.open(GUI.R()['aboutTabIcon']) - aboutTabImg = aboutTabImg.resize((GUI.R()['tabIconsSize'], GUI.R()['tabIconsSize']), PLimg.ANTIALIAS) - aboutTabImg = PLitk.PhotoImage(aboutTabImg) - mainMenu.add(aboutTab, image = aboutTabImg, state = TK.NORMAL) - - - # Nagłówek - aboutHeader = TKttk.Label(aboutTab) - aboutHeader.config(style = 'tabHeader.TLabel') - aboutHeader.config(text = 'O PROGRAMIE') - aboutHeader.pack(fill = TK.X) - - - # Zawartość - aboutFrame = TKttk.Frame(aboutTab) - aboutFrame.config(style = 'tabFrame.TFrame') - aboutFrame.pack(fill = TK.BOTH, expand = 1, padx = GUI.R()['tabFramePadding'], pady = GUI.R()['tabFramePadding']) - - ###################################################################### - - - - # Mainloop - window.mainloop() -window() \ No newline at end of file +# Inicjacja okna +root = TK.Tk() +windowInit = mainWindow(root) +root.mainloop() \ No newline at end of file From 2dc050849424242111a7fffa5a35f94d548136d6 Mon Sep 17 00:00:00 2001 From: Mateusz Skoczek Date: Fri, 7 Aug 2020 22:16:01 +0200 Subject: [PATCH 16/29] 4.0 Alpha (Build 20220) --- .vscode/launch.json | 15 +++ changelog-UC.txt | 6 +- default-configs/config.cfg | 20 +-- generator.pyw | 242 +++++++++++++++++++++++++++++-------- 4 files changed, 219 insertions(+), 64 deletions(-) create mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..40b827b --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Python: Current File", + "type": "python", + "request": "launch", + "program": "${file}", + "console": "integratedTerminal" + }, + ] +} \ No newline at end of file diff --git a/changelog-UC.txt b/changelog-UC.txt index c25a095..f98cec9 100644 --- a/changelog-UC.txt +++ b/changelog-UC.txt @@ -84,4 +84,8 @@ 4.0 Alpha (Build 20219) - Ukończenie karty "GENERATOR CSV" (poza przyciskiem "START") - Usunięcie instrukcji w karcie "FORMAT DANYCH" w celu późniejszego umieszczenia ich w pliku readme -- Przekształcenie interfejsu na podstawie funkcji w interfejs na podstawie klasy \ No newline at end of file +- Przekształcenie interfejsu na podstawie funkcji w interfejs na podstawie klasy + +4.0 Alpha (Build 20220) +- Zastosowanie nowego systemu przetwarzania plików konfiguracyjnych dla pliku 'config.cfg' (tylko odczyt) +- Rozpoczęcie prac nad skryptem przetwarzającym dane \ No newline at end of file diff --git a/default-configs/config.cfg b/default-configs/config.cfg index b387eb7..198f4a3 100644 --- a/default-configs/config.cfg +++ b/default-configs/config.cfg @@ -1,10 +1,10 @@ -secret = entersecretstringhere -allowedCharactersInSeparator = ['`', '~', '!', '@', '#', '$', '%', '^', '&', '(', ')', '-', '_', '=', '+', '[', ']', '\', '|', ';', ':', ''', '"', ',', '<', '.', '>', '/', '?', ' '] -inputCoding = utf-8 -outputCoding = utf-8 -domain = losobolew.pl -quota = 500 -country = Rzeczypospolita Polska -yearsOfLO = 4 -yearsOfBS = 3 -schoolyearStart = \ No newline at end of file +secret(S) = entersecretstringhere +allowedCharactersInSeparator(SCA) = ['`', '~', '!', '@', '#', '$', '%', '^', '&', '(', ')', '-', '_', '=', '+', '[', ']', '\', '|', ';', ':', ''', '"', ',', '<', '.', '>', '/', '?', ' '] +inputCoding(S) = utf-8 +outputCoding(S) = utf-8 +domain(S) = losobolew.pl +quota(I) = 500 +country(S) = Rzeczypospolita Polska +yearsOfLO(I) = 4 +yearsOfBS(I) = 3 +schoolyearStart(D) = 01.09.* *:*:* \ No newline at end of file diff --git a/generator.pyw b/generator.pyw index d98d6b1..ee7f221 100644 --- a/generator.pyw +++ b/generator.pyw @@ -61,7 +61,10 @@ MSGlist = { 'E0006' : 'Niepoprawne dane w pliku formatu', 'A0001' : 'Czy chcesz zapisać? Zostanie utworzony nowy plik', 'A0002' : 'Czy chcesz zapisać? Plik zostanie nadpisany', - 'A0003' : 'Czy chcesz rozpocząć przetwarzanie plików?' + 'A0003' : 'Czy chcesz rozpocząć przetwarzanie plików?', + 'E0007' : 'Wymagany przynajmniej jeden plik wejściowy', + 'E0008' : 'Nie można odnaleźć jednego z powyższych plików', + 'E0009' : 'Nie można odnaleźć jednego z powyższych format presetów', } def MSG(code, terminate, *optionalInfo): @@ -104,7 +107,7 @@ def MSG(code, terminate, *optionalInfo): appdata = PT.Path.home() / 'Appdata/Roaming' #TODO -#SU.rmtree(str(appdata) + '/Generator CSV') +SU.rmtree(str(appdata) + '/Generator CSV') #TODO if 'Generator CSV' not in [x for x in OS.listdir(appdata)]: try: @@ -122,7 +125,8 @@ if 'Generator CSV' not in [x for x in OS.listdir(appdata)]: # ----------------------------- # Ładowanie pliku konfiguracyjnego # ---------------------------- # class CFG: - def __checkInstance(self, write): + # Funkcje sprawdzające istnienie + def __checkIfConfigFileExist(self, write): if write: try: file = open((str(appdata) + '\Generator CSV\config.cfg'), 'a') @@ -141,65 +145,117 @@ class CFG: except Exception as exceptInfo: MSG('E0002', True, exceptInfo) - def __checkContent(self, write, content): - if write: - return [True, content] + def __checkIfRecordExist(self, content, record): + if record in list(content.keys()): + return [True] else: - class functions: - def string(self, var): - if var in list(content.keys()): - return [True] - else: - return [False, 'Brak danych - klucz: %s' % var] - def array(self, var): - if var in list(content.keys()): - new_contentVar = (content[var])[1:-1].split(', ') - xnew_contentVar = [] - for x in new_contentVar: - xnew_contentVar.append(x[1:-1]) - content[var] = xnew_contentVar - else: - return [False, 'Brak danych - klucz: %s' % var] - functions = functions() - functions.string('secret') - functions.array('allowedCharactersInSeparator') - return [True, content] + return [False, 'Brak danych - klucz: %s' % record] + - def R(self): - self.__checkInstance(False) + # Funkcje sprawdzające poprawność recordu + def __checkSCA(self, write, record, var): + if write: + pass + else: + try: + var = var[1:-2].split(', ') + var = [x[1:-1] for x in var] + except: + return (False, 'Niepoprawne dane - klucz: %s' % record) + for x in var: + if len(x) != 1: + return (False, 'Niepoprawne dane - klucz: %s' % record) + return [True, var] + + def __checkI(self, write, record, var): + if write: + pass + else: + try: + var = int(var) + except: + return (False, 'Niepoprawne dane - klucz: %s' % record) + return [True, var] + + def __checkD(self, write, record, var): + if write: + pass + else: + varToReturn = {} + var = var.split(' ') + try: + var[0] = var[0].split('.') + var[1] = var[1].split(':') + var = var[0] + var[1] + dateLabels = ['D', 'M', 'Y', 'h', 'm', 's'] + if len(var) != len(dateLabels): + return (False, 'Niepoprawne dane - klucz: %s' % record) + index = 0 + for x in var: + if x != '*': + varToReturn[dateLabels[index]] = int(x) + else: + varToReturn[dateLabels[index]] = None + index += 1 + except: + return (False, 'Niepoprawne dane - klucz: %s' % record) + var = varToReturn + return [True, var] + + + + def R(self, record): + self.__checkIfConfigFileExist(False) content = {} for x in CD.open((str(appdata) + '\Generator CSV\config.cfg'), 'r', 'utf-8').read().split('\n'): x = x.split(' = ') try: - content[x[0]] = (x[1]).strip('\r') + name = x[0].split('(')[0] + var = x[1] + type = x[0].split('(')[1].strip(')') + content[name] = [var, type] except: continue - contentCheckingOutput = self.__checkContent(False, content) - if contentCheckingOutput[0]: - return contentCheckingOutput[1] - else: - MSG('E0003', True, contentCheckingOutput[1]) - - def W(self, changes): - content = self.R() - for x in changes: - content[x] = changes[x] - contentCheckingOutput = self.__checkContent(True, content) - if contentCheckingOutput[0]: - if self.__checkInstance(True): - with CD.open((str(appdata) + '\Generator CSV\config.cfg'), 'w', 'utf-8') as file: - contentToSave = contentCheckingOutput[1] - for x in contentToSave: - file.write('%s = %s\n' % (x, str(contentToSave[x]))) + checkingOutput = self.__checkIfRecordExist(content, record) + if not checkingOutput[0]: + MSG('E0003', True, checkingOutput[1]) + var = content[record] + if var[1] == 'S': + # String + var = var[0] + return var + elif var[1] == 'SCA': + # Single char array + checkingOutput = self.__checkSCA(False, record, var[0]) + if checkingOutput[0]: + return checkingOutput[1] else: - return False + MSG('E0003', True, checkingOutput[1]) + elif var[1] == 'I': + # Integer + checkingOutput = self.__checkI(False, record, var[0]) + if checkingOutput[0]: + return checkingOutput[1] + else: + MSG('E0003', True, checkingOutput[1]) + elif var[1] == 'D': + # Date (DD.MM.RRRR HH:MM:SS) + checkingOutput = self.__checkD(False, record, var[0]) + if checkingOutput[0]: + return checkingOutput[1] + else: + MSG('E0003', True, checkingOutput[1]) else: - MSG('E0004', False, contentCheckingOutput[1]) + MSG('E0003', True, 'Nie można rozpoznać typu klucza %s' % record) + + def W(self): + pass + + CFG = CFG() -checkInstance = CFG.R() - +print(CFG.R('allowedCharactersInSeparator')) # ---------------------------------- # Ładowanie pliku stylu # ---------------------------------- # @@ -691,7 +747,7 @@ class FMT: return [False, 'Brak danych - klucz: %s' % var] def separator_string(self, var): if var in list(content.keys()): - allowedCharactersInSeparator = CFG.R()['allowedCharactersInSeparator'] + allowedCharactersInSeparator = CFG.R('allowedCharactersInSeparator') check = content[var] check = check.strip('') for x in check: @@ -702,7 +758,7 @@ class FMT: return [False, 'Brak danych - klucz: %s' % var] def separator_array(self, var): if var in list(content.keys()): - allowedCharactersInSeparator = CFG.R()['allowedCharactersInSeparator'] + allowedCharactersInSeparator = CFG.R('allowedCharactersInSeparator') new_contentVar = (content[var])[2:-2].split("', '") check = new_contentVar for x in check: @@ -836,8 +892,80 @@ FMT = FMT() # ---------------------------------- # Przetwarzanie plików # ----------------------------------- # class dataProcess: + # Funkcje sprawdzające + def __checkIfAtLeastOneInputFileIsFilled(files): + filledFiles = [] + index = 0 + for x in files: + if not (x[0] == '' or x[1] == ''): + filledFiles.append(index) + index += 1 + if len(filledFiles) != 0: + return (True, filledFiles) + else: + return (False) + + def __checkIfInputFilesIsReadable(self, files, filledFiles): + for x in filledFiles: + try: + check = CD.open((files[x])[0], 'r', CFG.R('inputCoding')) + except: + return False + return True + + def __checkIfInputFilesFormatPresetsExist(self, files, filledFiles): + for x in filledFiles: + if (files[x])[1] not in FMT.getList(): + return False + return True + + def __checkIfCreateOutputFilesIsPossible(self, files): + for x in files: + try: + check = CD.open(x, 'w', CFG.R('outputCoding')) + except: + return False + return true + + # + + + def start(self, files): - pass + checkingOutput = [] + + testOutput = self.__checkIfAtLeastOneInputFileIsFilled(files[:-1]) + checkingOutput.append(testOutput[0]) + if not testOutput[0]: + return checkingOutput + filledFiles = testOutput[1] + + testOutput = self.__checkIfInputFilesIsReadable(files[:-1], filledFiles) + checkingOutput.append(testOutput) + if not testOutput: + return checkingOutput + + testOutput = self.__checkIfInputFilesFormatPresetsExist(files[:-1], filledFiles) + checkingOutput.append(testOutput) + if not testOutput: + return checkingOutput + + testOutput = self.__checkIfInputFilesFormatPresetsExist(files[-1]) + checkingOutput.append(testOutput) + if not testOutput: + return checkingOutput + + input = [] + for x in filledFiles: + input.append(files[x]) + output = files[-1] + + testOutput = self.__checkIfCreateOutputFilesIsPossible(files[-1]) + checkingOutput.append(testOutput) + if not testOutput: + return checkingOutput + + dataProcess = dataProcess() @@ -1916,7 +2044,15 @@ class mainWindow: filesList = (GIF1, GIF2, GIF3, GIF4, GOF) output = dataProcess.start(filesList) if not output[0]: - print('x') + MSG('E0007', False) + else: + if not output[1]: + MSG('E0008', False) + else: + if not output[2]: + MSG('E0009', False) + else: + pass else: return From 01f2103f2a088b97b4c83f80bd61bbdb1affb3e9 Mon Sep 17 00:00:00 2001 From: Mateusz Skoczek Date: Sat, 8 Aug 2020 01:53:49 +0200 Subject: [PATCH 17/29] 4.0 Alpha (Build 20221) --- assets/icon.png | Bin 0 -> 44017 bytes assets/tab-icons/icon.png | Bin 0 -> 44017 bytes changelog-UC.txt | 6 +- default-configs/style.cfg | 217 ++++---- generator.pyw | 954 ++++++++++++++---------------------- assets/icon.ico => icon.ico | Bin 6 files changed, 486 insertions(+), 691 deletions(-) create mode 100644 assets/icon.png create mode 100644 assets/tab-icons/icon.png rename assets/icon.ico => icon.ico (100%) diff --git a/assets/icon.png b/assets/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..e0314abee281359a16f991ae1a1c9cceceda9a91 GIT binary patch literal 44017 zcmV*(KsLXLP)004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00006 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%0AJXF1`vsEtxd99m+EY*(&>9dam-Kh` zeD0xtKWGKVM&rr0u?0oX8X5pmP|$!P5EK|TD0F)0SQX-xxKg{LyA%Ri~`T6-cYTmqX&Fs1^ zlu}r>Y#E+-{PA!d?Yb^V#v^dJW5$dbxb@asv2fu+M59r-t{Zv^09YRD5X$ZT8b}!`OEm|M?VU~ zFkssexUM_G#xa_6AKngvuz1U|%q2^feE3JV{jd`7?I_dgCcj*nSMXBAQIcy7K@}9q zW5#Jcly^>wi&E5w5=aYFu~Sb+Bz4j4?!`(a@L}2)-i?L9Pd545?J=jKzx=pA7)j z#6p!_;MPv8{%RpN*AUNxA___gpEA$jq{m5|xoUgUtT%nHH+$gHwX3VQwzZzhqY*+) z6OI;02?vJ3kgupXamJkAomEsh9321efB$=4UvKZ3l+eT9P;-!xo%t6;1Og-s`XPiM zo`}P;{N2w;=Xf(7Z?8=jHBp}G}3Su=wlZ;XANEisah7m`jQJi_^8Myi8n=y6jRG(>A%FqZHXhBDU{R6;=h)7B)y1KgR z7cE-!?qL7>CpL&Nk-Yn2nr&xNfhqr+3n-*aN{5~Fm$wb{U4S>?<$F^jbM5e;*)rVs zDUVP<0YobhXfUl&C}}Ff`CT(-&I~OwfBMs(sy1!hbfLjH=w5<3a#6rJ>>IVn~ z;i@?yki%O=wwhm5RD>IEyb%{&bP){02=fL4JILW~a|&F(LWt1VnTW@WJ=eLSv89Os zz!YN1s-lwBC3&&eh>&2KfNBb&fwWX;8yvufrl$O@Z=eyqp#W&y(l})(l|Gr87V)G2 zHw<`6f=PukW;;jMHT-c_aZ$MVTE1lI30kRR-LwNn*&ZcwPW{heUqLB_loF<8!4m=n z1qJxbXFrSUuD>3!SS-{Aj+6|-AmAzRbTH1)(z5OS>({TZ3XU<-+$@q zmDH8u^PcC0Ni5mOs&(KqrylB7&f)L@0hr+j0ok?<${1|h!g=SPhp*o7HI$W)fhK@d z5*j0os6vRt$~&?v7Xl#!lrT6+H+tW1|NNQ8r7H*k6i16ym63mUB~J9{bo+PlSa*rc{*4hML-^ znNXlw1KQ6K5YSKrw5?;usRM~&Yj4-xs{oKv(&rYv^6_3LotI_=(x4DTgEIz}_E0xw zZ0kvL7Hq0z{&-)uY*~GKd;976vDo3=(m5Pf$PNUWI&~_(``z#2-S2vLm^)zGHWG=% zVJ`r*Qczl9Af1e8%eX9iUx}Hwy2Oe$Tbx0A9y}?bZH|Wqe+jaLU05)`P zFW%I)eIfEAL@?qP=%fUO1|puqth)NYFOwQ@Lok-bh1>`es) z1^C?OK8H{J&;Nu=(13CJa0X=zgfXO*Li_f%_dfQoe@BCTMftIKVJ!b%=1Ik%p{a(T z8sH3Z<<|5ihmYIqb@#So$*PsJdgH@0Tt*;CraTuRAg(}ZB)_eyaLm7_5i=a^`F7;f z$wY!{m0R8Cusec)onUQF;HSO5T~JCx`ZUftY}-ajNeQmH>MDHU3tvE9US1d?9T?Yw zxPimTTM)Q8&8UsO-rk}mix*!W>?e|BJNKW8+Vs~}n_7?SAmrR9$)#|aC4y!h;M#qF(a$8gGW*1a6| z2-7q}V$+ZjXg>|WU48!=8p8ANp7*>5U-`;cP+ndRAw(zuf?_m>lK=>wTSy6mbG~xL z%2S_x{BauqrZW>$t0rtN;MNW%B?vSK0X!)nERJE%eRp4Ca7^X4Y^zb?LB{ za5ccF2@c{L|ESqtV#oe<^0e9E?ElykPrR?MuP@IujUy%=JBR%X*LA@;hvPUf3jIhH0X+r~CM)o_cC( zuzzY%Mb}s(Z;7cjh)}R81E7$ELZ@~LS2bupJ0zX`RGXz%QA%P<#POU*ef{aFbo52ZEazxumgfj0+o^y z1VBSW1Ah3!A7b|G*&&C(;pQ47@X2DiCIBT!rBjoZEm=A#*v|>~s;kESK{}4&O2Nn! zB~>Oz99?BpR9zPyhG7_bNaKQmcy#ycr(ucQ9bT zDLwA4{_$GR;MCS9#x#wZT9#E_65AOk!t}p+QvDM!h?wktB1Cnlp-u`>L$~O$eLd3C zbiVC+f84m;b~%nwfR0nLK`qLy8-kCn18Y*K`LeYYk9yC5U)ea&I=?G>$*Wo<&b9QG zL%@WTSfON?w2|*Lg}g2UV<*%FoRJR|T5_azX~oa;>E2!**m|4h-N0ydxY*56k~}wj z(XeA!R9TIE;VFW6zD06z>+;%_25jLR~ zCKiIQuPB2@M+mlP!Z_+6mah$RN-7x#IEa}b%2 zj}JQ*9ra3|dAvvjV79=FFpU``CHF{2P<>2w-N9wt*CD8J^t*n?)}b381LF7D+Ud(N z_B~-3wqV0ce4h@ND7?_6Zy!}?E-RB+gVC)zfj3%n+5=%-!+gHkMbRn5^8t7NP)2tY z?U0n2i1Sp{K?S;Be!JMf;RcXU5x;(%{1wnqqZw7}+>00~+O(xf#5w*eF?qC$8u66h^b`|0qvb*RnyYoJi$La7mEeXH9Z;H=us=^Xm&S?Jqo~Zeq&Kok+Px?wc z^bhH~3f~iML?g*-KDSe@CwH<%eOS(UvvYLizi~|$*0n(%Q=Raglllc#3PnLPsjH`& zuEgU5xlR)!6N|`Uh0*2f=w<61zhW5}M^=JjIbtpWFaYjU$sV;-PEh8ee)v6xjgOr| z?1zpr{&ZvGffMKbErtd`LcZF*>OA<^$k%3xm0M9!&!4RD6Gnr$s0WE_j$@@sVxxkj z;tQvLk;H@YAQi`sgwaO6cUUBdo3KV!P=7p`SA;?7!!3=L}7u3@9%`(%{VdG zAIzd`dITb3@>6HspX6U_!#uq&yD*f<3xedks%BF-+p!#TpK6|MboehTdFpqdwubh*(*EDS?NN(}Q?>Dkb`@BH ziTSK_2vb+}_?)&Hk>mjF+ivQmsMlD@fQwUDe=?#pw#}o6Hj+=wvyRk!l#LiY$A<8% zvnyC1aV%eIZ9N;yes)|Fv1T0j=q1=gglV;i*<3uA?%-(0tk+;Bki&Pwn1?1U5%p>J z^_UJ8hewB_?tpD*@2o`0moibEou5XAKQw|eWoT&$KtliSt$D89gW3|2mq()r4{Qf&dg|{=W5`iJAVDf zkR|9wb{VHpMqrRZhp!+=qC!f#r|5ewcjGhM@A`uq3CpC)3MOMLgw= zb(*MaJ+1j;M}E=d%q3 zyyuU_#E>@|v3Z9Xn{01c>zK`v^6ANM7iZo#F9to+WN7E~IIq`jCpqdicJT8@urQ9) z8K8kJNzaIbw=hH0`idf-G;0V3YmmIvN8R#r(~yvIR8{4Xer+i6Y%`Rbea&mHXkLuN z!P(Q0aD#S+#+V?;B7z0neuKIwL#^wC+3gYSSeqLpTj%@{k&zq=qKK!bK)`8E9D&=v zwC7?X1_O}N8Sb%~4ZcuL0l*2|S-vps6-EkXx%JQjg6zG# zwl?9y=gOjp7H7Y0UJ1G4{1+SONW`xobcMA)ZVa@(Q~d^Qh+hJN1JW|WC@5xso(K{Y z>#MTY7$qN542w~N#Xf(T=IexyX@Wgkt&+GjM1>>J8+XGODaZ51Fso%<)`sQF@ChHW zzntx|Skyx0d?p(e#2RsB8YO0%J>hcnc(*|N7mZ5k4I<-pAnyLjcks(u7WZ0PiDj^w zI*d9?nJTAUMZS58mY(nhtpp}^1{q?88amMMRH+beQO>(sc1w%~RuEu>$we$>*u-w> zinGzs&#VKG#YK`j2ME>iS|ACAX@L4Gif}X~_$m|7sLxWy3)xK6MXo2QZwiXS$2D~tedLQIXwsv*m)g333TL<37Jq71 zJZq?SU?dN%M45wDF#?k?tHE3X&s;P}=xe`a`{SRO;rCCgf1SuS7)l|}DpeXRGl(2= zsHkgEem;@4pnp@I@CBpnsJV^2^Oe9cq$FNlI#c8FIwjHb;UWh5sjD=oKp96I?d%MKsmm`H65ACA?b zf)~Ga9=7N-IDK#RBdgGb48PdVkD^uA?mv(I>Pf)zrx^QV$s+Y+$A0b%C308} zC*l6&51CnZ8$VB+@9?`EJ2N=Vtv}wxR`R5>7QeUp2X=IF%EsOh01CYa{qK~n8ad*aN{Oq0_>0`>D{Ni!6=8Jj=ZFrx2kGINt<{Z zRf@>302?p@PP}q9UmVuuYCsH?T}{7!MCX4w%Hl6BMmKbN-1bt!_ffAB@ud|NlEHE9*Zjv)XmLKj9yX?pBgc(sp_6 zIH@Vypy_%-J499Cv$t?I)lorM&yEZqj3k$2+hMY&$GAm})Qf`UHTgS@pkp~OQ+cp3 z3*PDB@ki@*7ZVc`hqLZD0#$|o|1$r5lF7+Q_^T?&QFQ@Ll81^+*Ho3|{Y9;iIV({w z!`pMThpiW#UDwCUuD_J{@=8h=4A23PpAIU zj_XAgy%h(w+Vi4vb_5!`unWVTEtjtrLyE+5DF74|HRraEp~`hzM)={ZH$gojM3O1| z;uAV~z~8CO=lGQCw+0ETPG7&hql1=M>VXxa9qtM9Ab6zu_8HhO@l(DhE0L_0(OSH* zW9Ow0hspM}@YrJwxW+}{?8q(%FtV`p^2{5AZf$L`JBj>hLsGL$JNAU3>U)Y!+bmuG z%`CQ!IOwnUCZ;$m2uI}g7 zi;&k-ape{@NWj9zCbF%N=S3dfjBtSJR}cam9e}pze0HGxY>jvUzWW#XY zG2zC_1bEqN@0epdV=p4vf90*m`v^cUtWK~{&Tg1^!qj&pZvwdfS9kueRY(g8EO7Yx zeA*qm#GrS!Z6qHVDJe60jA%hO`?Yq$YY_2vnxKc>0wVHID3W$eU^4@+|$bj#QINuRKRheK1;iW&3tS&sMaEV$W>@h5%U`EhSh5YwiR zj}Z|BcyWitv~$Wx33Buo^>g&>g5*wv*_)wXrasGAgkdn3ppX*-v?6gtuqHFm>itQ@ zK7xvqi&Z|}V71O^sw#X1c6#jsYAi7?9v*TCB(OWFn5u2OD*eZAt z?QJ9}NZ2k^nLeXZ*Z*!~Pj^-=`YO*4#X`pl@t?<`rN&K^?{G`VDBee>0pLY!+1J#6 zEP~ktOEEEy6z^A!Z%C#V^p2X5YGxZuL^9j6F~*l?=W6UTP-i{8+EuRC9e%GF;!5pW zrk;+E%K{N+9M=u89g9B5!W!ApaIg~6u$~+T5oLT9&6w1^@NM(Y#gjqo9BqYecFg>% z@h<;gJ3S_vVt%Tm3DeGQ>o7Tik}w-^J}%S^GADsmL8zrbA=j`^hb>OZGhsHOr4Y*~ zo9yT^`uh$8&#GofETo$ZR%wWA1r~2OywZ*MdJ_wE@>G<)Zm+T4?p2!HtHM`c%CCE6 zLXgzM01?-f)f*T6mg{ z7#5s`*jwN{ffl>@{WL0&zx%m7B8G+JtEkQvGk&L>P&5tJm^ zNkq-*cTY%++`2qUegK zTwL5F4i${5m;J+#YNS9|k|L9IXqn+*mU*T+vjDOhfvS`}93{vxmxq9xo?wK;YK=5R z?-x6!!*T=G#M^sT%Ag0=a^udoGvE-CRCtIkE72Fr7!s~m27>M$9-AHaC%u3Y0q=`F z7?z3#@Up;qVXkbuP+&D^f&(a1(^(HjUQ)Q>B#Zs_G9d3J)jdKrOMZi0TOJ(j{l8x{7KtM)b5gV{d^2NFg`Yf*ccC+z?Z_V?;4{5s^}!Jiw@t}pd&l3v{@i+P$E zMTqfIM*sZM9ViVVgtvU@%G|pQRF}};Hd_XhX3qf@pA~0jY7~@<#C`YM-FFHKP=P(c zZKpwxPwmwBiLtGx9c0EpF`HUiLZ9~lHnY#lC8o=~z?6e@l&SY?pIce@TnZ3hAc62c zLOLFgoy`H0=q4xiSsrrWSqvPUq^_x{X>zeMWct(tbQF|d9N7COr>8?wQc~D!IhNm< z5<|27FNP+qHhd_HRdV8(G>Uh6KHwljP7cdgVl1Skr3DvGy(#BGvZ`GS_=ut+@Jsri zbe?@Lsk2k?Ojzr{%oWr{qwfkOOu-{HnH$BeE#)7GKN7NNZ^Q|mAZ!7o2f+~`MH`HU z|7rsOPbD24U$c$?32$8h*!AB?G&4IJu6-*VO=P;=AG>)K2IoJn`{nX)zr=hrjRW|h zGWeYPfvysWR#OEy=5a`^)fjNmDYwqO`8QJE7=GH07Y%G&IN#hfby%undWg&3Gab(q zi27+pXO%)O!%%}YECJI^DXP(HOWaRFk?5H;Q3!yV=D%dzxCjXe;ofE~3eUMZ)cIG8 z&`ikI;pgTiSc~D9vtDk%#(Xbu-<3VJvJyk}d^?c$O_?d&arqXwk7A?QI*0+1qg8K} zt$F8*s;&(pJQG5F!H*}QRpvbruWvzJd;eq&XSBNdwzf8c zBJLRwGy+POZbKGk5f$iNW03kH}oqqQ@CkHSdjsqcL5E^=ebq$40er0|}5_2Fg zk&%&+IPV&j$d?9!j*i|XfP)n}WkUey?J!b75vIA{zheL|hQSkq$Z-bHRJyR?Bqogy z5Q<1#@R*VU+PsG;aYQx;3oATKli5Sgbs{>D0 zzUOsSzk-DX+i;W0YM<~`0TCow{Gt#nQtlJr_ISEH>z!1fVIvJ!M^8LFINM~Y6caB8 zW!{b163e9BQ`VVpJo8~9TUuJeCAwV6fo;Tm|I5oJK=3lPKEFqX6^^h>uVQ7fnB&3h zy*bAV0h(qCpeJ&Yqk#Z-UievaIQ&03&%x|>q@x%k;nTKD5H*2;z?ZnGSi^iN55NoF zWMy1L(WL(qWOjEV0xse@e@lu;aMj1`9nu(etq;j#tfQCHKu7;nc=nBjnMLL{?gGPX z*+Uh}(^{-sNFQdgECnn|&$?64OM=Sn5RHgC*$G)!HP?l=+=(8x7GKOfUoK4Qn*%(D z>5;!l+x??+P$XO6X7uf$@rk&Ob*gg}AxV5Fbfs=P$UuVKbxTSyottTfm$3*a%E;6E z?%oq~bK34+UXf~1;o;$FLS9aP{*A;L51P8u!a?FxtUxFW&W$*}<%Jzs8#0#56H1Gj z4p+rWsFPDi&qu#Bm()a_MDK2#gQ$X<#LXA^>R7+qt8g5gN;Cj|Hi_As(NOu>6OQ54Kh_Jl*;kryMIZN7T+e^L*e=d+>Z!3D@%PGxvn%3r zMo5S9#U|~uI<49AJv}SeoPq!TJ7e_y8sK4Ff1rv1`qKYe@!JEP z&QFn4zJtcg*0eC>(QFYRZZW$G4q)d7)c%qCP47}zgH4Iy64amNHp1|g#kfgDW|Y!< z-%|TARVGbu55zlTUt_&Yl)_i1ujU%fu{waMD+uL(w!x^NE2h*iSi&x<$CeXM0$UQ; zPDtw*t9V>viA=mFv!bRrXcX-m#Gc2}wCrVE3)iR_b_*&Rwzx;i{lgmbQ=bWx^b@_+VdUZt zlnmAa(O||A5G(c%aN9E+wXd$OzD}`3`9I1fSAAHn3=?;Cb@ePF0-6dS#W0}7iJBE` z`e9DbdQ)iq4*S32=?{>0GW2+8^Zp;D!0HMQcN0O_;mUKqa3I+Uiw;gP(EUy8dv1My zI*HoNrq)4=m)EUi;>C*>r736*fJ8ZQ1saD-X4R}ufHE4*g~jwyh2Vul>yXw zlD!#oq|rpLC4z#UqZHp_#lTRo4SW~M_3wXs3HP2FUU4sAD() z*35MFo7U!e_uYSz7nr1xn;Vz@`}fuUcOMoSV%-HP(7(=)kdFdu>~|fR2fJ?uT)_QU-7M1ldO^ zcDLW7>}DL1y%@o2Z`s(KrCvG@H<5U)LX)SC^4x8&wt$IVyRb01J|bBi=$!`RrU09b#6 z|NXmhX#6aA(srp_ZqST(?oHlvnV?WqW&7)u@bkl^jk7b3MwHGEquBq;?*>Glg0aYW z0qg+;JhSr9Z0O6&+dB%cu7+a=W=6#fUM!?Lb6%Q3*8vLDe%;cOP5|8nx^Ptm8p`l9arfsWsNFpyu(VISR|>|e;%K<1vKhzCe%A?gi;HrkBmLOpnmKuv zs>I;AXqlP^DB-%CrcBc~Ez#`&k{_7TRpAic;E)jTf2|8h20fM>n+mf2y|%Vi;Qpqe zG7dV%W%Lc|KZLRJr1JTY9P>h@0%gv@Y|4{u2 z*Dr;E+%sK!C~zIJO#OEdWCfjw z+aePU6h1gS>@Dp<$Rgb00O77F&_=dSy`$o`kUI!nNUWN)nJZpfG-Q^V#7sY0sp0Oj zu+*}YTdUSBIi^tdC*@*g+=^EMRAfxF8>KM>m8dlkG0jA$40anhtSA?TLW8ML3d7`m znN5r-+S^tT=PQjbXCZ-JHZBZ@{S|sJN0AYBBIdtKN<7b4e}d`+9B2TG6}(Lz!zEX= zfySoUtMm(Eju{XTAXoAW_yaH;NubY2tHXy2+H|Q<=M(tc`7~V==xn9DcM|vT;L+@l zP85kotw!6+_H+94H9e7!7#ELV1d>NIrEP_m$T~I6RYjh0vI8j$1grg zk?yhSPLdv}cn>`wG1P;?{wXQKxL=wiYd8(!6)|J?a3a!P}-C!zW1kx#u%1CMjHyxn*lJTv1oAVEXL)YaQvIr>giSgaC5xvOtTsw>)hO9Mi>$xMx z?vA+ZlaRuUHUIw!wsfwCTr=;?g+f%CN$8e(O(G%0CX6Czj zR|Tt+ipziiONm!9QEK6KKmaci%1{{5=S(elmon{m7YGKLot-VMs924iO|>dJS_Cj- zDPX>^8coA(!KtOgmK+naPbP|=g5GK|_In4b1 zAAo$}y+PuwclzN%%!E%sJ^kZ`i3H_Fc!%qf1pEb5lLEqLX1>R8XifD7vliGXFxrey zW)VZ`edHK*gqpFqQ4S{H?um0SV2~j{S&HJnG$Dv273cSi(m>p#pXx zJa*!vZr4^LmW-2{zl zyZzjPPOmRpg)?kTJ^n4Cj0;JjjP16}=PZHYU=zi1Mk68?gI?h>5}H6;_6lmGza@!) zjW9bUz)fK==r&4kzF`C6?jYA&{g5WRJ7 zNz%qu^dJZ_-+h}&+2^eEwvwBPIm((G7HT?y$v0gNub7Is-sNRddtXx1g(Gq<)yjc+ zc#(j7dZjv)B!y=^7;K3t;cJqFu#FWSzVAm{lU0C_d5-SastQm2mj*Rj+^#IEr9 zHR$1MV#GJk1|q<}DhuBtx8XH?lsu}-08-J(Om8~M{IetdYJ(0H2kaw+t%ZT}76Kh` zf}w80NxLsFwNjEWQ3vASpkLk{I_moWXlF^KSX6JRCS+d7^7K3bo1TK!*x8W%BoTsiSX1cgcY87m}G6dXUwPVvF5) zh>sGe%BW;8hnGz#T!J!#n_eA?6pF^o*z{e^AZcC0N_MICN9+4&mG3#zR-9{}Fuj^o z(bbt?px*O7`La6vFZ~ojD<74WJjTo>t~ht0F1l0TcT7|B^N|2)z+O0tuE20ge=PQA zbI_C?988LNeY8Vw!Z6FJl0ET9bPyAQFgN$>Yg&#>e!D(2(i@V@UUn>aKlSY9B@*(h&>LP~47tbpU|g*?D{rIe;X^*aID?D|s7Go#@6V z(1Jw}Ac=SE3V^xW8Z*VV#X8IN_hUEBM~hE5eUY!|lYzl#aOQSOzZ(=hHtN9>7H_ix zD7|nSS0aYdSYGSc?aDLe(=btsQ8RY`tB5V(8Ob6KmcB!=|AZdxYq%+akJW=s#WQ}{ zR&_D15_o|xe15wvWX(gf(OfCYf;6}fYNbo7_TtHhJ{xuT3plmxzXFiNO**PO7%3^K zgmbhyb9w&E%nSi3X!p%%zrgJz1U{L2mReG|ZDugL;CL!{n3;ggo~-<$McpWk6DEd+ zxwU*z$6%t~QNWtsX*Yj(V%vR^#p%_Z70p+1&rH0HtS{bejYiuLTRa}8F?Vysh&qV| zI(_^6Q17+kI>3_s<@EO})Ff!%W;e4rF%+P?fcRy=%ypJ^>W?}hazp;6^@vk*ekPZi z=u)VBE;s!o2v3p@ai_69%!S0Ft#1Riquqmwe#;KPv99VYT4 zviDgxYHz93yoZ}7qFoFhER7bGz%C|6BFRd0?^qQSs+>V~->S0qnLPby*G*2rR1U$2 z7LC^@rZZYv>1U2KDdylXuz=w&HiA# z>97H`7;XO2vd$BC5^~=P!+1UwAl2q2q4%1H9`MJDToeB{J}YJk5&|87Da{0Oeo99i z2xcWdE?_W^vgaQ^QS~1ESX?`&Ho5?ik{m5mBLk>_xSyZUe~_yI&^_~Cj&e!P(Sm3m z7Xf4=5-{b2)ds>-GB5j&&`8zaEl>kJRaLoo+0})~DSKgabNkhj=}T(ZC68B`$p`Ec z-xf2)DK4$*Pr-&iT4WOgkZHDRZ`Q8X=Vs1A3g;9pi9;Z2@XWYlm43E;70fmmH3|rT z%)3Qc<|^;<)j7@5Uu~d~V-H@#AXk?mpE`gK*9}BL^_B~z=V>jiFC>f7q{)Xa0SGWa z8^jEol`qd(ap*3KNxkss|{edwP1<|MIgFeHgC>2BC-Fe4SX)rI7S) z8^3`mFcy{*DK*-Qbag;2DSaAv>Nodv{QMMmwe+(=DyTcjUQ@hEUASKWmjV{U#P$Z&i-DCLerA$m5k zniv_8bamwb;DesCHHY-eTzywyPN zj(;gC2nN$@x9zR&b!rjUgYv(05y2VV5fcs{Zrh6erSOgSWWU9a5L8QA>}0gKSQBoq zSIHCng*1#zj^$%4Ma3Vb{_mhL?mk?h52z(ae4i+vEbtb;dMs|1-TQ&;)@5qRJZBYu zk7o%nqejjN8Z=rDT%w-BEd-%!ME%61WMuzmql=1uzJMhi&vg_Wi^wAL7r2BWb0AAsW?>_{vUUFFzq+9- zk!=R$KhxQeurs;@;_!S3+OFwwF_yLCWSJ_006@$N!`w;z-J#%PjGEe0iAL>+TBpuu z*+Hi_b%c+3k+iU)}ms`_-M0~Pigq*_|9$s z=K>>W!j&CxR`^(jgni8DGH3)Pm>%n7OTA6V4NvlF5;Qnj*jr> zIvBG_^ZijqqojbOK%ETc&K2k#jy;?Rs&Eb1e|4Og@E-^;0(%)Bt;lQAAnMKSxi&B9 zsp!iuWNM9lAG`v4xP??`4&F*`xHTU?V1<2}mwNb`8a+@N`?$(VVQG?$K*7cePm*#0Y;RESEG3@mtB z=%Vy{P)s^VeDOyrPU7IsuW1Dwx5o)uVGS1*!?YEImaL|G7#EW zu#aQlxo`aMo;Pradu$R0q)a+fEfRT5T%P5~QUX5v_N1>`T>MgC6BT-v#fR|b)8}hb zQYa8>_3y!}^N{UD`?DDui0zQ(Vy5QjC2Eq8Q4f?3Gdn3a23*;7yQQwCp?`&p@B4#Q zJUp-@ z4($+wTGd>3&*na7`Q!JYlPe%47=CAmD|aJsz^N;W{2p<3563iL70sp#59%>gL?7!n z8XK;$Ywb&)Y5B~OS`|Xv@U#D#WMHO1FJ9kAs!^+TzCflv6O<7HmF5wVGBYo$*rwgR zIB1&ACm;arLe-^cNOmn2i?32K86qV6H!#@RT*7VBC!kq0Rp7m|>(3&I=_Ds&S%x1B zP9JW%7r=QZEl#>i!h;|Ji7nqC)8|gz3$UZ+-52yq* z*J6RrvN6^&gb&MW1AB?z3B`q)FeL!u8dy3SRDk_UuPU^!V)2bsP3!w}69;imV_r+2 z>(lO9D%PWq!Th%(Fr(pX%@|%Civ}_tU77dASHqbCZrNCIdZ({qliz2&ox#y6CyxAr zt|_+=)9peo60tZjT3-47|1LwbyJ?czO!=3)I2;4W3=joocBKRb!81jprBw2#jr8l3 zg#K&5e*NsGrKObtAf7SVg7XJ!2{8yFa;yf zvqfE6DeWMp``xuZ*$m&mGd$h^6xuoz^u1no4#!iA&$H1Owj81jLJm(sS43)H*=;NP z6{L}^lj&dJ*KXVHATgEZm!DKGX|JkeqJazJoTK5Pw z(~UvLEpWa1`~A#mt`J5naRk!DrOa$DnbP1ZA1phjgz}!U*5Wsj=R^gCB1K66mqo8u z&-(p8V&zdE ziUSR-7@y_bXWSR2A`E+*LZx1&=oLfWF)>*Z=eL!l<3^%)U(mbUr6eam{`>ZPrx{e$ zX`S0yXYT#SJV22QBppV)Q5hjm+!RJueHkR$TbIQ*tL=Q9_bM)fSoGkJ4~|~(%QD)H z9voey0vFb}{_DBg9ODjmPmTUvx8~G`1&YOEPIh(# zkbX750aGhZ+N%4dEUV;LF)?3C7^V(-d6qOz4gO952`^4qAa48cd%woy*XvUk=k!SY zyX*wPEH5Fb)Z|*YL!Cj|$1#r1*H8CMVYpx6Kb=`b|eQBQC>CC1En66$H85fRp|cpfA9^ zdV#}xD#3mm+6n21)`11v&`-N{->=<&2j2W0XkmI;Ii*7Pyu);}L+9%&7_oaqgNj=C zR@Ce9Z#hplUbyMrwKwY}Gwq~K+i`?Hgj>oy?juEZ6%{1AcOeqKRs7F8S&L?ESrb+JU`X4-?&iDfc#CsSHr$_ppa+O`rVo_)`?uUAbQX;N z&I_ZS=UKbL(EQcbxfj}Nr3I+)>+UG12I-xI zslWWxB@n0Ea00{|2zYrZD6MY2&S-eBE3My0bxPuhv|}tY{ralZaWi>tOlRe3I5ED% z`Duvd02AZCqp@TM;wXZ=51RLsO)~Y8;5I5D8Ltd@d=Fui3J_)+`0@P0LR66GEj3`k zKVYwR8TVHh9zbUnh9Rt7>#nZ-efR&>Uy)rYFA@!n z+;^C!OX@2uM4)ZT|1pRgq{J=FQy)$sgOfIGMvzrGEOL5h^Yu*rBo4JrM_onm=;OYk zvxLwTaqqnOb9B_#*l|@+^@@H2W3&tIU3JIOKztT%eC z|H8z-9pI6l@uTEDO)u{d^tnbVf8{y-Q6j@Keee6+xbmLV&hCVbqx>obUd1Ip7vlz) zZ5!P&h<@l_*@8?Bx@L1aem7TZX$5&0@|i3Rx*ngRh!2d$>Ef&B78D=@=sVqi!*JjB zIc~?{ksA)wb;xF|2fkAt#8On{{}cWHR_1)^Xc-Hpla>?F(lsW+$#0Pj(V790y-GQc z4_B_B1?FjspG!PGkG-Mmf2_Z8o5Eqg=PNcQ*@bbo-+g)(@sb!TZv1c$*p{s3X8EYN z@=kurt;|~KcbO3AGpZ_4JCMbSWbLiqADNfDR$)K`W%~X5T2?zDc*y&!M$mA#_5FwK z=P^ldaEfJKW8;hU+kU>Y_R|0yz_b8JiClC;+4yd`s-N^e2LSMcl9EyruwR~40Gfm^ zFW&)WW25Dy6s9&$oagbUN4L9ImcnDk98@LVTBz~8rBM2LN&4psJ(3s`<4g8>nP96@ zRT+OX`Ja;Yqk-@K{e4g5F{W?+uEuY4;-uqpUzv}`EedJ$Q&%!&F1G@NeLU`IFLbZ% zkWN-5bHVssJ}Ky}%f6-YdX??=Of*Ed(C^pO-?z`A z{~HpmAJhT8d}k07at7&o8-Pl!GX8x6xl7aERRYu7fNx~Q@Amtj(bKqr0R!;L%%d&Cc zMROur!p(U0_ch@pO==Q4+#>G3#@6_*5N$_aVnQ1ccrJ-=jp2|2C`?{+QFAvEFCCOg zR^@2#3ZP6rXMS#+%GtI{?0$apakX0<*sAyt(g+A^s3ePu;?wWkb>E!u|IMW`ul(b& zotCbk@}W*AXWME1I15{bDJnv=l?5x2EbvXlbO)Q>evGu9B+}_0$N3#56YE!&4H#D@ z*iThw^2I^;>j$Xv28gm9*MT>;Q+t32k_fa=PUvIX6wt3hk>PweUScV~306%=rSJ0F8ib2uDZpomI$;g*K#b2_F8p~YF!n0xMnBs|?D8WlHZGoRVhiS)qH zx6+TtX!n*MluGb^hq-Qd8d*t4u;z)2ppB7CC#W_A6HHl7wB7dbJv;Bdt4u5U9)_Cm$=Ulq;ZEbtQ;Ge(kQQ-e8mhKUQ+b7O)zi!mqZY1Zwl)_O5^NZvxs^MV{Bzf7ye&zAR$Qq*GP;G?kbCZIqx- zZ8ED~kjiT2wp~*y3`qnjeiGEIwk%1fOoH}zVYo~yUXuKOhBn(lh zS&O51z}s<|8vcetV?*(9TD{|s9B?=U9E_DzeF_Bhdz&fMw6{jLZ0e%vKn!rUIeg>g|~OE2CS+%^MR+*Y>> zYv#-7tn^R&qT?4(%8MrS6HDl9zRVAgJi1?s? z_CDa&5cDbowD{w#%>AoI;~JAi98q3BwcGmZ_wBw{S%8QUhd-`>yE|h>8uzHwp^SN|lWP0PX>hkly4XXf< z_B6j0744*%J=d6ezdx;Rt8B%M1S^=-9}#2s)?tU=c6Q#J@4wtdoe&MU%euUb#k9@{qER(yu}Rl3ad+U}8B^(7cD5zW%>P`;Mpj_{z8UnVn>f>$F=@ zYTh{4?t6}FHFv|dKYvjQnx79J*N2f`5&!m*63FKf90e`B&|a;;cLp3L) zY1#&DekUu<>zONed>&qI&Q7YW(^}E(i}-8<`zz7NPONGf$@6+uF8ht)$4&KNZFIdG zUVR|VyIS=hhn?TwQ{cKzP*DZ^O_y_irUPArx&V#DE~x2>?uD`bjBoBE+0sT7Gs?5~ z+N3=iFS$snTspTi)hk8<(7c&TQgniCY%<}C|IWNj|EO-LG({ZnFvPHSnl|ip(!BRJ zgejn?=d1{d>#VSKIQGGy=O`l!a`zbb+Q&ZowpXQr9&j%u+OD}R_gmX7_nAJ+7zIAz zfb+U2A`&>?ajKFlhslRyGMImlA0R1|zx>DV(#0=kj^<7HzX13r2iOf4<`sPT_Pg$R zCbfNSe~_WEzU zv~gojTi>Atmd8T~BZrp<$43Kc(wtBj1h(xsb<=0u^M%iU{_Z(*=L-PXB?bNSmW@?g z_wW8r7k4pgYM6w=h4k%dY5D=8aru%6KXEz_9y5>kF&_amH0=A-{?3C}4g@)KK|vHl zm=X*HEMxEJ;8%9;+4GrJ*Pih6U3>HQw#{0-?DGZ1MV-QN0Hu;%Rw=mqWR#{1t z=6K*Zwkn%Z{?Ovp%db86g7;3$Yj-v_6l`v8_;nX?SGtx7rWur`h?yp7H{9AChuVL& zY15|Dk7Zkrxd9-Ij)0K3>pI|!0ZM~w20;i$y%bvqy1sVHQ;%J7*DG5lMkhC4|Gmw* z**X6f2nPEwNifs4ZID!cH^y_l(`y82jv*;!W?-Upn=Z~T*jiCuarHO9_#cA+(4-~q zZ)+-fs%7`ht%#pB=3!8jfQ}1-CiM%F^$c_K^4_w#&s*V`8-R;HaPgxvDyyCd37V8P zA_Rs4w9ueFF9slr_w{z%yraAAf@ZBI+IRnh4?JF3RrPUZ8oeUr03-xzJi#L67*uJk z@ifPfRP|<+j91Zld3n#}=N5hNmp}QRUI1u}N7T2rVc8>p>;g}nMT+1IhWB%fwRR*)yA&~$9-~j`ZfK))-Yhc8p z1ug9dZhr9TKcD_cXWK*@e|PUa&lD6De%|z0LsCg_&L<4PWGkE|&9Q<~I#E}E5V9vf zKmYRI-+x~x0PImN*%2AEHtpPY`R+p<9~$sd*o=U32F(Zr6bu3wiG)+1SMrlH%jzFl zNUWol=<|l!f2=Q_0v^U89RW%h1|ZRG=s8>3oA2o9 z>XQcbsl4_B8}e90tc{Oz{ot#Dj>%T|@}Kl1VmAK%`v=elk<AY;T1f;94i=9i=<%(8|FSdw-}|O7+UId>RcR%aP|iJP^+ig_-wq88=TXX_lm;aP zjOb*IHA3JD0Z!St5M%O$HA&i;?uvn z{nrz3{y#;BavM6EZg_F;&M&lx7~u87v0X3)7#gsYhF=qeOwYPctvd5l^KwcK9u0M$ zx59B9FE65==*r^qdza0a`JW^fAMh~(EnG0pKsZHQOT-L@5h7Qtd+xcPuHXMk^(d7S zxxr6A`^@86*_r=pT4s;I3=KuW2m_G{!gb*Y7u+(TY4S!g4KVfE6O z&>DVb!Gr-RHG1Km_s8{X?|6B~E7K?M|Iova{&Dqt&iep3-6pAm7*859O$(-BjK>We zo*1ZftU#K#fNc>({76=_N+g^aRn@KJSi;NhpZ7u)2scFv*T|+}5 zlNs3KHIkU#%{QI zj^BdA2x5*6N(c-9{+Oc+1>as*TK@U^n!0;TMc%%w@7q1!hA3z%5aPB_v_x%8_mVm*+xjEV2`0~Gh@vaYCbV)3={jW|q z_o=NLE^TaUySYy%a^qeLD%G}vN-Ip&~ynO9xhTl2f=bk#Z6N+EG z@>0Q;w#J*b4s?7xVVPbq3L-Q#C~U4FDG(GAr3tcmW$o-QmR3}5{pYgU2_tah4L25S zetFBaZ3o&u>#;muB_$})hb!kG?F3A77>d_^7E-ksQ-6BBp0LMjec{Kq-SUU|v*smZ z`?Vt5I+{a`9S1(MxxM+iVWzVsqp(c|8xopm0193qb*aO9W@i;%{iF9>_Tce8=gFLK z1^@td$D*EJ{_W8p?C9_O!ia~PF)bmv0Rt37Dp0CHg}^l8>0X{+`laulf9XS0zvCkx z`^2>^O-)y6SLQ)$Br9qqV+L3%R)CPS;OHsNixWWaY6iO-N|Hx9I4G-5F?F*vBB5Q7A~23*I1-*HhcjGnVfs;{_g#aVx? zCe{h{$ej>AfT_&(`4eSTl{ZyqeHBn+iNgoZE}Iz_B_eP`Q`ulU_B zKlz{c-F-x6*+O$MO3-jrQoim=>`tl6}OkNxfdbq6> zq$^+9=Mi zRKz0zN(fvf0SUN5!|_?@V6LD0eLwia!w-G`#T{Eq+Vo-n%I&}Xb@S}mvp-%lv-UTX z(KzEAt}8$Zfs!Xqo%l4z9zqCAGpWJhIL<^ZikzIB&LvBhTvcCRe{HJs?-w!B5gyEV zY4?tQ+r4-9!=p@nFzhvWL0sns9}Nhj3605Gla`Fex#^2<*L}X8sQr*Vf%P)38oxIqQ?= z>HkefOJ-Bk-V63LHT-12P(=|&;1UYL4G1Zrl!S+y;EpT2CfUDw-r}F0T~`0dd7=Cx zB$&T>{dMzSdikXr2ReJsvrNlUN&^(himj=PLXuh!)&S9<8c-+pU@6TMCP(){6Hvn7 z3IW3~;0OtBaJa4mM)kP29+A4bx?772ihllIKl^DU>~VS9`^nNuewQVJQH^NII2WfeZ-* zV=k6d)buY|vgo}? zK*#+9L@9)%0+R|p1-HQTJ#*%YW&crHP}Wct%o26u#YyhD`(ERzzdbeU(4pR+_xAQK zHcit~N+t~eIWng(Z2(Swv<3*FP~*xwT5Bk+hpVSo-aBXB+*`i=t^aHRfJSMPFy-XM zo!;G7rU~jQcr}L<0?K00R6)58rWxLYOY+Np z@rko8_{ALVJwkBd;YT0x-gn=9mv?&#~hFy=9QQJvJCunZ1I3bBY}j$KsZ4n z5l6`D1&ha#AxW$xGw0VApSJ4W1^MMW>P^oPO8}qy+`pc7sJrJsdb_&ctCjL<9GNtf zHUK9NsXKMp03@S8m6Y1DtVreb${T0Y*53Wy8@|^!dB3&c0Z+?N--^HN+4`++J9_4@ zaKJnkT*rmsF%ee|v@4Klcn}Gn=gK)Vb52FW8noN6u>Y|>H-*qK)C`2QSgF*h2xl!nSZFPu=q=7Ejazz>7l&g z8bXfn(q4DrRpz zlmD+2pfxPhj0b{&2QIwu!k=9A^{?-q{5^ZM;#-G0N}k@b>BD^k1E217W3?(X2tmf< zP5?O40cQjOFnGfWyNLN7T3J8uhgZ#Ay5o3XhquGK=1IM6%lg$D+xPuodw=(0Co=@) zx-db(7=LuAzM;}VrsCixO9De?aVI~21A{7#g&Rc>ng%M)#O5C+Gq?VKt4j}}D5MYc=7=y_VVVYx4N(}ZjOUNq)JDzYV%PaPlmzCXASy}ny zHQ%~+Vt&3^C&;?4CQG#UF5BPL@zaAUUKciq>5Q)-2&Etm0+$g`C^TDe`MCYL&y94-!AW(r?yGkwDCaeM>jS%etgrxmNQ~n23><8q6GvaUBWLqn}0p}}JblyM)53ko*Ot*yUd zZehjFTwmZ&J!KQ|!FS$yCx7OdXJ*^+#5K*$&6h|ieSW{6Boc|k(&XyMaN@KfI5wn4 zV1{8#{O-E03}t8Z78MoWHlw2Qp_|vNX-<86!RH3 zpcR3u0L2+7V{j4)WHJuFBj9mdQIk^`FD)$oWr0s$#4#rKm&~Sx=plQJuO(4sbozshp z|ND}atM8fS3H8n-=DR9>{9XH`HfV?K8R%TKcK_~=Zf$S7bl6GcYQiWr`1lf$hM<5T z8eXZ9kx&RFWGqMVt&3;PzKI5W8<#Iw7%Ad@RWYeU|L(54t-t>Duk&r&{#;vI+lO7p z@lnR;c=p&4et|UaELv+wDMct0>dw#4|5}Ol5s;6rpDKZlUBYR@Kyrro* z81C->YDZ+`+IA<3QHwzu1WLQfFd=Hf)qsSCmoXq5mNR^ryH77JUo)q$x8KMNBTER;xc-%%nYUD`LH9K$G!>3KJ{r;uXXZO5;Q$A^V*Bbx; z*q(^+-Cga|_IDguv!k{714G*J#5qNbC=i1Kia;bBP|DyjO(;77uT;n&WH4lUo?kw1 z{$14>`A?Tvf%u}Lk|X-z^G`lSpLpVl+LnXuDjDgB~aA#+}ZNJT?bpgHl*CD z7^QHihR_;}P=FA)Qb1cKECqOD4g$6m(}J0sYAY&!RyV!+xr&UOp@oxD%ioQh5(WSO zc50W|&^enAG|t)b%GRIu4-L)f5plRa3sh4mr9li1M+gv3L4<%83Wg&PV8%#ZAmibZ z;*zK4&YrtvW=3(p2}I`*dc?!AWA9GuiO2tR&R-vY;$ITNSsu6TQddb3#y|-LlmY_* z9bbhY<1q%WhMrV!{8zU=$?F*as#BjMAd~=VeA=XvgS&#F`{6*5+08Q1`5}8 zKv>ckQ&+%DNR)d+D9*{d>5^6F{yK|#yXH=b4|%t8${2tYO~PjTI}XfwW$&IdcMl)> zY^NQmiz`VTgTW>m)G*;n0n^~nQjEWg6?}1}GbA0EURZK)QFYy;IiZZ(va@rBi+ll9 zM~)C9!n5nvdLI1W2WNF1>RIXvab;UuTcydk1xHYmGI+p1goI`k?s#uGvXh+r2H@|t zf5Jcz0zm;PH4H+)6#)l9!gXM~j_b_`MoMQ?z2FH2?ysn<+_rrA^5*xRan6LrY|suA z7}?(6&gEDnPefz4zPxjLWiyj?BiivQ%YZZuxLSb{0tlHjzKlR>1)`KLvUR*PGxvql z%W5C2E}Hhob8@E*9`EaSEIH*2z+|55@5=wnOB-+A*W0nWpNsM#u86}aKo~HdTqJ}5 zVGITcxYVGr1Tr+k^sKxM(=&4aR##g7hY!t~xA*mqv19uyw6(Rh<`4h-z^5a_qf2Aq z$f8I%>{CjC83ta@r98O<9ZpX^c{w6I7s>Nc031rdK%lh*B}Dmrz8*^H+LfzTt~>X< z^Zsx3+2@SD-gj;q>G$kyYdd#m@4<@>M20TzijNk$UK3%%gkhMVQbS7@Lb#wjsm|+V z45lMtX(B6v+1t+a=3Fyx=By2;7nZ%Ny!&sIQ`!LR(}MQJVxd27URO<`_Ptx%n<~O4 zF{2hEQ7vJb-sJGq5im7{k`mnHFbD+`F8q|jWPE$|w6dp8tEu}-nJ?!+rq4gH$nwRe z9&78yjbz)lZN=-Ke_^I%WYK6M`pLG|wknZu4UfmeO~Zt6UFh+pMWr=_lmH=MoFS=h zOpjPC&Aa`N&7?*Su$Lf*%MEl{UwR=dRto{i7q1 z@EK9*7CV$cFan_g4I`QPrvwNi0M(Gvg`ZG(VvZ^`0?v$*^8e-eS!+H~HLszDSnuk2 z{@>%2HUKI1xpBkh%(u0(b=IFYti7^lZ20^rBe^bRfWaqR+6oGa0!nK@33wR?b6gk+ ziGn~7HKpZnedbL|tLpD6%gh@NG9zJu#_I`x)dO~5up14Vcjm2IyY}Kg|M|~ndcD3X z%P8@pM@p&#vsutftE@xaiqA3+Am`u=4bg*SkOarA2m36pU^NeIVZm4Q8PBTrtS$v$ggCke{`Cep|ikM-D?C1?QdzN z&ph+Yw7)&^WUW@JJP`1GXkcJqPIpgtj*ya3&X~mw!dbEoy%1ueBLc7P$glNgp8Tet z`nI3^FMyOHZqhsd8r!FiKBjJya>9S60SLyJ$j;90DlRU5CK`=CS5jKivTVhQ=K5Ln z!*gcMepLtDr!`^#**nxj(2IqBoWk?bYt_V!Rkn$00t>H)oZE|RyeA60^0$dk9P?Tln?5WA0_FR2&Sxa?x z!F`!tZ{IA-{||?pJ73vB4)ym{Y}vYX>G}=p&unRI4rFFzR58Y8NGWrql*G2}iOxqU zO->zBdGF&Lo>B@a<|QsZj{$93GD+MkyWATJPqZ z4^>oDbT3-8Xv6&Z^Pf2L?6d5Dbj~RrZ)vVR(BE@WOL$;*dvxf+zUWxtm_eaU3reYp zX$WTw&>Bi6S67HsFt`Da?SR=1ay-6+Gx7=>=a*G}`=c}FZ8+Y~)QL#i0K5+SwM*jA z){eohoIkC9t`@QQmlAg3!l77%CpaOl$3TK416BxQkhTQV1eReSVcReb4o(Q9ZNt() zfKbT+mIUKtK`DG z)>>;qh$4h&%d%7`6w;wkXfPNIwo*!;>*?v)Sx`_gQdU+rwrbU?=F-yA(b;q7{KIqG zA0ETO{yuISJgZ%fEqSSF@B4d4hECIjE*fxRpgs=-0phL%H!KiJV7m@X(}0!=OaWX2 z9x%vw0!$NC#0;@&);w+cydSG9FMV+u_m9pe)=44Jb*xDnfH%nc_^5wRd-IC*yS862 z6bWB4=*BCf91sSF06@UO6riMp!40@dLQ_CO!C({~N`aUS5{V^A82jE;NW0raB#4+rKP2IWMm|(udlCYcz8IgudgpR zkw_5Rwjrf7wAPl^nrNLYiXf#FoO4PDQA#Q4^Ld3~n8?h`Ok`(g59a6RAM*SC(W0WF zgXQJrO~u8<2WsnPzG=rGi+<0k}6WAUG zWf^b?Af$%Y3I;QwgaTm{LOEbG8BgJnfN2YOT#S0X{>>#-Rh!SLoBw~6{$O7KtZz19 z>5l(*NE?7R$>aN8;Rg;KT6!Qdd|BgQ-v|1fcxf1tJDkI?Oem#bx&)e2#I=M=HRQNJ z2~&V60gu$+Qo{t`rPMW~>I@O{(5#Y*a7kXl?{UUZ35Nm>(HI|gfPsO6fNMYs0av-; zCIKfJG~vK&OF1jI@NcC?@R6B$#g8wlnblkE3!c)By*ESJ0K5&h4EOtAYG^ordwcW0 zACj@U?nJoIrhsP2gd+tF!Wk5BggOl)90ULt5?pJ5CnEx-aG_m^kjJa?eW8Q7mTzMQ zHJWpSSzX2c;EUCTMV*yPowsj7BpuVphqrWDg`hQNFl(;_&i@}IIaWB#9HOY}t?fqy1EYFptlRn4N%P z43ZEiP9Z6Qqcj8om{MR`gRtboPXKTvBi2k%n2Nvz0nvym=w@c{K}K1$FekgWwybh{ zb-GAe2~f?gH+4>Jc5FN`hoHht?z?kr7@(X+ z?S#kC+T&8{bs6!-m=5-Y2WE5+^;I71?WrCd8P17mRTX5$JV&_Dgun%Xtpprsz+iAm zQnW)+k}LsF04N0_H5f@=0|o+RNZEU!S>L|;=@Qu5*%HS=0>xDoe) zIWwk2W2Sjyqz%APqE$-{P;v;a|HT7)Rw|bfn^-k}w!xd5KT7!`>gyT^& zlCUW?xIfF|%dhlheAG$UvxlP*jKyP!3jtdTC`O*BztOB zNoC`?v*w16_dR_#k{&m`Lo6>SRRCH6{2IWoxAh;Q9m9ijwzlm1;{K7o`axw^S_u?n z#Wjf$8?MRWdJH(6KtO?jfGG*a2$<4{8U&JR5UoHJz$pa>1CO2QA^_g1Q0A-h=f@b9hC}B0ora4Tqi~QEOtBddOb~b^11Nza zfCM1MM=*kDsPSWi5_lXyQwl-}2+^S9z7I+O#**pKjuc>8!RKlOG!17u=&0~#?XJ$v zd&EzTM?Q7d>NgxWkmjwEHUP(ng?R;Pw-&pLX3V&%EW(rBMaJXrph3FRsnGTHf+ zNVbO)0s{oh79dea`w7!ofe;8||J&=1Jzi2)(!a2*x@&G>Syv$ohG!9)j?_Cwqz%9^ zWH#XvKxF)hU9L0}LhBOg;y zhZ={=r%44eUS^$A0tyN(1PqoOofrxLCA(Z9da``MzMAsd##yBmTdML)TCxpa&wN6| zF95jbc;B2;l(Ydjj*JKVjE(;}2w>md;ek8CiI~50WVp0_uy6H{h~{T)NJkUzNlxg|MKRZUb8CMV^vAnpMxR)K%MD3C3EXfM$!i0 zB*W_oI5VqiAE~ISd3-*I8G|-P;dqAZsJ~}mAT%5a``UWjvS~uBZshTk;3 zhG5WAb!+I?n`5_oxp7UlLGz^64pYSTTOLWhhBi%)@cVpDb|4fh$;}%n%E%eZWx=Q) z%<)3I9uPqQQJt1VKdJct5}r{4QpL$@0000bbVXQnWMOn=I%9HWVRU5xGB7eQEigDO zFgH{(F*-6eIx#mZFfckWFmrU$zW@LLC3HntbYx+4WjbwdWNBu305UK#FfA}REigA! hGBG+bH99djD=;uRFfalsY6AcO002ovPDHLkV1jSpDbfG{ literal 0 HcmV?d00001 diff --git a/assets/tab-icons/icon.png b/assets/tab-icons/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..e0314abee281359a16f991ae1a1c9cceceda9a91 GIT binary patch literal 44017 zcmV*(KsLXLP)004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00006 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%0AJXF1`vsEtxd99m+EY*(&>9dam-Kh` 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 literal 0 HcmV?d00001 diff --git a/changelog-UC.txt b/changelog-UC.txt index f98cec9..fbc3df0 100644 --- a/changelog-UC.txt +++ b/changelog-UC.txt @@ -88,4 +88,8 @@ 4.0 Alpha (Build 20220) - Zastosowanie nowego systemu przetwarzania plików konfiguracyjnych dla pliku 'config.cfg' (tylko odczyt) -- Rozpoczęcie prac nad skryptem przetwarzającym dane \ No newline at end of file +- Rozpoczęcie prac nad skryptem przetwarzającym dane + +4.0 Alpha (Build 20221) +- Zastosowanie nowego systemu przetwarzania plików konfiguracyjnych dla pliku 'style.cfg' +- Lekkie zmiany w interfejsie \ No newline at end of file diff --git a/default-configs/style.cfg b/default-configs/style.cfg index addf2fd..4c6908c 100644 --- a/default-configs/style.cfg +++ b/default-configs/style.cfg @@ -1,108 +1,109 @@ -windowWidth = 1200 -windowHeight = 720 -windowWidthResizable = 0 -windowHeightResizable = 0 -windowMainBG = #21242D -mainIcon = assets/icon.ico -mainMenuBG = #21242D -mainMenuPosition = wn -tabIconsSize = 30 -generateTabIcon = assets/tab-icons/generate.png -tabFramesBorderWidth = 0 -unselectedTabBG = #21242D -menuTabsBorderWidth = 0 -menuTabsPadding = 10 -selectedTabBG = #333842 -disabledTabBG = #333842 -headerFont = Segoe UI;15 -headerBG = #333842 -headerTextColor = #C0C0C0 -headerPadding = 10 -headerWidth = 84 -tabFrameBG = #21242D -formatTabIcon = assets/tab-icons/format.png -settingsTabIcon = assets/tab-icons/settings.png -aboutTabIcon = assets/tab-icons/about.png -tabFramePadding = 10 -label1BG = #21242D -label1TextColor = #C0C0C0 -headerTextAnchor = center -combobox1ArrowColor = #C0C0C0 -combobox1ButtonColor = #333842 -combobox1BorderColor = #21242D -combobox1FieldBackground = #333842 -combobox1TextColor = #C0C0C0 -combobox1Relief = flat -combobox1BorderWidth = 0 -combobox1Padding = 5 -combobox1ListBoxBackground = #333842 -combobox1ListBoxForeground = #C0C0C0 -combobox1ListBoxSelectBackground = #737373 -combobox1ListBoxSelectForeground = #FFFFFF -button1TextAnchor = center -button1Background = #333842 -button1Foreground = #C0C0C0 -button1Padding = 4 -editingPresetSaveButtonWidth = 25 -editingPresetCancelButtonWidth = 25 -loadingButtonWidth = 15 -loadingListWidth = 85 -generateFilesLabelWidth = 15 -generateFilesLabelAnchor = center -spinbox1ArrowColor = #C0C0C0 -spinbox1FieldBackground = #333842 -spinbox1Relief = flat -spinbox1BorderWidth = 0 -spinbox1TextColor = #C0C0C0 -spinbox1ButtonColor = #333842 -spinbox1Padding = 7 -radiobutton1Background = #21242D -radiobutton1TextColor = #C0C0C0 -entry1FieldBackground = #333842 -entry1Relief = flat -entry1BorderWidth = 0 -entry1Padding = 7 -text1Background = #333842 -text1TextColor = #C0C0C0 -text1Relief = flat -entry1TextColor = #C0C0C0 -label2BG = #21242D -label2TextColor = #C0C0C0 -label3BG = #21242D -label3TextColor = #C0C0C0 -label3Anchor = w -radiobutton1IndicatorBackground = #21242D -loadingListPadX = 12 -EPOSTypeStudentRadiobuttonPadY = 5 -EPOSTypeStudentRadiobuttonWidth = 20 -EPOSTypeTeacherRadiobuttonPadY = 5 -EPOSTypeTeacherRadiobuttonWidth = 20 -EPOSPersonSeparatorEntryWidth = 56 -EPOSRowSeparatorEntryWidth = 56 -EPOSDataSeparatorTextWidth = 42 -EPOSDataSeparatorTextHeight = 17 -EPDataLocalizationPadX = 6 -EPDataLocalizationPadY = 6 -label3BG = #21242D -label3TextColor = #C0C0C0 -label3Font = Segoe UI;6 -GIFSLocalizationEntryWidth = 109 -GIFFrameSeparators = 16 -generateInputFilesPlusMinusButtonsWidth = 2 -generateResetButtonWidth = 10 -generateInputFilesPadding = 6 -generateOutputFilesPadding = 6 -combobox2ArrowColor = #C0C0C0 -combobox2ButtonColor = #333842 -combobox2BorderColor = #21242D -combobox2FieldBackground = #333842 -combobox2TextColor = #C0C0C0 -combobox2Relief = flat -combobox2BorderWidth = 0 -combobox2Padding = 7 -combobox2ListBoxBackground = #333842 -combobox2ListBoxForeground = #C0C0C0 -combobox2ListBoxSelectBackground = #737373 -combobox2ListBoxSelectForeground = #FFFFFF -EPOSLabelWidth = 30 -EPOSLabelAnchor = center \ No newline at end of file +windowWidth(I) = 1200 +windowHeight(I) = 720 +windowWidthResizable(B) = 0 +windowHeightResizable(B) = 0 +windowMainBG(C) = #21242D +mainIcon(P) = icon.ico +mainMenuBG(C) = #21242D +mainMenuPosition(FAposition) = wn +tabFramesBorderWidth(I) = 0 +unselectedTabBG(C) = #21242D +menuTabsBorderWidth(I) = 0 +menuTabsPadding(I) = 10 +selectedTabBG(C) = #333842 +disabledTabBG(C) = #333842 +tabFrameBG(C) = #21242D +headerFont(F) = Segoe UI;15 +headerBG(C) = #333842 +headerTextColor(C) = #C0C0C0 +headerPadding(I) = 10 +headerTextAnchor(FAanchor) = center +contentTabFrameBG(C) = #21242D +layoutFrameBG(C) = #21242D +label1BG(C) = #21242D +label1TextColor(C) = #C0C0C0 +label1Font(F) = Segoe UI;10 +label2BG(C) = #21242D +label2TextColor(C) = #C0C0C0 +label2Font(F) = Segoe UI;9 +combobox1ArrowColor(C) = #C0C0C0 +combobox1ButtonColor(C) = #333842 +combobox1BorderColor(C) = #21242D +combobox1FieldBackground(C) = #333842 +combobox1TextColor(C) = #C0C0C0 +combobox1Relief(FArelief) = flat +combobox1BorderWidth(I) = 0 +combobox1Padding(I) = 5 +combobox1ListBoxBackground(C) = #333842 +combobox1ListBoxForeground(C) = #C0C0C0 +combobox1ListBoxSelectBackground(C) = #737373 +combobox1ListBoxSelectForeground(C) = #FFFFFF +combobox2ArrowColor(C) = #C0C0C0 +combobox2ButtonColor(C) = #333842 +combobox2BorderColor(C) = #21242D +combobox2FieldBackground(C) = #333842 +combobox2TextColor(C) = #C0C0C0 +combobox2Relief(FArelief) = flat +combobox2BorderWidth(I) = 0 +combobox2Padding(I) = 7 +combobox2ListBoxBackground(C) = #333842 +combobox2ListBoxForeground(C) = #C0C0C0 +combobox2ListBoxSelectBackground(C) = #737373 +combobox2ListBoxSelectForeground(C) = #FFFFFF +button1TextAnchor(FAanchor) = center +button1Background(C) = #333842 +button1Foreground(C) = #C0C0C0 +button1Padding(I) = 4 +separator1BG(C) = #21242D +spinbox1ArrowColor(C) = #C0C0C0 +spinbox1FieldBackground(C) = #333842 +spinbox1Relief(FArelief) = flat +spinbox1BorderWidth(I) = 0 +spinbox1TextColor(C) = #C0C0C0 +spinbox1ButtonColor(C) = #333842 +spinbox1Padding(I) = 7 +entry1FieldBackground(C) = #333842 +entry1Relief(FArelief) = flat +entry1BorderWidth(I) = 0 +entry1Padding(I) = 7 +entry1TextColor(C) = #C0C0C0 +iconTabIcon(P) = assets/tab-icons/icon.png +tabIconsSize(I) = 30 +generateTabIcon(P) = assets/tab-icons/generate.png +headerFill(FAfill) = x +tabFramePadding(I) = 10 +contentTabFrameFill(FAfill) = both +contentTabFrameExpand(I) = 1 +outsidelayoutFramesPadX(I) = 6 +GIFFrameSeparators(I) = 16 +generateFilesLabelWidth(I) = 15 +generateFilesLabelAnchor(FAanchor) = center +generateInputFilesPadding(I) = 6 +generateHorizontalSeparatorPadY(I) = 10 +generateOutputFilesPadding(I) = 6 +generateStartButtonPadding(I) = 10 +generateStartButtonPadY(I) = 6 +formatTabIcon(P) = assets/tab-icons/format.png +loadingListPadX(I) = 12 +loadingButtonWidth(I) = 15 +formatHorizontalSeparatorPadY(I) = 10 +formatVerticalSeparatorPadY(I) = 10 +EPOSTypeFramePadY(I) = 0 +EPOSPersonSeparatorFramePadY(I) = 0 +EPOSRowSeparatorFramePadY(I) = 0 +EPOSDataSeparatorFramePadY(I) = 7 +EPOSLabelWidth(I) = 30 +EPOSLabelAnchor(FAanchor) = center +radiobutton1Background(C) = #21242D +radiobutton1TextColor(C) = #C0C0C0 +radiobutton1IndicatorBackground(C) = #21242D +text1Background(C) = #333842 +text1TextColor(C) = #C0C0C0 +text1Relief(FArelief) = flat +EPDataLocalizationPadX(I) = 6 +EPDataLocalizationPadY(I) = 6 +editingPresetButtonsPadY(I) = 6 +editingPresetSaveButtonWidth(I) = 25 +editingPresetCancelButtonWidth(I) = 25 +settingsTabIcon(P) = assets/tab-icons/settings.png +aboutTabIcon(P) = assets/tab-icons/about.png \ No newline at end of file diff --git a/generator.pyw b/generator.pyw index ee7f221..12fe2d6 100644 --- a/generator.pyw +++ b/generator.pyw @@ -12,7 +12,7 @@ # ----------------------------------------- # Zmienne # ----------------------------------------- # -class VARS: +class VAR: programName = 'Generator CSV' programVersion = '4.0' programCustomer = 'ZSP Sobolew' @@ -126,7 +126,7 @@ if 'Generator CSV' not in [x for x in OS.listdir(appdata)]: class CFG: # Funkcje sprawdzające istnienie - def __checkIfConfigFileExist(self, write): + def __checkIfFileExist(self, write): if write: try: file = open((str(appdata) + '\Generator CSV\config.cfg'), 'a') @@ -205,7 +205,7 @@ class CFG: def R(self, record): - self.__checkIfConfigFileExist(False) + self.__checkIfFileExist(False) content = {} for x in CD.open((str(appdata) + '\Generator CSV\config.cfg'), 'r', 'utf-8').read().split('\n'): x = x.split(' = ') @@ -250,381 +250,170 @@ class CFG: def W(self): pass + """ + content = self.R() + for x in changes: + content[x] = changes[x] + contentCheckingOutput = self.__checkContent(True, content) + if contentCheckingOutput[0]: + if self.__checkInstance(True): + with CD.open((str(appdata) + '\Generator CSV\config.cfg'), 'w', 'utf-8') as file: + contentToSave = contentCheckingOutput[1] + for x in contentToSave: + file.write('%s = %s\n' % (x, str(contentToSave[x]))) + else: + return False + else: + MSG('E0004', False, contentCheckingOutput[1]) + """ CFG = CFG() -print(CFG.R('allowedCharactersInSeparator')) + # ---------------------------------- # Ładowanie pliku stylu # ---------------------------------- # class GUI: - def __checkInstance(self): + # Funkcje sprawdzające istnienie + def __checkIfFileExist(self): try: open(str(appdata) + '\Generator CSV\style.cfg') except Exception as exceptInfo: MSG('E0004', True, exceptInfo) - - def __checkContent(self, content): - class functions: - def integer(self, var): - if var in list(content.keys()): - try: - check = int(content[var]) - except: - return [False, 'Niepoprawne dane - klucz: %s' % var] - else: - content[var] = int(content[var]) - return [True] - else: - return [False, 'Brak danych - klucz: %s' % var] - def bool(self, var): - if var in list(content.keys()): - if content[var] != '0' and content[var] != '1': - return [False, 'Niepoprawne dane - klucz: %s' % var] - else: - if content[var] == '0': - content[var] = False - return [True] - else: - content[var] = True - return [True] - else: - return [False, 'Brak danych - klucz: %s' % var] - def color(self, var): - if var in list(content.keys()): - if len(content[var]) != 7: - return [False, 'Niepoprawne dane - klucz: %s' % var] - else: - if content[var][0] != '#': - return [False, 'Niepoprawne dane - klucz: %s' % var] - else: - return [True] - else: - return [False, 'Brak danych - klucz: %s' % var] - def file(self, var): - if var in list(content.keys()): - try: - check = open(content[var]) - except: - return [False, 'Niepoprawne dane - klucz: %s' % var] - else: - return [True] - else: - return [False, 'Brak danych - klucz: %s' % var] - def fromArray(self, var, array): - if var in list(content.keys()): - if content[var] not in array: - return [False, 'Niepoprawne dane - klucz: %s' % var] - else: - return [True] - else: - return [False, 'Brak danych - klucz: %s' % var] - def font(self, var): - if var in list(content.keys()): - try: - check = int(content[var].split(';')[1]) - except: - return [False, 'Niepoprawne dane - klucz: %s' % var] - else: - content[var] = (content[var].split(';')[0], int(content[var].split(';')[1])) - return [True] - else: - return [False, 'Brak danych - klucz: %s' % var] - functions = functions() - check = functions.integer('windowWidth') - if not check[0]: - return check - check = functions.integer('windowHeight') - if not check[0]: - return check - check = functions.bool('windowWidthResizable') - if not check[0]: - return check - check = functions.bool('windowHeightResizable') - if not check[0]: - return check - check = functions.color('windowMainBG') - if not check[0]: - return check - check = functions.file('mainIcon') - if not check[0]: - return check - check = functions.color('mainMenuBG') - if not check[0]: - return check - check = functions.fromArray('mainMenuPosition', ['nw', 'ne', 'en', 'es', 'se', 'sw', 'ws', 'wn']) - if not check[0]: - return check - check = functions.integer('tabIconsSize') - if not check[0]: - return check - check = functions.file('generateTabIcon') - if not check[0]: - return check - check = functions.integer('tabFramesBorderWidth') - if not check[0]: - return check - check = functions.color('unselectedTabBG') - if not check[0]: - return check - check = functions.integer('menuTabsBorderWidth') - if not check[0]: - return check - check = functions.integer('menuTabsPadding') - if not check[0]: - return check - check = functions.color('selectedTabBG') - if not check[0]: - return check - check = functions.color('disabledTabBG') - if not check[0]: - return check - check = functions.font('headerFont') - if not check[0]: - return check - check = functions.color('headerBG') - if not check[0]: - return check - check = functions.color('headerTextColor') - if not check[0]: - return check - check = functions.integer('headerPadding') - if not check[0]: - return check - check = functions.integer('headerWidth') - if not check[0]: - return check - check = functions.color('tabFrameBG') - if not check[0]: - return check - check = functions.file('formatTabIcon') - if not check[0]: - return check - check = functions.integer('tabFramePadding') - if not check[0]: - return check - check = functions.color('label1BG') - if not check[0]: - return check - check = functions.color('label1TextColor') - if not check[0]: - return check - check = functions.fromArray('headerTextAnchor', ['center', 'nw', 'n', 'ne', 'w', 'e', 'sw', 's', 'se']) - if not check[0]: - return check - check = functions.color('combobox1ArrowColor') - if not check[0]: - return check - check = functions.color('combobox1ButtonColor') - if not check[0]: - return check - check = functions.color('combobox1BorderColor') - if not check[0]: - return check - check = functions.color('combobox1FieldBackground') - if not check[0]: - return check - check = functions.color('combobox1TextColor') - if not check[0]: - return check - check = functions.fromArray('combobox1Relief', ['flat', 'raised', 'sunken', 'groove', 'ridge']) - if not check[0]: - return check - check = functions.integer('combobox1BorderWidth') - if not check[0]: - return check - check = functions.integer('combobox1Padding') - if not check[0]: - return check - check = functions.color('combobox1ListBoxBackground') - if not check[0]: - return check - check = functions.color('combobox1ListBoxForeground') - if not check[0]: - return check - check = functions.color('combobox1ListBoxSelectBackground') - if not check[0]: - return check - check = functions.color('combobox1ListBoxSelectForeground') - if not check[0]: - return check - check = functions.fromArray('button1TextAnchor', ['center', 'nw', 'n', 'ne', 'w', 'e', 'sw', 's', 'se']) - if not check[0]: - return check - check = functions.color('button1Background') - if not check[0]: - return check - check = functions.color('button1Foreground') - if not check[0]: - return check - check = functions.integer('button1Padding') - if not check[0]: - return check - check = functions.integer('editingPresetSaveButtonWidth') - if not check[0]: - return check - check = functions.integer('editingPresetCancelButtonWidth') - if not check[0]: - return check - check = functions.integer('loadingButtonWidth') - if not check[0]: - return check - check = functions.integer('loadingListWidth') - if not check[0]: - return check - check = functions.integer('generateFilesLabelWidth') - if not check[0]: - return check - check = functions.fromArray('generateFilesLabelAnchor', ['center', 'nw', 'n', 'ne', 'w', 'e', 'sw', 's', 'se']) - if not check[0]: - return check - check = functions.color('spinbox1ArrowColor') - if not check[0]: - return check - check = functions.color('spinbox1FieldBackground') - if not check[0]: - return check - check = functions.fromArray('spinbox1Relief', ['flat', 'raised', 'sunken', 'groove', 'ridge']) - if not check[0]: - return check - check = functions.integer('spinbox1BorderWidth') - if not check[0]: - return check - check = functions.color('spinbox1TextColor') - if not check[0]: - return check - check = functions.color('spinbox1ButtonColor') - if not check[0]: - return check - check = functions.color('radiobutton1Background') - if not check[0]: - return check - check = functions.color('radiobutton1TextColor') - if not check[0]: - return check - check = functions.color('entry1FieldBackground') - if not check[0]: - return check - check = functions.fromArray('entry1Relief', ['flat', 'raised', 'sunken', 'groove', 'ridge']) - if not check[0]: - return check - check = functions.integer('entry1BorderWidth') - if not check[0]: - return check - check = functions.integer('entry1Padding') - if not check[0]: - return check - check = functions.color('text1Background') - if not check[0]: - return check - check = functions.color('text1TextColor') - if not check[0]: - return check - check = functions.fromArray('text1Relief', ['flat', 'raised', 'sunken', 'groove', 'ridge']) - if not check[0]: - return check - check = functions.color('entry1TextColor') - if not check[0]: - return check - check = functions.color('label2BG') - if not check[0]: - return check - check = functions.color('label2TextColor') - if not check[0]: - return check - check = functions.color('label3BG') - if not check[0]: - return check - check = functions.color('label3TextColor') - if not check[0]: - return check - check = functions.fromArray('label3Anchor', ['center', 'nw', 'n', 'ne', 'w', 'e', 'sw', 's', 'se']) - if not check[0]: - return check - check = functions.color('radiobutton1IndicatorBackground') - if not check[0]: - return check - check = functions.integer('loadingListPadX') - if not check[0]: - return check - check = functions.integer('EPOSTypeStudentRadiobuttonPadY') - if not check[0]: - return check - check = functions.integer('EPOSTypeStudentRadiobuttonWidth') - if not check[0]: - return check - check = functions.integer('EPOSTypeTeacherRadiobuttonWidth') - if not check[0]: - return check - check = functions.integer('EPOSTypeTeacherRadiobuttonPadY') - if not check[0]: - return check - check = functions.integer('EPOSPersonSeparatorEntryWidth') - if not check[0]: - return check - check = functions.integer('EPOSRowSeparatorEntryWidth') - if not check[0]: - return check - check = functions.integer('EPOSDataSeparatorTextWidth') - if not check[0]: - return check - check = functions.integer('EPOSDataSeparatorTextHeight') - if not check[0]: - return check - check = functions.integer('EPDataLocalizationPadX') - if not check[0]: - return check - check = functions.integer('EPDataLocalizationPadY') - if not check[0]: - return check - check = functions.color('label3BG') - if not check[0]: - return check - check = functions.color('label3TextColor') - if not check[0]: - return check - check = functions.font('label3Font') - if not check[0]: - return check - check = functions.integer('GIFSLocalizationEntryWidth') - if not check[0]: - return check - check = functions.integer('GIFFrameSeparators') - if not check[0]: - return check - check = functions.integer('generateInputFilesPlusMinusButtonsWidth') - if not check[0]: - return check - check = functions.integer('generateResetButtonWidth') - if not check[0]: - return check - check = functions.integer('generateInputFilesPadding') - if not check[0]: - return check - check = functions.integer('generateOutputFilesPadding') - if not check[0]: - return check - return [True, content] - def R(self): - self.__checkInstance() + def __checkIfRecordExist(self, content, record): + if record in list(content.keys()): + return [True] + else: + return [False, 'Brak danych - klucz: %s' % record] + + + # Funkcje sprawdzające poprawność rekordu + def __checkI(self, record, var): + try: + var = int(var) + except: + return (False, 'Niepoprawne dane - klucz: %s' % record) + return [True, var] + + def __checkB(self, record, var): + try: + var = int(var) + except: + return [False, 'Niepoprawne dane - klucz: %s' % record] + if var != 0 and var != 1: + return [False, 'Niepoprawne dane - klucz: %s' % record] + else: + if var == 0: + var = False + else: + var = True + return [True, var] + + def __checkC(self, record, var): + if len(var) != 7: + return [False, 'Niepoprawne dane - klucz: %s' % record] + else: + if var[0] != '#': + return [False, 'Niepoprawne dane - klucz: %s' % record] + return [True, var] + + def __checkP(self, record, var): + try: + check = open(var) + except: + return [False, 'Niepoprawne dane - klucz: %s' % record] + return [True, var] + + def __checkFA(self, record, var, array): + arrays = { + 'position' : ['nw', 'ne', 'en', 'es', 'se', 'sw', 'ws', 'wn'], + 'anchor' : ['center', 'nw', 'n', 'ne', 'w', 'e', 'sw', 's', 'se'], + 'relief' : ['flat', 'raised', 'sunken', 'groove', 'ridge'], + 'fill' : ['x', 'y', 'both'], + } + if var not in arrays[array]: + return [False, 'Niepoprawne dane - klucz: %s' % record] + return [True, var] + + def __checkF(self, record, var): + try: + check = int(var.split(';')[1]) + except: + return [False, 'Niepoprawne dane - klucz: %s' % record] + else: + var = (var.split(';')[0], int(var.split(';')[1])) + return [True, var] + + + + def R(self, record): + self.__checkIfFileExist() content = {} for x in CD.open((str(appdata) + '\Generator CSV\style.cfg'), 'r', 'utf-8').read().split('\n'): x = x.split(' = ') try: - content[x[0]] = (x[1]).strip('\r') + name = x[0].split('(')[0] + var = x[1] + type = x[0].split('(')[1].strip(')') + content[name] = [var.strip('\r'), type] except: continue - contentCheckingOutput = self.__checkContent(content) - if contentCheckingOutput[0]: - return contentCheckingOutput[1] + checkingOutput = self.__checkIfRecordExist(content, record) + if not checkingOutput[0]: + MSG('E0005', True, checkingOutput[1]) + var = content[record] + if var[1] == 'I': + # Integer + checkingOutput = self.__checkI(record, var[0]) + if checkingOutput[0]: + return checkingOutput[1] + else: + MSG('E0005', True, checkingOutput[1]) + elif var[1] == 'B': + # Boolean + checkingOutput = self.__checkB(record, var[0]) + if checkingOutput[0]: + return checkingOutput[1] + else: + MSG('E0005', True, checkingOutput[1]) + elif var[1] == 'C': + # Color + checkingOutput = self.__checkC(record, var[0]) + if checkingOutput[0]: + return checkingOutput[1] + else: + MSG('E0005', True, checkingOutput[1]) + elif var[1] == 'P': + # Path + checkingOutput = self.__checkP(record, var[0]) + if checkingOutput[0]: + return checkingOutput[1] + else: + MSG('E0005', True, checkingOutput[1]) + elif (var[1])[:2] == 'FA': + # From Array + checkingOutput = self.__checkFA(record, var[0], (var[1])[2:]) + if checkingOutput[0]: + return checkingOutput[1] + else: + MSG('E0005', True, checkingOutput[1]) + elif var[1] == 'F': + # Font + checkingOutput = self.__checkF(record, var[0]) + if checkingOutput[0]: + return checkingOutput[1] + else: + MSG('E0005', True, checkingOutput[1]) else: - MSG('E0005', True, contentCheckingOutput[1]) + MSG('E0005', True, 'Nie można rozpoznać typu klucza %s' % record) + + + GUI = GUI() -checkInstance = GUI.R() @@ -978,11 +767,11 @@ class mainWindow: def __init__(self, master): # Okno self.master = master - master.title('%s %s' % (VARS.programName, VARS.programVersion)) - master.geometry('%sx%s' % (str(GUI.R()['windowWidth']), str(GUI.R()['windowHeight']))) - master.resizable(width = GUI.R()['windowWidthResizable'], height = GUI.R()['windowHeightResizable']) - master.configure(bg = GUI.R()['windowMainBG']) - master.iconbitmap(GUI.R()['mainIcon']) + master.title('%s %s' % (VAR.programName, VAR.programVersion)) + master.geometry('%ix%i' % (GUI.R('windowWidth'), GUI.R('windowHeight'))) + master.resizable(width = GUI.R('windowWidthResizable'), height = GUI.R('windowHeightResizable')) + master.configure(bg = GUI.R('windowMainBG')) + master.iconbitmap(GUI.R('mainIcon')) @@ -991,116 +780,117 @@ class mainWindow: TKttk.Style().theme_create("main", parent = "default", settings = { "mainMenu.TNotebook": { "configure": { - "background": GUI.R()['mainMenuBG'], - "tabposition": GUI.R()['mainMenuPosition'], - "borderwidth": GUI.R()['tabFramesBorderWidth'], + "background": GUI.R('mainMenuBG'), + "tabposition": GUI.R('mainMenuPosition'), + "borderwidth": GUI.R('tabFramesBorderWidth'), }, }, "mainMenu.TNotebook.Tab": { "configure": { - "background": GUI.R()['unselectedTabBG'], - "borderwidth": GUI.R()['menuTabsBorderWidth'], - "padding": GUI.R()['menuTabsPadding'], + "background": GUI.R('unselectedTabBG'), + "borderwidth": GUI.R('menuTabsBorderWidth'), + "padding": GUI.R('menuTabsPadding'), }, "map": { "background": [ - ("selected", GUI.R()['selectedTabBG']), - ("disabled", GUI.R()['disabledTabBG']), + ("selected", GUI.R('selectedTabBG')), + ("disabled", GUI.R('disabledTabBG')), ] } }, "mainMenuTabFrame.TFrame": { "configure": { - "background": GUI.R()['tabFrameBG'], + "background": GUI.R('tabFrameBG'), }, }, "tabHeader.TLabel": { "configure": { - "font": GUI.R()['headerFont'], - "background": GUI.R()['headerBG'], - "foreground": GUI.R()['headerTextColor'], - "padding": GUI.R()['headerPadding'], - "anchor": GUI.R()['headerTextAnchor'], + "font": GUI.R('headerFont'), + "background": GUI.R('headerBG'), + "foreground": GUI.R('headerTextColor'), + "padding": GUI.R('headerPadding'), + "anchor": GUI.R('headerTextAnchor'), }, }, - "tabFrame.TFrame": { + "contentTabFrame.TFrame": { "configure": { - "background": GUI.R()['tabFrameBG'], + "background": GUI.R('contentTabFrameBG'), }, }, "layoutFrame.TFrame": { "configure": { - "background": GUI.R()['tabFrameBG'], + "background": GUI.R('layoutFrameBG'), }, }, "label1.TLabel": { "configure": { - "background": GUI.R()['label1BG'], - "foreground": GUI.R()['label1TextColor'], - "font": ('Segoe UI', 10) + "background": GUI.R('label1BG'), + "foreground": GUI.R('label1TextColor'), + "font": GUI.R('label1Font'), }, }, "label2.TLabel": { "configure": { - "background": GUI.R()['label2BG'], - "foreground": GUI.R()['label2TextColor'], + "background": GUI.R('label2BG'), + "foreground": GUI.R('label2TextColor'), + "font" : GUI.R('label2Font') }, }, "combobox1.TCombobox": { "configure": { - "arrowcolor": GUI.R()['combobox1ArrowColor'], - "background": GUI.R()['combobox1ButtonColor'], - "bordercolor": GUI.R()['combobox1BorderColor'], - "fieldbackground": GUI.R()['combobox1FieldBackground'], - "foreground": GUI.R()['combobox1TextColor'], - "relief": GUI.R()['combobox1Relief'], - "borderwidth": GUI.R()['combobox1BorderWidth'], - "padding": GUI.R()['combobox1Padding'], + "arrowcolor": GUI.R('combobox1ArrowColor'), + "background": GUI.R('combobox1ButtonColor'), + "bordercolor": GUI.R('combobox1BorderColor'), + "fieldbackground": GUI.R('combobox1FieldBackground'), + "foreground": GUI.R('combobox1TextColor'), + "relief": GUI.R('combobox1Relief'), + "borderwidth": GUI.R('combobox1BorderWidth'), + "padding": GUI.R('combobox1Padding'), }, }, "combobox2.TCombobox": { "configure": { - "arrowcolor": GUI.R()['combobox2ArrowColor'], - "background": GUI.R()['combobox2ButtonColor'], - "bordercolor": GUI.R()['combobox2BorderColor'], - "fieldbackground": GUI.R()['combobox2FieldBackground'], - "foreground": GUI.R()['combobox2TextColor'], - "relief": GUI.R()['combobox2Relief'], - "borderwidth": GUI.R()['combobox2BorderWidth'], - "padding": GUI.R()['combobox2Padding'], + "arrowcolor": GUI.R('combobox2ArrowColor'), + "background": GUI.R('combobox2ButtonColor'), + "bordercolor": GUI.R('combobox2BorderColor'), + "fieldbackground": GUI.R('combobox2FieldBackground'), + "foreground": GUI.R('combobox2TextColor'), + "relief": GUI.R('combobox2Relief'), + "borderwidth": GUI.R('combobox2BorderWidth'), + "padding": GUI.R('combobox2Padding'), }, }, "button1.TButton": { "configure": { - "anchor": GUI.R()['button1TextAnchor'], - "background": GUI.R()['button1Background'], - "foreground": GUI.R()['button1Foreground'], - "padding": GUI.R()['button1Padding'], + "anchor": GUI.R('button1TextAnchor'), + "background": GUI.R('button1Background'), + "foreground": GUI.R('button1Foreground'), + "padding": GUI.R('button1Padding'), }, }, "separator1.TSeparator": { "configure": { - "background": GUI.R()['tabFrameBG'], + "background": GUI.R('separator1BG'), }, }, "spinbox1.TSpinbox": { "configure": { - "arrowcolor": GUI.R()['spinbox1ArrowColor'], - "fieldbackground": GUI.R()['spinbox1FieldBackground'], - "relief": GUI.R()['spinbox1Relief'], - "borderwidth": GUI.R()['spinbox1BorderWidth'], - "foreground": GUI.R()['spinbox1TextColor'], - "background": GUI.R()['spinbox1ButtonColor'], - "padding" : GUI.R()['spinbox1Padding'], + "arrowcolor": GUI.R('spinbox1ArrowColor'), + "fieldbackground": GUI.R('spinbox1FieldBackground'), + "relief": GUI.R('spinbox1Relief'), + "borderwidth": GUI.R('spinbox1BorderWidth'), + "foreground": GUI.R('spinbox1TextColor'), + "background": GUI.R('spinbox1ButtonColor'), + "padding" : GUI.R('spinbox1Padding'), }, }, "entry1.TEntry": { "configure": { - "fieldbackground": GUI.R()['entry1FieldBackground'], - "relief": GUI.R()['entry1Relief'], - "borderwidth": GUI.R()['entry1BorderWidth'], - "padding": GUI.R()['entry1Padding'], - "foreground": GUI.R()['entry1TextColor'], + "fieldbackground": GUI.R('entry1FieldBackground'), + "relief": GUI.R('entry1Relief'), + "borderwidth": GUI.R('entry1BorderWidth'), + "padding": GUI.R('entry1Padding'), + "foreground": GUI.R('entry1TextColor'), }, }, }) @@ -1110,14 +900,14 @@ class mainWindow: # Menu główne - self.mainMenu = TKttk.Notebook(master, width = master.winfo_width() - (2 * GUI.R()['menuTabsPadding'] + GUI.R()['tabIconsSize']), height = master.winfo_height()) + self.mainMenu = TKttk.Notebook(master, width = master.winfo_width() - (2 * GUI.R('menuTabsPadding') + GUI.R('tabIconsSize')), height = master.winfo_height()) self.mainMenu.config(style = "mainMenu.TNotebook") self.mainMenu.grid(row = 0) # Ikona self.iconTab = TKttk.Frame(self.mainMenu) - self.iconTabImg = PLimg.open(GUI.R()['mainIcon']) - self.iconTabImg = self.iconTabImg.resize((GUI.R()['tabIconsSize'], GUI.R()['tabIconsSize']), PLimg.ANTIALIAS) + self.iconTabImg = PLimg.open(GUI.R('iconTabIcon')) + self.iconTabImg = self.iconTabImg.resize((GUI.R('tabIconsSize'), GUI.R('tabIconsSize')), PLimg.ANTIALIAS) self.iconTabImg = PLitk.PhotoImage(self.iconTabImg) self.mainMenu.add(self.iconTab, image = self.iconTabImg, state = TK.DISABLED) @@ -1128,8 +918,8 @@ class mainWindow: self.generateTab = TKttk.Frame(self.mainMenu) self.generateTab.config(style = "mainMenuTabFrame.TFrame") - self.generateTabImg = PLimg.open(GUI.R()['generateTabIcon']) - self.generateTabImg = self.generateTabImg.resize((GUI.R()['tabIconsSize'], GUI.R()['tabIconsSize']), PLimg.ANTIALIAS) + self.generateTabImg = PLimg.open(GUI.R('generateTabIcon')) + self.generateTabImg = self.generateTabImg.resize((GUI.R('tabIconsSize'), GUI.R('tabIconsSize')), PLimg.ANTIALIAS) self.generateTabImg = PLitk.PhotoImage(self.generateTabImg) self.mainMenu.add(self.generateTab, image = self.generateTabImg, state = TK.NORMAL) @@ -1138,13 +928,13 @@ class mainWindow: self.generateHeader = TKttk.Label(self.generateTab) self.generateHeader.config(style = 'tabHeader.TLabel') self.generateHeader.config(text = 'GENERATOR CSV') - self.generateHeader.pack(fill = TK.X) + self.generateHeader.pack(fill = GUI.R('headerFill')) # Zawartość self.generateFrame = TKttk.Frame(self.generateTab) - self.generateFrame.config(style = 'tabFrame.TFrame') - self.generateFrame.pack(fill = TK.BOTH, expand = 1, padx = GUI.R()['tabFramePadding'], pady = GUI.R()['tabFramePadding']) + self.generateFrame.config(style = 'contentTabFrame.TFrame') + self.generateFrame.pack(fill = GUI.R('contentTabFrameFill'), expand = GUI.R('contentTabFrameExpand'), padx = GUI.R('tabFramePadding'), pady = GUI.R('tabFramePadding')) # (1) Pliki ################################################# @@ -1157,20 +947,20 @@ class mainWindow: self.generateInputFilesFrame = TKttk.Frame(self.generateFilesFrame) self.generateInputFilesFrame.config(style = 'layoutFrame.TFrame') - self.generateInputFilesFrame.pack(fill = TK.BOTH, expand = 1, padx = 6) + self.generateInputFilesFrame.pack(fill = TK.BOTH, expand = 1, padx = GUI.R('outsidelayoutFramesPadX')) # (3) Plik źródłowy 1 ################### self.GIF1Frame = TKttk.Frame(self.generateInputFilesFrame) self.GIF1Frame.config(style = 'layoutFrame.TFrame') - self.GIF1Frame.pack(fill = TK.X, expand = 1, pady = int((GUI.R()['GIFFrameSeparators'])/2)) + self.GIF1Frame.pack(fill = TK.X, expand = 1, pady = int((GUI.R('GIFFrameSeparators')/2))) # "Plik źródłowy (1)" self.GIF1Label = TKttk.Label(self.GIF1Frame) self.GIF1Label.config(style = 'label1.TLabel') - self.GIF1Label.config(width = GUI.R()['generateFilesLabelWidth']) - self.GIF1Label.config(anchor = GUI.R()['generateFilesLabelAnchor']) - self.GIF1Label.config(padding = ('0 0 %s 0' % str(2 * GUI.R()['generateInputFilesPadding']))) + self.GIF1Label.config(width = GUI.R('generateFilesLabelWidth')) + self.GIF1Label.config(anchor = GUI.R('generateFilesLabelAnchor')) + self.GIF1Label.config(padding = ('0 0 %s 0' % str(2 * GUI.R('generateInputFilesPadding')))) self.GIF1Label.config(text = 'Plik źródłowy (1)') self.GIF1Label.pack(side = TK.LEFT) @@ -1182,45 +972,45 @@ class mainWindow: # Lokalizacja self.GIF1SLocalizationFrame = TKttk.Frame(self.GIF1SFrame) self.GIF1SLocalizationFrame.config(style = 'layoutFrame.TFrame') - self.GIF1SLocalizationFrame.pack(side = TK.TOP, fill = TK.X, expand = 1, pady = GUI.R()['generateInputFilesPadding']) + self.GIF1SLocalizationFrame.pack(side = TK.TOP, fill = TK.X, expand = 1, pady = GUI.R('generateInputFilesPadding')) # Lokalizacja - Entry self.GIF1SLocalizationEntryVar = TK.StringVar() self.GIF1SLocalizationEntry = TKttk.Entry(self.GIF1SLocalizationFrame) self.GIF1SLocalizationEntry.config(style = 'entry1.TEntry') self.GIF1SLocalizationEntry.config(textvariable = self.GIF1SLocalizationEntryVar) - self.GIF1SLocalizationEntry.pack(side = TK.LEFT, expand = 1, fill = TK.X, padx = GUI.R()['generateInputFilesPadding']) + self.GIF1SLocalizationEntry.pack(side = TK.LEFT, expand = 1, fill = TK.X, padx = GUI.R('generateInputFilesPadding')) # Lokalizacja - Button self.GIF1SLocalizationButton = TKttk.Button(self.GIF1SLocalizationFrame) self.GIF1SLocalizationButton.config(style = 'button1.TButton') self.GIF1SLocalizationButton.config(text = 'Przeglądaj') self.GIF1SLocalizationButton.config(command = self.GIF1SLocalizationButtonAction) - self.GIF1SLocalizationButton.pack(side = TK.RIGHT, padx = GUI.R()['generateInputFilesPadding']) + self.GIF1SLocalizationButton.pack(side = TK.RIGHT, padx = GUI.R('generateInputFilesPadding')) # Format self.GIF1SFormatFrame = TKttk.Frame(self.GIF1SFrame) self.GIF1SFormatFrame.config(style = 'layoutFrame.TFrame') - self.GIF1SFormatFrame.pack(side = TK.BOTTOM, fill = TK.X, expand = 1, pady = GUI.R()['generateInputFilesPadding']) + self.GIF1SFormatFrame.pack(side = TK.BOTTOM, fill = TK.X, expand = 1, pady = GUI.R('generateInputFilesPadding')) # Format - Label self.GIF1SFormatLabel = TKttk.Label(self.GIF1SFormatFrame) self.GIF1SFormatLabel.config(style = 'label2.TLabel') self.GIF1SFormatLabel.config(text = 'Format') - self.GIF1SFormatLabel.pack(side = TK.LEFT, padx = GUI.R()['generateInputFilesPadding']) + self.GIF1SFormatLabel.pack(side = TK.LEFT, padx = GUI.R('generateInputFilesPadding')) # Format - Combobox self.GIF1SFormatComboboxVar = TK.StringVar() self.GIF1SFormatCombobox = TKttk.Combobox(self.GIF1SFormatFrame) self.GIF1SFormatCombobox.config(style = 'combobox1.TCombobox') - self.GIF1SFormatCombobox.option_add("*TCombobox*Listbox.background", GUI.R()['combobox1ListBoxBackground']) - self.GIF1SFormatCombobox.option_add("*TCombobox*Listbox.foreground", GUI.R()['combobox1ListBoxForeground']) - self.GIF1SFormatCombobox.option_add("*TCombobox*Listbox.selectBackground", GUI.R()['combobox1ListBoxSelectBackground']) - self.GIF1SFormatCombobox.option_add("*TCombobox*Listbox.selectForeground", GUI.R()['combobox1ListBoxSelectForeground']) + self.GIF1SFormatCombobox.option_add("*TCombobox*Listbox.background", GUI.R('combobox1ListBoxBackground')) + self.GIF1SFormatCombobox.option_add("*TCombobox*Listbox.foreground", GUI.R('combobox1ListBoxForeground')) + self.GIF1SFormatCombobox.option_add("*TCombobox*Listbox.selectBackground", GUI.R('combobox1ListBoxSelectBackground')) + self.GIF1SFormatCombobox.option_add("*TCombobox*Listbox.selectForeground", GUI.R('combobox1ListBoxSelectForeground')) self.GIF1SFormatCombobox.config(state = 'readonly') self.GIF1SFormatCombobox.config(textvariable = self.GIF1SFormatComboboxVar) self.GIF1SFormatCombobox['values'] = tuple(FMT.getList()) - self.GIF1SFormatCombobox.pack(side = TK.LEFT, expand = 1, fill = TK.X, padx = GUI.R()['generateInputFilesPadding']) + self.GIF1SFormatCombobox.pack(side = TK.LEFT, expand = 1, fill = TK.X, padx = GUI.R('generateInputFilesPadding')) ######################################### @@ -1228,15 +1018,15 @@ class mainWindow: self.GIF2Frame = TKttk.Frame(self.generateInputFilesFrame) self.GIF2Frame.config(style = 'layoutFrame.TFrame') - self.GIF2Frame.pack(fill = TK.X, expand = 1, pady = int((GUI.R()['GIFFrameSeparators'])/2)) + self.GIF2Frame.pack(fill = TK.X, expand = 1, pady = int((GUI.R('GIFFrameSeparators')/2))) - # "Plik źródłowy (2)" + # "Plik źródłowy (1)" self.GIF2Label = TKttk.Label(self.GIF2Frame) self.GIF2Label.config(style = 'label1.TLabel') - self.GIF2Label.config(width = GUI.R()['generateFilesLabelWidth']) - self.GIF2Label.config(anchor = GUI.R()['generateFilesLabelAnchor']) - self.GIF2Label.config(padding = ('0 0 %s 0' % str(2 * GUI.R()['generateInputFilesPadding']))) - self.GIF2Label.config(text = 'Plik źródłowy (2)') + self.GIF2Label.config(width = GUI.R('generateFilesLabelWidth')) + self.GIF2Label.config(anchor = GUI.R('generateFilesLabelAnchor')) + self.GIF2Label.config(padding = ('0 0 %s 0' % str(2 * GUI.R('generateInputFilesPadding')))) + self.GIF2Label.config(text = 'Plik źródłowy (1)') self.GIF2Label.pack(side = TK.LEFT) # Plik żródłowy (1) - Ustawienia @@ -1247,45 +1037,45 @@ class mainWindow: # Lokalizacja self.GIF2SLocalizationFrame = TKttk.Frame(self.GIF2SFrame) self.GIF2SLocalizationFrame.config(style = 'layoutFrame.TFrame') - self.GIF2SLocalizationFrame.pack(side = TK.TOP, fill = TK.X, expand = 1, pady = GUI.R()['generateInputFilesPadding']) + self.GIF2SLocalizationFrame.pack(side = TK.TOP, fill = TK.X, expand = 1, pady = GUI.R('generateInputFilesPadding')) # Lokalizacja - Entry self.GIF2SLocalizationEntryVar = TK.StringVar() self.GIF2SLocalizationEntry = TKttk.Entry(self.GIF2SLocalizationFrame) self.GIF2SLocalizationEntry.config(style = 'entry1.TEntry') self.GIF2SLocalizationEntry.config(textvariable = self.GIF2SLocalizationEntryVar) - self.GIF2SLocalizationEntry.pack(side = TK.LEFT, expand = 1, fill = TK.X, padx = GUI.R()['generateInputFilesPadding']) + self.GIF2SLocalizationEntry.pack(side = TK.LEFT, expand = 1, fill = TK.X, padx = GUI.R('generateInputFilesPadding')) # Lokalizacja - Button self.GIF2SLocalizationButton = TKttk.Button(self.GIF2SLocalizationFrame) self.GIF2SLocalizationButton.config(style = 'button1.TButton') self.GIF2SLocalizationButton.config(text = 'Przeglądaj') self.GIF2SLocalizationButton.config(command = self.GIF2SLocalizationButtonAction) - self.GIF2SLocalizationButton.pack(side = TK.RIGHT, padx = GUI.R()['generateInputFilesPadding']) + self.GIF2SLocalizationButton.pack(side = TK.RIGHT, padx = GUI.R('generateInputFilesPadding')) # Format self.GIF2SFormatFrame = TKttk.Frame(self.GIF2SFrame) self.GIF2SFormatFrame.config(style = 'layoutFrame.TFrame') - self.GIF2SFormatFrame.pack(side = TK.BOTTOM, fill = TK.X, expand = 1, pady = GUI.R()['generateInputFilesPadding']) + self.GIF2SFormatFrame.pack(side = TK.BOTTOM, fill = TK.X, expand = 1, pady = GUI.R('generateInputFilesPadding')) # Format - Label self.GIF2SFormatLabel = TKttk.Label(self.GIF2SFormatFrame) self.GIF2SFormatLabel.config(style = 'label2.TLabel') self.GIF2SFormatLabel.config(text = 'Format') - self.GIF2SFormatLabel.pack(side = TK.LEFT, padx = GUI.R()['generateInputFilesPadding']) + self.GIF2SFormatLabel.pack(side = TK.LEFT, padx = GUI.R('generateInputFilesPadding')) # Format - Combobox self.GIF2SFormatComboboxVar = TK.StringVar() self.GIF2SFormatCombobox = TKttk.Combobox(self.GIF2SFormatFrame) self.GIF2SFormatCombobox.config(style = 'combobox1.TCombobox') - self.GIF2SFormatCombobox.option_add("*TCombobox*Listbox.background", GUI.R()['combobox1ListBoxBackground']) - self.GIF2SFormatCombobox.option_add("*TCombobox*Listbox.foreground", GUI.R()['combobox1ListBoxForeground']) - self.GIF2SFormatCombobox.option_add("*TCombobox*Listbox.selectBackground", GUI.R()['combobox1ListBoxSelectBackground']) - self.GIF2SFormatCombobox.option_add("*TCombobox*Listbox.selectForeground", GUI.R()['combobox1ListBoxSelectForeground']) + self.GIF2SFormatCombobox.option_add("*TCombobox*Listbox.background", GUI.R('combobox1ListBoxBackground')) + self.GIF2SFormatCombobox.option_add("*TCombobox*Listbox.foreground", GUI.R('combobox1ListBoxForeground')) + self.GIF2SFormatCombobox.option_add("*TCombobox*Listbox.selectBackground", GUI.R('combobox1ListBoxSelectBackground')) + self.GIF2SFormatCombobox.option_add("*TCombobox*Listbox.selectForeground", GUI.R('combobox1ListBoxSelectForeground')) self.GIF2SFormatCombobox.config(state = 'readonly') self.GIF2SFormatCombobox.config(textvariable = self.GIF2SFormatComboboxVar) self.GIF2SFormatCombobox['values'] = tuple(FMT.getList()) - self.GIF2SFormatCombobox.pack(side = TK.LEFT, expand = 1, fill = TK.X, padx = GUI.R()['generateInputFilesPadding']) + self.GIF2SFormatCombobox.pack(side = TK.LEFT, expand = 1, fill = TK.X, padx = GUI.R('generateInputFilesPadding')) ######################################### @@ -1293,15 +1083,15 @@ class mainWindow: self.GIF3Frame = TKttk.Frame(self.generateInputFilesFrame) self.GIF3Frame.config(style = 'layoutFrame.TFrame') - self.GIF3Frame.pack(fill = TK.X, expand = 1, pady = int((GUI.R()['GIFFrameSeparators'])/2)) + self.GIF3Frame.pack(fill = TK.X, expand = 1, pady = int((GUI.R('GIFFrameSeparators')/2))) - # "Plik źródłowy (3)" + # "Plik źródłowy (1)" self.GIF3Label = TKttk.Label(self.GIF3Frame) self.GIF3Label.config(style = 'label1.TLabel') - self.GIF3Label.config(width = GUI.R()['generateFilesLabelWidth']) - self.GIF3Label.config(anchor = GUI.R()['generateFilesLabelAnchor']) - self.GIF3Label.config(padding = ('0 0 %s 0' % str(2 * GUI.R()['generateInputFilesPadding']))) - self.GIF3Label.config(text = 'Plik źródłowy (3)') + self.GIF3Label.config(width = GUI.R('generateFilesLabelWidth')) + self.GIF3Label.config(anchor = GUI.R('generateFilesLabelAnchor')) + self.GIF3Label.config(padding = ('0 0 %s 0' % str(2 * GUI.R('generateInputFilesPadding')))) + self.GIF3Label.config(text = 'Plik źródłowy (1)') self.GIF3Label.pack(side = TK.LEFT) # Plik żródłowy (1) - Ustawienia @@ -1312,45 +1102,45 @@ class mainWindow: # Lokalizacja self.GIF3SLocalizationFrame = TKttk.Frame(self.GIF3SFrame) self.GIF3SLocalizationFrame.config(style = 'layoutFrame.TFrame') - self.GIF3SLocalizationFrame.pack(side = TK.TOP, fill = TK.X, expand = 1, pady = GUI.R()['generateInputFilesPadding']) + self.GIF3SLocalizationFrame.pack(side = TK.TOP, fill = TK.X, expand = 1, pady = GUI.R('generateInputFilesPadding')) # Lokalizacja - Entry self.GIF3SLocalizationEntryVar = TK.StringVar() self.GIF3SLocalizationEntry = TKttk.Entry(self.GIF3SLocalizationFrame) self.GIF3SLocalizationEntry.config(style = 'entry1.TEntry') self.GIF3SLocalizationEntry.config(textvariable = self.GIF3SLocalizationEntryVar) - self.GIF3SLocalizationEntry.pack(side = TK.LEFT, expand = 1, fill = TK.X, padx = GUI.R()['generateInputFilesPadding']) + self.GIF3SLocalizationEntry.pack(side = TK.LEFT, expand = 1, fill = TK.X, padx = GUI.R('generateInputFilesPadding')) # Lokalizacja - Button self.GIF3SLocalizationButton = TKttk.Button(self.GIF3SLocalizationFrame) self.GIF3SLocalizationButton.config(style = 'button1.TButton') self.GIF3SLocalizationButton.config(text = 'Przeglądaj') self.GIF3SLocalizationButton.config(command = self.GIF3SLocalizationButtonAction) - self.GIF3SLocalizationButton.pack(side = TK.RIGHT, padx = GUI.R()['generateInputFilesPadding']) + self.GIF3SLocalizationButton.pack(side = TK.RIGHT, padx = GUI.R('generateInputFilesPadding')) # Format self.GIF3SFormatFrame = TKttk.Frame(self.GIF3SFrame) self.GIF3SFormatFrame.config(style = 'layoutFrame.TFrame') - self.GIF3SFormatFrame.pack(side = TK.BOTTOM, fill = TK.X, expand = 1, pady = GUI.R()['generateInputFilesPadding']) + self.GIF3SFormatFrame.pack(side = TK.BOTTOM, fill = TK.X, expand = 1, pady = GUI.R('generateInputFilesPadding')) # Format - Label self.GIF3SFormatLabel = TKttk.Label(self.GIF3SFormatFrame) self.GIF3SFormatLabel.config(style = 'label2.TLabel') self.GIF3SFormatLabel.config(text = 'Format') - self.GIF3SFormatLabel.pack(side = TK.LEFT, padx = GUI.R()['generateInputFilesPadding']) + self.GIF3SFormatLabel.pack(side = TK.LEFT, padx = GUI.R('generateInputFilesPadding')) # Format - Combobox self.GIF3SFormatComboboxVar = TK.StringVar() self.GIF3SFormatCombobox = TKttk.Combobox(self.GIF3SFormatFrame) self.GIF3SFormatCombobox.config(style = 'combobox1.TCombobox') - self.GIF3SFormatCombobox.option_add("*TCombobox*Listbox.background", GUI.R()['combobox1ListBoxBackground']) - self.GIF3SFormatCombobox.option_add("*TCombobox*Listbox.foreground", GUI.R()['combobox1ListBoxForeground']) - self.GIF3SFormatCombobox.option_add("*TCombobox*Listbox.selectBackground", GUI.R()['combobox1ListBoxSelectBackground']) - self.GIF3SFormatCombobox.option_add("*TCombobox*Listbox.selectForeground", GUI.R()['combobox1ListBoxSelectForeground']) + self.GIF3SFormatCombobox.option_add("*TCombobox*Listbox.background", GUI.R('combobox1ListBoxBackground')) + self.GIF3SFormatCombobox.option_add("*TCombobox*Listbox.foreground", GUI.R('combobox1ListBoxForeground')) + self.GIF3SFormatCombobox.option_add("*TCombobox*Listbox.selectBackground", GUI.R('combobox1ListBoxSelectBackground')) + self.GIF3SFormatCombobox.option_add("*TCombobox*Listbox.selectForeground", GUI.R('combobox1ListBoxSelectForeground')) self.GIF3SFormatCombobox.config(state = 'readonly') self.GIF3SFormatCombobox.config(textvariable = self.GIF3SFormatComboboxVar) self.GIF3SFormatCombobox['values'] = tuple(FMT.getList()) - self.GIF3SFormatCombobox.pack(side = TK.LEFT, expand = 1, fill = TK.X, padx = GUI.R()['generateInputFilesPadding']) + self.GIF3SFormatCombobox.pack(side = TK.LEFT, expand = 1, fill = TK.X, padx = GUI.R('generateInputFilesPadding')) ######################################### @@ -1358,15 +1148,15 @@ class mainWindow: self.GIF4Frame = TKttk.Frame(self.generateInputFilesFrame) self.GIF4Frame.config(style = 'layoutFrame.TFrame') - self.GIF4Frame.pack(fill = TK.X, expand = 1, pady = int((GUI.R()['GIFFrameSeparators'])/2)) + self.GIF4Frame.pack(fill = TK.X, expand = 1, pady = int((GUI.R('GIFFrameSeparators')/2))) - # "Plik źródłowy (4)" + # "Plik źródłowy (1)" self.GIF4Label = TKttk.Label(self.GIF4Frame) self.GIF4Label.config(style = 'label1.TLabel') - self.GIF4Label.config(width = GUI.R()['generateFilesLabelWidth']) - self.GIF4Label.config(anchor = GUI.R()['generateFilesLabelAnchor']) - self.GIF4Label.config(padding = ('0 0 %s 0' % str(2 * GUI.R()['generateInputFilesPadding']))) - self.GIF4Label.config(text = 'Plik źródłowy (4)') + self.GIF4Label.config(width = GUI.R('generateFilesLabelWidth')) + self.GIF4Label.config(anchor = GUI.R('generateFilesLabelAnchor')) + self.GIF4Label.config(padding = ('0 0 %s 0' % str(2 * GUI.R('generateInputFilesPadding')))) + self.GIF4Label.config(text = 'Plik źródłowy (1)') self.GIF4Label.pack(side = TK.LEFT) # Plik żródłowy (1) - Ustawienia @@ -1377,45 +1167,45 @@ class mainWindow: # Lokalizacja self.GIF4SLocalizationFrame = TKttk.Frame(self.GIF4SFrame) self.GIF4SLocalizationFrame.config(style = 'layoutFrame.TFrame') - self.GIF4SLocalizationFrame.pack(side = TK.TOP, fill = TK.X, expand = 1, pady = GUI.R()['generateInputFilesPadding']) + self.GIF4SLocalizationFrame.pack(side = TK.TOP, fill = TK.X, expand = 1, pady = GUI.R('generateInputFilesPadding')) # Lokalizacja - Entry self.GIF4SLocalizationEntryVar = TK.StringVar() self.GIF4SLocalizationEntry = TKttk.Entry(self.GIF4SLocalizationFrame) self.GIF4SLocalizationEntry.config(style = 'entry1.TEntry') self.GIF4SLocalizationEntry.config(textvariable = self.GIF4SLocalizationEntryVar) - self.GIF4SLocalizationEntry.pack(side = TK.LEFT, expand = 1, fill = TK.X, padx = GUI.R()['generateInputFilesPadding']) + self.GIF4SLocalizationEntry.pack(side = TK.LEFT, expand = 1, fill = TK.X, padx = GUI.R('generateInputFilesPadding')) # Lokalizacja - Button self.GIF4SLocalizationButton = TKttk.Button(self.GIF4SLocalizationFrame) self.GIF4SLocalizationButton.config(style = 'button1.TButton') self.GIF4SLocalizationButton.config(text = 'Przeglądaj') self.GIF4SLocalizationButton.config(command = self.GIF4SLocalizationButtonAction) - self.GIF4SLocalizationButton.pack(side = TK.RIGHT, padx = GUI.R()['generateInputFilesPadding']) + self.GIF4SLocalizationButton.pack(side = TK.RIGHT, padx = GUI.R('generateInputFilesPadding')) # Format self.GIF4SFormatFrame = TKttk.Frame(self.GIF4SFrame) self.GIF4SFormatFrame.config(style = 'layoutFrame.TFrame') - self.GIF4SFormatFrame.pack(side = TK.BOTTOM, fill = TK.X, expand = 1, pady = GUI.R()['generateInputFilesPadding']) + self.GIF4SFormatFrame.pack(side = TK.BOTTOM, fill = TK.X, expand = 1, pady = GUI.R('generateInputFilesPadding')) # Format - Label self.GIF4SFormatLabel = TKttk.Label(self.GIF4SFormatFrame) self.GIF4SFormatLabel.config(style = 'label2.TLabel') self.GIF4SFormatLabel.config(text = 'Format') - self.GIF4SFormatLabel.pack(side = TK.LEFT, padx = GUI.R()['generateInputFilesPadding']) + self.GIF4SFormatLabel.pack(side = TK.LEFT, padx = GUI.R('generateInputFilesPadding')) # Format - Combobox self.GIF4SFormatComboboxVar = TK.StringVar() self.GIF4SFormatCombobox = TKttk.Combobox(self.GIF4SFormatFrame) self.GIF4SFormatCombobox.config(style = 'combobox1.TCombobox') - self.GIF4SFormatCombobox.option_add("*TCombobox*Listbox.background", GUI.R()['combobox1ListBoxBackground']) - self.GIF4SFormatCombobox.option_add("*TCombobox*Listbox.foreground", GUI.R()['combobox1ListBoxForeground']) - self.GIF4SFormatCombobox.option_add("*TCombobox*Listbox.selectBackground", GUI.R()['combobox1ListBoxSelectBackground']) - self.GIF4SFormatCombobox.option_add("*TCombobox*Listbox.selectForeground", GUI.R()['combobox1ListBoxSelectForeground']) + self.GIF4SFormatCombobox.option_add("*TCombobox*Listbox.background", GUI.R('combobox1ListBoxBackground')) + self.GIF4SFormatCombobox.option_add("*TCombobox*Listbox.foreground", GUI.R('combobox1ListBoxForeground')) + self.GIF4SFormatCombobox.option_add("*TCombobox*Listbox.selectBackground", GUI.R('combobox1ListBoxSelectBackground')) + self.GIF4SFormatCombobox.option_add("*TCombobox*Listbox.selectForeground", GUI.R('combobox1ListBoxSelectForeground')) self.GIF4SFormatCombobox.config(state = 'readonly') self.GIF4SFormatCombobox.config(textvariable = self.GIF4SFormatComboboxVar) self.GIF4SFormatCombobox['values'] = tuple(FMT.getList()) - self.GIF4SFormatCombobox.pack(side = TK.LEFT, expand = 1, fill = TK.X, padx = GUI.R()['generateInputFilesPadding']) + self.GIF4SFormatCombobox.pack(side = TK.LEFT, expand = 1, fill = TK.X, padx = GUI.R('generateInputFilesPadding')) ######################################### @@ -1425,7 +1215,8 @@ class mainWindow: self.generateSeparator1 = TKttk.Separator(self.generateFilesFrame) self.generateSeparator1.config(style = 'separator1.TSeparator') - self.generateSeparator1.pack(fill = TK.X, pady = 10) + self.generateSeparator1.config(orient = TK.HORIZONTAL) + self.generateSeparator1.pack(fill = TK.X, pady = GUI.R('generateHorizontalSeparatorPadY')) ################################################### @@ -1433,19 +1224,19 @@ class mainWindow: self.generateOutputFilesFrame = TKttk.Frame(self.generateFilesFrame) self.generateOutputFilesFrame.config(style = 'layoutFrame.TFrame') - self.generateOutputFilesFrame.pack(fill = TK.X, pady = 10, padx = 12) + self.generateOutputFilesFrame.pack(fill = TK.X, padx = GUI.R('outsidelayoutFramesPadX')) # (3) Poczta ############################ self.GOFMailFrame = TKttk.Frame(self.generateOutputFilesFrame) self.GOFMailFrame.config(style = 'layoutFrame.TFrame') - self.GOFMailFrame.pack(pady = GUI.R()['generateOutputFilesPadding'], fill = TK.X, expand = 1) + self.GOFMailFrame.pack(pady = GUI.R('generateOutputFilesPadding'), fill = TK.X, expand = 1) # "Poczta" self.GOFMailLabel = TKttk.Label(self.GOFMailFrame) self.GOFMailLabel.config(style = 'label1.TLabel') - self.GOFMailLabel.config(width = GUI.R()['generateFilesLabelWidth']) - self.GOFMailLabel.config(anchor = GUI.R()['generateFilesLabelAnchor']) + self.GOFMailLabel.config(width = GUI.R('generateFilesLabelWidth')) + self.GOFMailLabel.config(anchor = GUI.R('generateFilesLabelAnchor')) self.GOFMailLabel.config(text = 'Poczta') self.GOFMailLabel.pack(side = TK.LEFT) @@ -1454,7 +1245,7 @@ class mainWindow: self.GOFMailEntry = TKttk.Entry(self.GOFMailFrame) self.GOFMailEntry.config(style = 'entry1.TEntry') self.GOFMailEntry.config(textvariable = self.GOFMailEntryVar) - self.GOFMailEntry.pack(padx = 2 * GUI.R()['generateOutputFilesPadding'], side = TK.LEFT, fill = TK.X, expand = 1) + self.GOFMailEntry.pack(padx = 2 * GUI.R('generateOutputFilesPadding'), side = TK.LEFT, fill = TK.X, expand = 1) # Plik poczty - Lokalizacja (Button) self.GOFMailButton = TKttk.Button(self.GOFMailFrame) @@ -1469,13 +1260,13 @@ class mainWindow: self.GOFOfficeFrame = TKttk.Frame(self.generateOutputFilesFrame) self.GOFOfficeFrame.config(style = 'layoutFrame.TFrame') - self.GOFOfficeFrame.pack(pady = GUI.R()['generateOutputFilesPadding'], fill = TK.X, expand = 1) + self.GOFOfficeFrame.pack(pady = GUI.R('generateOutputFilesPadding'), fill = TK.X, expand = 1) # "Office" self.GOFOfficeLabel = TKttk.Label(self.GOFOfficeFrame) self.GOFOfficeLabel.config(style = 'label1.TLabel') - self.GOFOfficeLabel.config(width = GUI.R()['generateFilesLabelWidth']) - self.GOFOfficeLabel.config(anchor = GUI.R()['generateFilesLabelAnchor']) + self.GOFOfficeLabel.config(width = GUI.R('generateFilesLabelWidth')) + self.GOFOfficeLabel.config(anchor = GUI.R('generateFilesLabelAnchor')) self.GOFOfficeLabel.config(text = 'Office') self.GOFOfficeLabel.pack(side = TK.LEFT) @@ -1484,7 +1275,7 @@ class mainWindow: self.GOFOfficeEntry = TKttk.Entry(self.GOFOfficeFrame) self.GOFOfficeEntry.config(style = 'entry1.TEntry') self.GOFOfficeEntry.config(textvariable = self.GOFOfficeEntryVar) - self.GOFOfficeEntry.pack(padx = 2 * GUI.R()['generateOutputFilesPadding'], side = TK.LEFT, fill = TK.X, expand = 1) + self.GOFOfficeEntry.pack(padx = 2 * GUI.R('generateOutputFilesPadding'), side = TK.LEFT, fill = TK.X, expand = 1) # Plik office - Lokalizacja (Button) self.GOFOfficeButton = TKttk.Button(self.GOFOfficeFrame) @@ -1503,7 +1294,8 @@ class mainWindow: self.generateSeparator2 = TKttk.Separator(self.generateFrame) self.generateSeparator2.config(style = 'separator1.TSeparator') - self.generateSeparator2.pack(fill = TK.X, pady = 10) + self.generateSeparator2.config(orient = TK.HORIZONTAL) + self.generateSeparator2.pack(fill = TK.X, pady = GUI.R('generateHorizontalSeparatorPadY')) ############################################################# @@ -1511,15 +1303,15 @@ class mainWindow: self.generateButtonsFrame = TKttk.Frame(self.generateFrame) self.generateButtonsFrame.config(style = 'layoutFrame.TFrame') - self.generateButtonsFrame.pack(fill = TK.X, pady = 10, padx = 12) + self.generateButtonsFrame.pack(fill = TK.X, padx = GUI.R('outsidelayoutFramesPadX')) # Przycisk "START" self.generateStartButton = TKttk.Button(self.generateButtonsFrame) self.generateStartButton.config(style = 'button1.TButton') - self.generateStartButton.config(padding = 10) + self.generateStartButton.config(padding = GUI.R('generateStartButtonPadding')) self.generateStartButton.config(text = 'START') self.generateStartButton.config(command = self.generateStartButtonAction) - self.generateStartButton.pack(side = TK.LEFT, fill = TK.X, expand = 1) + self.generateStartButton.pack(side = TK.LEFT, fill = TK.X, expand = 1, pady = GUI.R('generateStartButtonPadY')) ############################################################## @@ -1532,8 +1324,8 @@ class mainWindow: self.formatTab = TKttk.Frame(self.mainMenu) self.formatTab.config(style = "mainMenuTabFrame.TFrame") - self.formatTabImg = PLimg.open(GUI.R()['formatTabIcon']) - self.formatTabImg = self.formatTabImg.resize((GUI.R()['tabIconsSize'], GUI.R()['tabIconsSize']), PLimg.ANTIALIAS) + self.formatTabImg = PLimg.open(GUI.R('formatTabIcon')) + self.formatTabImg = self.formatTabImg.resize((GUI.R('tabIconsSize'), GUI.R('tabIconsSize')), PLimg.ANTIALIAS) self.formatTabImg = PLitk.PhotoImage(self.formatTabImg) self.mainMenu.add(self.formatTab, image = self.formatTabImg, state = TK.NORMAL) @@ -1542,20 +1334,20 @@ class mainWindow: self.formatHeader = TKttk.Label(self.formatTab) self.formatHeader.config(style = 'tabHeader.TLabel') self.formatHeader.config(text = 'FORMAT DANYCH') - self.formatHeader.pack(fill = TK.X) + self.formatHeader.pack(fill = GUI.R('headerFill')) # Zawartość self.formatFrame = TKttk.Frame(self.formatTab) - self.formatFrame.config(style = 'tabFrame.TFrame') - self.formatFrame.pack(fill = TK.BOTH, expand = 1, padx = GUI.R()['tabFramePadding'], pady = GUI.R()['tabFramePadding']) - + self.formatFrame.config(style = 'contentTabFrame.TFrame') + self.formatFrame.pack(fill = GUI.R('contentTabFrameFill'), expand = GUI.R('contentTabFrameExpand'), padx = GUI.R('tabFramePadding'), pady = GUI.R('tabFramePadding')) + # (1) Ładowanie presetu ##################################### self.loadingPresetFrame = TKttk.Frame(self.formatFrame) self.loadingPresetFrame.config(style = 'layoutFrame.TFrame') - self.loadingPresetFrame.pack(fill = TK.X, side = TK.TOP, pady = 5, padx = 10) + self.loadingPresetFrame.pack(fill = TK.X, side = TK.TOP, padx = GUI.R('outsidelayoutFramesPadX'))#, pady = 5, padx = 10) # "Wybierz preset do edycji lub wpisz nazwę nowego" self.loadingListLabel = TKttk.Label(self.loadingPresetFrame) @@ -1568,18 +1360,18 @@ class mainWindow: self.loadingList = TKttk.Combobox(self.loadingPresetFrame) self.loadingList.config(textvariable = self.loadingListVar) self.loadingList.config(style = 'combobox2.TCombobox') - self.loadingList.option_add("*TCombobox*Listbox.background", GUI.R()['combobox2ListBoxBackground']) - self.loadingList.option_add("*TCombobox*Listbox.foreground", GUI.R()['combobox2ListBoxForeground']) - self.loadingList.option_add("*TCombobox*Listbox.selectBackground", GUI.R()['combobox2ListBoxSelectBackground']) - self.loadingList.option_add("*TCombobox*Listbox.selectForeground", GUI.R()['combobox2ListBoxSelectForeground']) - self.loadingList.pack(side = TK.LEFT, padx = GUI.R()['loadingListPadX'], fill = TK.X, expand = 1) + self.loadingList.option_add("*TCombobox*Listbox.background", GUI.R('combobox2ListBoxBackground')) + self.loadingList.option_add("*TCombobox*Listbox.foreground", GUI.R('combobox2ListBoxForeground')) + self.loadingList.option_add("*TCombobox*Listbox.selectBackground", GUI.R('combobox2ListBoxSelectBackground')) + self.loadingList.option_add("*TCombobox*Listbox.selectForeground", GUI.R('combobox2ListBoxSelectForeground')) + self.loadingList.pack(side = TK.LEFT, padx = GUI.R('loadingListPadX'), fill = TK.X, expand = 1) self.loadingList['values'] = tuple(FMT.getList()) # Przycisk "WCZYTAJ" self.loadingButton = TKttk.Button(self.loadingPresetFrame) self.loadingButton.config(style = 'button1.TButton') self.loadingButton.config(command = self.loadingButtonAction) - self.loadingButton.config(width = GUI.R()['loadingButtonWidth']) + self.loadingButton.config(width = GUI.R('loadingButtonWidth')) self.loadingButton.config(text = 'WCZYTAJ') self.loadingButton.pack(side = TK.RIGHT) @@ -1590,7 +1382,7 @@ class mainWindow: self.formatSeparator1 = TKttk.Separator(self.formatFrame) self.formatSeparator1.config(style = 'separator1.TSeparator') self.formatSeparator1.config(orient = TK.HORIZONTAL) - self.formatSeparator1.pack(fill = TK.X, pady = 10) + self.formatSeparator1.pack(fill = TK.X, pady = GUI.R('formatHorizontalSeparatorPadY')) ############################################################# @@ -1598,8 +1390,8 @@ class mainWindow: self.editingPresetFrame = TKttk.Frame(self.formatFrame) self.editingPresetFrame.config(style = 'layoutFrame.TFrame') - self.editingPresetFrame.pack(fill = TK.BOTH, expand = 1, padx = 10) - + self.editingPresetFrame.pack(fill = TK.BOTH, expand = 1, padx = GUI.R('outsidelayoutFramesPadX')) + # (2) Ustawienia ################################## self.editingPresetSettingsFrame = TKttk.Frame(self.editingPresetFrame) @@ -1611,18 +1403,18 @@ class mainWindow: self.editingPresetOSFrame = TKttk.Frame(self.editingPresetSettingsFrame) self.editingPresetOSFrame.config(style = 'layoutFrame.TFrame') self.editingPresetOSFrame.pack(fill = TK.BOTH, expand = 1, side = TK.LEFT) - + # (5) Typ osoby ############### self.EPOSTypeFrame = TKttk.Frame(self.editingPresetOSFrame) self.EPOSTypeFrame.config(style = 'layoutFrame.TFrame') - self.EPOSTypeFrame.pack(fill = TK.X, expand = 1, pady = 5) + self.EPOSTypeFrame.pack(fill = TK.X, expand = 1, pady = GUI.R('EPOSTypeFramePadY')) # "Typ osoby" self.EPOSTypeLabel = TKttk.Label(self.EPOSTypeFrame) self.EPOSTypeLabel.config(style = 'label1.TLabel') - self.EPOSTypeLabel.config(width = GUI.R()['EPOSLabelWidth']) - self.EPOSTypeLabel.config(anchor = GUI.R()['EPOSLabelAnchor']) + self.EPOSTypeLabel.config(width = GUI.R('EPOSLabelWidth')) + self.EPOSTypeLabel.config(anchor = GUI.R('EPOSLabelAnchor')) self.EPOSTypeLabel.config(text = 'Typ osoby') self.EPOSTypeLabel.pack(side = TK.LEFT) @@ -1630,11 +1422,11 @@ class mainWindow: self.EPOSTypeVar = TK.BooleanVar(value = True) self.EPOSTypeStudentRadiobutton = TK.Radiobutton(self.EPOSTypeFrame) - self.EPOSTypeStudentRadiobutton.config(background = GUI.R()['radiobutton1Background']) - self.EPOSTypeStudentRadiobutton.config(foreground = GUI.R()['radiobutton1TextColor']) - self.EPOSTypeStudentRadiobutton.config(selectcolor = GUI.R()['radiobutton1IndicatorBackground']) - self.EPOSTypeStudentRadiobutton.config(activebackground = GUI.R()['radiobutton1Background']) - self.EPOSTypeStudentRadiobutton.config(activeforeground = GUI.R()['radiobutton1TextColor']) + self.EPOSTypeStudentRadiobutton.config(background = GUI.R('radiobutton1Background')) + self.EPOSTypeStudentRadiobutton.config(foreground = GUI.R('radiobutton1TextColor')) + self.EPOSTypeStudentRadiobutton.config(selectcolor = GUI.R('radiobutton1IndicatorBackground')) + self.EPOSTypeStudentRadiobutton.config(activebackground = GUI.R('radiobutton1Background')) + self.EPOSTypeStudentRadiobutton.config(activeforeground = GUI.R('radiobutton1TextColor')) self.EPOSTypeStudentRadiobutton.config(variable = self.EPOSTypeVar) self.EPOSTypeStudentRadiobutton.config(value = True) self.EPOSTypeStudentRadiobutton.config(state = TK.DISABLED) @@ -1642,11 +1434,11 @@ class mainWindow: self.EPOSTypeStudentRadiobutton.pack(side = TK.RIGHT, fill = TK.X, expand = 1) self.EPOSTypeTeacherRadiobutton = TK.Radiobutton(self.EPOSTypeFrame) - self.EPOSTypeTeacherRadiobutton.config(background = GUI.R()['radiobutton1Background']) - self.EPOSTypeTeacherRadiobutton.config(foreground = GUI.R()['radiobutton1TextColor']) - self.EPOSTypeTeacherRadiobutton.config(selectcolor = GUI.R()['radiobutton1IndicatorBackground']) - self.EPOSTypeTeacherRadiobutton.config(activebackground = GUI.R()['radiobutton1Background']) - self.EPOSTypeTeacherRadiobutton.config(activeforeground = GUI.R()['radiobutton1TextColor']) + self.EPOSTypeTeacherRadiobutton.config(background = GUI.R('radiobutton1Background')) + self.EPOSTypeTeacherRadiobutton.config(foreground = GUI.R('radiobutton1TextColor')) + self.EPOSTypeTeacherRadiobutton.config(selectcolor = GUI.R('radiobutton1IndicatorBackground')) + self.EPOSTypeTeacherRadiobutton.config(activebackground = GUI.R('radiobutton1Background')) + self.EPOSTypeTeacherRadiobutton.config(activeforeground = GUI.R('radiobutton1TextColor')) self.EPOSTypeTeacherRadiobutton.config(variable = self.EPOSTypeVar) self.EPOSTypeTeacherRadiobutton.config(value = False) self.EPOSTypeTeacherRadiobutton.config(state = TK.DISABLED) @@ -1659,13 +1451,13 @@ class mainWindow: self.EPOSPersonSeparatorFrame = TKttk.Frame(self.editingPresetOSFrame) self.EPOSPersonSeparatorFrame.config(style = 'layoutFrame.TFrame') - self.EPOSPersonSeparatorFrame.pack(fill = TK.X, expand = 1, pady = 5) + self.EPOSPersonSeparatorFrame.pack(fill = TK.X, expand = 1, pady = GUI.R('EPOSPersonSeparatorFramePadY')) # "Separator pomiędzy osobami" self.EPOSPersonSeparatorLabel = TKttk.Label(self.EPOSPersonSeparatorFrame) self.EPOSPersonSeparatorLabel.config(style = 'label1.TLabel') - self.EPOSPersonSeparatorLabel.config(width = GUI.R()['EPOSLabelWidth']) - self.EPOSPersonSeparatorLabel.config(anchor = GUI.R()['EPOSLabelAnchor']) + self.EPOSPersonSeparatorLabel.config(width = GUI.R('EPOSLabelWidth')) + self.EPOSPersonSeparatorLabel.config(anchor = GUI.R('EPOSLabelAnchor')) self.EPOSPersonSeparatorLabel.config(text = 'Separator pomiędzy osobami') self.EPOSPersonSeparatorLabel.pack(side = TK.LEFT) @@ -1675,7 +1467,6 @@ class mainWindow: self.EPOSPersonSeparatorEntry.config(style = 'entry1.TEntry') self.EPOSPersonSeparatorEntry.config(textvariable = self.EPOSPersonSeparatorVar) self.EPOSPersonSeparatorEntry.config(state = TK.DISABLED) - self.EPOSPersonSeparatorEntry.config(width = GUI.R()['EPOSPersonSeparatorEntryWidth']) self.EPOSPersonSeparatorEntry.pack(side = TK.RIGHT, fill = TK.X, expand = 1) ##################### @@ -1684,13 +1475,13 @@ class mainWindow: self.EPOSRowSeparatorFrame = TKttk.Frame(self.editingPresetOSFrame) self.EPOSRowSeparatorFrame.config(style = 'layoutFrame.TFrame') - self.EPOSRowSeparatorFrame.pack(fill = TK.X, expand = 1, pady = 5) + self.EPOSRowSeparatorFrame.pack(fill = TK.X, expand = 1, pady = GUI.R('EPOSRowSeparatorFramePadY')) # "Separator pomiędzy wierszami" self.EPOSRowSeparatorLabel = TKttk.Label(self.EPOSRowSeparatorFrame) self.EPOSRowSeparatorLabel.config(style = 'label1.TLabel') - self.EPOSRowSeparatorLabel.config(width = GUI.R()['EPOSLabelWidth']) - self.EPOSRowSeparatorLabel.config(anchor = GUI.R()['EPOSLabelAnchor']) + self.EPOSRowSeparatorLabel.config(width = GUI.R('EPOSLabelWidth')) + self.EPOSRowSeparatorLabel.config(anchor = GUI.R('EPOSLabelAnchor')) self.EPOSRowSeparatorLabel.config(text = 'Separator pomiędzy wierszami') self.EPOSRowSeparatorLabel.pack(side = TK.LEFT) @@ -1700,7 +1491,6 @@ class mainWindow: self.EPOSRowSeparatorEntry.config(style = 'entry1.TEntry') self.EPOSRowSeparatorEntry.config(textvariable = self.EPOSRowSeparatorVar) self.EPOSRowSeparatorEntry.config(state = TK.DISABLED) - self.EPOSRowSeparatorEntry.config(width = GUI.R()['EPOSRowSeparatorEntryWidth']) self.EPOSRowSeparatorEntry.pack(side = TK.RIGHT, fill = TK.X, expand = 1) ##################### @@ -1709,63 +1499,63 @@ class mainWindow: self.EPOSDataSeparatorFrame = TKttk.Frame(self.editingPresetOSFrame) self.EPOSDataSeparatorFrame.config(style = 'layoutFrame.TFrame') - self.EPOSDataSeparatorFrame.pack(fill = TK.BOTH, expand = 1, pady = 5) + self.EPOSDataSeparatorFrame.pack(fill = TK.BOTH, expand = 1, pady = GUI.R('EPOSDataSeparatorFramePadY')) # "Separatory pomiędzy danymi" self.EPOSDataSeparatorLabel = TKttk.Label(self.EPOSDataSeparatorFrame) self.EPOSDataSeparatorLabel.config(style = 'label1.TLabel') - self.EPOSDataSeparatorLabel.config(width = GUI.R()['EPOSLabelWidth']) - self.EPOSDataSeparatorLabel.config(anchor = GUI.R()['EPOSLabelAnchor']) + self.EPOSDataSeparatorLabel.config(width = GUI.R('EPOSLabelWidth')) + self.EPOSDataSeparatorLabel.config(anchor = GUI.R('EPOSLabelAnchor')) self.EPOSDataSeparatorLabel.config(text = 'Separatory pomiędzy danymi') self.EPOSDataSeparatorLabel.pack(side = TK.LEFT) # Entry - Separator pomiedzy wierszami self.EPOSDataSeparatorText = TK.Text(self.EPOSDataSeparatorFrame) self.EPOSDataSeparatorText.config(state = TK.DISABLED) - self.EPOSDataSeparatorText.config(background = GUI.R()['text1Background']) - self.EPOSDataSeparatorText.config(foreground = GUI.R()['text1TextColor']) - self.EPOSDataSeparatorText.config(relief = GUI.R()['text1Relief']) + self.EPOSDataSeparatorText.config(background = GUI.R('text1Background')) + self.EPOSDataSeparatorText.config(foreground = GUI.R('text1TextColor')) + self.EPOSDataSeparatorText.config(relief = GUI.R('text1Relief')) self.EPOSDataSeparatorText.pack(side = TK.TOP, fill = TK.BOTH) ##################### - + ############################### - # (4) Separator 2 ############# + # (5) Separator 2 ############# self.formatSeparator2 = TKttk.Separator(self.editingPresetSettingsFrame) self.formatSeparator2.config(style = 'separator1.TSeparator') self.formatSeparator2.config(orient = TK.VERTICAL) - self.formatSeparator2.pack(fill = TK.Y, padx = 12, expand = 1, side = TK.LEFT) + self.formatSeparator2.pack(fill = TK.Y, padx = GUI.R('formatVerticalSeparatorPadY'), side = TK.LEFT) ############################### - # (4) Lokalizacja danych ###### + # (5) Lokalizacja danych ###### self.editingPresetDLFrame = TKttk.Frame(self.editingPresetSettingsFrame) self.editingPresetDLFrame.config(style = 'layoutFrame.TFrame') self.editingPresetDLFrame.pack(fill = TK.BOTH, side = TK.RIGHT) self.editingPresetDLFrame.grid_columnconfigure(1, weight = 1) self.editingPresetDLFrame.grid_columnconfigure(2, weight = 1) - + # C1 - "Wiersz" self.EPDLC1Label = TKttk.Label(self.editingPresetDLFrame) self.EPDLC1Label.config(style = 'label1.TLabel') self.EPDLC1Label.config(text = 'Wiersz') - self.EPDLC1Label.grid(row = 0, column = 1, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) + self.EPDLC1Label.grid(row = 0, column = 1, padx = GUI.R('EPDataLocalizationPadX'), pady = GUI.R('EPDataLocalizationPadY')) # C2 - "Pozycja w wierszu" self.EPDLC2Label = TKttk.Label(self.editingPresetDLFrame) self.EPDLC2Label.config(style = 'label1.TLabel') self.EPDLC2Label.config(justify = TK.CENTER) self.EPDLC2Label.config(text = 'Pozycja\nw wierszu') - self.EPDLC2Label.grid(row = 0, column = 2, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) + self.EPDLC2Label.grid(row = 0, column = 2, padx = GUI.R('EPDataLocalizationPadX'), pady = GUI.R('EPDataLocalizationPadY')) # W1 - "Login" self.EPDLW1Label = TKttk.Label(self.editingPresetDLFrame) self.EPDLW1Label.config(style = 'label1.TLabel') self.EPDLW1Label.config(text = 'Login') - self.EPDLW1Label.grid(row = 1, column = 0, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) + self.EPDLW1Label.grid(row = 1, column = 0, padx = GUI.R('EPDataLocalizationPadX'), pady = GUI.R('EPDataLocalizationPadY')) # Lokalizacja loginu (wiersz) self.EPDLLoginRowVar = TK.IntVar() @@ -1775,7 +1565,7 @@ class mainWindow: self.EPDLLoginRowSpinbox.config(to = 1000000) self.EPDLLoginRowSpinbox.config(state = TK.DISABLED) self.EPDLLoginRowSpinbox.config(style = 'spinbox1.TSpinbox') - self.EPDLLoginRowSpinbox.grid(row = 1, column = 1, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) + self.EPDLLoginRowSpinbox.grid(row = 1, column = 1, padx = GUI.R('EPDataLocalizationPadX'), pady = GUI.R('EPDataLocalizationPadY')) # Lokalizacja loginu (pozycja w wierszu) self.EPDLLoginPosInRowVar = TK.IntVar() @@ -1785,13 +1575,13 @@ class mainWindow: self.EPDLLoginPosInRowSpinbox.config(to = 1000000) self.EPDLLoginPosInRowSpinbox.config(state = TK.DISABLED) self.EPDLLoginPosInRowSpinbox.config(style = 'spinbox1.TSpinbox') - self.EPDLLoginPosInRowSpinbox.grid(row = 1, column = 2, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) + self.EPDLLoginPosInRowSpinbox.grid(row = 1, column = 2, padx = GUI.R('EPDataLocalizationPadX'), pady = GUI.R('EPDataLocalizationPadY')) # W2 - "Imię" self.EPDLW2Label = TKttk.Label(self.editingPresetDLFrame) self.EPDLW2Label.config(style = 'label1.TLabel') self.EPDLW2Label.config(text = 'Imię') - self.EPDLW2Label.grid(row = 2, column = 0, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) + self.EPDLW2Label.grid(row = 2, column = 0, padx = GUI.R('EPDataLocalizationPadX'), pady = GUI.R('EPDataLocalizationPadY')) # Lokalizacja imienia (wiersz) self.EPDLFnameRowVar = TK.IntVar() @@ -1801,7 +1591,7 @@ class mainWindow: self.EPDLFnameRowSpinbox.config(to = 1000000) self.EPDLFnameRowSpinbox.config(state = TK.DISABLED) self.EPDLFnameRowSpinbox.config(style = 'spinbox1.TSpinbox') - self.EPDLFnameRowSpinbox.grid(row = 2, column = 1, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) + self.EPDLFnameRowSpinbox.grid(row = 2, column = 1, padx = GUI.R('EPDataLocalizationPadX'), pady = GUI.R('EPDataLocalizationPadY')) # Lokalizacja imienia (pozycja w wierszu) self.EPDLFnamePosInRowVar = TK.IntVar() @@ -1811,13 +1601,13 @@ class mainWindow: self.EPDLFnamePosInRowSpinbox.config(to = 1000000) self.EPDLFnamePosInRowSpinbox.config(state = TK.DISABLED) self.EPDLFnamePosInRowSpinbox.config(style = 'spinbox1.TSpinbox') - self.EPDLFnamePosInRowSpinbox.grid(row = 2, column = 2, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) + self.EPDLFnamePosInRowSpinbox.grid(row = 2, column = 2, padx = GUI.R('EPDataLocalizationPadX'), pady = GUI.R('EPDataLocalizationPadY')) # W3 - "Nazwisko" self.EPDLW3Label = TKttk.Label(self.editingPresetDLFrame) self.EPDLW3Label.config(style = 'label1.TLabel') self.EPDLW3Label.config(text = 'Nazwisko') - self.EPDLW3Label.grid(row = 3, column = 0, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) + self.EPDLW3Label.grid(row = 3, column = 0, padx = GUI.R('EPDataLocalizationPadX'), pady = GUI.R('EPDataLocalizationPadY')) # Lokalizacja nazwiska (wiersz) self.EPDLLnameRowVar = TK.IntVar() @@ -1827,7 +1617,7 @@ class mainWindow: self.EPDLLnameRowSpinbox.config(to = 1000000) self.EPDLLnameRowSpinbox.config(state = TK.DISABLED) self.EPDLLnameRowSpinbox.config(style = 'spinbox1.TSpinbox') - self.EPDLLnameRowSpinbox.grid(row = 3, column = 1, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) + self.EPDLLnameRowSpinbox.grid(row = 3, column = 1, padx = GUI.R('EPDataLocalizationPadX'), pady = GUI.R('EPDataLocalizationPadY')) # Lokalizacja nazwiska (pozycja w wierszu) self.EPDLLnamePosInRowVar = TK.IntVar() @@ -1837,13 +1627,13 @@ class mainWindow: self.EPDLLnamePosInRowSpinbox.config(to = 1000000) self.EPDLLnamePosInRowSpinbox.config(state = TK.DISABLED) self.EPDLLnamePosInRowSpinbox.config(style = 'spinbox1.TSpinbox') - self.EPDLLnamePosInRowSpinbox.grid(row = 3, column = 2, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) + self.EPDLLnamePosInRowSpinbox.grid(row = 3, column = 2, padx = GUI.R('EPDataLocalizationPadX'), pady = GUI.R('EPDataLocalizationPadY')) # W4 - "Szkoła" self.EPDLW4Label = TKttk.Label(self.editingPresetDLFrame) self.EPDLW4Label.config(style = 'label1.TLabel') self.EPDLW4Label.config(text = 'Szkoła') - self.EPDLW4Label.grid(row = 4, column = 0, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) + self.EPDLW4Label.grid(row = 4, column = 0, padx = GUI.R('EPDataLocalizationPadX'), pady = GUI.R('EPDataLocalizationPadY')) # Lokalizacja nazwiska (wiersz) self.EPDLSchoolRowVar = TK.IntVar() @@ -1853,7 +1643,7 @@ class mainWindow: self.EPDLSchoolRowSpinbox.config(to = 1000000) self.EPDLSchoolRowSpinbox.config(state = TK.DISABLED) self.EPDLSchoolRowSpinbox.config(style = 'spinbox1.TSpinbox') - self.EPDLSchoolRowSpinbox.grid(row = 4, column = 1, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) + self.EPDLSchoolRowSpinbox.grid(row = 4, column = 1, padx = GUI.R('EPDataLocalizationPadX'), pady = GUI.R('EPDataLocalizationPadY')) # Lokalizacja nazwiska (pozycja w wierszu) self.EPDLSchoolPosInRowVar = TK.IntVar() @@ -1863,13 +1653,13 @@ class mainWindow: self.EPDLSchoolPosInRowSpinbox.config(to = 1000000) self.EPDLSchoolPosInRowSpinbox.config(state = TK.DISABLED) self.EPDLSchoolPosInRowSpinbox.config(style = 'spinbox1.TSpinbox') - self.EPDLSchoolPosInRowSpinbox.grid(row = 4, column = 2, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) + self.EPDLSchoolPosInRowSpinbox.grid(row = 4, column = 2, padx = GUI.R('EPDataLocalizationPadX'), pady = GUI.R('EPDataLocalizationPadY')) # W5 - "Klasa" self.EPDLW5Label = TKttk.Label(self.editingPresetDLFrame) self.EPDLW5Label.config(style = 'label1.TLabel') self.EPDLW5Label.config(text = 'Klasa') - self.EPDLW5Label.grid(row = 5, column = 0, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) + self.EPDLW5Label.grid(row = 5, column = 0, padx = GUI.R('EPDataLocalizationPadX'), pady = GUI.R('EPDataLocalizationPadY')) # Lokalizacja nazwiska (wiersz) self.EPDLClassRowVar = TK.IntVar() @@ -1879,7 +1669,7 @@ class mainWindow: self.EPDLClassRowSpinbox.config(to = 1000000) self.EPDLClassRowSpinbox.config(state = TK.DISABLED) self.EPDLClassRowSpinbox.config(style = 'spinbox1.TSpinbox') - self.EPDLClassRowSpinbox.grid(row = 5, column = 1, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) + self.EPDLClassRowSpinbox.grid(row = 5, column = 1, padx = GUI.R('EPDataLocalizationPadX'), pady = GUI.R('EPDataLocalizationPadY')) # Lokalizacja nazwiska (pozycja w wierszu) self.EPDLClassPosInRowVar = TK.IntVar() @@ -1889,20 +1679,20 @@ class mainWindow: self.EPDLClassPosInRowSpinbox.config(to = 1000000) self.EPDLClassPosInRowSpinbox.config(state = TK.DISABLED) self.EPDLClassPosInRowSpinbox.config(style = 'spinbox1.TSpinbox') - self.EPDLClassPosInRowSpinbox.grid(row = 5, column = 2, padx = GUI.R()['EPDataLocalizationPadX'], pady = GUI.R()['EPDataLocalizationPadY']) + self.EPDLClassPosInRowSpinbox.grid(row = 5, column = 2, padx = GUI.R('EPDataLocalizationPadX'), pady = GUI.R('EPDataLocalizationPadY')) ############################### ######################################### ################################################### - + # (1) Separator 3 ########################################### self.formatSeparator3 = TKttk.Separator(self.formatFrame) self.formatSeparator3.config(style = 'separator1.TSeparator') self.formatSeparator3.config(orient = TK.HORIZONTAL) - self.formatSeparator3.pack(fill = TK.X, expand = 1, pady = 6) + self.formatSeparator3.pack(fill = TK.X, pady = GUI.R('formatHorizontalSeparatorPadY')) ############################################################# @@ -1910,14 +1700,14 @@ class mainWindow: self.editingPresetButtonsFrame = TKttk.Frame(self.formatFrame) self.editingPresetButtonsFrame.config(style = 'layoutFrame.TFrame') - self.editingPresetButtonsFrame.pack(fill = TK.X, expand = 1, side = TK.BOTTOM) + self.editingPresetButtonsFrame.pack(fill = TK.X, side = TK.BOTTOM, pady = GUI.R('editingPresetButtonsPadY')) # Przycisk 'ZAPISZ' self.editingPresetSaveButton = TKttk.Button(self.editingPresetButtonsFrame) self.editingPresetSaveButton.config(command = self.editingPresetSaveButtonAction) self.editingPresetSaveButton.config(state = TK.DISABLED) self.editingPresetSaveButton.config(style = 'button1.TButton') - self.editingPresetSaveButton.config(width = GUI.R()['editingPresetSaveButtonWidth']) + self.editingPresetSaveButton.config(width = GUI.R('editingPresetSaveButtonWidth')) self.editingPresetSaveButton.config(text = 'ZAPISZ') self.editingPresetSaveButton.pack(side = TK.LEFT, expand = 1) @@ -1926,7 +1716,7 @@ class mainWindow: self.editingPresetCancelButton.config(command = self.editingPresetCancelButtonAction) self.editingPresetCancelButton.config(state = TK.DISABLED) self.editingPresetCancelButton.config(style = 'button1.TButton') - self.editingPresetCancelButton.config(width = GUI.R()['editingPresetCancelButtonWidth']) + self.editingPresetCancelButton.config(width = GUI.R('editingPresetCancelButtonWidth')) self.editingPresetCancelButton.config(text = 'Anuluj') self.editingPresetCancelButton.pack(side = TK.RIGHT, expand = 1) @@ -1941,8 +1731,8 @@ class mainWindow: self.settingsTab = TKttk.Frame(self.mainMenu) self.settingsTab.config(style = "mainMenuTabFrame.TFrame") - self.settingsTabImg = PLimg.open(GUI.R()['settingsTabIcon']) - self.settingsTabImg = self.settingsTabImg.resize((GUI.R()['tabIconsSize'], GUI.R()['tabIconsSize']), PLimg.ANTIALIAS) + self.settingsTabImg = PLimg.open(GUI.R('settingsTabIcon')) + self.settingsTabImg = self.settingsTabImg.resize((GUI.R('tabIconsSize'), GUI.R('tabIconsSize')), PLimg.ANTIALIAS) self.settingsTabImg = PLitk.PhotoImage(self.settingsTabImg) self.mainMenu.add(self.settingsTab, image = self.settingsTabImg, state = TK.NORMAL) @@ -1951,13 +1741,13 @@ class mainWindow: self.settingsHeader = TKttk.Label(self.settingsTab) self.settingsHeader.config(style = 'tabHeader.TLabel') self.settingsHeader.config(text = 'USTAWIENIA') - self.settingsHeader.pack(fill = TK.X) + self.settingsHeader.pack(fill = GUI.R('headerFill')) # Zawartość self.settingsFrame = TKttk.Frame(self.settingsTab) - self.settingsFrame.config(style = 'tabFrame.TFrame') - self.settingsFrame.pack(fill = TK.BOTH, expand = 1, padx = GUI.R()['tabFramePadding'], pady = GUI.R()['tabFramePadding']) + self.settingsFrame.config(style = 'contentTabFrame.TFrame') + self.settingsFrame.pack(fill = GUI.R('contentTabFrameFill'), expand = GUI.R('contentTabFrameExpand'), padx = GUI.R('tabFramePadding'), pady = GUI.R('tabFramePadding')) ###################################################################### @@ -1968,8 +1758,8 @@ class mainWindow: self.aboutTab = TKttk.Frame(self.mainMenu) self.aboutTab.config(style = "mainMenuTabFrame.TFrame") - self.aboutTabImg = PLimg.open(GUI.R()['aboutTabIcon']) - self.aboutTabImg = self.aboutTabImg.resize((GUI.R()['tabIconsSize'], GUI.R()['tabIconsSize']), PLimg.ANTIALIAS) + self.aboutTabImg = PLimg.open(GUI.R('aboutTabIcon')) + self.aboutTabImg = self.aboutTabImg.resize((GUI.R('tabIconsSize'), GUI.R('tabIconsSize')), PLimg.ANTIALIAS) self.aboutTabImg = PLitk.PhotoImage(self.aboutTabImg) self.mainMenu.add(self.aboutTab, image = self.aboutTabImg, state = TK.NORMAL) @@ -1978,16 +1768,16 @@ class mainWindow: self.aboutHeader = TKttk.Label(self.aboutTab) self.aboutHeader.config(style = 'tabHeader.TLabel') self.aboutHeader.config(text = 'O PROGRAMIE') - self.aboutHeader.pack(fill = TK.X) + self.aboutHeader.pack(fill = GUI.R('headerFill')) # Zawartość self.aboutFrame = TKttk.Frame(self.aboutTab) - self.aboutFrame.config(style = 'tabFrame.TFrame') - self.aboutFrame.pack(fill = TK.BOTH, expand = 1, padx = GUI.R()['tabFramePadding'], pady = GUI.R()['tabFramePadding']) + self.aboutFrame.config(style = 'contentTabFrame.TFrame') + self.aboutFrame.pack(fill = GUI.R('contentTabFrameFill'), expand = GUI.R('contentTabFrameExpand'), padx = GUI.R('tabFramePadding'), pady = GUI.R('tabFramePadding')) ###################################################################### - + # Akcje przycisków - TAB1 diff --git a/assets/icon.ico b/icon.ico similarity index 100% rename from assets/icon.ico rename to icon.ico From 677346efcc0aedeb158d853aac496742de51c8b3 Mon Sep 17 00:00:00 2001 From: Mateusz Skoczek Date: Fri, 21 Aug 2020 00:40:52 +0200 Subject: [PATCH 18/29] 4.0 Alpha (Build 20234) --- .vscode/settings.json | 4 + changelog-UC.txt | 9 +- default-configs/config.cfg | 6 +- generator.pyw | 896 +++++++++++++++++++++++++------------ 4 files changed, 637 insertions(+), 278 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..82a8511 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "python.linting.pylintEnabled": true, + "python.linting.enabled": true +} \ No newline at end of file diff --git a/changelog-UC.txt b/changelog-UC.txt index fbc3df0..3d67d8e 100644 --- a/changelog-UC.txt +++ b/changelog-UC.txt @@ -92,4 +92,11 @@ 4.0 Alpha (Build 20221) - Zastosowanie nowego systemu przetwarzania plików konfiguracyjnych dla pliku 'style.cfg' -- Lekkie zmiany w interfejsie \ No newline at end of file +- Lekkie zmiany w interfejsie + +4.0 Alpha (Build 20234) +- Ulepszony system sprawdzania katalogu appdata +- Zastosowanie nowego systemu przetwarzania plików konfiguracyjnych dla plików formatu +- Wstępne zastosowanie nowego systemu przetwarzania plików konfiguracyjnych dla pliku 'config.cfg' w zapisie danych +- Ukończenie skryptu przetwarzającego dane +- Poprawki błędów \ No newline at end of file diff --git a/default-configs/config.cfg b/default-configs/config.cfg index 198f4a3..67c6e3e 100644 --- a/default-configs/config.cfg +++ b/default-configs/config.cfg @@ -1,10 +1,10 @@ secret(S) = entersecretstringhere allowedCharactersInSeparator(SCA) = ['`', '~', '!', '@', '#', '$', '%', '^', '&', '(', ')', '-', '_', '=', '+', '[', ']', '\', '|', ';', ':', ''', '"', ',', '<', '.', '>', '/', '?', ' '] inputCoding(S) = utf-8 -outputCoding(S) = utf-8 +mailOutputCoding(S) = utf-8 +officeOutputCoding(S) = utf-8 domain(S) = losobolew.pl quota(I) = 500 country(S) = Rzeczypospolita Polska -yearsOfLO(I) = 4 -yearsOfBS(I) = 3 +schoolData(MSAs) = [LO, 4, 0]|[BS, 3, 1] schoolyearStart(D) = 01.09.* *:*:* \ No newline at end of file diff --git a/generator.pyw b/generator.pyw index 12fe2d6..30f6c89 100644 --- a/generator.pyw +++ b/generator.pyw @@ -15,6 +15,8 @@ class VAR: programName = 'Generator CSV' programVersion = '4.0' + programVersionStage = 'Alpha' + programVersionBuild = 'Build' programCustomer = 'ZSP Sobolew' programAuthors = ['Mateusz Skoczek'] programToW = ['styczeń', 2019, 'wrzesień', 2020] @@ -40,6 +42,7 @@ import tkinter as TK from tkinter import ttk as TKttk from tkinter import messagebox as TKmsb from tkinter import filedialog as TKfld +import tkcalendar as TKcal from PIL import ImageTk as PLitk from PIL import Image as PLimg @@ -65,6 +68,12 @@ MSGlist = { 'E0007' : 'Wymagany przynajmniej jeden plik wejściowy', 'E0008' : 'Nie można odnaleźć jednego z powyższych plików', 'E0009' : 'Nie można odnaleźć jednego z powyższych format presetów', + 'E0010' : 'Nie można przetworzyć danych z plików wejściowych z pomocą podanych format presetów', + 'E0011' : 'Niepoprawne dane w plikach wejściowych', + 'E0012' : 'Nie można przetworzyć danych na format wyjściowy', + 'E0013' : 'Nie można utworzyć plików wejściowych', + 'E0014' : 'Nie można zapisać plików wejściowych', + 'I0001' : 'Operacja ukończona pomyślnie', } def MSG(code, terminate, *optionalInfo): @@ -81,7 +90,7 @@ def MSG(code, terminate, *optionalInfo): # Informacja elif code[0] == 'I': - TKmsb.showerror('Informacja', '%s\n%s' % (MSGlist[code], optionalInfo[0])) + TKmsb.showinfo('Informacja', '%s\n%s' % (MSGlist[code], optionalInfo[0])) if terminate: SS.exit(0) @@ -107,16 +116,36 @@ def MSG(code, terminate, *optionalInfo): appdata = PT.Path.home() / 'Appdata/Roaming' #TODO -SU.rmtree(str(appdata) + '/Generator CSV') +#SU.rmtree(str(appdata) + '/Generator CSV') #TODO -if 'Generator CSV' not in [x for x in OS.listdir(appdata)]: - try: - OS.mkdir(str(appdata) + '/Generator CSV') - SU.copy('default-configs/config.cfg', str(appdata) + '\Generator CSV\config.cfg') - SU.copy('default-configs/style.cfg', str(appdata) + '\Generator CSV\style.cfg') - OS.mkdir(str(appdata) + '/Generator CSV/format-presets') - except Exception as exceptInfo: - MSG('E0001', True, exceptInfo) + +def checkAppdata(): + if 'Generator CSV' not in [x for x in OS.listdir(appdata)]: + try: + OS.mkdir(str(appdata) + '/Generator CSV') + SU.copy('default-configs/config.cfg', str(appdata) + '\Generator CSV\config.cfg') + SU.copy('default-configs/style.cfg', str(appdata) + '\Generator CSV\style.cfg') + OS.mkdir(str(appdata) + '/Generator CSV/format-presets') + except Exception as exceptInfo: + MSG('E0001', True, exceptInfo) + else: + if 'config.cfg' not in [x for x in OS.listdir(str(appdata) + '/Generator CSV')]: + try: + SU.copy('default-configs/config.cfg', str(appdata) + '\Generator CSV\config.cfg') + except Exception as exceptInfo: + MSG('E0001', True, exceptInfo) + if 'style.cfg' not in [x for x in OS.listdir(str(appdata) + '/Generator CSV')]: + try: + SU.copy('default-configs/style.cfg', str(appdata) + '\Generator CSV\style.cfg') + except Exception as exceptInfo: + MSG('E0001', True, exceptInfo) + if 'format-presets'not in [x for x in OS.listdir(str(appdata) + '/Generator CSV')]: + try: + OS.mkdir(str(appdata) + '/Generator CSV/format-presets') + except Exception as exceptInfo: + MSG('E0001', True, exceptInfo) + +checkAppdata() @@ -129,9 +158,10 @@ class CFG: def __checkIfFileExist(self, write): if write: try: + checkAppdata() file = open((str(appdata) + '\Generator CSV\config.cfg'), 'a') except Exception as exceptInfo: - MSG('E0002', False, exceptInfo) + MSG('E0002', True, exceptInfo) return False else: if not file.writable(): @@ -141,6 +171,7 @@ class CFG: return True else: try: + checkAppdata() open(str(appdata) + '\Generator CSV\config.cfg') except Exception as exceptInfo: MSG('E0002', True, exceptInfo) @@ -202,12 +233,37 @@ class CFG: var = varToReturn return [True, var] + def __checkMSAs(self, write, record, var): + if write: + pass + else: + var = var.split('|') + var = [x.strip('\r').strip('[').strip(']').split(', ') for x in var] + newVar = [] + for x in var: + if len(x) != 3: + return (False, 'Niepoprawne dane - klucz: %s' % record) + try: + if x[2] == '0': + x[2] = False + elif x[2] == '1': + x[2] = True + else: + return (False, 'Niepoprawne dane - klucz: %s' % record) + x = [x[0], int(x[1]), x[2]] + newVar.append(x) + except: + return (False, 'Niepoprawne dane - klucz: %s' % record) + var = newVar + return [True, var] + + def R(self, record): self.__checkIfFileExist(False) content = {} - for x in CD.open((str(appdata) + '\Generator CSV\config.cfg'), 'r', 'utf-8').read().split('\n'): + for x in CD.open((str(appdata) + '\Generator CSV\config.cfg'), 'r', 'utf-8').read().strip('\r').split('\n'): x = x.split(' = ') try: name = x[0].split('(')[0] @@ -222,7 +278,7 @@ class CFG: var = content[record] if var[1] == 'S': # String - var = var[0] + var = var[0].strip('\r') return var elif var[1] == 'SCA': # Single char array @@ -245,27 +301,79 @@ class CFG: return checkingOutput[1] else: MSG('E0003', True, checkingOutput[1]) + elif var[1] == 'MSAs': + # Multiple Specified Arrays - schoolData + checkingOutput = self.__checkMSAs(False, record, var[0]) + if checkingOutput[0]: + return checkingOutput[1] + else: + MSG('E0003', True, checkingOutput[1]) else: MSG('E0003', True, 'Nie można rozpoznać typu klucza %s' % record) - def W(self): - pass - """ - content = self.R() + def W(self, preset, changes): + self.__checkIfFolderExist() + file = CD.open(str(appdata) + '\Generator CSV\config.cfg', 'r', 'utf-8').read().split('\n') + if file[-1] == '': + file = file[:-1] + content = {} + for x in file: + x = x.split(' = ') + try: + name = x[0].split('(')[0] + var = x[1] + type = x[0].split('(')[1].strip(')') + content[name] = [var, type] + except Exception as exceptInfo: + MSG('E0003', False, exceptInfo) for x in changes: - content[x] = changes[x] - contentCheckingOutput = self.__checkContent(True, content) - if contentCheckingOutput[0]: - if self.__checkInstance(True): - with CD.open((str(appdata) + '\Generator CSV\config.cfg'), 'w', 'utf-8') as file: - contentToSave = contentCheckingOutput[1] - for x in contentToSave: - file.write('%s = %s\n' % (x, str(contentToSave[x]))) + name = x + var = changes[name] + type = (content[name])[1] + if type == 'S': + # String + pass + elif type == 'SCA': + # Single char array + checkingOutput = self.__checkSCA(True, name, var) + if checkingOutput[0]: + var = checkingOutput[1] + else: + MSG('E0006', False, checkingOutput[1]) + return False + elif type == 'I': + # Integer + checkingOutput = self.__checkI(True, name, var) + if checkingOutput[0]: + var = checkingOutput[1] + else: + MSG('E0006', False, checkingOutput[1]) + return False + elif type == 'D': + # Date (DD.MM.RRRR HH:MM:SS) + checkingOutput = self.__checkD(True, name, var) + if checkingOutput[0]: + var = checkingOutput[1] + else: + MSG('E0006', False, checkingOutput[1]) + return False + elif type == 'MSAs': + # Multiple Specified Arrays - schoolData + checkingOutput = self.__checkMSAs(True, name, var) + if checkingOutput[0]: + var = checkingOutput[1] + else: + MSG('E0006', False, checkingOutput[1]) + return False else: + MSG('E0003', False, 'Nie można rozpoznać typu klucza %s' % name) return False - else: - MSG('E0004', False, contentCheckingOutput[1]) - """ + content[name] = [var, type] + with CD.open(str(appdata) + '\Generator CSV\config.cfg', 'w', 'utf-8') as file: + for x in content: + file.write('%s(%s) = %s\n' % (x, (content[x])[1], (content[x][0]))) + return True + CFG = CFG() @@ -280,9 +388,10 @@ class GUI: # Funkcje sprawdzające istnienie def __checkIfFileExist(self): try: + checkAppdata() open(str(appdata) + '\Generator CSV\style.cfg') except Exception as exceptInfo: - MSG('E0004', True, exceptInfo) + checkAppdata() def __checkIfRecordExist(self, content, record): if record in list(content.keys()): @@ -353,7 +462,7 @@ class GUI: def R(self, record): self.__checkIfFileExist() content = {} - for x in CD.open((str(appdata) + '\Generator CSV\style.cfg'), 'r', 'utf-8').read().split('\n'): + for x in CD.open((str(appdata) + '\Generator CSV\style.cfg'), 'r', 'utf-8').read().strip('\r').split('\n'): x = x.split(' = ') try: name = x[0].split('(')[0] @@ -422,201 +531,84 @@ GUI = GUI() # ------------------------------- # Zarządzanie plikami formatu # ------------------------------- # class FMT: - def __checkFolderInstance(self): - if 'Generator CSV' not in [x for x in OS.listdir(appdata)]: - OS.mkdir(str(appdata) + '/Generator CSV') + # Funkcje sprawdzające istnienie + def __checkIfFolderExist(self): + checkAppdata() + + def __checkIfRecordExist(self, content, record): + if record in list(content.keys()): + return [True] else: - if 'format-presets' not in [x for x in OS.listdir(str(appdata) + '\Generator CSV')]: - OS.mkdir(str(appdata) + '/Generator CSV/format-presets') + return [False, 'Brak danych - klucz: %s' % record] + - def __checkContent(self, write, content): + # Funkcje sprawdzające poprawność rekordu + def __checkB(self, write, record, var): if write: - class functions: - def bool(self, var): - if var in list(content.keys()): - if content[var] != True and content[var] != False: - return [False, 'Niepoprawne dane - klucz: %s' % var] - else: - if content[var] == False: - content[var] = '0' - return [True] - else: - content[var] = '1' - return [True] - else: - return [False, 'Brak danych - klucz: %s' % var] - def separator_string(self, var): - if var in list(content.keys()): - allowedCharactersInSeparator = CFG.R()['allowedCharactersInSeparator'] - check = content[var] - check = check.strip('') - for x in check: - if x not in allowedCharactersInSeparator: - return [False, 'Niepoprawne dane - klucz: %s' % var] - return [True] - else: - return [False, 'Brak danych - klucz: %s' % var] - def separator_array(self, var): - if var in list(content.keys()): - allowedCharactersInSeparator = CFG.R()['allowedCharactersInSeparator'] - check = content[var] - for x in check: - x = x.strip('') - for y in x: - if y not in allowedCharactersInSeparator: - return [False, 'Niepoprawne dane - klucz: %s' % var] - content[var] = str(content[var]) - return [True] - else: - return [False, 'Brak danych - klucz: %s' % var] - def integer(self, var): - if var in list(content.keys()): - content[var] = str(content[var]) - return [True] - else: - return [False, 'Brak danych - klucz: %s' % var] - functions = functions() - check = functions.bool('student') - if not check[0]: - return check - check = functions.separator_string('personSeparator') - if not check[0]: - return check - check = functions.separator_string('rowSeparator') - if not check[0]: - return check - check = functions.separator_array('dataSeparators') - if not check[0]: - return check - check = functions.integer('loginRow') - if not check[0]: - return check - check = functions.integer('loginPositionInRow') - if not check[0]: - return check - check = functions.integer('fnameRow') - if not check[0]: - return check - check = functions.integer('fnamePositionInRow') - if not check[0]: - return check - check = functions.integer('lnameRow') - if not check[0]: - return check - check = functions.integer('lnamePositionInRow') - if not check[0]: - return check - check = functions.integer('schoolRow') - if not check[0]: - return check - check = functions.integer('schoolPositionInRow') - if not check[0]: - return check - check = functions.integer('classRow') - if not check[0]: - return check - check = functions.integer('classPositionInRow') - if not check[0]: - return check - return [True, content] + if var == True: + var = '1' + elif var == False: + var = '0' + else: + return [False, 'Niepoprawne dane - klucz: %s' % record] else: - class functions: - def bool(self, var): - if var in list(content.keys()): - if content[var] != '0' and content[var] != '1': - return [False, 'Niepoprawne dane - klucz: %s' % var] - else: - if content[var] == '0': - content[var] = False - return [True] - else: - content[var] = True - return [True] - else: - return [False, 'Brak danych - klucz: %s' % var] - def separator_string(self, var): - if var in list(content.keys()): - allowedCharactersInSeparator = CFG.R('allowedCharactersInSeparator') - check = content[var] - check = check.strip('') - for x in check: - if x not in allowedCharactersInSeparator: - return [False, 'Niepoprawne dane - klucz: %s' % var] - return [True] - else: - return [False, 'Brak danych - klucz: %s' % var] - def separator_array(self, var): - if var in list(content.keys()): - allowedCharactersInSeparator = CFG.R('allowedCharactersInSeparator') - new_contentVar = (content[var])[2:-2].split("', '") - check = new_contentVar - for x in check: - x = x.strip('') - for y in x: - if y not in allowedCharactersInSeparator: - return [False, 'Niepoprawne dane - klucz: %s' % var] - content[var] = new_contentVar - return [True] - else: - return [False, 'Brak danych - klucz: %s' % var] - def integer(self, var): - if var in list(content.keys()): - try: - check = int(content[var]) - except: - return [False, 'Niepoprawne dane - klucz: %s' % var] - else: - content[var] = int(content[var]) - return [True] - else: - return [False, 'Brak danych - klucz: %s' % var] - functions = functions() - check = functions.bool('student') - if not check[0]: - return check - check = functions.separator_string('personSeparator') - if not check[0]: - return check - check = functions.separator_string('rowSeparator') - if not check[0]: - return check - check = functions.separator_array('dataSeparators') - if not check[0]: - return check - check = functions.integer('loginRow') - if not check[0]: - return check - check = functions.integer('loginPositionInRow') - if not check[0]: - return check - check = functions.integer('fnameRow') - if not check[0]: - return check - check = functions.integer('fnamePositionInRow') - if not check[0]: - return check - check = functions.integer('lnameRow') - if not check[0]: - return check - check = functions.integer('lnamePositionInRow') - if not check[0]: - return check - check = functions.integer('schoolRow') - if not check[0]: - return check - check = functions.integer('schoolPositionInRow') - if not check[0]: - return check - check = functions.integer('classRow') - if not check[0]: - return check - check = functions.integer('classPositionInRow') - if not check[0]: - return check - return [True, content] + try: + var = int(var) + except: + return [False, 'Niepoprawne dane - klucz: %s' % record] + if var != 0 and var != 1: + return [False, 'Niepoprawne dane - klucz: %s' % record] + else: + if var == 0: + var = False + else: + var = True + return [True, var] + + def __checkSs(self, record, var): + allowedCharactersInSeparator = CFG.R('allowedCharactersInSeparator') + check = var + check = check.strip('') + for x in check: + if x not in allowedCharactersInSeparator: + return [False, 'Niepoprawne dane - klucz: %s' % var] + return [True, var] + def __checkAs(self, write, record, var): + allowedCharactersInSeparator = CFG.R('allowedCharactersInSeparator') + if write: + check = var + for x in check: + x = x.strip('') + for y in x: + if y not in allowedCharactersInSeparator: + return [False, 'Niepoprawne dane - klucz: %s' % var] + var = str(var) + else: + new_contentVar = (var)[2:-2].split("', '") + check = new_contentVar + for x in check: + x = x.strip('') + for y in x: + if y not in allowedCharactersInSeparator: + return [False, 'Niepoprawne dane - klucz: %s' % var] + var = new_contentVar + return [True, var] + + def __checkI(self, write, record, var): + if write: + var = str(var) + else: + try: + var = int(var) + except: + return (False, 'Niepoprawne dane - klucz: %s' % record) + return [True, var] + + + # Funkcja zwracająca listę presetów def getList(self): - self.__checkFolderInstance() + self.__checkIfFolderExist() filesList = OS.listdir(str(appdata) + '/Generator CSV/format-presets') formatPresetsList = [] for x in filesList: @@ -625,23 +617,58 @@ class FMT: else: continue return formatPresetsList + + - def R(self, preset): + def R(self, preset, record): + self.__checkIfFolderExist() if preset in self.getList(): path = str(appdata) + '/Generator CSV/format-presets/%s.fmt' % preset - file = CD.open(path, 'r', 'utf-8').read().split('\n') + file = CD.open(path, 'r', 'utf-8').read().strip('\r').split('\n') content = {} for x in file: x = x.split(' = ') try: - content[x[0]] = (x[1]).strip('\r') + name = x[0].split('(')[0] + var = x[1] + type = x[0].split('(')[1].strip(')') + content[name] = [var, type] except: continue - contentCheckingOutput = self.__checkContent(False, content) - if contentCheckingOutput[0]: - content = contentCheckingOutput[1] + checkingOutput = self.__checkIfRecordExist(content, record) + if not checkingOutput[0]: + MSG('E0006', False, checkingOutput[1]) + var = content[record] + if var[1] == 'B': + # Boolean + checkingOutput = self.__checkB(False, record, var[0]) + if checkingOutput[0]: + return checkingOutput[1] + else: + MSG('E0006', False, checkingOutput[1]) + elif var[1] == 'Ss': + # String - separator + checkingOutput = self.__checkSs(record, var[0]) + if checkingOutput[0]: + return checkingOutput[1] + else: + MSG('E0006', False, checkingOutput[1]) + elif var[1] == 'As': + # Array - separator + checkingOutput = self.__checkAs(False, record, var[0]) + if checkingOutput[0]: + return checkingOutput[1] + else: + MSG('E0006', False, checkingOutput[1]) + elif var[1] == 'I': + # Integer + checkingOutput = self.__checkI(False, record, var[0]) + if checkingOutput[0]: + return checkingOutput[1] + else: + MSG('E0006', False, checkingOutput[1]) else: - MSG('E0006', False, contentCheckingOutput[1]) + MSG('E0006', True, 'Nie można rozpoznać typu klucza %s' % record) else: content = { "student" : True, @@ -659,19 +686,86 @@ class FMT: "classRow" : 0, "classPositionInRow" : 0, } - return content - - def W(self, preset, content): - contentCheckingOutput = self.__checkContent(True, content) - if contentCheckingOutput[0]: - contentToSave = contentCheckingOutput[1] - with CD.open(str(appdata) + '/Generator CSV/format-presets/%s.fmt' % preset, 'w', 'utf-8') as file: - for x in contentToSave: - file.write(x + ' = ' + content[x] + '\n') - return True + var = content[record] + return var + + def W(self, preset, changes): + self.__checkIfFolderExist() + if preset in self.getList(): + file = CD.open(str(appdata) + '/Generator CSV/format-presets/%s.fmt' % preset, 'r', 'utf-8').read().split('\n') + if file[-1] == '': + file = file[:-1] + content = {} + for x in file: + x = x.split(' = ') + try: + name = x[0].split('(')[0] + var = x[1] + type = x[0].split('(')[1].strip(')') + content[name] = [var, type] + except Exception as exceptInfo: + MSG('E0006', False, exceptInfo) else: - MSG('E0006', False, contentCheckingOutput[1]) - return False + content = { + "student" : ['1', 'B'], + "personSeparator" : ['', 'Ss'], + "rowSeparator" : ['', 'Ss'], + "dataSeparators" : ['', 'As'], + "loginRow" : ['0', 'I'], + "loginPositionInRow" : ['0', 'I'], + "fnameRow" : ['0', 'I'], + "fnamePositionInRow" : ['0', 'I'], + "lnameRow" : ['0', 'I'], + "lnamePositionInRow" : ['0', 'I'], + "schoolRow" : ['0', 'I'], + "schoolPositionInRow" : ['0', 'I'], + "classRow" : ['0', 'I'], + "classPositionInRow" : ['0', 'I'], + } + for x in changes: + name = x + var = changes[name] + type = (content[name])[1] + if type == 'B': + checkingOutput = self.__checkB(True, name, var) + if checkingOutput[0]: + var = checkingOutput[1] + else: + MSG('E0006', False, checkingOutput[1]) + return False + elif type == 'Ss': + checkingOutput = self.__checkSs(name, var) + if checkingOutput[0]: + var = checkingOutput[1] + else: + MSG('E0006', False, checkingOutput[1]) + return False + elif type == 'As': + checkingOutput = self.__checkAs(True, name, var) + if checkingOutput[0]: + var = checkingOutput[1] + else: + MSG('E0006', False, checkingOutput[1]) + return False + elif type == 'I': + # Integer + checkingOutput = self.__checkI(True, name, var) + if checkingOutput[0]: + var = checkingOutput[1] + else: + MSG('E0006', False, checkingOutput[1]) + return False + else: + MSG('E0003', False, 'Nie można rozpoznać typu klucza %s' % name) + return False + content[name] = [var, type] + with CD.open(str(appdata) + '/Generator CSV/format-presets/%s.fmt' % preset, 'w', 'utf-8') as file: + for x in content: + file.write('%s(%s) = %s\n' % (x, (content[x])[1], (content[x][0]))) + return True + + + FMT = FMT() @@ -681,8 +775,8 @@ FMT = FMT() # ---------------------------------- # Przetwarzanie plików # ----------------------------------- # class dataProcess: - # Funkcje sprawdzające - def __checkIfAtLeastOneInputFileIsFilled(files): + # Funkcje sprawdzające istnienie + def __checkIfAtLeastOneInputFileIsFilled(self, files): filledFiles = [] index = 0 for x in files: @@ -708,15 +802,180 @@ class dataProcess: return False return True - def __checkIfCreateOutputFilesIsPossible(self, files): - for x in files: - try: - check = CD.open(x, 'w', CFG.R('outputCoding')) - except: - return False - return true + def __checkIfCreatingOutputFilesIsPossible(self, files): + try: + check = CD.open(files[0], 'w', CFG.R('mailOutputCoding')) + check = CD.open(files[1], 'w', CFG.R('officeOutputCoding')) + except: + return False + return True + - # + # Funkcje sprawdzające poprawność + def __checkLogin(self, var, student): + if student and var[-1] != 'u': + return [False, 'Brak końcówki "u" w loginie ucznia: '] + if student: + try: + x = int(var[:-1]) + except: + return [False, 'Niedozwolone znaki w loginie osoby: '] + else: + try: + x = int(var) + except: + return [False, 'Niedozwolone znaki w loginie osoby: '] + return [True] + + def __checkFname(self, var): + if not var.isalpha(): + return [False, 'Niedozwolone znaki w imieniu osoby: '] + return [True] + + def __checkLname(self, var): + if not var.isalpha(): + return [False, 'Niedozwolone znaki w nazwisku osoby: '] + return [True] + + def __checkSchool(self, var): + allowedSchools = [x[0] for x in CFG.R('schoolData')] + if var not in allowedSchools: + return [False, 'Niewspierana szkoła w danych osoby: '] + return [True] + + def __checkClass(self, var, school): + if len(var) != 2: + return [False, 'Niepoprawny format klasy w danych osoby: '] + if not var[0].isdigit(): + return [False, 'Niepoprawny format klasy w danych osoby: '] + if not var[1].isalpha(): + return [False, 'Niepoprawny format klasy w danych osoby: '] + schoolData = {} + for x in CFG.R('schoolData'): + schoolData[x[0]] = x[1] + if int(var[0]) == 0 or int(var[0]) > schoolData[school]: + return [False, 'Numer klasy nie zgadza się z ilością klas szkoły w danych osoby: '] + return [True] + + + + # Funkcje operujące na danych + def __getData(self, input): + data = [] + for x in input: + path = x[0] + format = x[1] + personSeparator = FMT.R(format, 'personSeparator').replace('', '\r\n') + linesSeparator = FMT.R(format, 'rowSeparator').replace('', '\r\n') + dataSeparators = [x.replace('', '\n') for x in FMT.R(format, 'dataSeparators')] + loginLocation = [FMT.R(format, 'loginRow'), FMT.R(format, 'loginPositionInRow')] + fnameLocation = [FMT.R(format, 'fnameRow'), FMT.R(format, 'fnamePositionInRow')] + lnameLocation = [FMT.R(format, 'lnameRow'), FMT.R(format, 'lnamePositionInRow')] + schoolLocation = [FMT.R(format, 'schoolRow'), FMT.R(format, 'schoolPositionInRow')] + classLocation = [FMT.R(format, 'classRow'), FMT.R(format, 'classPositionInRow')] + student = FMT.R(format, 'student') + file = CD.open(path, 'r', CFG.R('inputCoding')).read().split(personSeparator) + for x in file: + lines = x.split(linesSeparator) + dataX = [] + for line in lines: + line = [line] + for a in dataSeparators: + line2 = [] + for b in line: + line2 += b.split(a) + line = line2 + dataX.append(line) + login = dataX[loginLocation[0] - 1][loginLocation[1] - 1] + fname = dataX[fnameLocation[0] - 1][fnameLocation[1] - 1] + lname = dataX[lnameLocation[0] - 1][lnameLocation[1] - 1] + if student: + school = dataX[schoolLocation[0] - 1][schoolLocation[1] - 1] + classX = dataX[classLocation[0] - 1][classLocation[1] - 1] + data.append([student, login, fname, lname, school, classX]) + else: + data.append([student, login, fname, lname]) + return data + + def __processData(self, data): + mailData = [] + officeData = [] + schoolData = {} + for x in CFG.R('schoolData'): + schoolData[x[0]] = [x[1], x[2]] + for x in data: + mail = '' + office = '' + mail += x[2].lower().replace('ę', 'e').replace('ó', 'o').replace('ą', 'a').replace('ś', 's').replace('ł', 'l').replace('ż', 'z').replace('ź', 'z').replace('ć', 'c').replace('ń', 'n') + mail += '.' + mail += x[3].lower().replace('ę', 'e').replace('ó', 'o').replace('ą', 'a').replace('ś', 's').replace('ł', 'l').replace('ż', 'z').replace('ź', 'z').replace('ć', 'c').replace('ń', 'n') + if x[0]: + classIndicator = '' + actualYear = TM.localtime() + schoolDuration = (schoolData[x[4]])[0] + if actualYear[1] < CFG.R('schoolyearStart')['M'] or (actualYear[1] == CFG.R('schoolyearStart')['M'] and actualYear[2] < CFG.R('schoolyearStart')['D']): + yearOfGraduation = actualYear[0] + (schoolDuration - int((x[5])[0])) + else: + yearOfGraduation = actualYear[0] + (schoolDuration - int((x[5])[0])) + 1 + mail += str(yearOfGraduation) + if (schoolData[x[4]])[1]: + mail += x[4].lower() + else: + mail += (x[5])[1].lower() + mail += '@' + mail += CFG.R('domain') + office += mail + mail += ',' + mail += x[1] + mail += ':' + mail += (x[2])[0].lower().replace('ę', 'e').replace('ó', 'o').replace('ą', 'a').replace('ś', 's').replace('ł', 'l').replace('ż', 'z').replace('ź', 'z').replace('ć', 'c').replace('ń', 'n').upper() + mail += (x[3])[0].lower().replace('ę', 'e').replace('ó', 'o').replace('ą', 'a').replace('ś', 's').replace('ł', 'l').replace('ż', 'z').replace('ź', 'z').replace('ć', 'c').replace('ń', 'n').upper() + mail += ',' + mail += str(CFG.R('quota')) + office += ',' + office += x[2] + office += ',' + office += x[3] + office += ',' + office += '%s %s' % (x[2], x[3]) + office += ',' + if x[0]: + office += 'uczeń' + else: + office += 'nauczyciel' + office += ',' + office += ',' + office += ',' + if x[0]: + office += str(yearOfGraduation) + if (schoolData[x[4]])[1]: + office += x[4].lower() + else: + office += (x[5])[1].lower() + office += ',' + office += ',' + office += ',' + office += ',' + office += ',' + office += ',' + office += ',' + office += ',' + office += ',' + office += CFG.R('country') + mailData.append(mail) + officeData.append(office) + return [mailData, officeData] + + def __saveData(self, output, data): + mailPath = output[0] + officePath = output[1] + mailData = data[0] + officeData = data[1] + with CD.open(mailPath, 'w', CFG.R('mailOutputCoding')) as mail: + mail.write('\n'.join(mailData)) + with CD.open(officePath, 'w', CFG.R('officeOutputCoding')) as office: + office.write('\n'.join(officeData)) + @@ -738,21 +997,77 @@ class dataProcess: checkingOutput.append(testOutput) if not testOutput: return checkingOutput - - testOutput = self.__checkIfInputFilesFormatPresetsExist(files[-1]) - checkingOutput.append(testOutput) - if not testOutput: - return checkingOutput input = [] for x in filledFiles: input.append(files[x]) output = files[-1] + + try: + data = self.__getData(input) + except: + checkingOutput.append(False) + return checkingOutput + else: + checkingOutput.append(True) + + for x in data: + student = x[0] + login = x[1] + loginCheckingOutput = self.__checkLogin(login, student) + if not loginCheckingOutput[0]: + loginCheckingOutput[1] = loginCheckingOutput[1] + str(x[1:]) + checkingOutput.append(loginCheckingOutput) + return checkingOutput + fname = x[2] + fnameCheckingOutput = self.__checkFname(fname) + if not fnameCheckingOutput[0]: + fnameCheckingOutput[1] = fnameCheckingOutput[1] + str(x[1:]) + checkingOutput.append(fnameCheckingOutput) + return checkingOutput + lname = x[3] + lnameCheckingOutput = self.__checkLname(lname) + if not lnameCheckingOutput[0]: + lnameCheckingOutput[1] = lnameCheckingOutput[1] + str(x[1:]) + checkingOutput.append(lnameCheckingOutput) + return checkingOutput + if student: + school = x[4] + schoolCheckingOutput = self.__checkSchool(school) + if not schoolCheckingOutput[0]: + schoolCheckingOutput[1] = schoolCheckingOutput[1] + str(x[1:]) + checkingOutput.append(schoolCheckingOutput) + return checkingOutput + classX = x[5] + classCheckingOutput = self.__checkClass(classX, school) + if not classCheckingOutput[0]: + classCheckingOutput[1] = classCheckingOutput[1] + str(x[1:]) + checkingOutput.append(classCheckingOutput) + return checkingOutput + checkingOutput.append([True]) + + try: + data = self.__processData(data) + except: + checkingOutput.append(False) + return checkingOutput + else: + checkingOutput.append(True) - testOutput = self.__checkIfCreateOutputFilesIsPossible(files[-1]) + testOutput = self.__checkIfCreatingOutputFilesIsPossible(files[-1]) checkingOutput.append(testOutput) if not testOutput: return checkingOutput + + try: + self.__saveData(output, data) + except: + checkingOutput.append(False) + return checkingOutput + else: + checkingOutput.append(True) + return checkingOutput + dataProcess = dataProcess() @@ -1026,7 +1341,7 @@ class mainWindow: self.GIF2Label.config(width = GUI.R('generateFilesLabelWidth')) self.GIF2Label.config(anchor = GUI.R('generateFilesLabelAnchor')) self.GIF2Label.config(padding = ('0 0 %s 0' % str(2 * GUI.R('generateInputFilesPadding')))) - self.GIF2Label.config(text = 'Plik źródłowy (1)') + self.GIF2Label.config(text = 'Plik źródłowy (2)') self.GIF2Label.pack(side = TK.LEFT) # Plik żródłowy (1) - Ustawienia @@ -1091,7 +1406,7 @@ class mainWindow: self.GIF3Label.config(width = GUI.R('generateFilesLabelWidth')) self.GIF3Label.config(anchor = GUI.R('generateFilesLabelAnchor')) self.GIF3Label.config(padding = ('0 0 %s 0' % str(2 * GUI.R('generateInputFilesPadding')))) - self.GIF3Label.config(text = 'Plik źródłowy (1)') + self.GIF3Label.config(text = 'Plik źródłowy (3)') self.GIF3Label.pack(side = TK.LEFT) # Plik żródłowy (1) - Ustawienia @@ -1156,7 +1471,7 @@ class mainWindow: self.GIF4Label.config(width = GUI.R('generateFilesLabelWidth')) self.GIF4Label.config(anchor = GUI.R('generateFilesLabelAnchor')) self.GIF4Label.config(padding = ('0 0 %s 0' % str(2 * GUI.R('generateInputFilesPadding')))) - self.GIF4Label.config(text = 'Plik źródłowy (1)') + self.GIF4Label.config(text = 'Plik źródłowy (4)') self.GIF4Label.pack(side = TK.LEFT) # Plik żródłowy (1) - Ustawienia @@ -1347,7 +1662,7 @@ class mainWindow: self.loadingPresetFrame = TKttk.Frame(self.formatFrame) self.loadingPresetFrame.config(style = 'layoutFrame.TFrame') - self.loadingPresetFrame.pack(fill = TK.X, side = TK.TOP, padx = GUI.R('outsidelayoutFramesPadX'))#, pady = 5, padx = 10) + self.loadingPresetFrame.pack(fill = TK.X, side = TK.TOP, padx = GUI.R('outsidelayoutFramesPadX')) # "Wybierz preset do edycji lub wpisz nazwę nowego" self.loadingListLabel = TKttk.Label(self.loadingPresetFrame) @@ -1833,6 +2148,7 @@ class mainWindow: GOF = (GOFMailFilename, GOFOfficeFilename) filesList = (GIF1, GIF2, GIF3, GIF4, GOF) output = dataProcess.start(filesList) + print(output) if not output[0]: MSG('E0007', False) else: @@ -1842,45 +2158,70 @@ class mainWindow: if not output[2]: MSG('E0009', False) else: - pass + if not output[3]: + MSG('E0010', False) + else: + if not (output[4])[0]: + MSG('E0011', False, (output[4])[1]) + else: + if not output[5]: + MSG('E0012', False) + else: + if not output[6]: + MSG('E0013', False) + else: + if not output[7]: + MSG('E0014', False) + else: + MSG('I0001', False) + self.GIF1SLocalizationEntryVar.set('') + self.GIF1SFormatComboboxVar.set('') + self.GIF2SLocalizationEntryVar.set('') + self.GIF2SFormatComboboxVar.set('') + self.GIF3SLocalizationEntryVar.set('') + self.GIF3SFormatComboboxVar.set('') + self.GIF4SLocalizationEntryVar.set('') + self.GIF4SFormatComboboxVar.set('') + self.GOFMailEntryVar.set('') + self.GOFOfficeEntryVar.set('') + else: return # Akcje przycisków - TAB2 def loadingButtonAction(self): - formatFileContent = FMT.R(self.loadingList.get()) self.loadingList['state'] = TK.DISABLED self.loadingButton['state'] = TK.DISABLED - self.EPOSTypeVar.set(formatFileContent['student']) + self.EPOSTypeVar.set(FMT.R(self.loadingList.get(), 'student')) self.EPOSTypeStudentRadiobutton['state'] = TK.NORMAL self.EPOSTypeTeacherRadiobutton['state'] = TK.NORMAL self.EPOSPersonSeparatorEntry['state'] = TK.NORMAL - self.EPOSPersonSeparatorVar.set(formatFileContent['personSeparator']) + self.EPOSPersonSeparatorVar.set(FMT.R(self.loadingList.get(), 'personSeparator')) self.EPOSRowSeparatorEntry['state'] = TK.NORMAL - self.EPOSRowSeparatorVar.set(formatFileContent['rowSeparator']) + self.EPOSRowSeparatorVar.set(FMT.R(self.loadingList.get(), 'rowSeparator')) self.EPOSDataSeparatorText['state'] = TK.NORMAL - self.EPOSDataSeparatorText.insert(TK.END, '\n'.join(formatFileContent['dataSeparators'])) + self.EPOSDataSeparatorText.insert(TK.END, '\n'.join(FMT.R(self.loadingList.get(), 'dataSeparators'))) self.EPDLLoginRowSpinbox['state'] = TK.NORMAL - self.EPDLLoginRowVar.set(formatFileContent['loginRow']) + self.EPDLLoginRowVar.set(FMT.R(self.loadingList.get(), 'loginRow')) self.EPDLLoginPosInRowSpinbox['state'] = TK.NORMAL - self.EPDLLoginPosInRowVar.set(formatFileContent['loginPositionInRow']) + self.EPDLLoginPosInRowVar.set(FMT.R(self.loadingList.get(), 'loginPositionInRow')) self.EPDLFnameRowSpinbox['state'] = TK.NORMAL - self.EPDLFnameRowVar.set(formatFileContent['fnameRow']) + self.EPDLFnameRowVar.set(FMT.R(self.loadingList.get(), 'fnameRow')) self.EPDLFnamePosInRowSpinbox['state'] = TK.NORMAL - self.EPDLFnamePosInRowVar.set(formatFileContent['fnamePositionInRow']) + self.EPDLFnamePosInRowVar.set(FMT.R(self.loadingList.get(), 'fnamePositionInRow')) self.EPDLLnameRowSpinbox['state'] = TK.NORMAL - self.EPDLLnameRowVar.set(formatFileContent['lnameRow']) + self.EPDLLnameRowVar.set(FMT.R(self.loadingList.get(), 'lnameRow')) self.EPDLLnamePosInRowSpinbox['state'] = TK.NORMAL - self.EPDLLnamePosInRowVar.set(formatFileContent['lnamePositionInRow']) + self.EPDLLnamePosInRowVar.set(FMT.R(self.loadingList.get(), 'lnamePositionInRow')) self.EPDLSchoolRowSpinbox['state'] = TK.NORMAL - self.EPDLSchoolRowVar.set(formatFileContent['schoolRow']) + self.EPDLSchoolRowVar.set(FMT.R(self.loadingList.get(), 'schoolRow')) self.EPDLSchoolPosInRowSpinbox['state'] = TK.NORMAL - self.EPDLSchoolPosInRowVar.set(formatFileContent['schoolPositionInRow']) + self.EPDLSchoolPosInRowVar.set(FMT.R(self.loadingList.get(), 'schoolPositionInRow')) self.EPDLClassRowSpinbox['state'] = TK.NORMAL - self.EPDLClassRowVar.set(formatFileContent['classRow']) + self.EPDLClassRowVar.set(FMT.R(self.loadingList.get(), 'classRow')) self.EPDLClassPosInRowSpinbox['state'] = TK.NORMAL - self.EPDLClassPosInRowVar.set(formatFileContent['classPositionInRow']) + self.EPDLClassPosInRowVar.set(FMT.R(self.loadingList.get(), 'classPositionInRow')) self.editingPresetSaveButton['state'] = TK.NORMAL self.editingPresetCancelButton['state'] = TK.NORMAL @@ -1935,6 +2276,12 @@ class mainWindow: self.editingPresetSaveButton['state'] = TK.DISABLED self.editingPresetCancelButton['state'] = TK.DISABLED self.loadingList['values'] = tuple(FMT.getList()) + + def updatePresetListInGenerateTab(self): + self.GIF1SFormatCombobox['values'] = tuple(FMT.getList()) + self.GIF2SFormatCombobox['values'] = tuple(FMT.getList()) + self.GIF3SFormatCombobox['values'] = tuple(FMT.getList()) + self.GIF4SFormatCombobox['values'] = tuple(FMT.getList()) def editingPresetSave(self): formatFileContentToSave = { @@ -1956,6 +2303,7 @@ class mainWindow: if not FMT.W(self.loadingList.get(), formatFileContentToSave): return self.editingPresetClear() + self.updatePresetListInGenerateTab() def editingPresetSaveButtonAction(self): if self.loadingList.get() not in FMT.getList(): From b2fcfc9eb504f6da0f6ecc6619f0eef9f25607e4 Mon Sep 17 00:00:00 2001 From: Mateusz Skoczek Date: Thu, 27 Aug 2020 13:55:50 +0200 Subject: [PATCH 19/29] 4.0 Alpha (Build 20240) --- changelog-UC.txt | 8 ++- default-configs/config.cfg | 3 +- generator.pyw | 108 +++++++++++++++++++++++-------------- 3 files changed, 77 insertions(+), 42 deletions(-) diff --git a/changelog-UC.txt b/changelog-UC.txt index 3d67d8e..3526f45 100644 --- a/changelog-UC.txt +++ b/changelog-UC.txt @@ -99,4 +99,10 @@ - Zastosowanie nowego systemu przetwarzania plików konfiguracyjnych dla plików formatu - Wstępne zastosowanie nowego systemu przetwarzania plików konfiguracyjnych dla pliku 'config.cfg' w zapisie danych - Ukończenie skryptu przetwarzającego dane -- Poprawki błędów \ No newline at end of file +- Poprawki błędów + +4.0 Alpha (Build 20240) +- Przeniesienie informacji o dozwolonych znakach separatora do zmiennej w programie +- Przeniesienie informacji o kodowaniu wejsciowym z pliku 'config.cfg' do plików formatu +- Zmiany w interfejsie w związku z dodaniem do plików formatu informacji o kodowaniu wejściowym +- Stworzenie listy dozwolonych kodowań w zmiennej w programie \ No newline at end of file diff --git a/default-configs/config.cfg b/default-configs/config.cfg index 67c6e3e..ea808c4 100644 --- a/default-configs/config.cfg +++ b/default-configs/config.cfg @@ -1,6 +1,5 @@ secret(S) = entersecretstringhere -allowedCharactersInSeparator(SCA) = ['`', '~', '!', '@', '#', '$', '%', '^', '&', '(', ')', '-', '_', '=', '+', '[', ']', '\', '|', ';', ':', ''', '"', ',', '<', '.', '>', '/', '?', ' '] -inputCoding(S) = utf-8 +allowedCoding(A) = ['utf-8', 'ANSI'] mailOutputCoding(S) = utf-8 officeOutputCoding(S) = utf-8 domain(S) = losobolew.pl diff --git a/generator.pyw b/generator.pyw index 30f6c89..9fbd10d 100644 --- a/generator.pyw +++ b/generator.pyw @@ -13,6 +13,7 @@ # ----------------------------------------- # Zmienne # ----------------------------------------- # class VAR: + # Informacje o programie programName = 'Generator CSV' programVersion = '4.0' programVersionStage = 'Alpha' @@ -21,6 +22,12 @@ class VAR: programAuthors = ['Mateusz Skoczek'] programToW = ['styczeń', 2019, 'wrzesień', 2020] + # Dozwolone kodowanie plików + allowedCoding = ['utf-8'] + + # Dozwolone znaki + allowedCharactersInSeparator = ['`', '~', '!', '@', '#', '$', '%', '^', '&', '(', ')', '-', '_', '=', '+', '[', ']', ' ', '?', '/', '>', '.', '<', ',', '"', "'", ':', ';', '|'] + @@ -184,20 +191,6 @@ class CFG: # Funkcje sprawdzające poprawność recordu - def __checkSCA(self, write, record, var): - if write: - pass - else: - try: - var = var[1:-2].split(', ') - var = [x[1:-1] for x in var] - except: - return (False, 'Niepoprawne dane - klucz: %s' % record) - for x in var: - if len(x) != 1: - return (False, 'Niepoprawne dane - klucz: %s' % record) - return [True, var] - def __checkI(self, write, record, var): if write: pass @@ -259,7 +252,6 @@ class CFG: - def R(self, record): self.__checkIfFileExist(False) content = {} @@ -280,13 +272,6 @@ class CFG: # String var = var[0].strip('\r') return var - elif var[1] == 'SCA': - # Single char array - checkingOutput = self.__checkSCA(False, record, var[0]) - if checkingOutput[0]: - return checkingOutput[1] - else: - MSG('E0003', True, checkingOutput[1]) elif var[1] == 'I': # Integer checkingOutput = self.__checkI(False, record, var[0]) @@ -333,14 +318,6 @@ class CFG: if type == 'S': # String pass - elif type == 'SCA': - # Single char array - checkingOutput = self.__checkSCA(True, name, var) - if checkingOutput[0]: - var = checkingOutput[1] - else: - MSG('E0006', False, checkingOutput[1]) - return False elif type == 'I': # Integer checkingOutput = self.__checkI(True, name, var) @@ -566,22 +543,20 @@ class FMT: return [True, var] def __checkSs(self, record, var): - allowedCharactersInSeparator = CFG.R('allowedCharactersInSeparator') check = var check = check.strip('') for x in check: - if x not in allowedCharactersInSeparator: + if x not in VAR.allowedCharactersInSeparator: return [False, 'Niepoprawne dane - klucz: %s' % var] return [True, var] def __checkAs(self, write, record, var): - allowedCharactersInSeparator = CFG.R('allowedCharactersInSeparator') if write: check = var for x in check: x = x.strip('') for y in x: - if y not in allowedCharactersInSeparator: + if y not in VAR.allowedCharactersInSeparator: return [False, 'Niepoprawne dane - klucz: %s' % var] var = str(var) else: @@ -590,7 +565,7 @@ class FMT: for x in check: x = x.strip('') for y in x: - if y not in allowedCharactersInSeparator: + if y not in VAR.allowedCharactersInSeparator: return [False, 'Niepoprawne dane - klucz: %s' % var] var = new_contentVar return [True, var] @@ -604,6 +579,11 @@ class FMT: except: return (False, 'Niepoprawne dane - klucz: %s' % record) return [True, var] + + def __checkSc(self, record, var): + if var not in VAR.allowedCoding: + return [False, 'Niepoprawne dane - klucz: %s' % var] + return [True, var] # Funkcja zwracająca listę presetów @@ -667,6 +647,13 @@ class FMT: return checkingOutput[1] else: MSG('E0006', False, checkingOutput[1]) + elif var[1] == 'Sc': + # Integer + checkingOutput = self.__checkSc(record, var[0]) + if checkingOutput[0]: + return checkingOutput[1] + else: + MSG('E0006', False, checkingOutput[1]) else: MSG('E0006', True, 'Nie można rozpoznać typu klucza %s' % record) else: @@ -685,6 +672,7 @@ class FMT: "schoolPositionInRow" : 0, "classRow" : 0, "classPositionInRow" : 0, + "inputCoding" : 'utf-8', } var = content[record] return var @@ -721,6 +709,7 @@ class FMT: "schoolPositionInRow" : ['0', 'I'], "classRow" : ['0', 'I'], "classPositionInRow" : ['0', 'I'], + "inputCoding" : ['utf-8', 'Sc'] } for x in changes: name = x @@ -755,6 +744,13 @@ class FMT: else: MSG('E0006', False, checkingOutput[1]) return False + elif type == 'Sc': + checkingOutput = self.__checkSc(name, var) + if checkingOutput[0]: + var = checkingOutput[1] + else: + MSG('E0006', False, checkingOutput[1]) + return False else: MSG('E0003', False, 'Nie można rozpoznać typu klucza %s' % name) return False @@ -791,7 +787,7 @@ class dataProcess: def __checkIfInputFilesIsReadable(self, files, filledFiles): for x in filledFiles: try: - check = CD.open((files[x])[0], 'r', CFG.R('inputCoding')) + check = CD.open((files[x])[0], 'r', FMT.R((files[x])[1], 'inputCoding')) except: return False return True @@ -874,7 +870,7 @@ class dataProcess: schoolLocation = [FMT.R(format, 'schoolRow'), FMT.R(format, 'schoolPositionInRow')] classLocation = [FMT.R(format, 'classRow'), FMT.R(format, 'classPositionInRow')] student = FMT.R(format, 'student') - file = CD.open(path, 'r', CFG.R('inputCoding')).read().split(personSeparator) + file = CD.open(path, 'r', FMT.R(format, 'inputCoding')).read().split(personSeparator) for x in file: lines = x.split(linesSeparator) dataX = [] @@ -1995,7 +1991,36 @@ class mainWindow: self.EPDLClassPosInRowSpinbox.config(state = TK.DISABLED) self.EPDLClassPosInRowSpinbox.config(style = 'spinbox1.TSpinbox') self.EPDLClassPosInRowSpinbox.grid(row = 5, column = 2, padx = GUI.R('EPDataLocalizationPadX'), pady = GUI.R('EPDataLocalizationPadY')) - + + # + self.formatSeparator4Frame = TKttk.Frame(self.editingPresetDLFrame) + self.formatSeparator4Frame.config(style = 'layoutFrame.TFrame') + self.formatSeparator4Frame.grid(row = 6, column = 0, columnspan = 3) + + self.formatSeparator4 = TKttk.Separator(self.formatSeparator4Frame) + self.formatSeparator4.config(style = 'separator1.TSeparator') + self.formatSeparator4.config(orient = TK.HORIZONTAL) + self.formatSeparator4.pack(padx = GUI.R('formatHorizontalSeparatorPadY'), fill = TK.X, expand = 1) + + # "Kodowanie" + self.formatInputCodingLabel = TKttk.Label(self.editingPresetDLFrame) + self.formatInputCodingLabel.config(style = 'label1.TLabel') + self.formatInputCodingLabel.config(text = 'Kodowanie') + self.formatInputCodingLabel.grid(row = 7, column = 0, padx = GUI.R('EPDataLocalizationPadX'), pady = GUI.R('EPDataLocalizationPadY')) + + # Kodowanie - Combobox + self.formatInputCodingVar = TK.StringVar() + self.formatInputCodingCombobox = TKttk.Combobox(self.editingPresetDLFrame) + self.formatInputCodingCombobox.config(textvariable = self.formatInputCodingVar) + self.formatInputCodingCombobox.config(state = TK.DISABLED) + self.formatInputCodingCombobox.config(style = 'combobox1.TCombobox') + self.formatInputCodingCombobox.option_add("*TCombobox*Listbox.background", GUI.R('combobox2ListBoxBackground')) + self.formatInputCodingCombobox.option_add("*TCombobox*Listbox.foreground", GUI.R('combobox2ListBoxForeground')) + self.formatInputCodingCombobox.option_add("*TCombobox*Listbox.selectBackground", GUI.R('combobox2ListBoxSelectBackground')) + self.formatInputCodingCombobox.option_add("*TCombobox*Listbox.selectForeground", GUI.R('combobox2ListBoxSelectForeground')) + self.formatInputCodingCombobox.grid(row = 7, column = 1, columnspan = 2, padx = GUI.R('EPDataLocalizationPadX'), pady = GUI.R('EPDataLocalizationPadY')) + self.formatInputCodingCombobox['values'] = tuple(VAR.allowedCoding) + ############################### ######################################### @@ -2148,7 +2173,6 @@ class mainWindow: GOF = (GOFMailFilename, GOFOfficeFilename) filesList = (GIF1, GIF2, GIF3, GIF4, GOF) output = dataProcess.start(filesList) - print(output) if not output[0]: MSG('E0007', False) else: @@ -2222,6 +2246,8 @@ class mainWindow: self.EPDLClassRowVar.set(FMT.R(self.loadingList.get(), 'classRow')) self.EPDLClassPosInRowSpinbox['state'] = TK.NORMAL self.EPDLClassPosInRowVar.set(FMT.R(self.loadingList.get(), 'classPositionInRow')) + self.formatInputCodingCombobox['state'] = 'readonly' + self.formatInputCodingVar.set(FMT.R(self.loadingList.get(), 'inputCoding')) self.editingPresetSaveButton['state'] = TK.NORMAL self.editingPresetCancelButton['state'] = TK.NORMAL @@ -2241,6 +2267,7 @@ class mainWindow: "schoolPositionInRow" : 0, "classRow" : 0, "classPositionInRow" : 0, + "inputCoding" : '', } self.loadingList['state'] = TK.NORMAL self.loadingButton['state'] = TK.NORMAL @@ -2273,6 +2300,8 @@ class mainWindow: self.EPDLClassRowVar.set(formatFileContent['classRow']) self.EPDLClassPosInRowSpinbox['state'] = TK.DISABLED self.EPDLClassPosInRowVar.set(formatFileContent['classPositionInRow']) + self.formatInputCodingCombobox['state'] = TK.DISABLED + self.formatInputCodingVar.set(formatFileContent['inputCoding']) self.editingPresetSaveButton['state'] = TK.DISABLED self.editingPresetCancelButton['state'] = TK.DISABLED self.loadingList['values'] = tuple(FMT.getList()) @@ -2299,6 +2328,7 @@ class mainWindow: "schoolPositionInRow" : int(self.EPDLSchoolPosInRowSpinbox.get()), "classRow" : int(self.EPDLClassRowSpinbox.get()), "classPositionInRow" : int(self.EPDLClassPosInRowSpinbox.get()), + "inputCoding" : self.formatInputCodingCombobox.get() } if not FMT.W(self.loadingList.get(), formatFileContentToSave): return From 78d28884871b4b95463f8c9588d223f018a413a1 Mon Sep 17 00:00:00 2001 From: Mateusz Skoczek Date: Fri, 28 Aug 2020 01:16:22 +0200 Subject: [PATCH 20/29] 4.0 Alpha (Build 20241) --- changelog-UC.txt | 6 +- default-configs/config.cfg | 5 +- default-configs/style.cfg | 30 +- generator.pyw | 555 ++++++++++++++++++++++++++++++++++++- 4 files changed, 582 insertions(+), 14 deletions(-) diff --git a/changelog-UC.txt b/changelog-UC.txt index 3526f45..1176460 100644 --- a/changelog-UC.txt +++ b/changelog-UC.txt @@ -105,4 +105,8 @@ - Przeniesienie informacji o dozwolonych znakach separatora do zmiennej w programie - Przeniesienie informacji o kodowaniu wejsciowym z pliku 'config.cfg' do plików formatu - Zmiany w interfejsie w związku z dodaniem do plików formatu informacji o kodowaniu wejściowym -- Stworzenie listy dozwolonych kodowań w zmiennej w programie \ No newline at end of file +- Stworzenie listy dozwolonych kodowań w zmiennej w programie + +4.0 Alpha (Build 20241) +- Ukończenie karty 'USTAWIENIA' +- Zastosowanie nowego systemu przetwarzania plików konfiguracyjnych dla pliku 'config.cfg' w zapisie danych \ No newline at end of file diff --git a/default-configs/config.cfg b/default-configs/config.cfg index ea808c4..628ff5e 100644 --- a/default-configs/config.cfg +++ b/default-configs/config.cfg @@ -1,7 +1,6 @@ secret(S) = entersecretstringhere -allowedCoding(A) = ['utf-8', 'ANSI'] -mailOutputCoding(S) = utf-8 -officeOutputCoding(S) = utf-8 +mailOutputCoding(Sc) = utf-8 +officeOutputCoding(Sc) = utf-8 domain(S) = losobolew.pl quota(I) = 500 country(S) = Rzeczypospolita Polska diff --git a/default-configs/style.cfg b/default-configs/style.cfg index 4c6908c..ea75d87 100644 --- a/default-configs/style.cfg +++ b/default-configs/style.cfg @@ -26,6 +26,9 @@ label1Font(F) = Segoe UI;10 label2BG(C) = #21242D label2TextColor(C) = #C0C0C0 label2Font(F) = Segoe UI;9 +label3BG(C) = #21242D +label3TextColor(C) = #C0C0C0 +label3Font(F) = Segoe UI;7 combobox1ArrowColor(C) = #C0C0C0 combobox1ButtonColor(C) = #333842 combobox1BorderColor(C) = #21242D @@ -106,4 +109,29 @@ editingPresetButtonsPadY(I) = 6 editingPresetSaveButtonWidth(I) = 25 editingPresetCancelButtonWidth(I) = 25 settingsTabIcon(P) = assets/tab-icons/settings.png -aboutTabIcon(P) = assets/tab-icons/about.png \ No newline at end of file +settingsHorizontalSeparatorPadY(I) = 10 +settingsVerticalSeparatorPadY(I) = 10 +settingsCodeLabelWidth(I) = 35 +settingsCodeLabelAnchor(FAanchor) = w +settingsOtherLabelWidth(I) = 35 +settingsOtherLabelAnchor(FAanchor) = w +settingsSchoolDataLabelAnchor(FAanchor) = center +settingsButtonsPadY(I) = 6 +settingsButtonSaveWidth(I) = 20 +settingsButtonCancelWidth(I) = 15 +settingsButtonPDUOWidth(I) = 35 +settingsButtonPDUWWidth(I) = 35 +settingsButtonZPFWidth(I) = 35 +aboutTabIcon(P) = assets/tab-icons/about.png +ZPFWindowWidth(I) = 500 +ZPFWindowHeight(I) = 400 +ZPFWindowWidthResizable(B) = 0 +ZPFWindowHeightResizable(B) = 0 +ZPFWindowMainBG(C) = #21242D +listbox1BG(C) = #333842 +listbox1TextColor(C) = #C0C0C0 +listbox1Relief(FArelief) = flat +listbox1BorderWidth(I) = 0 +listbox1ActiveStyle(FAactivestyle) = none +listbox1HighlightThickness(I) = 0 +listbox1SelectBG(C) = #4F4F4F \ No newline at end of file diff --git a/generator.pyw b/generator.pyw index 9fbd10d..1e4c5f7 100644 --- a/generator.pyw +++ b/generator.pyw @@ -72,6 +72,10 @@ MSGlist = { 'A0001' : 'Czy chcesz zapisać? Zostanie utworzony nowy plik', 'A0002' : 'Czy chcesz zapisać? Plik zostanie nadpisany', 'A0003' : 'Czy chcesz rozpocząć przetwarzanie plików?', + 'A0004' : 'Czy chcesz zapisać?', + 'A0005' : 'Czy na pewno chcesz przywrócić domyślne ustawienia ogólne?', + 'A0006' : 'Czy na pewno chcesz przywrócić domyślne ustawienia wyglądu?', + 'A0007' : 'Czy na pewno chcesz usunąc zaznaczone format presety?', 'E0007' : 'Wymagany przynajmniej jeden plik wejściowy', 'E0008' : 'Nie można odnaleźć jednego z powyższych plików', 'E0009' : 'Nie można odnaleźć jednego z powyższych format presetów', @@ -81,6 +85,8 @@ MSGlist = { 'E0013' : 'Nie można utworzyć plików wejściowych', 'E0014' : 'Nie można zapisać plików wejściowych', 'I0001' : 'Operacja ukończona pomyślnie', + 'I0002' : 'Aplikacja zostanie zamknięta w celu przeładowania ustawień', + 'E0015' : 'Nie można usunąć wybranych format presetów', } def MSG(code, terminate, *optionalInfo): @@ -193,7 +199,7 @@ class CFG: # Funkcje sprawdzające poprawność recordu def __checkI(self, write, record, var): if write: - pass + var = str(var) else: try: var = int(var) @@ -203,7 +209,52 @@ class CFG: def __checkD(self, write, record, var): if write: - pass + varX = '' + if var['D'] == None: + varX += '*' + else: + day = str(var['D']) + if len(day) == 1: + day = '0' + day + varX += day + varX += '.' + if var['M'] == None: + varX += '*' + else: + month = str(var['M']) + if len(month) == 1: + month = '0' + month + varX += month + varX += '.' + if var['Y'] == None: + varX += '*' + else: + varX += str(var['Y']) + varX += ' ' + if var['h'] == None: + varX += '*' + else: + hour = str(var['h']) + if len(hour) == 1: + hour = '0' + hour + varX += hour + varX += ':' + if var['m'] == None: + varX += '*' + else: + minute = str(var['m']) + if len(minute) == 1: + minute = '0' + minute + varX += minute + varX += ':' + if var['s'] == None: + varX += '*' + else: + seconds = str(var['s']) + if len(seconds) == 1: + seconds = '0' + seconds + varX += seconds + var = varX else: varToReturn = {} var = var.split(' ') @@ -228,7 +279,23 @@ class CFG: def __checkMSAs(self, write, record, var): if write: - pass + varX = [] + while var.count(''): + var.remove('') + for x in var: + check = x.split(' | ') + if len(check) != 3: + return (False, 'Niepoprawne dane - klucz: %s' % record) + try: + checkX = int(check[1]) + except: + return (False, 'Niepoprawne dane - klucz: %s' % record) + if not (check[2] == '0' or check[2] == '1'): + return (False, 'Niepoprawne dane - klucz: %s' % record) + x = x.replace(' | ', ', ') + x = '[' + x + ']' + varX.append(x) + var = '|'.join(varX) else: var = var.split('|') var = [x.strip('\r').strip('[').strip(']').split(', ') for x in var] @@ -250,6 +317,12 @@ class CFG: var = newVar return [True, var] + def __checkSc(self, record, var): + var = var.strip('\r') + if var not in VAR.allowedCoding: + return [False, 'Niepoprawne dane - klucz: %s' % record] + return [True, var] + def R(self, record): @@ -272,6 +345,13 @@ class CFG: # String var = var[0].strip('\r') return var + elif var[1] == 'Sc': + # Integer + checkingOutput = self.__checkSc(record, var[0]) + if checkingOutput[0]: + return checkingOutput[1] + else: + MSG('E0003', True, checkingOutput[1]) elif var[1] == 'I': # Integer checkingOutput = self.__checkI(False, record, var[0]) @@ -296,8 +376,8 @@ class CFG: else: MSG('E0003', True, 'Nie można rozpoznać typu klucza %s' % record) - def W(self, preset, changes): - self.__checkIfFolderExist() + def W(self, changes): + self.__checkIfFileExist(True) file = CD.open(str(appdata) + '\Generator CSV\config.cfg', 'r', 'utf-8').read().split('\n') if file[-1] == '': file = file[:-1] @@ -318,6 +398,14 @@ class CFG: if type == 'S': # String pass + elif type == 'Sc': + # Integer + checkingOutput = self.__checkSc(name, var) + if checkingOutput[0]: + var = checkingOutput[1] + else: + MSG('E0006', False, checkingOutput[1]) + return False elif type == 'I': # Integer checkingOutput = self.__checkI(True, name, var) @@ -420,6 +508,7 @@ class GUI: 'anchor' : ['center', 'nw', 'n', 'ne', 'w', 'e', 'sw', 's', 'se'], 'relief' : ['flat', 'raised', 'sunken', 'groove', 'ridge'], 'fill' : ['x', 'y', 'both'], + 'activestyle' : ['dotbox', 'none', 'underline'] } if var not in arrays[array]: return [False, 'Niepoprawne dane - klucz: %s' % record] @@ -1147,6 +1236,13 @@ class mainWindow: "font" : GUI.R('label2Font') }, }, + "label3.TLabel": { + "configure": { + "background": GUI.R('label3BG'), + "foreground": GUI.R('label3TextColor'), + "font" : GUI.R('label3Font') + }, + }, "combobox1.TCombobox": { "configure": { "arrowcolor": GUI.R('combobox1ArrowColor'), @@ -1992,7 +2088,7 @@ class mainWindow: self.EPDLClassPosInRowSpinbox.config(style = 'spinbox1.TSpinbox') self.EPDLClassPosInRowSpinbox.grid(row = 5, column = 2, padx = GUI.R('EPDataLocalizationPadX'), pady = GUI.R('EPDataLocalizationPadY')) - # + # Separator self.formatSeparator4Frame = TKttk.Frame(self.editingPresetDLFrame) self.formatSeparator4Frame.config(style = 'layoutFrame.TFrame') self.formatSeparator4Frame.grid(row = 6, column = 0, columnspan = 3) @@ -2000,7 +2096,7 @@ class mainWindow: self.formatSeparator4 = TKttk.Separator(self.formatSeparator4Frame) self.formatSeparator4.config(style = 'separator1.TSeparator') self.formatSeparator4.config(orient = TK.HORIZONTAL) - self.formatSeparator4.pack(padx = GUI.R('formatHorizontalSeparatorPadY'), fill = TK.X, expand = 1) + self.formatSeparator4.pack(padx = GUI.R('formatHorizontalSeparatorPadY'), pady = 10, fill = TK.X, expand = 1) # "Kodowanie" self.formatInputCodingLabel = TKttk.Label(self.editingPresetDLFrame) @@ -2013,7 +2109,7 @@ class mainWindow: self.formatInputCodingCombobox = TKttk.Combobox(self.editingPresetDLFrame) self.formatInputCodingCombobox.config(textvariable = self.formatInputCodingVar) self.formatInputCodingCombobox.config(state = TK.DISABLED) - self.formatInputCodingCombobox.config(style = 'combobox1.TCombobox') + self.formatInputCodingCombobox.config(style = 'combobox2.TCombobox') self.formatInputCodingCombobox.option_add("*TCombobox*Listbox.background", GUI.R('combobox2ListBoxBackground')) self.formatInputCodingCombobox.option_add("*TCombobox*Listbox.foreground", GUI.R('combobox2ListBoxForeground')) self.formatInputCodingCombobox.option_add("*TCombobox*Listbox.selectBackground", GUI.R('combobox2ListBoxSelectBackground')) @@ -2036,7 +2132,7 @@ class mainWindow: ############################################################# - # (2) Przyciski ############################################# + # (1) Przyciski ############################################# self.editingPresetButtonsFrame = TKttk.Frame(self.formatFrame) self.editingPresetButtonsFrame.config(style = 'layoutFrame.TFrame') @@ -2089,6 +2185,334 @@ class mainWindow: self.settingsFrame.config(style = 'contentTabFrame.TFrame') self.settingsFrame.pack(fill = GUI.R('contentTabFrameFill'), expand = GUI.R('contentTabFrameExpand'), padx = GUI.R('tabFramePadding'), pady = GUI.R('tabFramePadding')) + # (1) Ustwienia ############################################# + + self.settingsMainFrame = TKttk.Frame(self.settingsFrame) + self.settingsMainFrame.config(style = 'layoutFrame.TFrame') + self.settingsMainFrame.pack(side = TK.TOP, fill = TK.BOTH, expand = 1) + + # (2) Po lewo ##################################### + + self.settingsLeftFrame = TKttk.Frame(self.settingsMainFrame) + self.settingsLeftFrame.config(style = 'layoutFrame.TFrame') + self.settingsLeftFrame.pack(side = TK.LEFT, fill = TK.BOTH, expand = 1) + + # (3) Kodowanie ######################### + + self.settingsCodeFrame = TKttk.Frame(self.settingsLeftFrame) + self.settingsCodeFrame.config(style = 'layoutFrame.TFrame') + self.settingsCodeFrame.pack(side = TK.TOP, fill = TK.X) + + # (4) Kodowanie dla pliku poczty + + self.settingsMailCodeFrame = TKttk.Frame(self.settingsCodeFrame) + self.settingsMailCodeFrame.config(style = 'layoutFrame.TFrame') + self.settingsMailCodeFrame.pack(side = TK.TOP, fill = TK.X, pady = 6, expand = 1) + + # 'Kodowanie wyjściowe dla pliku poczty' + self.settingsMailCodeLabel = TKttk.Label(self.settingsMailCodeFrame) + self.settingsMailCodeLabel.config(style = 'label1.TLabel') + self.settingsMailCodeLabel.config(width = GUI.R('settingsCodeLabelWidth')) + self.settingsMailCodeLabel.config(anchor = GUI.R('settingsCodeLabelAnchor')) + self.settingsMailCodeLabel.config(text = 'Kodowanie wyjściowe dla pliku poczty') + self.settingsMailCodeLabel.pack(side = TK.LEFT) + + # Kodowanie dla poczty - Combobox + self.settingsMailCodeVar = TK.StringVar() + self.settingsMailCodeCombobox = TKttk.Combobox(self.settingsMailCodeFrame) + self.settingsMailCodeCombobox.config(textvariable = self.settingsMailCodeVar) + self.settingsMailCodeCombobox.config(style = 'combobox2.TCombobox') + self.settingsMailCodeCombobox.option_add("*TCombobox*Listbox.background", GUI.R('combobox2ListBoxBackground')) + self.settingsMailCodeCombobox.option_add("*TCombobox*Listbox.foreground", GUI.R('combobox2ListBoxForeground')) + self.settingsMailCodeCombobox.option_add("*TCombobox*Listbox.selectBackground", GUI.R('combobox2ListBoxSelectBackground')) + self.settingsMailCodeCombobox.option_add("*TCombobox*Listbox.selectForeground", GUI.R('combobox2ListBoxSelectForeground')) + self.settingsMailCodeCombobox.pack(side = TK.RIGHT, fill = TK.X, expand = 1) + self.settingsMailCodeCombobox['values'] = tuple(VAR.allowedCoding) + self.settingsMailCodeCombobox.set(CFG.R('mailOutputCoding')) + + ############################### + + # (4) Kodowanie dla pliku office + + self.settingsOfficeCodeFrame = TKttk.Frame(self.settingsCodeFrame) + self.settingsOfficeCodeFrame.config(style = 'layoutFrame.TFrame') + self.settingsOfficeCodeFrame.pack(side = TK.BOTTOM, fill = TK.X, pady = 6, expand = 1) + + # 'Kodowanie wyjściowe dla pliku office' + self.settingsOfficeCodeLabel = TKttk.Label(self.settingsOfficeCodeFrame) + self.settingsOfficeCodeLabel.config(style = 'label1.TLabel') + self.settingsOfficeCodeLabel.config(width = GUI.R('settingsCodeLabelWidth')) + self.settingsOfficeCodeLabel.config(anchor = GUI.R('settingsCodeLabelAnchor')) + self.settingsOfficeCodeLabel.config(text = 'Kodowanie wyjściowe dla pliku office') + self.settingsOfficeCodeLabel.pack(side = TK.LEFT) + + # Kodowanie dla poczty - Combobox + self.settingsOfficeCodeVar = TK.StringVar() + self.settingsOfficeCodeCombobox = TKttk.Combobox(self.settingsOfficeCodeFrame) + self.settingsOfficeCodeCombobox.config(textvariable = self.settingsOfficeCodeVar) + self.settingsOfficeCodeCombobox.config(style = 'combobox2.TCombobox') + self.settingsOfficeCodeCombobox.option_add("*TCombobox*Listbox.background", GUI.R('combobox2ListBoxBackground')) + self.settingsOfficeCodeCombobox.option_add("*TCombobox*Listbox.foreground", GUI.R('combobox2ListBoxForeground')) + self.settingsOfficeCodeCombobox.option_add("*TCombobox*Listbox.selectBackground", GUI.R('combobox2ListBoxSelectBackground')) + self.settingsOfficeCodeCombobox.option_add("*TCombobox*Listbox.selectForeground", GUI.R('combobox2ListBoxSelectForeground')) + self.settingsOfficeCodeCombobox.pack(side = TK.RIGHT, fill = TK.X, expand = 1) + self.settingsOfficeCodeCombobox['values'] = tuple(VAR.allowedCoding) + self.settingsOfficeCodeCombobox.set(CFG.R('officeOutputCoding')) + + ############################### + + ######################################### + + # (3) Separator ######################### + + self.settingsSeparator3 = TKttk.Separator(self.settingsLeftFrame) + self.settingsSeparator3.config(style = 'separator1.TSeparator') + self.settingsSeparator3.config(orient = TK.HORIZONTAL) + self.settingsSeparator3.pack(fill = TK.X, pady = GUI.R('settingsHorizontalSeparatorPadY')) + + ######################################### + + # (3) Inne dane ######################### + + self.settingsOtherFrame = TKttk.Frame(self.settingsLeftFrame) + self.settingsOtherFrame.config(style = 'layoutFrame.TFrame') + self.settingsOtherFrame.pack(fill = TK.X) + + # (4) Domena ################## + + self.settingsOtherDomainFrame = TKttk.Frame(self.settingsOtherFrame) + self.settingsOtherDomainFrame.config(style = 'layoutFrame.TFrame') + self.settingsOtherDomainFrame.pack(fill = TK.X, pady = 6, expand = 1) + + # 'Domena (używana w mailu)' + self.settingsOtherDomainLabel = TKttk.Label(self.settingsOtherDomainFrame) + self.settingsOtherDomainLabel.config(style = 'label1.TLabel') + self.settingsOtherDomainLabel.config(width = GUI.R('settingsOtherLabelWidth')) + self.settingsOtherDomainLabel.config(anchor = GUI.R('settingsOtherLabelAnchor')) + self.settingsOtherDomainLabel.config(text = 'Domena (używana w mailu)') + self.settingsOtherDomainLabel.pack(side = TK.LEFT) + + # Domena - Entry + self.settingsOtherDomainVar = TK.StringVar() + self.settingsOtherDomainEntry = TKttk.Entry(self.settingsOtherDomainFrame) + self.settingsOtherDomainEntry.config(style = 'entry1.TEntry') + self.settingsOtherDomainEntry.config(textvariable = self.settingsOtherDomainVar) + self.settingsOtherDomainEntry.pack(side = TK.RIGHT, fill = TK.X, expand = 1) + self.settingsOtherDomainVar.set(CFG.R('domain')) + + ############################### + + # (4) Quota ################### + + self.settingsOtherQuotaFrame = TKttk.Frame(self.settingsOtherFrame) + self.settingsOtherQuotaFrame.config(style = 'layoutFrame.TFrame') + self.settingsOtherQuotaFrame.pack(fill = TK.X, pady = 6, expand = 1) + + # 'Quota (MB)' + self.settingsOtherQuotaLabel = TKttk.Label(self.settingsOtherQuotaFrame) + self.settingsOtherQuotaLabel.config(style = 'label1.TLabel') + self.settingsOtherQuotaLabel.config(width = GUI.R('settingsOtherLabelWidth')) + self.settingsOtherQuotaLabel.config(anchor = GUI.R('settingsOtherLabelAnchor')) + self.settingsOtherQuotaLabel.config(text = 'Quota (MB)') + self.settingsOtherQuotaLabel.pack(side = TK.LEFT) + + # Domena - Entry + self.settingsOtherQuotaVar = TK.IntVar() + self.settingsOtherQuotaSpinbox = TKttk.Spinbox(self.settingsOtherQuotaFrame) + self.settingsOtherQuotaSpinbox.config(textvariable = self.settingsOtherQuotaVar) + self.settingsOtherQuotaSpinbox.config(from_ = 0) + self.settingsOtherQuotaSpinbox.config(to = 10000000000000000000000) + self.settingsOtherQuotaSpinbox.config(style = 'spinbox1.TSpinbox') + self.settingsOtherQuotaSpinbox.pack(side = TK.RIGHT, fill = TK.X, expand = 1) + self.settingsOtherQuotaSpinbox.set(CFG.R('quota')) + + ############################### + + # (4) Kraj ################## + + self.settingsOtherCountryFrame = TKttk.Frame(self.settingsOtherFrame) + self.settingsOtherCountryFrame.config(style = 'layoutFrame.TFrame') + self.settingsOtherCountryFrame.pack(fill = TK.X, pady = 6, expand = 1) + + # 'Kraj (zapisany w danych na office)' + self.settingsOtherCountryLabel = TKttk.Label(self.settingsOtherCountryFrame) + self.settingsOtherCountryLabel.config(style = 'label1.TLabel') + self.settingsOtherCountryLabel.config(width = GUI.R('settingsOtherLabelWidth')) + self.settingsOtherCountryLabel.config(anchor = GUI.R('settingsOtherLabelAnchor')) + self.settingsOtherCountryLabel.config(text = 'Kraj (zapisany w danych na office)') + self.settingsOtherCountryLabel.pack(side = TK.LEFT) + + # Domena - Entry + self.settingsOtherCountryVar = TK.StringVar() + self.settingsOtherCountryEntry = TKttk.Entry(self.settingsOtherCountryFrame) + self.settingsOtherCountryEntry.config(style = 'entry1.TEntry') + self.settingsOtherCountryEntry.config(textvariable = self.settingsOtherCountryVar) + self.settingsOtherCountryEntry.pack(side = TK.RIGHT, fill = TK.X, expand = 1) + self.settingsOtherCountryVar.set(CFG.R('country')) + + ############################### + + # (4) Rozpoczęcir roku szkolnego + + self.settingsOtherDRRSFrame = TKttk.Frame(self.settingsOtherFrame) + self.settingsOtherDRRSFrame.config(style = 'layoutFrame.TFrame') + self.settingsOtherDRRSFrame.pack(fill = TK.X, expand = 1, pady = 6) + + # 'Rozpoczęcie roku szkolnego (Dzień | Miesiąc)' + self.settingsOtherDRRSLabel = TKttk.Label(self.settingsOtherDRRSFrame) + self.settingsOtherDRRSLabel.config(style = 'label1.TLabel') + self.settingsOtherDRRSLabel.config(width = GUI.R('settingsOtherLabelWidth')) + self.settingsOtherDRRSLabel.config(anchor = GUI.R('settingsOtherLabelAnchor')) + self.settingsOtherDRRSLabel.config(text = 'Rozpoczęcie roku szkolnego (DD | MM)') + self.settingsOtherDRRSLabel.pack(side = TK.LEFT) + + # Rozpoczęcie roku szkolnego - Miesiąc + self.settingsOtherDRRSMonthVar = TK.IntVar() + self.settingsOtherDRRSMonthSpinbox = TKttk.Spinbox(self.settingsOtherDRRSFrame) + self.settingsOtherDRRSMonthSpinbox.config(textvariable = self.settingsOtherDRRSMonthVar) + self.settingsOtherDRRSMonthSpinbox.config(from_ = 1) + self.settingsOtherDRRSMonthSpinbox.config(to = 12) + self.settingsOtherDRRSMonthSpinbox.config(style = 'spinbox1.TSpinbox') + self.settingsOtherDRRSMonthSpinbox.pack(side = TK.RIGHT, fill = TK.X, expand = 1, padx = (6, 0)) + self.settingsOtherDRRSMonthSpinbox.set(CFG.R('schoolyearStart')['M']) + + # Rozpoczęcie roku szkolnego - Dzień + self.settingsOtherDRRSDayVar = TK.IntVar() + self.settingsOtherDRRSDaySpinbox = TKttk.Spinbox(self.settingsOtherDRRSFrame) + self.settingsOtherDRRSDaySpinbox.config(textvariable = self.settingsOtherDRRSDayVar) + self.settingsOtherDRRSDaySpinbox.config(from_ = 1) + self.settingsOtherDRRSDaySpinbox.config(to = 31) + self.settingsOtherDRRSDaySpinbox.config(style = 'spinbox1.TSpinbox') + self.settingsOtherDRRSDaySpinbox.pack(side = TK.RIGHT, fill = TK.X, expand = 1, padx = (0, 6)) + self.settingsOtherDRRSDaySpinbox.set(CFG.R('schoolyearStart')['D']) + + ############################### + + ######################################### + + ################################################### + + # (2) Separator ################################### + + self.settingsSeparator2 = TKttk.Separator(self.settingsMainFrame) + self.settingsSeparator2.config(style = 'separator1.TSeparator') + self.settingsSeparator2.config(orient = TK.VERTICAL) + self.settingsSeparator2.pack(side = TK.LEFT, fill = TK.Y, padx = GUI.R('settingsVerticalSeparatorPadY')) + + ################################################### + + # (2) Dane o szkołach ############################# + + self.settingsSchoolDataFrame = TKttk.Frame(self.settingsMainFrame) + self.settingsSchoolDataFrame.config(style = 'layoutFrame.TFrame') + self.settingsSchoolDataFrame.pack(side = TK.RIGHT, fill = TK.BOTH) + + # 'Dane o szkołach' + self.settingsSchoolDataLabel = TKttk.Label(self.settingsSchoolDataFrame) + self.settingsSchoolDataLabel.config(style = 'label1.TLabel') + self.settingsSchoolDataLabel.config(anchor = GUI.R('settingsSchoolDataLabelAnchor')) + self.settingsSchoolDataLabel.config(text = 'Dane o szkołach') + self.settingsSchoolDataLabel.pack(side = TK.TOP, pady = 6) + + # Label - oznaczenia kolumn + self.settingsSchoolDataInstructionLabel = TKttk.Label(self.settingsSchoolDataFrame) + self.settingsSchoolDataInstructionLabel.config(style = 'label3.TLabel') + self.settingsSchoolDataInstructionLabel.config(anchor = GUI.R('settingsSchoolDataLabelAnchor')) + self.settingsSchoolDataInstructionLabel.config(text = 'OZNACZENIE SZKOŁY | ILOŚĆ KLAS | CZY OZNACZENIE SZKOŁY W ZNACZNIKU KLASY? (0/1)') + self.settingsSchoolDataInstructionLabel.pack() + + # Dane o szkołach - Text + self.settingsSchoolDataText = TK.Text(self.settingsSchoolDataFrame) + self.settingsSchoolDataText.config(background = GUI.R('text1Background')) + self.settingsSchoolDataText.config(foreground = GUI.R('text1TextColor')) + self.settingsSchoolDataText.config(relief = GUI.R('text1Relief')) + self.settingsSchoolDataText.config(width = 50) + self.settingsSchoolDataText.pack(pady = 6, fill = TK.Y, expand = 1) + for x in CFG.R('schoolData'): + if x[2]: + x[2] = '1' + else: + x[2] = '0' + x[1] = str(x[1]) + self.settingsSchoolDataText.insert(TK.END, (' | '.join(x) + '\n')) + + ################################################### + + ############################################################# + + # (1) Separator ############################################# + + self.settingsSeparator1 = TKttk.Separator(self.settingsFrame) + self.settingsSeparator1.config(style = 'separator1.TSeparator') + self.settingsSeparator1.config(orient = TK.HORIZONTAL) + self.settingsSeparator1.pack(fill = TK.X, pady = GUI.R('settingsHorizontalSeparatorPadY')) + + ############################################################# + + # (1) Przyciski ############################################# + + self.settingsButtonsFrame = TKttk.Frame(self.settingsFrame) + self.settingsButtonsFrame.config(style = 'layoutFrame.TFrame') + self.settingsButtonsFrame.pack(side = TK.BOTTOM, fill = TK.X, pady = GUI.R('settingsButtonsPadY')) + + # (2) Przyciski ZAPISZ i Anuluj ################### + + self.settingsButtonsSaveCancelFrame = TKttk.Frame(self.settingsButtonsFrame) + self.settingsButtonsSaveCancelFrame.config(style = 'layoutFrame.TFrame') + self.settingsButtonsSaveCancelFrame.pack(side = TK.LEFT) + + # Przycisk ZAPISZ + self.settingsButtonSave = TKttk.Button(self.settingsButtonsSaveCancelFrame) + self.settingsButtonSave.config(command = self.settingsButtonSaveAction) + self.settingsButtonSave.config(style = 'button1.TButton') + self.settingsButtonSave.config(width = GUI.R('settingsButtonSaveWidth')) + self.settingsButtonSave.config(text = 'ZAPISZ') + self.settingsButtonSave.pack(side = TK.LEFT, padx = 6) + + # Przycisk Anuluj + self.settingsButtonCancel = TKttk.Button(self.settingsButtonsSaveCancelFrame) + self.settingsButtonCancel.config(command = self.settingsButtonCancelAction) + self.settingsButtonCancel.config(style = 'button1.TButton') + self.settingsButtonCancel.config(width = GUI.R('settingsButtonCancelWidth')) + self.settingsButtonCancel.config(text = 'Anuluj') + self.settingsButtonCancel.pack(side = TK.RIGHT, padx = 6) + + ################################################### + + # (2) Inne przyciski ############################## + + self.settingsButtonsOtherFrame = TKttk.Frame(self.settingsButtonsFrame) + self.settingsButtonsOtherFrame.config(style = 'layoutFrame.TFrame') + self.settingsButtonsOtherFrame.pack(side = TK.RIGHT) + + # Przycisk "Zarządzaj presetami formatu" + self.settingsButtonZPF = TKttk.Button(self.settingsButtonsOtherFrame) + self.settingsButtonZPF.config(command = self.settingsButtonZPFAction) + self.settingsButtonZPF.config(style = 'button1.TButton') + self.settingsButtonZPF.config(width = GUI.R('settingsButtonZPFWidth')) + self.settingsButtonZPF.config(text = 'Zarządzaj presetami formatu') + self.settingsButtonZPF.pack(side = TK.RIGHT, padx = 6) + + # Przycisk "Przywróć domyślne ustawienia wyglądu" + self.settingsButtonPDUW = TKttk.Button(self.settingsButtonsOtherFrame) + self.settingsButtonPDUW.config(command = self.settingsButtonPDUWAction) + self.settingsButtonPDUW.config(style = 'button1.TButton') + self.settingsButtonPDUW.config(width = GUI.R('settingsButtonPDUWWidth')) + self.settingsButtonPDUW.config(text = 'Przywróć domyślne ustawienia wyglądu') + self.settingsButtonPDUW.pack(side = TK.RIGHT, padx = 6) + + # Przycisk "Przywróć domyślne ustwienia ogólne" + self.settingsButtonPDUO = TKttk.Button(self.settingsButtonsOtherFrame) + self.settingsButtonPDUO.config(command = self.settingsButtonPDUOAction) + self.settingsButtonPDUO.config(style = 'button1.TButton') + self.settingsButtonPDUO.config(width = GUI.R('settingsButtonPDUOWidth')) + self.settingsButtonPDUO.config(text = 'Przywróć domyślne ustawienia ogólne') + self.settingsButtonPDUO.pack(side = TK.RIGHT, padx = 6) + + ################################################### + + ############################################################# + ###################################################################### @@ -2349,6 +2773,119 @@ class mainWindow: def editingPresetCancelButtonAction(self): self.editingPresetClear() + + # Akcje przycisków - TAB3 + + def settingsReset(self): + self.settingsMailCodeCombobox.set(CFG.R('mailOutputCoding')) + self.settingsOfficeCodeCombobox.set(CFG.R('officeOutputCoding')) + self.settingsOtherDomainVar.set(CFG.R('domain')) + self.settingsOtherQuotaSpinbox.set(CFG.R('quota')) + self.settingsOtherCountryVar.set(CFG.R('country')) + self.settingsOtherDRRSMonthSpinbox.set(CFG.R('schoolyearStart')['M']) + self.settingsOtherDRRSDaySpinbox.set(CFG.R('schoolyearStart')['D']) + self.settingsSchoolDataText.delete('1.0', TK.END) + for x in CFG.R('schoolData'): + if x[2]: + x[2] = '1' + else: + x[2] = '0' + x[1] = str(x[1]) + self.settingsSchoolDataText.insert(TK.END, (' | '.join(x) + '\n')) + + def settingsButtonSaveAction(self): + if MSG('A0004', False): + changes = {} + changes['mailOutputCoding'] = self.settingsMailCodeCombobox.get() + changes['officeOutputCoding'] = self.settingsOfficeCodeCombobox.get() + changes['domain'] = self.settingsOtherDomainVar.get() + changes['quota'] = self.settingsOtherQuotaSpinbox.get() + changes['country'] = self.settingsOtherCountryVar.get() + changes['schoolyearStart'] = { + 'D' : self.settingsOtherDRRSDaySpinbox.get(), + 'M' : self.settingsOtherDRRSMonthSpinbox.get(), + 'Y' : None, + 'h' : None, + 'm' : None, + 's' : None, + } + changes['schoolData'] = (self.settingsSchoolDataText.get("1.0", TK.END)).split('\n') + CFG.W(changes) + self.settingsReset() + else: + return + + def settingsButtonCancelAction(self): + self.settingsReset() + + def settingsButtonPDUOAction(self): + if MSG('A0005', False): + try: + OS.remove(str(appdata) + '\Generator CSV\config.cfg') + SU.copy('default-configs/config.cfg', str(appdata) + '\Generator CSV\config.cfg') + except Exception as exceptInfo: + MSG('E0001', True, exceptInfo) + MSG('I0002', True) + else: + return + + def settingsButtonPDUWAction(self): + if MSG('A0006', False): + try: + OS.remove(str(appdata) + '\Generator CSV\style.cfg') + SU.copy('default-configs/style.cfg', str(appdata) + '\Generator CSV\style.cfg') + except Exception as exceptInfo: + MSG('E0001', True, exceptInfo) + MSG('I0002', True) + else: + return + + def deleteSelectedFPButtonAction(self): + if MSG('A0007', False): + selected = self.selectFPListbox.curselection() + for x in selected: + try: + OS.remove(str(appdata) + ('/Generator CSV/format-presets') + ('\%s.fmt' % self.selectFPListbox.get(x))) + except Exception as exceptInfo: + MSG('E0015', True, exceptInfo) + MSG('I0001', False) + self.updatePresetListInGenerateTab() + self.loadingList['values'] = tuple(FMT.getList()) + self.selectFPListbox.delete(0, TK.END) + for x in FMT.getList(): + self.selectFPListbox.insert(TK.END, x) + else: + return + + def settingsButtonZPFAction(self): + # Pod okno + self.ZPFWindow = TK.Toplevel(self.master) + self.ZPFWindow.title("Zarządzanie presetami formatu") + self.ZPFWindow.geometry('%ix%i' % (GUI.R('ZPFWindowWidth'), GUI.R('ZPFWindowHeight'))) + self.ZPFWindow.resizable(width = GUI.R('ZPFWindowWidthResizable'), height = GUI.R('ZPFWindowHeightResizable')) + self.ZPFWindow.configure(bg = GUI.R('ZPFWindowMainBG')) + self.ZPFWindow.iconbitmap(GUI.R('mainIcon')) + + # Wybór format presetu - listbox + self.selectFPListbox = TK.Listbox(self.ZPFWindow) + self.selectFPListbox.config(activestyle = GUI.R('listbox1ActiveStyle')) + self.selectFPListbox.config(bg = GUI.R('listbox1BG')) + self.selectFPListbox.config(fg = GUI.R('listbox1TextColor')) + self.selectFPListbox.config(relief = GUI.R('listbox1Relief')) + self.selectFPListbox.config(bd = GUI.R('listbox1BorderWidth')) + self.selectFPListbox.config(highlightthickness = GUI.R('listbox1HighlightThickness')) + self.selectFPListbox.config(selectbackground = GUI.R('listbox1SelectBG')) + self.selectFPListbox.config(selectmode = TK.MULTIPLE) + self.selectFPListbox.pack(fill = TK.BOTH, expand = 1, padx = 6, pady = 6) + for x in FMT.getList(): + self.selectFPListbox.insert(TK.END, x) + + # Usuń zaznaczone - Button + self.deleteSelectedFPButton = TKttk.Button(self.ZPFWindow) + self.deleteSelectedFPButton.config(style = 'button1.TButton') + self.deleteSelectedFPButton.config(text = 'Usuń zaznaczone') + self.deleteSelectedFPButton.config(command = self.deleteSelectedFPButtonAction) + self.deleteSelectedFPButton.pack(fill = TK.X, padx = 6, pady = 6) From 98f2a73d108f7ac75ad53b36c16fcb81d26963af Mon Sep 17 00:00:00 2001 From: Mateusz Skoczek Date: Fri, 28 Aug 2020 14:04:42 +0200 Subject: [PATCH 21/29] 4.0 Beta (Build 20241.1) --- changelog-UC.txt | 5 +- default-configs/style.cfg | 10 ++++ generator.pyw | 111 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 121 insertions(+), 5 deletions(-) diff --git a/changelog-UC.txt b/changelog-UC.txt index 1176460..e7d7f86 100644 --- a/changelog-UC.txt +++ b/changelog-UC.txt @@ -109,4 +109,7 @@ 4.0 Alpha (Build 20241) - Ukończenie karty 'USTAWIENIA' -- Zastosowanie nowego systemu przetwarzania plików konfiguracyjnych dla pliku 'config.cfg' w zapisie danych \ No newline at end of file +- Zastosowanie nowego systemu przetwarzania plików konfiguracyjnych dla pliku 'config.cfg' w zapisie danych + +4.0 Beta (Build 20241.1) +- Ukończenie karty 'O PROGRAMIE' \ No newline at end of file diff --git a/default-configs/style.cfg b/default-configs/style.cfg index ea75d87..e3db8dc 100644 --- a/default-configs/style.cfg +++ b/default-configs/style.cfg @@ -29,6 +29,9 @@ label2Font(F) = Segoe UI;9 label3BG(C) = #21242D label3TextColor(C) = #C0C0C0 label3Font(F) = Segoe UI;7 +label4BG(C) = #21242D +label4TextColor(C) = #C0C0C0 +label4Font(F) = Segoe UI;25 combobox1ArrowColor(C) = #C0C0C0 combobox1ButtonColor(C) = #333842 combobox1BorderColor(C) = #21242D @@ -123,6 +126,13 @@ settingsButtonPDUOWidth(I) = 35 settingsButtonPDUWWidth(I) = 35 settingsButtonZPFWidth(I) = 35 aboutTabIcon(P) = assets/tab-icons/about.png +button2TextAnchor(FAanchor) = center +button2Background(C) = #21242D +button2Padding(I) = 0 +aboutLogoButtonImg(P) = assets/icon.png +aboutLogoButtonImgSize(I) = 250 +aboutInstructionButtonWidth(I) = 15 +aboutOtherInfoFramePadX(I) = 10 ZPFWindowWidth(I) = 500 ZPFWindowHeight(I) = 400 ZPFWindowWidthResizable(B) = 0 diff --git a/generator.pyw b/generator.pyw index 1e4c5f7..0d427e3 100644 --- a/generator.pyw +++ b/generator.pyw @@ -16,11 +16,11 @@ class VAR: # Informacje o programie programName = 'Generator CSV' programVersion = '4.0' - programVersionStage = 'Alpha' - programVersionBuild = 'Build' + programVersionStage = 'Beta' + programVersionBuild = '20241.1' programCustomer = 'ZSP Sobolew' programAuthors = ['Mateusz Skoczek'] - programToW = ['styczeń', 2019, 'wrzesień', 2020] + programToW = ['styczeń', '2019', 'wrzesień', '2020'] # Dozwolone kodowanie plików allowedCoding = ['utf-8'] @@ -49,7 +49,6 @@ import tkinter as TK from tkinter import ttk as TKttk from tkinter import messagebox as TKmsb from tkinter import filedialog as TKfld -import tkcalendar as TKcal from PIL import ImageTk as PLitk from PIL import Image as PLimg @@ -1243,6 +1242,13 @@ class mainWindow: "font" : GUI.R('label3Font') }, }, + "label4.TLabel": { + "configure": { + "background": GUI.R('label4BG'), + "foreground": GUI.R('label4TextColor'), + "font": GUI.R('label4Font'), + }, + }, "combobox1.TCombobox": { "configure": { "arrowcolor": GUI.R('combobox1ArrowColor'), @@ -1275,6 +1281,13 @@ class mainWindow: "padding": GUI.R('button1Padding'), }, }, + "button2.TButton": { + "configure": { + "anchor": GUI.R('button2TextAnchor'), + "background": GUI.R('button2Background'), + "padding": GUI.R('button2Padding'), + }, + }, "separator1.TSeparator": { "configure": { "background": GUI.R('separator1BG'), @@ -2540,6 +2553,93 @@ class mainWindow: self.aboutFrame.config(style = 'contentTabFrame.TFrame') self.aboutFrame.pack(fill = GUI.R('contentTabFrameFill'), expand = GUI.R('contentTabFrameExpand'), padx = GUI.R('tabFramePadding'), pady = GUI.R('tabFramePadding')) + # (1) Info & Logo ########################################### + + self.aboutInfoLogoFrame = TKttk.Frame(self.aboutFrame) + self.aboutInfoLogoFrame.config(style = 'layoutFrame.TFrame') + self.aboutInfoLogoFrame.pack(fill = TK.BOTH, expand = 1) + + # (2) Logo ######################################## + + self.aboutLogoFrame = TKttk.Frame(self.aboutInfoLogoFrame) + self.aboutLogoFrame.config(style = 'layoutFrame.TFrame') + self.aboutLogoFrame.pack(fill = TK.BOTH, expand = 1) + + # Logo - Button + self.aboutLogoButton = TKttk.Button(self.aboutLogoFrame) + self.aboutLogoButton.config(style = 'button2.TButton') + self.aboutLogoButtonImg = PLimg.open(GUI.R('aboutLogoButtonImg')) + self.aboutLogoButtonImg = self.aboutLogoButtonImg.resize((GUI.R('aboutLogoButtonImgSize'), GUI.R('aboutLogoButtonImgSize')), PLimg.ANTIALIAS) + self.aboutLogoButtonImg = PLitk.PhotoImage(self.aboutLogoButtonImg) + self.aboutLogoButton.config(image = self.aboutLogoButtonImg) + self.aboutLogoButton.pack(expand = 1) + + ################################################### + + # (2) Informacje ################################## + + self.aboutInfoFrame = TKttk.Frame(self.aboutInfoLogoFrame) + self.aboutInfoFrame.config(style = 'layoutFrame.TFrame') + self.aboutInfoFrame.pack(fill = TK.BOTH, expand = 1) + + # Nazwa programu + self.aboutInfoProgramNameLabel = TKttk.Label(self.aboutInfoFrame) + self.aboutInfoProgramNameLabel.config(style = 'label4.TLabel') + self.aboutInfoProgramNameLabel.config(text = VAR.programName) + self.aboutInfoProgramNameLabel.pack() + + # Wersja programu + self.aboutInfoProgramNameLabel = TKttk.Label(self.aboutInfoFrame) + self.aboutInfoProgramNameLabel.config(style = 'label1.TLabel') + self.aboutInfoProgramNameLabel.config(text = 'Wersja %s %s (Build %s)' % (VAR.programVersion, VAR.programVersionStage, VAR.programVersionBuild)) + self.aboutInfoProgramNameLabel.pack() + + # (3) Pozostałe informacje ############## + + self.aboutOtherInfoFrame = TKttk.Frame(self.aboutInfoFrame) + self.aboutOtherInfoFrame.config(style = 'layoutFrame.TFrame') + self.aboutOtherInfoFrame.pack(pady = GUI.R('aboutOtherInfoFramePadX')) + + # Czas pracy + self.aboutOIToWLabel = TKttk.Label(self.aboutOtherInfoFrame) + self.aboutOIToWLabel.config(style = 'label2.TLabel') + self.aboutOIToWLabel.config(text = '© %s %s - %s %s' % (VAR.programToW[0], VAR.programToW[1], VAR.programToW[2], VAR.programToW[3])) + self.aboutOIToWLabel.pack() + + # Autorzy + self.aboutOIAuthorsLabel = TKttk.Label(self.aboutOtherInfoFrame) + self.aboutOIAuthorsLabel.config(style = 'label2.TLabel') + self.aboutOIAuthorsLabel.config(text = '\n'.join(VAR.programAuthors)) + self.aboutOIAuthorsLabel.pack() + + # Dla kogo + self.aboutOICustomerLabel = TKttk.Label(self.aboutOtherInfoFrame) + self.aboutOICustomerLabel.config(style = 'label2.TLabel') + self.aboutOICustomerLabel.config(text = 'dla %s' % VAR.programCustomer) + self.aboutOICustomerLabel.pack() + + ######################################### + + ################################################### + + ############################################################# + + # (1) Instrukcja ############################################ + + self.aboutInstructionFrame = TKttk.Frame(self.aboutFrame) + self.aboutInstructionFrame.config(style = 'layoutFrame.TFrame') + self.aboutInstructionFrame.pack(fill = TK.X, side = TK.BOTTOM) + + # Instrukcja - Button + self.aboutInstructionButton = TKttk.Button(self.aboutInstructionFrame) + self.aboutInstructionButton.config(command = self.aboutInstructionButtonAction) + self.aboutInstructionButton.config(style = 'button1.TButton') + self.aboutInstructionButton.config(width = GUI.R('aboutInstructionButtonWidth')) + self.aboutInstructionButton.config(text = 'Instrukcja') + self.aboutInstructionButton.pack(side = TK.RIGHT) + + ############################################################# + ###################################################################### @@ -2886,6 +2986,9 @@ class mainWindow: self.deleteSelectedFPButton.config(text = 'Usuń zaznaczone') self.deleteSelectedFPButton.config(command = self.deleteSelectedFPButtonAction) self.deleteSelectedFPButton.pack(fill = TK.X, padx = 6, pady = 6) + + def aboutInstructionButtonAction(self): + pass From 65cfcfe67be27af430a4ae70070a34f79c16d857 Mon Sep 17 00:00:00 2001 From: Mateusz Skoczek Date: Fri, 28 Aug 2020 21:34:16 +0200 Subject: [PATCH 22/29] 4.0 Beta (Build 20241.2) --- .vscode/launch.json | 1 + .vscode/settings.json | 4 -- assets/documentation-page/icon.ico | Bin 0 -> 143535 bytes assets/documentation-page/icon.png | Bin 0 -> 44017 bytes {default-configs => configs}/config.cfg | 0 {default-configs => configs}/style.cfg | 0 changelog-UC.txt => dev-changelog.txt | 6 +- documentation/about_program.html | 12 ++++ documentation/content.css | 3 + documentation/how_to_use.html | 12 ++++ documentation/index.html | 28 +++++++++ documentation/main.css | 77 +++++++++++++++++++++++ documentation/other.html | 12 ++++ documentation/program_documentation.html | 12 ++++ generator.pyw | 23 ++++--- 15 files changed, 176 insertions(+), 14 deletions(-) delete mode 100644 .vscode/settings.json create mode 100644 assets/documentation-page/icon.ico create mode 100644 assets/documentation-page/icon.png rename {default-configs => configs}/config.cfg (100%) rename {default-configs => configs}/style.cfg (100%) rename changelog-UC.txt => dev-changelog.txt (94%) create mode 100644 documentation/about_program.html create mode 100644 documentation/content.css create mode 100644 documentation/how_to_use.html create mode 100644 documentation/index.html create mode 100644 documentation/main.css create mode 100644 documentation/other.html create mode 100644 documentation/program_documentation.html diff --git a/.vscode/launch.json b/.vscode/launch.json index 40b827b..cb017cc 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -11,5 +11,6 @@ "program": "${file}", "console": "integratedTerminal" }, + ] } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 82a8511..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "python.linting.pylintEnabled": true, - "python.linting.enabled": true -} \ No newline at end of file diff --git a/assets/documentation-page/icon.ico b/assets/documentation-page/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..49f2657d956b72b61846b88ec66f59098ecfd126 GIT binary patch literal 143535 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%0AJXF1`vsEtxd99m+EY*(&>9dam-Kh` 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 literal 0 HcmV?d00001 diff --git a/default-configs/config.cfg b/configs/config.cfg similarity index 100% rename from default-configs/config.cfg rename to configs/config.cfg diff --git a/default-configs/style.cfg b/configs/style.cfg similarity index 100% rename from default-configs/style.cfg rename to configs/style.cfg diff --git a/changelog-UC.txt b/dev-changelog.txt similarity index 94% rename from changelog-UC.txt rename to dev-changelog.txt index e7d7f86..ed16d73 100644 --- a/changelog-UC.txt +++ b/dev-changelog.txt @@ -112,4 +112,8 @@ - Zastosowanie nowego systemu przetwarzania plików konfiguracyjnych dla pliku 'config.cfg' w zapisie danych 4.0 Beta (Build 20241.1) -- Ukończenie karty 'O PROGRAMIE' \ No newline at end of file +- Ukończenie karty 'O PROGRAMIE' + +4.0 Beta (Build 20241.2) +- Dodanie do paska tytułu programVersionStage +- Pzzygotowanie strony na dokumentację i instrukcję \ No newline at end of file diff --git a/documentation/about_program.html b/documentation/about_program.html new file mode 100644 index 0000000..32e1bfc --- /dev/null +++ b/documentation/about_program.html @@ -0,0 +1,12 @@ + + + + + + + Generator CSV + + +

XD

+ + \ No newline at end of file diff --git a/documentation/content.css b/documentation/content.css new file mode 100644 index 0000000..37baf1b --- /dev/null +++ b/documentation/content.css @@ -0,0 +1,3 @@ +p { + color: #C0C0C0 +} \ No newline at end of file diff --git a/documentation/how_to_use.html b/documentation/how_to_use.html new file mode 100644 index 0000000..a421c3a --- /dev/null +++ b/documentation/how_to_use.html @@ -0,0 +1,12 @@ + + + + + + + Generator CSV + + +

no siema

+ + \ No newline at end of file diff --git a/documentation/index.html b/documentation/index.html new file mode 100644 index 0000000..706d989 --- /dev/null +++ b/documentation/index.html @@ -0,0 +1,28 @@ + + + + + + + Generator CSV + + +
+ +
+

GENERATOR CSV

+
+
+
+
+ +
+ + \ No newline at end of file diff --git a/documentation/main.css b/documentation/main.css new file mode 100644 index 0000000..205face --- /dev/null +++ b/documentation/main.css @@ -0,0 +1,77 @@ +body { + background-color: #21242D; + margin: 0px; +} + + + + +header { + width: 100%; + position: sticky; +} + +#header-image { + background-color: #333842; + width: 100px; + height: 100px; + float: left; + padding: 25px; +} + +#header-text { + background-color: #333842; + overflow: auto; + line-height: 40px; + font-family: 'Segoe UI'; + color: #C0C0C0; + font-size: 30px; + padding: 25px; +} + + + + +nav { + width: 100%; +} + +#menu-ul { + width: 100%; + padding: 0; + margin: 0; + list-style: none; +} + +#menu-li a { + display: block; + float: left; + text-align: center; + font-family: 'Segoe UI'; + padding: 0.8%; + margin-right: 0.05%; + margin-left: 0.05%; + font-size: 20px; + width: 23.3%; + text-decoration: none; + color: #C0C0C0; + background-color: #2e313b; +} + +#menu-li a:hover { + color: #C0C0C0; + background: #333842; +} + + + + +section { + width: 100%; +} + +iframe { + width: 99%; + border: none; + margin: 0.5%; +} \ No newline at end of file diff --git a/documentation/other.html b/documentation/other.html new file mode 100644 index 0000000..9dcbac4 --- /dev/null +++ b/documentation/other.html @@ -0,0 +1,12 @@ + + + + + + + Generator CSV + + +

w sumie jeszcze nie wiem co tu dam

+ + \ No newline at end of file diff --git a/documentation/program_documentation.html b/documentation/program_documentation.html new file mode 100644 index 0000000..7c5586e --- /dev/null +++ b/documentation/program_documentation.html @@ -0,0 +1,12 @@ + + + + + + + Generator CSV + + +

siema siema kurwa witam

+ + \ No newline at end of file diff --git a/generator.pyw b/generator.pyw index 0d427e3..d751bfe 100644 --- a/generator.pyw +++ b/generator.pyw @@ -17,7 +17,7 @@ class VAR: programName = 'Generator CSV' programVersion = '4.0' programVersionStage = 'Beta' - programVersionBuild = '20241.1' + programVersionBuild = '20241.2' programCustomer = 'ZSP Sobolew' programAuthors = ['Mateusz Skoczek'] programToW = ['styczeń', '2019', 'wrzesień', '2020'] @@ -42,6 +42,7 @@ import time as TM import codecs as CD import pathlib as PT import shutil as SU +import subprocess as SP # GUI @@ -86,6 +87,7 @@ MSGlist = { 'I0001' : 'Operacja ukończona pomyślnie', 'I0002' : 'Aplikacja zostanie zamknięta w celu przeładowania ustawień', 'E0015' : 'Nie można usunąć wybranych format presetów', + 'E0016' : 'Nie można uruchomić pliku instrukcji (documentation/index.html)', } def MSG(code, terminate, *optionalInfo): @@ -135,20 +137,20 @@ def checkAppdata(): if 'Generator CSV' not in [x for x in OS.listdir(appdata)]: try: OS.mkdir(str(appdata) + '/Generator CSV') - SU.copy('default-configs/config.cfg', str(appdata) + '\Generator CSV\config.cfg') - SU.copy('default-configs/style.cfg', str(appdata) + '\Generator CSV\style.cfg') + SU.copy('configs/config.cfg', str(appdata) + '\Generator CSV\config.cfg') + SU.copy('configs/style.cfg', str(appdata) + '\Generator CSV\style.cfg') OS.mkdir(str(appdata) + '/Generator CSV/format-presets') except Exception as exceptInfo: MSG('E0001', True, exceptInfo) else: if 'config.cfg' not in [x for x in OS.listdir(str(appdata) + '/Generator CSV')]: try: - SU.copy('default-configs/config.cfg', str(appdata) + '\Generator CSV\config.cfg') + SU.copy('configs/config.cfg', str(appdata) + '\Generator CSV\config.cfg') except Exception as exceptInfo: MSG('E0001', True, exceptInfo) if 'style.cfg' not in [x for x in OS.listdir(str(appdata) + '/Generator CSV')]: try: - SU.copy('default-configs/style.cfg', str(appdata) + '\Generator CSV\style.cfg') + SU.copy('configs/style.cfg', str(appdata) + '\Generator CSV\style.cfg') except Exception as exceptInfo: MSG('E0001', True, exceptInfo) if 'format-presets'not in [x for x in OS.listdir(str(appdata) + '/Generator CSV')]: @@ -1166,7 +1168,7 @@ class mainWindow: def __init__(self, master): # Okno self.master = master - master.title('%s %s' % (VAR.programName, VAR.programVersion)) + master.title('%s %s %s' % (VAR.programName, VAR.programVersion, VAR.programVersionStage)) master.geometry('%ix%i' % (GUI.R('windowWidth'), GUI.R('windowHeight'))) master.resizable(width = GUI.R('windowWidthResizable'), height = GUI.R('windowHeightResizable')) master.configure(bg = GUI.R('windowMainBG')) @@ -2922,7 +2924,7 @@ class mainWindow: if MSG('A0005', False): try: OS.remove(str(appdata) + '\Generator CSV\config.cfg') - SU.copy('default-configs/config.cfg', str(appdata) + '\Generator CSV\config.cfg') + SU.copy('configs/config.cfg', str(appdata) + '\Generator CSV\config.cfg') except Exception as exceptInfo: MSG('E0001', True, exceptInfo) MSG('I0002', True) @@ -2933,7 +2935,7 @@ class mainWindow: if MSG('A0006', False): try: OS.remove(str(appdata) + '\Generator CSV\style.cfg') - SU.copy('default-configs/style.cfg', str(appdata) + '\Generator CSV\style.cfg') + SU.copy('configs/style.cfg', str(appdata) + '\Generator CSV\style.cfg') except Exception as exceptInfo: MSG('E0001', True, exceptInfo) MSG('I0002', True) @@ -2988,7 +2990,10 @@ class mainWindow: self.deleteSelectedFPButton.pack(fill = TK.X, padx = 6, pady = 6) def aboutInstructionButtonAction(self): - pass + try: + OS.startfile('documentation\index.html') + except Exception as exceptInfo: + MSG('E0016', False, exceptInfo) From 1655e17bbe53812b90e0bbd13fbb27a8ee3efd5f Mon Sep 17 00:00:00 2001 From: Mateusz Skoczek Date: Sat, 29 Aug 2020 20:43:36 +0200 Subject: [PATCH 23/29] 4.0 Beta (Build 20242) --- .../documentation-page/about-program/icon.png | Bin 0 -> 44017 bytes .../how-to-use/example-input-data.png | Bin 0 -> 4419 bytes .../generate-input-file-settings.png | Bin 0 -> 3354 bytes .../generate-output-file-settings.png | Bin 0 -> 3675 bytes .../how-to-use/input-name.png | Bin 0 -> 3735 bytes dev-changelog.txt | 7 +- documentation/about_program.html | 58 ++++++- documentation/content.css | 79 ++++++++- .../{other.html => description.html} | 2 +- documentation/how_to_use.html | 153 +++++++++++++++++- documentation/index.html | 36 +++-- documentation/main.css | 18 ++- documentation/program_documentation.html | 2 +- generator.pyw | 5 +- 14 files changed, 327 insertions(+), 33 deletions(-) create mode 100644 assets/documentation-page/about-program/icon.png create mode 100644 assets/documentation-page/how-to-use/example-input-data.png create mode 100644 assets/documentation-page/how-to-use/generate-input-file-settings.png create mode 100644 assets/documentation-page/how-to-use/generate-output-file-settings.png create mode 100644 assets/documentation-page/how-to-use/input-name.png rename documentation/{other.html => description.html} (54%) diff --git a/assets/documentation-page/about-program/icon.png b/assets/documentation-page/about-program/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..e0314abee281359a16f991ae1a1c9cceceda9a91 GIT binary patch literal 44017 zcmV*(KsLXLP)004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00006 zVoOIv00000008+zyMF)xfB;EEK~#9!?0t8f9aY)?_mnC3?%wU$lDZ|EO-~339YSvw zq^gKw!xuyayNIG<0aRWUdqoia=?jPgBE6SDdat|LY<4%>>uoc0%I}Z4Gr5}r1X4EP ze)-I2!<~NSoaa2}Dc^@6ha(PKoCJx-<5pL9SFESEH@~O1H;)h!8R+RnSz%%SH5XmB zWl!g=Xl-E~!~GR5r>8T*HX4*~vn@+R3JXkwvEf3~N)Q0m%sSMsn8SVoIn)o3!&}1! zEv(M?z?jZ~zM8E&TBo#hbkuI!(KfMvV89$28X{V2+h8naFqWr<%r_b1B%U&+O`5d$ zD_4K!@_I90?0U`nH?N$ybn~X~NQ+&W@Wf_Ut9Hh@AhxZgTyaP5Z)E<&s(!; zW5x2-D<`%m21;zgs!UH!b`!~?uf6GOlasDfl=NJrqz6qY0p}o;fJZENQX)+hC?Nm` zl$SywC(xEzKpESk;CCoaRwnysKLZpsrYNnaxJtuw9hj7YaiAxaKu>%~GYuG;z$63) zp@^6!it=NCX*}52({}GoiyrNsFn0X5s*;kSvS>j(56rVboGH}Shq{e(*om9~7)97P z)X%pxHBDT*W#bRmZEfN$ogI0CqRP{1QV;d=(n=61{qe&n1u+SzX@jvmKx=5Fp#cn1 z4R8hpKtTgq!=wgM(nEhLwH1`fJ>6fKCQEaQ1QF06P+$NB1O*%bpcKTUpu{3nD^R5Y zsX&Palmn7*z*Fo3s^kTIgM+<8{e!KWl^!a#^Wqaq$GBsQN^V@!-?e#Wak+b_+d2my zIRS8pu%@G(zqoQ~)sDX2hQY+}u|rOJ{^x)Gqm#M@`b*+kQpFjh!J!C)WE6&=pwa-e zf}$FVP$&WjC}^qyN&paOXxJ{G34tbnA{tB+a3NtUil}D8b;MMAdM4s{U8cZ*O-YfI z8XyFOQg~V;MI;mt*lG6U-g2LpKNKEZpE9UTakrMz12~Aqj6qLQStF?O3 z@@1#p@R#2m*D=srCn;*A5S2n{C`#Zl1!mdMg!%(mYbYgXXT%NIU^3HMFX)YgJw}ueGA2 z$wU3k-U-MFfCGdkEvW>-vQ4YY{`=fBv%dZ7pPoR7zG;2a z7Lt^LGSeikNg)gh7l76ToO4i0;Rp#p1Db%q|47U*079UYgq8{v0HxGFPANcXP)ht+ z+4GhQi3u9 zMgXQM7^N6fQ0Q6Q7_#k1k7ZiDoY6ilWN!qlXHsS576R?<>A6c|1>JSy#@;ii+^4lB zrjjZqrHm%EC=o&wNF^g$g9#;Px>69SGzevog!oGkLO}?Hk_!GPC%z2=6cT_U1YiWp zmlyD~hNA?iL17v;6-vQTz`*d}XDy8n^>%hOcWrCwm{M1}zIMXI+t-Uk|1_SLKGfg$ zNJ7pHxS#NR^A`KnrHiMn+Pe9q9t$_Nw(lrSI}VQ&#GojJBm~3h6f~irrUj(|sx-LD zsMAD42?+{+Cf6#n&ej@)5OBg^XsT?^2lBYxW;51Tz|CZNtZ>t~va%K9$}2Y{AUFK# z$$urP$5bFM5+lB+^bwml8`%Fk^SexV@x$OvS^~ zC)KvqnbDj_;cX%(0Nw_+XwBM_17n_9yy)y_7rlILJP|+274GC=rnn{)jByY~;Rywy zGIJqiAeq6yv;@-8rn=~$87EHEb8d#D%w8OTV{b&7HfX0t;iMK zLj#C=E~LSrID=pWic;T3pfxDx@T7oH5{7L+D(P!GQUOB3Gz@SJ5J$pR1dQOnRYj%$ zo-wZa)oJ4h(;~Z+|`!%@28)f-o9hUyBycMwrNLOu{0Uu`B5aC zGz9f$Jq_Q8M=1l%80$!8U9Xh}mjZ?opc*KMMp2ZX-yRf4*(rOBua?%7lsQ>&H&$ znr|12xBUX|AmjwVKH^VL{CCbXi(mU}M>>91pOc)JV(@1(0%#Qse1Dt@C84wi=Nz0; zKmo$@AUzMJX`;f;8?G)WToH{#o{L%0w%OCCH%^^g_grayp{O+?Z+lkXux`DPNF<8; z`ufT{J3GsVhK7oh$z;Ckx)vcs5kj<-k_E>TLO=*nW5UT6honh;unat0|CT+biNlu|I_ zyBmd8AO;1Y06YQ1^H9Rfb~EKQjxQ|!*^Gv1Pn}X#J#c7x$Ki*Z0C)pzjt^i%)7FY7 zmoGkRdr$ZEEuA}NrkOTfMufEUoH66EG{vj&_)zM&V~>*)##Ct&L{n(g5o52<^Xh9$NJmFU>B^NW$8FxcxvZP?t1VR%YIL(kl!j%uAedGg@o>YBZ3 zDver00!YIpUCANRJ2Ys^p?H*}z54At+Ry4sCeC*lsvS-$gvp^T1CEk#g#_gsU<3jh zQYio$CZix}52hlp2um1*tQ}|PKiyC{eoJ{t=>sQ~RQ4U}J~Wz<699WbqgEsik$>mI z_rL$;Ws5IzrCgYDy(l#}T=+8)w=4)L{XiH1R1+}DK&1-~0+C1|pPOiyFcHENKPoFN zdurh^$FG?;ZDz+5!sMHIj@8STk>1|knipSw_2hNy*3WBcX({%EsN{^*fS^hU8A&FS zRBPqi7XVNSNGW|EJ@aD+0vDa{Is}wbP)cPCkR%ic!9D;ZewRXozsbNb$vI5Z0ONds zG1j59Zq!Qk&ze2GdFIR+OO8GE*k|S+ecaHSZS$q+;YjE3K=F!=>*nO=<$b-QzxSk` z#4xnU;Ta4bC6Le%8UUvtj6n(y#8a@S0h3T)yMjg@&1G&pE#Ntlq58JrWwIFwc(AW)gXsFVaiz!-&;0-SSyux127W@d*{zB`dIU-J(J zdp3e&vq4G5cA+(dl;Jx|r9c@4kU56{X*Zz=A=0*OQCe0yG;Zv;C)ztZo{r|{wVrXt z8I8v*SkQ9pQOE6dt-et@bReB7?)OqSt%ZRo z0_{m~1#rsXCX_WnbGi*njGRL2gBgtjKOgnM59qPVeGi2 zQwMSLF$?A|`@l&jx4+RnAAS5WzIE%?Q=fkN>GPYK8ml7_dzKJlax$6pBl!#tDJ2X| z!7wtzPG`1*ft{f}yJJ@oBAJAS4h4?Z8jLeoww+c=t+5Pq{rve0*3O^5;K?g5|H#re zy7#8OUb1M@hN^y%K6$`Ry{}uOkL{7^8Wcn!DTNXmh9+R12G4WDGH)+%CNwt41v-AHkEmDvRDnd>G003Ux-W0k2*(Wb(>g&92?e>;=(&Q$!Oejj= zdcs$eIf3VSuuKa~DiBY=7J%u9L=1f0tonw3*G-#x*H-R!e`fxf&J^3;s8DTSvbgb)&1i}JG4;jv@K{;j{S@9CqDKDz15)6d#`>S<@}G1%vY zct2UUy@{7mKCz>}=c<;Tp7Y{FSGoo(^C+P(IE3;w0j&WS63Soz(O}S^2?zOURvHo#N=ZZ+hfb$az-?3&mMj`GX3THTU3kV5brU9a9L4v(&V1R z{e}&5r4lEkQ|WR~N+9Ulk6b zd0bm(=d9tOp-&9E>GM^djkJ;=lz}n|M+yi4%wXV*!AqrKfxrY%6pO*mkA3^S)8_ml zN{r-GVy6y3vyFPZV+FvE9=xx5`PMBTZ0qj%eE0BR8Ddf5nGA8qfoWNwT7xPLrr`Vc zgo_9_v`I*7d0FYJC(S$NFO^3A)AOcIOU@}O+ilF<|JT2;rAwF2T(xG+bjGdEcXf8n z92y+5B9RE?rUB0reqa#ikU~HR0n4&7$u@ps4WSeS1X2oUqCs}~T5nb*-ya}v@c-H1 ziuubEvh!I8qkgoX){sI#DFvn#4630C07j8?(g5QSQhJ5OMeew&vH#|v|5$k9NlUM} z?z*-;-Rrg1ZKO3dT+r<#Pg>W$?P7qOAJ?+Ng9bAVc<_^xkgw8T*fkm1FOE*D0qinm=~Jp4HSR9y69NUw-z(4?p}KDdm|1 zgM+milyK98s};Z*Jb#J@(EySD5HT2o6utsM2?5bN*ZaOvPGHv+|DI-Lq5*jjzUu2~ z1Vr(S0MO7`noRI66v$)}N>DD~2oHp1toqvbR7g*NX~1OMH5gwsu6E)p=fCgWfBWbM zKelRb&-DDZt$FL3HlN%Y#kK8&19K%MRUV_vfd(@S2qC~FV8howxKQ9C4I8wnGFouo zoSOPO&Z%x_!#fD?7y-W}(^vU$yCRLmO0cQ+aOF&APAh5Lt zOFM`v0z;_Qnh6shs;H>EwPwvvMaza98)hrslqD5cJX2@_sRBoeoue){PvPe1dF?Z+N_Y}l|{ zwSWM@{Tmls6)9cW+`8?mJ}N(zw2d(dP9O<|0s++w$fQc=3(psr1j5o}!{n0k|C?M~ z`KR~QOnFBLf_FqeT&I=!^S}P_x-C0eKiAUNJzg*ZgBnOG1#KFTLcnAcHlbjy07*G0 zq#RWxrBBq=)O`N~7hkeCN_eV)@2(#H;-^15_Q@xo{GUWJd1~K4e~l*uC`jmE2N1g6 za(L^|T8HjTN~v2=Sh#d@{p5eY``z#U^92`N(7C5`o*rs1SiW`3nejyOx7+*r%9P1L zBM~G$53Z6hEek|CzUId{m?vR75>d*2dESW&e^e1GXq`ol?D&BrzE|Gxe|OGb*R=Ip z&D|Ym3zM6YGYAlP1klvaOEnn>6#_^)h%v*dEGd2V#Ch|7F?~YyD>G{AhU?A9h-8!h z_m_KOPd@qN{O<1V>w0>6&UPHfRsfMof*S^i-q~TphRz9qw}l{>NoHaY2qj26jwmWB z>Yg-d(jW8l^B*|&*kf0H;uD_;;|rR!3r|yH+fa9Pb9dL&b&Z=Zji*!ZHj4_1<4Pc{ zB_#KkTo4Ktp|A-9PdUSWu-_)`F;_Zo{j}OC}@8ww-;(?9DCs~2kh!6rJ zo<^}pnv2r%C-WO-{O-KjM<0G^Mbl zK%rEM2+?-ZyklOQJ!9q<$5)MQA6r)8HISWLDfj=sdt-n5$NxLY6W)#6TUw50l-fjR zOmdkP6w1$56HD6w;cfB!*elB!(Sh1MF8NCfG08cHcdA`u8Bp|w_okYsI5^=~Fj z82|VA^XISp&=nsEHG+*=p{cu#4G;Ct>g(_O<@&CU2?I8(@`&bw5eTY5I0GCHg#w7h z-3_zGjQgLXYo@$()Yu6}@1eIp|M27&7A^XDQ%~pFo^8>zP>32fbkajiDU2;G zzNc>b)Zbim&iSh=?c(8DLPpH`KmF0|$36D=V_)bW7&tGTPUkbme80R>KKqUKLxB}C z{C!%50w6$k@=XqJ9oe}Hfn6ntHl)mFBhp$yLn0E5ZY!^-xc|y4ul(^v@4I+Ju+J7P zSlduf?3In{j_!#Me0kH$qF;FD!?xfhQW#q>X65urH9tS&n0XJpXX@M$8DrZUw~c!#q?ay9W?aXB1(1fAKH4hUlaA*!&rsJmh6wBOpX{6AiM&8N3d ztKP|m^skMpiCI`wwPNe0i?i0SrZFa=t z)3@JJ*)-7ehyG;ztg12P|2eK<_P4)w;ibFzl>hka-{(K~+;gAWyk*O^u5fF?2&7gS zcHKQ+I~P=b=#d?lpc&vO0Z-b1^dyJ(=(Sl8No-nO>3 zk0;Zq3DxD5w;WS9_2C8OZ!J2hzKUI{M_p2{`@|u4U zrA4g~*=?w9aT4T(*IusZ-qA5M2zzMXtd+($&`v>^nadNG~U~nYyIUff0=a0 z&wlnLt@MYqQpKPIO2H2w)!GkZReQGq1)4!P10Cwdc?V%n0zhSQ4-_R}oWt`x7?gt( z3OAibL1AHIU48v;jyvJ_-&}L`r{eo^zZE(WdHB^AKGoRL@>3&XrwgL_cO5fj#vPYU zXjpfs+v2U@uoD1HT4Pb;hQcSGd-|G&6~fOOs2+i#;E6cFiZnNWJo-j&jG*)!QSk6IRS8RuqXS< zo<4~b8CtQ7=fD>LEMvI!6P2V=UPWca>PZtPeCC)1$1J((e?IlPlg1XU$%_8=!evWe zdtb|7_bs$AR_RIsniitiu>U)8Y}Ku`RpXalS~>Z3IUhzF4m$zxw?(fMJhNiS4QmH` zKHp|4Ye>1Uqc-eB5(R02a?Lv?MG8Oiwd+3r%w!{PucQ;7`1nUF2l@x^XlmN}9!jYl zu;)m5M0$I2*aNiIuq>;!wzl^7U-`;cemr;HQG2aMv{6gzA1^+2dsBbc#fhj<;Sx=x z_5}(RLX8FAI<=tetJfWW*8PY2z4ir%jR07m>?6-CT2%Y|n$@>#?&-WB6^)v4ra=t` znMxtX8JOpwP*Po!qD^&^s?WXR!b`UTP?O8c!+GPYU;avTaB%SACCe6nJDqZBEYl*4 zF{IOJn5GFKL`a*J!#e|7Yf({A@nUs#^*4X>yWhUJr(?eJz+cZ9%roz4cavqwd=ruo z2qFEXB50UGq5ynTeMQy1b;T9GymZp^!-8kvu<7Z4dgZwV4=i8w$L4tdgqB$TKXr?^V%num&@$I|H=z57H-_I;RlPCEdC7$t+gW&qP2$S zd9ZC8N-3CTjwjj^Ok#ihKCyj zW$+k-q8y&*B4*m)t^?EYP*zybK4LI`lioQabr{cHO4>0iF+l8f8s&6}qH zpxH}e?LhbVr`9j~NQWE$N}Q5H0pizhBqS_mLIIRIB3)y}{!=}v<_jm*&1jvnmuk$T z0i(J$V3U`kkG}H!>6^D~xuZ!Z>OGUfrW{HrFaVq}2Jkt*{ z#HUtsbMwVLy*(35+xi-S7XYBzhypzvK?DLlQ#Hxk5UM!V6cp%SZO4< zw3~n(_dJz zr&A0cZlBW{GfXg@p1B3<$Wk?t>w+{AvL{bntmjB%iS~X53bW|+|MwJGz z)k$Jyf4lYa;w2|9TCwcU!zLLwXaWw^$WLk*rirAJ2ImZU3dz|M>wbI5DX0H>Y+-!1Fu^ z*G~fUT$gqA^qlk1Ll6D(>oKQAKDjRoGCA0ovJ0i`A1|dhMkgBPvp6)+xO7)$Su#A|H(yz|MD5%yTo(C)8qOPdq;dztlKXq~K%ux|1FluZ7Pj23D z^b0E&|F|`ktabBEzqmuv0|y|eL@Xsy$j!mZii)p&_=6w(+sQ`Bh)k{K?X9=;^mbn@ zrQ`->P)dV>fM^00xcIWS~{5~ap>_)EnENe&6~gd zvBu3?{!>>!Sm=&hTt zUg`|qVVQhjCdoUbhQUL=1X!n%ObNtff%YNSLImaX|gQkTBQv|kV;2MAysN{BE zja_)d^!zb*o@|taA)BAO_OqqOEI8&@?d|PXOR2b43R?TL)5Nb%0ZQnI!4KpHe-67t zXKl!Mh5`?floFO@KoSM1RoU|8%l~%6*S>c3uw@u9E0v~1~*Tl;!T4)r@9aslw*gAZ12Z*Kl&>u~RU&)|fC zBEn6$t^>{}3{Rm1k zkCQ_Vdjx^+E*3%rk)yH3En9y4*T4Psnuq@La2PrG;d3wWjxV0{vzol(zfmcYgab?n zSW?51fJ1<|MeXI=8_(U;)O1PHA&wS2q)ULmEq%G&Vh6q z1(pSxOrwI?9W$#Ze`C(9S@%pfcB)H1{QhmVKmPHL|2IAye~-!i0-CmEBbiEq895=4 z!~TVaUt!3wEGQ}AI1U(N(G|;Ae9sjk_V0%t{LuyPd2bQ`=1iH=Poysy z5)!7xK?%jMmx7s>2Zxf%m%CbS)*H>(69cV(Ijd;=A+ZS@GU;<)^M)}GFJAQZZ9{#R zIZ=bhm4r4q7^Sc%gOT=7ASvp`PrBtJ@4x(>kIh&R^0Gbg$m10c{paDI3=9oiAhe`H zNk4aA2$(!K_;c9*V2r`_yf9KU;3njhMw+&4`RZTpy8H9@+;dMp08Ar9pE+Yr{HWP; zK38T%AB%Vb2BC1$Da7*f;CKQlMv$;s*@hk4Z`;znGPb<7XmRZ6XP)_K zPha0floO^2;7S3)8H85Qlt3SHt40n-B50<#C^I-zrr2lzB?Ks7Z&Ir+*ij`k|;DHCO zTD@`=3(g%^Id;=g6Kg+HV;3y4wFXfFrl%1T6q*pE4Mv;OL+@U^z45Cr^zEoPnD2Ga z1i)IAW>3dE#=f+A*$us3sy4+5Jn6xp6vA;4V+?t&#H7N~|D1B-i9en{XYN4u`Kwp2 zzBTQnuOtAASSHe`blC8@sJ$%_t2j?8=bULzf)tc|#c;mM&49+=* z#^f<|Gqy~vt^G;?;j2xdU}y~}386G30bGMd+6VhS`TXX!?|VMof6&~62TcIHvTSke zH-Gs3_q*bQr=(4Wxbm5IEXrU|0+~#ys=R`gr_7xD_4B69-7uH<%)Xz${)@49o&TO+ z_V@Q+X;~IDG=vZ^84px1-$j!X0y!K4NTpy|R^Sx?Aq18k$=}|({SWUx_dV|mj+;s> zIk~iQ`B61fZi*_hjYtVl4N4hYA)pKfFKXH?J)J*VymsY#m!<~~iap?<34jgj)?L!x z+jFtbkJ1Fu5C#Wl96FH(r39rV#hph@o$c@OaoK_RQnq_m@kyWJ05F8dv<{cNl+=wU&)e4Ca@m1?_5&sW*6TFcMw0fXrY)c9>hGPe z31FE9v=orSgBBi0%E6qvDXY&v?TkN+DJ=~x{#RUadDUyLzWQsT^#lR}k|XTM;cx;W z5K_WZ65KS<)!jY!H^2GKcdq~R=L!HI3g*gKmSI3h zf$qWn58bisnd4toLkG+ra3D1R{^%1=d~8EU%NJamGY`tI+^QrZwh8JwsEXuo{lKZ` z{C4()>ZX~*RsisouY9SZr>E!l!u1-c2BhnP=T`PP97Z6NflvmF>gAu*h54q#0az5C7SKe2IUXtuyK`<`!xufGe+$3`fW@yYGMby3ztY;; zdX83#5cvFmkSb^8&*89vn@WRI<}(W!9H1JSg05M+=DHic_|dohJ1@gxKUl=9{%Y+8t@i7f-R}Zm(efqpUfI0%!d^Gch-rc{=9f^?8hM1E*wgK^CQQ10 ze(luG;GD&;EQ)PwY5rbsUvH)FTiNN%(*RU%@aJ$CA)8(-fRYl_U;x$V?CzMediCnB z|MTGo^8jGF5!Gi;p1tI#;;LIKiP^;zcKWmx0WmGoDV^i`;)z?7ma)xVdcTc`{U!jm zYC)DI`x@3ZZN8=}HC(N26E4+AdoBp0h=QYFSUB^lr+og*)6aNbXO#NSeeN@nuYCQh zH}?(oA8Y4FAa*Iktq34#F3B&4!v{g_i6BCiX@C<7=?cF>F$iyJZ2I({|8(bB!8ucm zd|6&v_UI(5;NF-dF4Y8_F=*EVG=XC(IufhM~Lq!FcI5DKPzJ6JM4oY&jk^}~POfA6^9nEJ8fhiB9^{5)pa>z#BOgb*0a zfWbI4q3D-_uHC%(tPQPAv-j7t?l%GO$n(#Qx#!>i94AZz%HU9phU*wA6l_Y@&YwN^ z$0yX!N(AS;|I*7Umn~g(b26RE^Euc6zlKM)5Xcz-ft_{if+8kC@X6s=)Z}n}_BR7X zcAvNyrI11ZN`qwVTZ!S}x?leMmp6X&@++bMFo(zVQ4{JLCsdC8Re@!ytlm+*lz-$v8nk3o6_-9yQ3xspdbwnrZn=>9!ioPPN|#v zvu~br!LnJUWdMNR-hF4GE4(|rq+3G;MU+QDB?HwQL@^Le0ZoC?L!o|15e%51j$u&O zZrA;z6`tpXW!Hn}JW|WBXERO*A;VH@5Q9T90!{@tA>Dnf&_p&bBKF2zzT!xD)&33p}uT`NXYl+rPwY3sOix zX;4a_rG%}Bsx7a4yt=05zU*^ed*+1;wzssr8vqH4Ga7~gA;dxaREG~3V<7`gFz{{L z_AAq7sH=@p%!L?Pz&cn+yN5^}L?)vLWgWsk%G@>CWUU=-TQZr9PJ zQE1NvOa_-}f5cLP?!mq}>$h(^>22Qq?Xv>k_sGLXZ|&ak5iieTl2SlPSeyZ#1WkLG zK6TnXr%a#s>~UtX0)RVy^DFDFyYBizs(+w1&yFDBIN|IS%w#$24#rqGgEGcKV#<<| z5=@*p5o5=WMO|Iph@_O!1jZQt@BjV}EiEmuENeGy;GGJgH8^Foy|wL%Ki~Q1M*uuD z)yUHT;t#Ym{;O+f@Zz*eot+>Ggb@%|LK6x{tBK9+Z6AAbsPp-;vC{Z-!rxLuzHI@} z(ckykF6NCLAQ~3qFqDQ;5>Zd1CRVm(er>}qPA(sx3XWg4eEG@!ef^i^QAS-UL$y_B z74aO69eA|BFbt&AX;fEN!(I?EcIZQy#RLn#Fz#0Z0*)*761c%FyLFTWf={NWGr z!OJhlv}w~|7)EGx%5KNev}IS>nKWq+ZY%YybeIRL`0`<*`z3zZ7*9m?Qi;gN!0YG*UL_>$=)LxVmjy!&|-oTNVI+ zUb(dF)itX>*5{=nl4<}1ln^kb5RpK!9ew_clNUa8VeK>p08K61$g)>oy?Us(Zwe`VJ&+kqFc=8gF3-`Z&Ft@XQCfpC1`y49dwMT_{+VZI0Kjx&psKJW zK5zQWn@eCI9}G5`qCIebm;D_VXI`TjRriODApB8m}x}e0ulr(7RYclxPAN zgaAsxq8yeZaqP@Fzxv3W1zo{8zxdf5Gh3RseOwD+02-ba5b}`LMI5ytgoHvMl}dpz z2HUpr;SYZpAN}Y@!6*&gYQfCOGSzHT>2&BK9Bo_nb1NxPR8)j7ed$ZM{PN2YiA2Jg zKj792&O4HY03!sR5CA0rqZsJ#pBNt;{``t1ix~jSAWTe*Ms3KwXLHW#eC*KC$lQ z>=Qa$T0b_}*FTM0CR`;Tm4a!Q(DKOrxNjAKOEkMo^$91OfGe)J0+wxOlB9O3j~YOH zco;i6I>JiGqY;TjB6J6a1e%tGYp=Z)lO|2VFMjb0Boc|;f}M_Nc+#JS8G+}zh}t$b zY}#1dtnlomiy0)sLzPe4fpLeN?nE4XRuE}rP>z7=D~FK?T%n%l!8A=26&2yiE3brY+mJ$lX4GY^H3kL-aR2@H zebin0D#)D6Iz;kdj7n5N84?N&AB542mqlaOv}W& z=BDZrD>4bd#(lZ}J_~@~u6nijp~oJ*Ugk#)S86aqK|rCEf(?qw(vl}XeDP&37>ub} zW~)aYc_jMVpZ?-2NjF_eOb*+$AfW+`QN3n8)KD3^X(0p_E?kId)24wk>hsOPkDF*~ zYr`FP+=2f-@(7BHi;+wuVB5CenMN4`ZN+gML?RKtt{%@+*#TghCKjH0Dk>@~@cr+9 zAM4hw%N%0>(C|DDobhnk69~Y=zW>mm%7}t#7|=q2QbxMFd(K|HXz3XM?g4<~trF+| zy0_lfwxj*hG$F^Ow1lP%oN+i(AZd{KoBO-p58&o~y8XUB@9MQ{77S`{Vai~{GHfVE zf)fA3e2qFB^S_Ghsh8s6H zX3m<08^7@lEI8&EMDy~HN~b_G0Z>Y*5dl%0AJXF1`vsEtxd99m+EY*(&>9dam-Kh` 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 literal 0 HcmV?d00001 diff --git a/assets/documentation-page/how-to-use/example-input-data.png b/assets/documentation-page/how-to-use/example-input-data.png new file mode 100644 index 0000000000000000000000000000000000000000..ad321b9e953162ba5fb376f5117f67fec3468328 GIT binary patch literal 4419 zcmZu#XHXMdvyK>~O9_M;S}00y(h`WYP$V=#0!Xj&(wkK2fzUfh4@i*`X-ZLQ06{=N z6p)S}y(&eim-qg-Gv9po$DW;M&g_rfv*&qs7OSVDPDRc_4gdhCG&NKWZhX*Kg^*L>oRdZ%5|jPr8t&dR@48WeFWQq@6;=W*i&t(;YKCfJcq zey)9L{&wS*SeS}4?mV6QOXuF^-tuhcV$l5l<=Lt6Vd(Y1wUQDzO5<8!0HEf=jEhwu z>v=G)C;6pE$|l%`^>wxZK&7p~yL+mabQv#W-+t$vr%MpnESUI;8Sk`C7SrDWa({#~gxbkN--t(x& z%(PHZTaigW`RM}=rnT{Xp}wF_JQ5*Z(SfG)M)OZIDKP0$S0yo&&fR~(L*u+H(R(7V z=M^s`b~q3yP7Q_}au%u#ZASWE+i(bp(%ZbQ229Znc;c^#?5S(jCkvpmdR5%)tz?hT z4S%ipw-gm)8yqa=>N$KSQ3G#TaAc*A;HHWqUFW_%RkRz2HO)!I8@BwEa?)^jZW2zP z{O=gTdX!}aRl0lQD5O{9hj>t&qH?wf=;b3`n9?^()Q3S#`mRt`Za*kp1=qN0Ynuy3 z6&Wr`*H=zRSnI<)=Q^lJK2voasDiU-f<)Ibd;*a1MUSP@HI)i`&-ZWAYdlL{rKoav z%@MKzztdmpO_-ne#WD;c4}ANkRZ1t5-bXtWTjY{ddjh5uL2sTIV+&lKlTzi^$?oQ) zOl!x=(1@W^E+zarEE4941JJ!yUCytJq83g}{g;r}OVfo9dOrAkDcr)Y0xd4Iee8hn z+GD3*fJ;Zn26-|-A*#ALIp3G^D?!KS5ILNNC>+;c|L39mA`sH3i zG>E}*AfUf9qToc?CGp-b{+ z7``@Ixh(#)ro8r%jcX+Fh#rU2V)eWy!(Md2Gztzwd`h&Kj9Ql||DsF!^?__RA0y4E zXzv>TN-XbP=Db~H(UAQsB6Lrl{%@1ymU>|5PoRiTU)LG+Q1oZ2-Q=Bl(G!ZbTJFyD z%9Ak5zh-IyPX(ZSi%O4k{O;Kdk^b+G6o%5*+edM~OUOB%-)Kc-zUUOi7?~Yjw6Ve4 zow+cA|13kuai{sZRR|^XYCBS3NY)j7ye{H8(k`WVw5$1oX?qf$*!0y7zT;WEe0IcY zmva|*vSW5PqsWVNw7P=G{KPaTruwklmG$JZoWZ;CcZ&SqW#(vc(tGc!*WkG==ZKZN zmKP0wfS=q5ZFXn(yh_B-V#e|Key^i66@M1NasS8EktU|KG=}VzBMz<3JG1&>GNCd6 z!lxrmxO{?&6l9=9M7RVr2f2fraJT4cS_&VFGmVF~v;_Z4! zT9C|}^vl21Jd{+8R|h9kD4xu;p`AHNo^jDe2eGBH403#z@GypU_ujMi>je7frP74J z!B`UN74nHY|5;$Y-YnW%ZtYEC>#I;neQNphtWJ~o759wd=mlLfY3ozlfJ+Z@~kVv#Oc8M#s2f1!Bd$_XWUljjP^%M=C_9NMHT5VZK zyb=?ptPfW#02Ld=#{SG-y%-=GnLPTu0+x_zw(*(#qpG3m;0DebyAY+* zH%wl)MUf}+2pJR(s*{gcF|l(qs(`(W`E`2kRzCJhIhTd-t`3IsE&Ip!*FjTXOsdjz z2A3&ox!pD3{#bWz&F1=!{k|x@)oEvW1T2JpJ!&V}f4G3KjNG0dRnOC(>f-XNg#=lO zKCHsjbV>rOx7w8`j*WFPt`q#XVst9gB&~V*{jMPH5&7(3Ti^`j+iEIqcSYD7^jFYW6TQhWNTS-_8H%eL~^*zjoYZ1a(_KDtU)b}i`r+NocuiQ?#c75x&^RQFaO{!#+C^391U4pX_CI7Wn}!h+6_lzx!+R-#W~|l3izM0 zLB%xD^sun3(MU)SY}_Z2gmngkLCWNq%sV;ZUIw$7;(ee#(#hp#(wqtE@saF7C4OHh z-+nD!hY1`c9&hBRUA3>#h@eCLeB?C{_ldL}6>{%r!gp%bDSYKa{SZK!yl_(j`SRpS z`Bncz5K5WF65HJFD*6}KItp>T`{Tm_a{~j6BP-uH<*x_7NItDf`!X}DpZj5wo4z#( z@@BGlBoV>8AoPq1lg~_W#^Gi^rie!AEyF*EC8yEo$cOoMxIwDY-c4~ol|aRHy9N7g zW#9XIKq}C2g!;a^bhb*Glzl;0mfa>UPY2V7(Vh%>v0WZ8Ehrd87b*>E95_B-tWn+! zWg{F?f5Mh(AR`dxm!xB?$scofx2?hOxR{D+FzVV2C><$eR;?HC2w4dx?xj?dwrQR!`q;X8_9==Jr)`l!+i>VYtma~Zi~T?K_?O(` zrX`eV&5-{sE&q8L>xo4PE{!$sI&Sh1%*O_UyYZ)Lr>#>RWMJznu@O+73nm6pUW}Qx zlwF~j1ri7$PR^9k%R!-qdNSV*Vk6~tXBF18`Ig>4veqUzt5EBN+^W&g!)^UWSmoqA zm_+%z5~%A1c4<{#NK)J|p5C{w{GUe`aw41dE4t~ok-r%s$}@Kh%&rJ_w}oC-r{-rw zR}@(!EVqr0on(4s1dR#l^z|U*4v!c6m(SE>z~$=8Y${W#vS6e9nqWgknQ&qVetN}*u^j4!JBUb5+skB(}tq|;A z>!bV=NYx#7hGveR3uoq%nE;zcv=-=>cAm!hOKbqw!fBjyH|Dgs{WHqOmHMf|LT^_6 zcE};Yz%SPDy`UJps$&DwZNrATdf| z8IwfY%=q%<@%sz$chlN*Hl;Iq)*$b;g$~)URwT$|xu1q35iNP_#30q!ZUAguU<~g> zQyFK_LPH4KAuSM4GJ`l}&YxG~`QnJ<4y)lPqs2Io)aZGwmQS$oj+%|bUZIX05X zX$UX(>Kswj##sYZunQ4oW&dDf0rYh%I5$^NjN!P0O?GN$B1wKo-_s~Mqln(-)cbHr zBrE<|?~APv+_3~cfG)+Bzp#LV>bdCgTCE0dn`aNd_~5aA8qls+)~%i1f&9OlmuMky zyv^dDtkD?0gpNA5BVIXBe{))xdmq4bYRF78tONE_i#aL#YuuYC15$x-1z3)6`v!&U zhUC^4o!-D|ViO4~ve%3Yb}bbfOd?Xv$_xAIIr2ntyUlpUV?)!4?PKK+XQe+60)Xr} zVhlNN=6_U|z}*gV|BA7q!~Q`=mZ}Fr)0FGT;JupTd^XXV(c6kYna54zbp;XK*H5=p zTmq#N<|_WQiybV*wD7zV4gx;ge5R2*5p#B}h#hM3GRwY=Pf>**M!wZ}#NDy*g4p%8 z4~-Gmw01+J^vOn+Wt5mjSctn(inFLxm$Iz6*V+ac%r7mV#w_`e&RpK>2xfAC|BeqJ*Cg7iVEV&<)2 zWP3)N#UlGHIE;95m9!~XFU(vTCLjx%#4|`P4-frV?l4SHfVs$i$4CFIw~v@__M4hj z)wU3rx2tpL@_0`~5;aUoBK18$BXez6z_&g4&~j>9iP_8r!_~2iF=Kh^(4&mI!vCSK zSqVMwa!QE(#*DKKXbKH5s!J}3;Z-EsO?=xkA=V&x5ScIgrOY7p(wP>b&WP0C3jF(e zn}MevocBh}ypcTXrbFMj3WA5d2$GyS)9#&HUAsBjt5@FaS(vvaeVX<)z1I6c68FvW zctg43vOS5GX||m?%Y>;X&V$*Wk=sUFfr-_4;}lYwez~t~4Cz1oz9*Gfj--j|T#sP~ z0{F=G8vS+}8H+0|;)fk2i@^vUkLR0wvO@>u=9E)v#xcdrJIMF@fH;Gxp-}TToU{*J zQaz|zC1l(p}HTFYbMn-46Fj5X|-q-`KMSrn$3IG7{ zmhEd7>6Ew}0Q^v9Wp08Db(UNkmrUc!h0UJ^T?5pTk9ON`zw9?f z?*<<~pA!PT1MdAQ`C)taNOI%$Df7_6(Cy`(F~-QdU5B?e;XbEXtWDGb6i&LN_+nlJ`wa1=+b{~)1>}Wx_(`?tW7b5l>pj++1x&o) zmu)qRX=RWh+Y%B5))VHB4)S@twl?)!k56u6QA(ilACfds)e+k&A*FyOXCXOfAKhZU z3g{_X$s}d%a{#!LdF+F>bNn-c80dDAL$q1lC=lHl@IVynW@$8~3?&MIjvX#vE4(<- zeenMtO8GhH1)wG!4Qc?4Ux{pI_ug6QM6;+1$1oVTEt!qJB74LMJUf_+fq3@ul0(v0 z7Y_U*6(+0Om7T~xj2WHLmDH1%Nlx$1_|a?Hf)F}duzFYhn8vOht?z=2T(`KZkxOyX zuJh_yq}#ox${y+?{K7x97;QavmUM~YzwOEV+8W(U1rcN1u(1<@((o?Uc91&L&gg_I zLvrcJBBVn4M}B_-vCM!d$NizDOLyP=RaQeJWGsD-lz#jeSJHRBOJUSjwY0^(+#9tu zhN~yG;qE+R;l>TBTHB6bSKo=c)BF{a@rKq6BaxcvcYLVANTE^IK1nHh; z*p4~fe)qE!#z2HSUU}ApLm&t=5w<(13T!u?(RUjO4BhOc@F^9#qm;lCAGs47?Qp7V z<@jGXWkWjheepvb)EK*-snl2OX3vY7_uE{VC0K@0QLKyoU%tKg#K+~vpD%gcFUQ%l zgV8)h4i5Gd)&1GTM!8^P%$NX4w|iulb|!|FtWl?q$ed7iSzdJbgRjs=Tq|~P{*x^J z52p8M9f~i{`vdkUBm7W`rF9>7}rn;`Lm=~fn$D&@ych;A|D3A56 z;1gd5M(-4YS}OKr&+88d_stht7WRiOug>B9r-rH|arM8Ysgd0F!L1$B*Nrxmp!F!8 zp0rVq`=OwXs8NfV1U_~sIyzL}{tV|Pp;;5bw*P(oKE)iw-Mi8*LlqigUe!>nrN1-n zI+2%R=I_;ammYl6HgP>@$d6I{*8X=eKY(@R%pjj2W53%4dMAOXS3%3(BP$5PV)>8< zg-u1a&$dyV=X$E3$@|vEF{UBn;LU_Xesy#yO6pDV<5iGQO!KjI}iF8CW1RCLy@2==80oqw-J4)~< z4)_iw-+@4Km{VyF=cp8D9xo+ZdD>z5o}5HE6_wZM?IPdsT(79BR>6^nnT?S=n$pV3 z>J?0NOSyd;riS7c>Q)6*I^T2J7aK7XwKXhVw{YX>=0Z%5BYTG3KZ2Rosk>HrLLRK( zByW~bYN5lNXp2ymv#U7vjY-Eu;1RxBY3F?i(ex3t`SB;lDjKVPC8vP|%Y56E&4{0C zV`Whb@<)ZCVT#OU`UUL;y|gXY_);T@>`Dba1^DRQUY)ni;Vq-LmMwz;;M(mDouu~K z0%80V+U6@ywYp7 z*6=9@?mxSZPJT^6p-i2|-$j`d%74gZ#B54}-SZ118iBhOh~``nzAgXoGf6p%_c~TN zDtT@mpGJy#@G1}q&31fRi-bh+-I5_AKbmpe*{4bBTspxIx#Wn<&8`0yWHTp2-N-mk zL43?zLVxXy%$?nPvk2ax{q4Azs= z3Xwq?1kmkibEKN-y)qTtiuY?}94=I6d(0jJgH8ZfvwR=Fy+l~00hRUULx>oX>4!dg~@SEld z?B)#QmQX^e4xX}1PNB{U?Y?HZa;3#nIwzNSVzMSKX!gGDpvE<9r zpwdOTS9SvjrZ6G;wX&)j7V@mWgR!=*%*s|iHltg0VW4c>!Nx}X^+)= zd}}p-D5qx+qFkXtprVaP|nGo2s`Wr?5E zpoFJF4(Gt8Xsv{)`)5HNFS^*dHCA4T{6x4;3y=8w<9PY_cy(#Xn4SSKZoFTne$5lFv>OG*2A_1=jC<@0<>G^V5G;^u(z5h?Q#j_%8TVwN(`*i8zBeyi$H$8Yl{ zFe1hwo12SP9F~ukI`)>wx#gV5`uI+Me?`5Y|K?&Txt^S6oxKK&6L-{bl1u zNplOw>NbuWc9qtJ>OvI^73R!?>k0ZVFgPr`?7=FJP&4`Xtv`mt>o&l(vWi`uGJAh2 z22I^_JN`UBanJxeub%mdsd*&C^LW{_ldLG6da`^n^J};N?T%TOUi@$>kKf+As_8rO zw4*saV_}VtE~ei%C~L@W%BRs}!O(p%6Ov0(a`6tjFlq`BkC5&!vNr;^7nP#LW?4o* za`-rgjynW(USFzV0EzOi`1xyJ%e%#~P%<;Vs^ykf%yxdZbqofc>KfnZ`=ie9N=nPD z7D?bE^=X1xE%kD=ZYBrb-?C^E+gU;;+u*0rS;#`k$U0GDq)5v?w@%$r^uuU`zD$^Js|T&M>{A5`h3_^V>vWb z7-Tm_LkLNBvM?pWpBNG4J!-&+omj=U(Q$uIGt`8|$*41fK+fKWrmSSiKFdRXf>S}|^x&>wdgUL<98%g z>NG!qw4$%0WgcR;IBM^1?lyK}CHFmQ%iHk4U22pkG`Zz9STY)aF)!ep{R{2bi%es^ z0c>o-ymh_NR%i3(Z^-DJ8AgN)!{LAFITS4Y@xcWWUA0~F&85a_Eq!T}w4CkJovF}% zE34hUD=Tv+{9YS62c4tf=X`YL^IBLAyH5jc;}Q@T2w%rrnah|^GH{G!E{Zcj8K$Z* z{s=I%v1+jc!!=_TM!q0_ZTK-@c)=YF28M@OXDE!-@69pFV+)}eVwB7F!Qp|j2jIEk zxfdtv0%_yWVy_bh0SkEJcN768XvwoHKs|7)EK$F%!v|h7$pHQ(sSjsp+!RF#ty6$S zv;==|_}Y&p>g``u$R>8MqQK95T_YOmcZel`D}~Sq+g0_b5K=cx1$=66vLM5e0KTNh zOL#kaTwTlPkxde=l^5t0&aYJW{P#Nl99-dtQboM}9Mj;5qae^bgnsWktp2f&*rUM9 z#p}~ns12{*X%<^R+$Ah#tu5eY@Wr(-;XPI$o@)uL|0j1S^&CA?Q%D3|4r_@rqP?9F zyWf*a>XC2toQoCl*rv~4!qYgf&-AoDsT->J8Q~qLBfh+$zRUptt9yyHc4x&;7 zg5HaUZdSj}H-*_HPW0_P!Q|(S_N4`B#+N7BJ->i))jy4gJJ4n@w7fA=nt8aqB{u3~{GV$n%gy4C)G7&VJ-LaE2VAQI zLvQ*$28PnN`h8~z1p8+4v3$QbDq-TRNJsR#cP_R**nhE_Lo?p?i4Ehw+w(zAug))z@L*tRk)ka^FY-kJEM|*#e=;05TyMoyf=V@b6%mHAu@Ts1r!f+_>JT6#xxUfzsiVN8 z=8)5=LMp!oKZp9io@_A;Y!AK5yR#K`pJ z%Yg>Lj}4M8q$gz8hC;+P10lQhlxSN9pIVvp1P#3j#4LTIa1HTDmCh|C9Mo3fN>HL0 z+uR2iw$dcVi#B%#_2HjPe-m!Wo6u9J{>7>h?`A!kcFS}cy6Y9@cubgUnt&`G>HC{+ zsb&#O&_%y{+xb5;!}g7M1neC1CI=1N<`)vH18#o@NWwSx)DZp>imrqCiXAN0QI z;+n_9mdA{`gCF4vqbv+$fcZU*5HVZ90uy#eqCLp0n-TEaMi_#w)RuFdt>pH=H&m+m zZZz;BC6~wq@tELakSMF-rz&}SfDCbc6iLOm$9n?Sn4m`5?Dw2j$ZVj8uGCu}ZYZo~ znHj?e9zRwBFex3sX`|#ZuCSdIkUx;sW#P^>5xhg!V-3LC&}za&MuX>`!&yKecg2s~ z2CS%yPniI9RaE$JyzACKT4PnnF4FiJ0dVpAQ2G7xlH)s<@OeL&E&>>-tZCRJT#O56 zAz&!-2*Wj5QP}teFJ$_%;l zFLmYnRDK5aiO#Qyk@BU%`)9}fu-0xl?s6_;nKzzGKrikjl^-YwRR!s6p>r)#VEkhtgn4~x z85={8A`@~GlNukq+;GP97alY7oBR?Ypgzt!Qn>_Z(UNCG@%IqL-JYALvLlsSO>&+a z!g&8m**onCsS+>aS#JFAhsd8Y*1lc+eYfGum0$HG(B<}(#!BiW(625=3*X5%cHeR< z^&iYJFQEz^7W6{S zeA=G-S+FgPOj|dxZR9iyQ|nl=3Gb=dOC(P~+8YP7VbjJoch&p3IoUK|)1V2h*@{+t@dpHZX%$i`d&bu89@!B`M`Y=_{me>ibXD*P zVi$^~r{u2@f(|lDgBil4u3Ww_!ks<9F|~QOw~xzfATo9$KYg-e5RJS~S?T+{dXd%n z`9vFBSG~>PO^oc#212>U{J5YQFB?6-k28Cgp0oMMT}i2KIZcBGR)|X~S9kTAlfvKk zGV_dY-!7OdN!tt|dX{3Rzi^(|x)Hv2zcR>Z@^*Nz0&Dciyxy8|=MUXIu;xYDAx=<= z1Y=aOD(xqqq_miNL$DYL%oj(dWjfQbCxXQZK_5mUmT%mZ?#3b$sxhk~Pp`fzC6@NW zB18^?)C5f>-byOwQb+%UIPZ^zpC?eZ4VhnqxIx^xD6e4-g&8-F1~#@!|Y1@{fhu=I+u8FGoqs8arR&invs|qzpp+ zMI)4PaKqIF+=%3Q@se3?YC!Ispmm*D?CZwB;QhRM-;6Z#bJ%Gmu>uQ)WJ`qCFjscV zbN~;(y;moA&>FrbArtWc;y9j+5zKKFWn+!(wKFAEan(%j)h|{YeoI^kK`#tvGHJ2x zF}_5Ep+U~oe38>{M0E+}6VCk0dwvt03D4eNa4FCul#|M6#cq4Dn(^J&pO8o)R(0#M zn=nV|AhNIglCR;duC7FD=kP%}53}HU%>oSMucSuqwN$;W$$f^b2E8niH6s-dn34xL@7$T>{=YY9c37l+1<# zzhqF*Lj*nvSEzCNRODF=v`@CQN<*`DOL1y9l_uyl`>Jhbvg0dtG8H(an-|Dlb5wOm z`0X@UbXHM%GyZ>6xW^^klFkHxrEYJL8`}|5GkohvfSQTJIM>n#d$W<1JV2*5?m@IZ zBhE@`GC`*SYAvyJ=nCaJ1r#^owPl_@gaswZIPkl&cwnQhodon|kfAr1V-#7nj8wIZ zxH*Rb3^-fChfeKitba7PgXOp6#6CF(8jOxX{RUM>e-r}3*nRiq7Vobc@awi`PBQRy ze6^v`;5N>vvmj8Hf$x#yf8TFH{>>HOAFBSpHz(oA!z0Nr>UDUevsQpOLHak0b;`8w GMgIr#=cudz literal 0 HcmV?d00001 diff --git a/assets/documentation-page/how-to-use/input-name.png b/assets/documentation-page/how-to-use/input-name.png new file mode 100644 index 0000000000000000000000000000000000000000..94522b6d6b64f59d1f10ef7ddf74b79560672e55 GIT binary patch literal 3735 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 literal 0 HcmV?d00001 diff --git a/dev-changelog.txt b/dev-changelog.txt index ed16d73..855ad40 100644 --- a/dev-changelog.txt +++ b/dev-changelog.txt @@ -116,4 +116,9 @@ 4.0 Beta (Build 20241.2) - Dodanie do paska tytułu programVersionStage -- Pzzygotowanie strony na dokumentację i instrukcję \ No newline at end of file +- Pzzygotowanie strony na dokumentację i instrukcję + +4.0 Beta (Build 20242) +- Ukończenie instrukcji +- Ukończenie sekcji strony "O programie" +- Ukończenie sekcji strony "Opis" \ No newline at end of file diff --git a/documentation/about_program.html b/documentation/about_program.html index 32e1bfc..646817c 100644 --- a/documentation/about_program.html +++ b/documentation/about_program.html @@ -7,6 +7,62 @@ Generator CSV -

XD

+

+
+
+
+ +
+
+
+

+

+ Generator CSV +

+

+ Autorzy: +

+

+ Mateusz Skoczek (styczeń 2019 - wrzesień 2020)
+
+ dla ZSP Sobolew
+

+
+
+
+
+
+
+

+ Inne informacje:
+

+ Język programowania, użyty w programie: Python
+
+ Biblioteki: +

+ +
    +
  • sys
  • +
  • os
  • +
  • time
  • +
  • codecs
  • +
  • pathlib
  • +
  • shutil
  • +
+
+

+
+ Biblioteki GUI: +

+ +
    +
  • tkinter
  • +
  • pillow
  • +
+
+

+
+ Języki, użyte do stworzenia strony z instrukcją i dokumentacją techniczną: HTML, CSS, JavaScript +

\ No newline at end of file diff --git a/documentation/content.css b/documentation/content.css index 37baf1b..3b7d964 100644 --- a/documentation/content.css +++ b/documentation/content.css @@ -1,3 +1,80 @@ +h1 { + color: #C0C0C0; + font-family: 'Segoe UI'; + font-size: 35px; + text-align: center; +} + +h5 { + color: #C0C0C0; + font-family: 'Segoe UI'; + font-size: 15px; +} + p { - color: #C0C0C0 + color: #C0C0C0; + font-family: 'Segoe UI'; +} + +#description-text { + color: #C0C0C0; + font-family: 'Segoe UI'; + font-size: 17px; + line-height: 25px; + text-align: center; +} + +#about-program-image { + text-align: center; +} +#about-program-main-text { + color: #C0C0C0; + font-family: 'Segoe UI'; + font-size: 17px; + line-height: 25px; + text-align: center; +} + +#about-program-text-centered { + text-align: center; +} + +td { + color: #C0C0C0; + font-family: 'Segoe UI'; +} + +th { + color: #C0C0C0; + font-family: 'Segoe UI'; +} + +.accordion { + background-color: #333842; + color: #444; + cursor: pointer; + padding: 18px; + width: 100%; + text-align: left; + border: none; + outline: none; + transition: 0.4s; + color: #C0C0C0; + font-size: 18px; + font-family: 'Segoe UI'; +} + +.active, .accordion:hover { + background-color: #3a3f4b; +} + +.panel { + padding: 0 18px; + background-color: #2e313b; + display: none; + overflow: hidden; +} + +li { + color: #C0C0C0; } \ No newline at end of file diff --git a/documentation/other.html b/documentation/description.html similarity index 54% rename from documentation/other.html rename to documentation/description.html index 9dcbac4..824ae4b 100644 --- a/documentation/other.html +++ b/documentation/description.html @@ -7,6 +7,6 @@ Generator CSV -

w sumie jeszcze nie wiem co tu dam

+

Program "Generator CSV" służy do przetwarzania plików danymi uczniów/nauczycieli i generowania plików wyjściowych w formacie pozwalającym na import danych na stronach szkoły oraz portal.office.com

\ No newline at end of file diff --git a/documentation/how_to_use.html b/documentation/how_to_use.html index a421c3a..663c5ff 100644 --- a/documentation/how_to_use.html +++ b/documentation/how_to_use.html @@ -7,6 +7,157 @@ Generator CSV -

no siema

+ +
+

Dane dla przykładu:

+
+

W praktyce układ danych wygląda tak (* - nieznaczące dla programu dane):

+

[imię] [nazwisko]
[klasa] [szkoła]
[login], [haslo*]

+

Uruchom program i otwórz zakładkę FORMAT DANYCH. W pierwszej kolejności należy nadać nazwę presetowi formatu w polu zaznaczonym poniżej lub wybrać preset już istniejący z listy rozwijanej

+
+

Aby przejśc do edycji presetu należy wcisnąć przycisk WCZYTAJ

+
+
Typ osoby
+

Wybierz odpowiednią opcję w zależności od tego jakich osób dane znajdują się w pliku wejściowym

+
Separator pomiędzy danymi
+

W tym polu należy wpisać znaki, które oddzielają zbiór danych pojeńczych osób. Jeżeli zbiór danych odziela znak końca wiersza (wciśnięcie ENTER) należy wpisać <enter> (ilość zależna od ilości znaków końca wiersza). Niedozwolone znaki to litery, cyfry oraz * i \. Dla przykładowych danych będzie to "<enter><enter>"

+
Separator pomiędzy wierszami
+

W tym polu należy wpisać znaki, które oddzielają wiersze. Zazwyczaj jest to jeden znak końca wiersza. Jeżeli wiersze odziela znak końca wiersza (wciśnięcie ENTER) należy wpisać <enter> (ilość zależna od ilości znaków końca wiersza). Niedozwolone znaki to litery, cyfry oraz * i \. Dla przykładowych danych będzie to "<enter>"

+
Separatory pomiędzy danymi
+

W tym polu należy wpisać znaki, które oddzielają pojedyńcze dane. Każdy kolejny separator musi być zapisany w następnej linijce. Jeżeli dane odziela znak końca wiersza (wciśnięcie ENTER) należy wpisać <enter> (ilość zależna od ilości znaków końca wiersza). Niedozwolone znaki to litery, cyfry oraz * i \. Dla przykładowych danych będzie to (znaki są podkreślone aby je wyróżnić):
"""
, 

"""

+
Prawa kolumna - pozycja danych w zbiorze
+

W tej sekcji należy dla każdego typu danych ustawić jego położenie w zbiorze. Typy SZKOŁA i KLASA nie są istotne jeżeli w sekcji TYP OSOBY zosła zaznaczona opcja Nauczyciele. Dla przykładowych danych uczniów bedzie to:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
WierszPozycja
w wierszu
Login31
Imię11
Nazwisko12
Szkoła22
Klasa21
+
Prawa kolumna - Kodowanie
+

W tym polu należy wybrać kodowanie pliku wejściowego.

+
+

Aby zapisać preset należy wcisnąć przycisk ZAPISZ. Aby anulować tworzenie należy wcisnąć przycisk Anuluj

+
+ + +
+

Uruchom program i przejdź do zakładki USTAWIENIA.

+
+
Kodowanie wyjściowe dla pliku poczty
+

W tym polu należy wybrać kodowanie, w jakim ma być zapisany plik csv z danymi uczniów do importu w panelu administracyjnym serwisu szkoły

+
Kodowanie wyjściowe dla pliku office
+

W tym polu należy wybrać kodowanie, w jakim ma być zapisany plik csv z danymi uczniów do importu w panelu administracyjnym serwisu portal.office.com

+
Domena (używana w mailu)
+

W tym polu należy wpisać domenę serwisu szkoły. Będzie ona używana w mailu każdego uzytkownika. Przykład: jan.kowalski2023a@losobolew.pl

+
Quota (MB)
+

W tym polu należy wpisać ilość miejsca (w megabajtach) przeznaczoną dla każdego użytkownika na przechowywanie maili. Te dane są używane w pliku wyjściowym poczty. Przykład: jan.kowalski2023a@losobolew.pl,1234567u:JK,500

+
Kraj (zapisany w danych na office)
+

W tym polu należy wpisać nazwę kraju zamieszkania użytkowników. Będzie ona wykorzystywana w pliku wyjściowym office oraz widoczna w profilu użytkownika w serwisie office. Przykład: jan.kowalski2023a@losobolew.pl,Jan,Kowalski,Jan Kowalski,uczeń,1b LO,,,,,,,,,Rzeczpospolita Polska

+
Rozpoczęcie roku szkolnego (DD | MM)
+

W tych polach należy wpisać datę rozpoczęcia roku szkolnego (z reguły). W pierwszym polu (po lewo) należy wpisać dzień, a w drugim (po prawo) miesiąc. Te dane są używane do obliczania roku w znaczniku klasy (przykłądowy znacznik klasy: 2023a).

+
Dane o szkołach
+

W tym polu należy wprowadzić dane każdej szkoły w zespole szkół (po jednej szkole w linijce). Wymagane są trzy "kolumny danych". Są one odzielone znakami " | ".
W pierwszej kolumnie należy wpisać oznaczenie szkoły (dowolne bez spacji, najlepiej jak najkrótsze). Te dane będą używane w znaczniku klasy, jeżeli w trzeciej kolumnie została wybrana opcja "1".
W drugiej kolmnie należy wpisać liczbę klas w danej szkole. Te dane używane są do obliczenia roku w znaczniku klasy.
W trzeciej kolumnie można wybrać opcję "1" lub "0" (Prawda/Fałsz). Wybierz "1" jeżeli chcesz, aby w znaczniku klasy znajdowało się oznaczenie szkoły (przykład: 2023bs). Wybierz "0" jeżeli chcesz, aby w znaczniku klasy znajdowała się litera klasy (przykład: 2023a).

+
+

Aby zapisać ustawienia należy wcisnąć przycisk ZAPISZ. Aby anulować zmiany wciśnij przycisk Anuluj

+
+ + +
+
Krok 1
+

W pierwszej kolejności należy przygotować pliki wejściowe oraz stworzyć presety formatu dla tych plików, według instrukcji w sekcji "Tworzenie/edytowanie format presetu dla danego pliku źródłowego" (jednego presetu formatu można używac do kilku plików jeżeli pliki mają ten sam format i to samo kodowanie)

+
Krok 2
+

Przejdź do zakładki GENERATOR CSV. Możesz przetworzyć naraz 4 pliki wejściowe. Dla każdego pliku należy wybrać jego lokalizację (wpisać ją w polu, oznaczonym kolorem czerwonym na poniższym zdjęciu, lub otworzyć okno wyboru lokalizacji za pomocą przycisku "Przeglądaj" i tam wybrać plik. Następnie należy wybrać preset formatu, odpowiedni dla pliku wejściowego, w polu oznaczonym kolorem zielonym na poniższym zdjęciu.

+
+
Krok 3
+

Wybierz lokalizację zapisu pliku wyjściowego dla serwisu szkoły, wpisując lokalizację wraz z nazwą pliku w polu, oznaczonym kolorem czerwonym na poniższym zdjęciu, lub otwierając okno wyboru lokalizacji za pomocą przycisku "Przeglądaj" obok tego pola i tam wybierając plik.
Wybierz lokalizację zapisu pliku wyjściowego dla serwisu portal.office.com, wpisując lokalizację wraz z nazwą pliku w polu, oznaczonym kolorem zielonym na poniższym zdjęciu, lub otwierając okno wyboru lokalizacji za pomocą przycisku "Przeglądaj" obok tego pola i tam wybierając plik.

+
+
Krok 4
+

Aby rozpocząć generowanie wciśnij przycisk START i potwierdź komunikat. Pliki wyjściowe zostaną zapisane w wybranych lokalizacjach. Przed importem upewnij się że w plikach nie występują błędy.

+
+ + +
+
Krok 1
+

Uruchom program i przejdź do zakładki USTAWIENIA.

+
Krok 2
+

Naciśnij przycisk "Zarządzaj presetami formatu".

+
Krok 3
+

Zaznacz presety formatu które chcesz usunąć.

+
Krok 4
+

Naciśnij przycisk "Usuń zaznaczone"

+
+ + +
+
Krok 1
+

Uruchom program i przejdź do zakładki USTAWIENIA.

+
Krok 2
+

Naciśnij przycisk "Przywróć domyślne ustawienia ogólne". Po potwierdzeniu i ponownym uruchomieniu programu, ustawienia zostaną przywrócone do ustawień fabrycznych.

+
+ + +
+
Krok 1
+

Uruchom program i przejdź do zakładki USTAWIENIA.

+
Krok 2
+

Naciśnij przycisk "Przywróć domyślne ustawienia wyglądu". Po potwierdzeniu i ponownym uruchomieniu programu, wygląd zostanie przywrócony do ustawień fabrycznych.

+
+ + +
+
Krok 1
+

Otwórz menu start i wpisz "%appdata%"

+
Krok 2
+

Przejdź do folderu "Generator CSV"

+
Krok 3
+

Za ustawienia programu odpowiedzialny jest plik "config.cfg", a za wygląd plik "style.cfg". Użyj dowolnego edytora plików tekstowych, do edycji tych plików. Więcej informacji o tych plikach znajduje się w Dokumentacji technicznej.
Aby przywrócić te pliki do stanu fabrycznego wykonaj kroki z sekcji odpowiednio "Przywracanie domyślnych ustawień ogólnych programu" lub "Przywracanie domyślnego wyglądu programu"

+
+ + \ No newline at end of file diff --git a/documentation/index.html b/documentation/index.html index 706d989..685195b 100644 --- a/documentation/index.html +++ b/documentation/index.html @@ -7,22 +7,24 @@ Generator CSV -
- -
-

GENERATOR CSV

-
-
-
-
- -
+
+
+ +
+

GENERATOR CSV

+
+ +
+
+ +
+
\ No newline at end of file diff --git a/documentation/main.css b/documentation/main.css index 205face..a4d8e5e 100644 --- a/documentation/main.css +++ b/documentation/main.css @@ -1,11 +1,11 @@ -body { +body, html{ background-color: #21242D; margin: 0px; + height: 100%; + width: 100%; } - - header { width: 100%; position: sticky; @@ -38,6 +38,7 @@ nav { #menu-ul { width: 100%; + height: 50px; padding: 0; margin: 0; list-style: none; @@ -48,11 +49,11 @@ nav { float: left; text-align: center; font-family: 'Segoe UI'; - padding: 0.8%; margin-right: 0.05%; margin-left: 0.05%; font-size: 20px; - width: 23.3%; + width: 24.9%; + line-height: 50px; text-decoration: none; color: #C0C0C0; background-color: #2e313b; @@ -65,13 +66,16 @@ nav { - section { width: 100%; } iframe { + float: left; width: 99%; border: none; - margin: 0.5%; + padding: 0.5%; + height: calc(100% - 220px); + overflow: scroll; + position: absolute; } \ No newline at end of file diff --git a/documentation/program_documentation.html b/documentation/program_documentation.html index 7c5586e..deefc17 100644 --- a/documentation/program_documentation.html +++ b/documentation/program_documentation.html @@ -7,6 +7,6 @@ Generator CSV -

siema siema kurwa witam

+

nic

\ No newline at end of file diff --git a/generator.pyw b/generator.pyw index d751bfe..71cdc2c 100644 --- a/generator.pyw +++ b/generator.pyw @@ -16,8 +16,8 @@ class VAR: # Informacje o programie programName = 'Generator CSV' programVersion = '4.0' - programVersionStage = 'Beta' - programVersionBuild = '20241.2' + programVersionStage = '' + programVersionBuild = '20242' programCustomer = 'ZSP Sobolew' programAuthors = ['Mateusz Skoczek'] programToW = ['styczeń', '2019', 'wrzesień', '2020'] @@ -42,7 +42,6 @@ import time as TM import codecs as CD import pathlib as PT import shutil as SU -import subprocess as SP # GUI From 931607314807b2193b791c250421980744ff1c19 Mon Sep 17 00:00:00 2001 From: Mateusz Skoczek Date: Mon, 31 Aug 2020 23:46:14 +0200 Subject: [PATCH 24/29] 4.0 Beta (Build 20244) --- dev-changelog.txt | 9 ++++- generator.pyw | 84 ++++++++++++++++++++++++++++++++++++----------- 2 files changed, 73 insertions(+), 20 deletions(-) diff --git a/dev-changelog.txt b/dev-changelog.txt index 855ad40..162d67d 100644 --- a/dev-changelog.txt +++ b/dev-changelog.txt @@ -121,4 +121,11 @@ 4.0 Beta (Build 20242) - Ukończenie instrukcji - Ukończenie sekcji strony "O programie" -- Ukończenie sekcji strony "Opis" \ No newline at end of file +- Ukończenie sekcji strony "Opis" + +4.0 Beta (Build 20244) +- Wsparcie dla kodowania ANSI +- Wyeliminowanie błędu wyrzucającego wyjątek po naciśnięciu przycisku START bez wprowadzenia danych powyżej +- Poprawki w pliku wyjściowym office (usunięcie dwóch przecinków pomiedzy stanowiskiem a klasą) +- Wyeliminowanie błędu pokazującego w komunikacie o błędnych danych, przy zapisie format presetu, wpisanej wartości zamiast recordu +- Wyeliminowanie błędu pozwalającego na zapisanie wprowadzonego w spinboxie tekstu \ No newline at end of file diff --git a/generator.pyw b/generator.pyw index 71cdc2c..5bcf42f 100644 --- a/generator.pyw +++ b/generator.pyw @@ -23,7 +23,7 @@ class VAR: programToW = ['styczeń', '2019', 'wrzesień', '2020'] # Dozwolone kodowanie plików - allowedCoding = ['utf-8'] + allowedCoding = ['utf-8', 'ANSI'] # Dozwolone znaki allowedCharactersInSeparator = ['`', '~', '!', '@', '#', '$', '%', '^', '&', '(', ')', '-', '_', '=', '+', '[', ']', ' ', '?', '/', '>', '.', '<', ',', '"', "'", ':', ';', '|'] @@ -199,6 +199,10 @@ class CFG: # Funkcje sprawdzające poprawność recordu def __checkI(self, write, record, var): if write: + try: + var = int(var) + except: + return (False, 'Niepoprawne dane - klucz: %s' % record) var = str(var) else: try: @@ -213,6 +217,12 @@ class CFG: if var['D'] == None: varX += '*' else: + try: + var['D'] = int(var['D']) + except: + return (False, 'Niepoprawne dane - klucz: %s' % record) + if int(var['s']) > 31 or int(var['s']) < 1: + return (False, 'Niepoprawne dane - klucz: %s' % record) day = str(var['D']) if len(day) == 1: day = '0' + day @@ -221,6 +231,12 @@ class CFG: if var['M'] == None: varX += '*' else: + try: + var['M'] = int(var['M']) + except: + return (False, 'Niepoprawne dane - klucz: %s' % record) + if int(var['s']) > 12 or int(var['s']) < 1: + return (False, 'Niepoprawne dane - klucz: %s' % record) month = str(var['M']) if len(month) == 1: month = '0' + month @@ -229,11 +245,23 @@ class CFG: if var['Y'] == None: varX += '*' else: + try: + var['Y'] = int(var['Y']) + except: + return (False, 'Niepoprawne dane - klucz: %s' % record) + if int(var['Y']) == 0: + return (False, 'Niepoprawne dane - klucz: %s' % record) varX += str(var['Y']) varX += ' ' if var['h'] == None: varX += '*' else: + try: + var['h'] = int(var['h']) + except: + return (False, 'Niepoprawne dane - klucz: %s' % record) + if int(var['h']) > 23 or int(var['h']) < 1: + return (False, 'Niepoprawne dane - klucz: %s' % record) hour = str(var['h']) if len(hour) == 1: hour = '0' + hour @@ -242,6 +270,12 @@ class CFG: if var['m'] == None: varX += '*' else: + try: + var['m'] = int(var['m']) + except: + return (False, 'Niepoprawne dane - klucz: %s' % record) + if int(var['m']) > 59 or int(var['m']) < 0: + return (False, 'Niepoprawne dane - klucz: %s' % record) minute = str(var['m']) if len(minute) == 1: minute = '0' + minute @@ -250,6 +284,12 @@ class CFG: if var['s'] == None: varX += '*' else: + try: + var['s'] = int(var['s']) + except: + return (False, 'Niepoprawne dane - klucz: %s' % record) + if int(var['s']) > 59 or int(var['s']) < 0: + return (False, 'Niepoprawne dane - klucz: %s' % record) seconds = str(var['s']) if len(seconds) == 1: seconds = '0' + seconds @@ -268,6 +308,10 @@ class CFG: index = 0 for x in var: if x != '*': + try: + x = int(x) + except: + return (False, 'Niepoprawne dane - klucz: %s' % record) varToReturn[dateLabels[index]] = int(x) else: varToReturn[dateLabels[index]] = None @@ -636,7 +680,7 @@ class FMT: check = check.strip('') for x in check: if x not in VAR.allowedCharactersInSeparator: - return [False, 'Niepoprawne dane - klucz: %s' % var] + return [False, 'Niepoprawne dane - klucz: %s' % record] return [True, var] def __checkAs(self, write, record, var): @@ -646,7 +690,7 @@ class FMT: x = x.strip('') for y in x: if y not in VAR.allowedCharactersInSeparator: - return [False, 'Niepoprawne dane - klucz: %s' % var] + return [False, 'Niepoprawne dane - klucz: %s' % record] var = str(var) else: new_contentVar = (var)[2:-2].split("', '") @@ -655,12 +699,16 @@ class FMT: x = x.strip('') for y in x: if y not in VAR.allowedCharactersInSeparator: - return [False, 'Niepoprawne dane - klucz: %s' % var] + return [False, 'Niepoprawne dane - klucz: %s' % record] var = new_contentVar return [True, var] def __checkI(self, write, record, var): if write: + try: + var = int(var) + except: + return (False, 'Niepoprawne dane - klucz: %s' % record) var = str(var) else: try: @@ -671,7 +719,7 @@ class FMT: def __checkSc(self, record, var): if var not in VAR.allowedCoding: - return [False, 'Niepoprawne dane - klucz: %s' % var] + return [False, 'Niepoprawne dane - klucz: %s' % record] return [True, var] @@ -869,9 +917,9 @@ class dataProcess: filledFiles.append(index) index += 1 if len(filledFiles) != 0: - return (True, filledFiles) + return [True, filledFiles] else: - return (False) + return [False] def __checkIfInputFilesIsReadable(self, files, filledFiles): for x in filledFiles: @@ -1029,8 +1077,6 @@ class dataProcess: else: office += 'nauczyciel' office += ',' - office += ',' - office += ',' if x[0]: office += str(yearOfGraduation) if (schoolData[x[4]])[1]: @@ -2843,16 +2889,16 @@ class mainWindow: "personSeparator" : self.EPOSPersonSeparatorEntry.get(), "rowSeparator" : self.EPOSRowSeparatorEntry.get(), "dataSeparators" : (self.EPOSDataSeparatorText.get("1.0", TK.END)).split('\n')[:-1], - "loginRow" : int(self.EPDLLoginRowSpinbox.get()), - "loginPositionInRow" : int(self.EPDLLoginPosInRowSpinbox.get()), - "fnameRow" : int(self.EPDLFnameRowSpinbox.get()), - "fnamePositionInRow" : int(self.EPDLFnamePosInRowSpinbox.get()), - "lnameRow" : int(self.EPDLLnameRowSpinbox.get()), - "lnamePositionInRow" : int(self.EPDLLnamePosInRowSpinbox.get()), - "schoolRow" : int(self.EPDLSchoolRowSpinbox.get()), - "schoolPositionInRow" : int(self.EPDLSchoolPosInRowSpinbox.get()), - "classRow" : int(self.EPDLClassRowSpinbox.get()), - "classPositionInRow" : int(self.EPDLClassPosInRowSpinbox.get()), + "loginRow" : self.EPDLLoginRowSpinbox.get(), + "loginPositionInRow" : self.EPDLLoginPosInRowSpinbox.get(), + "fnameRow" : self.EPDLFnameRowSpinbox.get(), + "fnamePositionInRow" : self.EPDLFnamePosInRowSpinbox.get(), + "lnameRow" : self.EPDLLnameRowSpinbox.get(), + "lnamePositionInRow" : self.EPDLLnamePosInRowSpinbox.get(), + "schoolRow" : self.EPDLSchoolRowSpinbox.get(), + "schoolPositionInRow" : self.EPDLSchoolPosInRowSpinbox.get(), + "classRow" : self.EPDLClassRowSpinbox.get(), + "classPositionInRow" : self.EPDLClassPosInRowSpinbox.get(), "inputCoding" : self.formatInputCodingCombobox.get() } if not FMT.W(self.loadingList.get(), formatFileContentToSave): From 066e1f6a17361c1c1b32540ba37ed9277ea0ac91 Mon Sep 17 00:00:00 2001 From: Mateusz Skoczek Date: Tue, 1 Sep 2020 19:15:30 +0200 Subject: [PATCH 25/29] 4.0 Beta (Build 20245) --- configs/config.cfg | 6 +- configs/style.cfg | 5 + dev-changelog.txt | 13 +- documentation/about_program.html | 37 ---- documentation/how_to_use.html | 4 + documentation/program_documentation.html | 2 +- generator.pyw | 231 ++++++++++++++++++++--- 7 files changed, 235 insertions(+), 63 deletions(-) diff --git a/configs/config.cfg b/configs/config.cfg index 628ff5e..4880304 100644 --- a/configs/config.cfg +++ b/configs/config.cfg @@ -5,4 +5,8 @@ domain(S) = losobolew.pl quota(I) = 500 country(S) = Rzeczypospolita Polska schoolData(MSAs) = [LO, 4, 0]|[BS, 3, 1] -schoolyearStart(D) = 01.09.* *:*:* \ No newline at end of file +schoolyearStart(D) = 01.09.* *:*:* +ifHeadlineInMail(B) = 0 +headlineInMail(S) = beta +ifHeadlineInOffice(B) = 0 +headlineInOffice(S) = beta \ No newline at end of file diff --git a/configs/style.cfg b/configs/style.cfg index e3db8dc..4130065 100644 --- a/configs/style.cfg +++ b/configs/style.cfg @@ -103,6 +103,9 @@ EPOSLabelAnchor(FAanchor) = center radiobutton1Background(C) = #21242D radiobutton1TextColor(C) = #C0C0C0 radiobutton1IndicatorBackground(C) = #21242D +checkbutton1Background(C) = #21242D +checkbutton1TextColor(C) = #C0C0C0 +checkbutton1IndicatorBackground(C) = #21242D text1Background(C) = #333842 text1TextColor(C) = #C0C0C0 text1Relief(FArelief) = flat @@ -118,6 +121,8 @@ settingsCodeLabelWidth(I) = 35 settingsCodeLabelAnchor(FAanchor) = w settingsOtherLabelWidth(I) = 35 settingsOtherLabelAnchor(FAanchor) = w +settingsHeadlineLabelWidth(I) = 35 +settingsHeadlineLabelAnchor(FAanchor) = w settingsSchoolDataLabelAnchor(FAanchor) = center settingsButtonsPadY(I) = 6 settingsButtonSaveWidth(I) = 20 diff --git a/dev-changelog.txt b/dev-changelog.txt index 162d67d..3d16cc8 100644 --- a/dev-changelog.txt +++ b/dev-changelog.txt @@ -128,4 +128,15 @@ - Wyeliminowanie błędu wyrzucającego wyjątek po naciśnięciu przycisku START bez wprowadzenia danych powyżej - Poprawki w pliku wyjściowym office (usunięcie dwóch przecinków pomiedzy stanowiskiem a klasą) - Wyeliminowanie błędu pokazującego w komunikacie o błędnych danych, przy zapisie format presetu, wpisanej wartości zamiast recordu -- Wyeliminowanie błędu pozwalającego na zapisanie wprowadzonego w spinboxie tekstu \ No newline at end of file +- Wyeliminowanie błędu pozwalającego na zapisanie wprowadzonego w spinboxie tekstu. + +4.0 Beta (Build 20245) +- Wyeliminowanie błędu uniemożliwającego pierwsze uruchomienie programu +- Wyeliminowanie błędu pozwalającego na wybranie tej samej lokalizacji dla pliku wyjściowego poczty i office +- Skrócenie sekcji strony "O programie" +- Dodanie mechanizmu usuwania katalogu programu w appdata po aktualizacji programu +- Wyeliminowanie błędu uniemożliwiającego zapisanie ustawień +- Dodanie możliwości dodawania nagłówków do plików wyjściowych +- Wsparcie dla kodowania ISO-8859-2 +- Wyeliminowanie błędu wyrzucającego wyjątek w przypadku próby zapisania format presetu o nazwie zawierającej zakazane znaki. +- Dodanie mechanizmu pozwalającego na automatyczne resetowanie katalogu programu w appdata podczas uruchamiania programu \ No newline at end of file diff --git a/documentation/about_program.html b/documentation/about_program.html index 646817c..e046a42 100644 --- a/documentation/about_program.html +++ b/documentation/about_program.html @@ -27,42 +27,5 @@
dla ZSP Sobolew

-
-
-
-
-
-
-

- Inne informacje:
-

- Język programowania, użyty w programie: Python
-
- Biblioteki: -

- -
    -
  • sys
  • -
  • os
  • -
  • time
  • -
  • codecs
  • -
  • pathlib
  • -
  • shutil
  • -
-
-

-
- Biblioteki GUI: -

- -
    -
  • tkinter
  • -
  • pillow
  • -
-
-

-
- Języki, użyte do stworzenia strony z instrukcją i dokumentacją techniczną: HTML, CSS, JavaScript -

\ No newline at end of file diff --git a/documentation/how_to_use.html b/documentation/how_to_use.html index 663c5ff..1d3a51a 100644 --- a/documentation/how_to_use.html +++ b/documentation/how_to_use.html @@ -79,6 +79,10 @@

W tym polu należy wpisać ilość miejsca (w megabajtach) przeznaczoną dla każdego użytkownika na przechowywanie maili. Te dane są używane w pliku wyjściowym poczty. Przykład: jan.kowalski2023a@losobolew.pl,1234567u:JK,500

Kraj (zapisany w danych na office)

W tym polu należy wpisać nazwę kraju zamieszkania użytkowników. Będzie ona wykorzystywana w pliku wyjściowym office oraz widoczna w profilu użytkownika w serwisie office. Przykład: jan.kowalski2023a@losobolew.pl,Jan,Kowalski,Jan Kowalski,uczeń,1b LO,,,,,,,,,Rzeczpospolita Polska

+
Nagłówek dla pliku wyjściowego poczty
+

Jeżeli chcesz aby w pierwszej linii pliku wyjściowego dla poczty znajdował się nagłowek, zaznacz opcję "Umieść w pliku". Zawartość nagłówka można edytować w polu tekstowym po lewej.

+
Nagłówek dla pliku wyjściowego office
+

Jeżeli chcesz aby w pierwszej linii pliku wyjściowego dla office znajdował się nagłowek, zaznacz opcję "Umieść w pliku". Zawartość nagłówka można edytować w polu tekstowym po lewej.

Rozpoczęcie roku szkolnego (DD | MM)

W tych polach należy wpisać datę rozpoczęcia roku szkolnego (z reguły). W pierwszym polu (po lewo) należy wpisać dzień, a w drugim (po prawo) miesiąc. Te dane są używane do obliczania roku w znaczniku klasy (przykłądowy znacznik klasy: 2023a).

Dane o szkołach
diff --git a/documentation/program_documentation.html b/documentation/program_documentation.html index deefc17..71ad1e4 100644 --- a/documentation/program_documentation.html +++ b/documentation/program_documentation.html @@ -7,6 +7,6 @@ Generator CSV -

nic

+

reset_appdata

\ No newline at end of file diff --git a/generator.pyw b/generator.pyw index 5bcf42f..71f33fc 100644 --- a/generator.pyw +++ b/generator.pyw @@ -16,14 +16,14 @@ class VAR: # Informacje o programie programName = 'Generator CSV' programVersion = '4.0' - programVersionStage = '' - programVersionBuild = '20242' + programVersionStage = 'Beta' + programVersionBuild = '20245' programCustomer = 'ZSP Sobolew' programAuthors = ['Mateusz Skoczek'] programToW = ['styczeń', '2019', 'wrzesień', '2020'] # Dozwolone kodowanie plików - allowedCoding = ['utf-8', 'ANSI'] + allowedCoding = ['utf-8', 'ANSI', 'iso-8859-2'] # Dozwolone znaki allowedCharactersInSeparator = ['`', '~', '!', '@', '#', '$', '%', '^', '&', '(', ')', '-', '_', '=', '+', '[', ']', ' ', '?', '/', '>', '.', '<', ',', '"', "'", ':', ';', '|'] @@ -87,6 +87,7 @@ MSGlist = { 'I0002' : 'Aplikacja zostanie zamknięta w celu przeładowania ustawień', 'E0015' : 'Nie można usunąć wybranych format presetów', 'E0016' : 'Nie można uruchomić pliku instrukcji (documentation/index.html)', + 'E0017' : 'Nie można zapisać pliku formatu', } def MSG(code, terminate, *optionalInfo): @@ -124,37 +125,76 @@ def MSG(code, terminate, *optionalInfo): +# ----------------------------------- # Opcje deweloperskie # ----------------------------------- # + +dev_config = [] +if 'dev.cfg' in [x for x in OS.listdir('.\configs')]: + try: + dev_config = CD.open(r'.\configs\dev.cfg', 'r', 'utf-8').read().split('\n') + except Exception as exceptInfo: + print('DEVELOPER CONSOLE LOG: Nie można załadować listy aktywnych opcji developerskich') + else: + print('DEVELOPER CONSOLE LOG: Pomyślnie załadowano listę aktywnych opcji developerskich') + print('DEVELOPER CONSOLE LOG: Lista aktywnych opcji developerskich: %s' % str(dev_config)) + + + + + # ------------------------- # Sprawdzanie katalogu programu w APPDATA # ------------------------- # appdata = PT.Path.home() / 'Appdata/Roaming' -#TODO -#SU.rmtree(str(appdata) + '/Generator CSV') -#TODO +if 'reset_appdata' in dev_config: + try: + SU.rmtree(str(appdata) + '\Generator CSV') + OS.mkdir(str(appdata) + '\Generator CSV') + versionFile = CD.open((str(appdata) + r'\Generator CSV\version'), 'w', 'utf-8') + versionFile.write(VAR.programVersionBuild) + versionFile.close() + SU.copy('configs\config.cfg', str(appdata) + '\Generator CSV\config.cfg') + SU.copy('configs\style.cfg', str(appdata) + '\Generator CSV\style.cfg') + OS.mkdir(str(appdata) + r'\Generator CSV\format-presets') + except Exception as exceptInfo: + print("DEVELOPER CONSOLE LOG: Folder 'Generator CSV' w folderze 'APPDATA' nie został zresetowany z powodu błędu: %s" % exceptInfo) + else: + print("DEVELOPER CONSOLE LOG: Folder 'Generator CSV' w folderze 'APPDATA' został zresetowany pomyślnie") def checkAppdata(): if 'Generator CSV' not in [x for x in OS.listdir(appdata)]: try: - OS.mkdir(str(appdata) + '/Generator CSV') - SU.copy('configs/config.cfg', str(appdata) + '\Generator CSV\config.cfg') - SU.copy('configs/style.cfg', str(appdata) + '\Generator CSV\style.cfg') - OS.mkdir(str(appdata) + '/Generator CSV/format-presets') + OS.mkdir(str(appdata) + '\Generator CSV') + versionFile = CD.open((str(appdata) + r'\Generator CSV\version'), 'w', 'utf-8') + versionFile.write(VAR.programVersionBuild) + versionFile.close() + SU.copy('configs\config.cfg', str(appdata) + '\Generator CSV\config.cfg') + SU.copy('configs\style.cfg', str(appdata) + '\Generator CSV\style.cfg') + OS.mkdir(str(appdata) + r'\Generator CSV\format-presets') except Exception as exceptInfo: MSG('E0001', True, exceptInfo) else: - if 'config.cfg' not in [x for x in OS.listdir(str(appdata) + '/Generator CSV')]: + if 'version' not in [x for x in OS.listdir(str(appdata) + '\Generator CSV')]: + SU.rmtree(str(appdata) + '\Generator CSV') + checkAppdata() + else: + versionFile = CD.open((str(appdata) + r'\Generator CSV\version'), 'r', 'utf-8') + if versionFile.read() != VAR.programVersionBuild: + versionFile.close() + SU.rmtree(str(appdata) + '\Generator CSV') + checkAppdata() + if 'config.cfg' not in [x for x in OS.listdir(str(appdata) + '\Generator CSV')]: try: - SU.copy('configs/config.cfg', str(appdata) + '\Generator CSV\config.cfg') + SU.copy('configs\config.cfg', str(appdata) + '\Generator CSV\config.cfg') except Exception as exceptInfo: MSG('E0001', True, exceptInfo) - if 'style.cfg' not in [x for x in OS.listdir(str(appdata) + '/Generator CSV')]: + if 'style.cfg' not in [x for x in OS.listdir(str(appdata) + '\Generator CSV')]: try: - SU.copy('configs/style.cfg', str(appdata) + '\Generator CSV\style.cfg') + SU.copy('configs\style.cfg', str(appdata) + '\Generator CSV\style.cfg') except Exception as exceptInfo: MSG('E0001', True, exceptInfo) - if 'format-presets'not in [x for x in OS.listdir(str(appdata) + '/Generator CSV')]: + if 'format-presets' not in [x for x in OS.listdir(str(appdata) + '\Generator CSV')]: try: - OS.mkdir(str(appdata) + '/Generator CSV/format-presets') + OS.mkdir(str(appdata) + r'\Generator CSV\format-presets') except Exception as exceptInfo: MSG('E0001', True, exceptInfo) @@ -221,7 +261,7 @@ class CFG: var['D'] = int(var['D']) except: return (False, 'Niepoprawne dane - klucz: %s' % record) - if int(var['s']) > 31 or int(var['s']) < 1: + if int(var['D']) > 31 or int(var['D']) < 1: return (False, 'Niepoprawne dane - klucz: %s' % record) day = str(var['D']) if len(day) == 1: @@ -235,7 +275,7 @@ class CFG: var['M'] = int(var['M']) except: return (False, 'Niepoprawne dane - klucz: %s' % record) - if int(var['s']) > 12 or int(var['s']) < 1: + if int(var['M']) > 12 or int(var['M']) < 1: return (False, 'Niepoprawne dane - klucz: %s' % record) month = str(var['M']) if len(month) == 1: @@ -307,6 +347,7 @@ class CFG: return (False, 'Niepoprawne dane - klucz: %s' % record) index = 0 for x in var: + x = x.strip('\r') if x != '*': try: x = int(x) @@ -366,6 +407,26 @@ class CFG: if var not in VAR.allowedCoding: return [False, 'Niepoprawne dane - klucz: %s' % record] return [True, var] + + def __checkB(self, write, record, var): + if write: + if var: + var = '1' + else: + var = '0' + else: + try: + var = int(var) + except: + return [False, 'Niepoprawne dane - klucz: %s' % record] + if var != 0 and var != 1: + return [False, 'Niepoprawne dane - klucz: %s' % record] + else: + if var == 0: + var = False + else: + var = True + return [True, var] @@ -417,6 +478,13 @@ class CFG: return checkingOutput[1] else: MSG('E0003', True, checkingOutput[1]) + elif var[1] == 'B': + # Boolean + checkingOutput = self.__checkB(False, record, var[0]) + if checkingOutput[0]: + return checkingOutput[1] + else: + MSG('E0003', True, checkingOutput[1]) else: MSG('E0003', True, 'Nie można rozpoznać typu klucza %s' % record) @@ -474,6 +542,14 @@ class CFG: else: MSG('E0006', False, checkingOutput[1]) return False + elif type == 'B': + # Boolean + checkingOutput = self.__checkB(True, name, var) + if checkingOutput[0]: + var = checkingOutput[1] + else: + MSG('E0006', False, checkingOutput[1]) + return False else: MSG('E0003', False, 'Nie można rozpoznać typu klucza %s' % name) return False @@ -889,12 +965,16 @@ class FMT: MSG('E0006', False, checkingOutput[1]) return False else: - MSG('E0003', False, 'Nie można rozpoznać typu klucza %s' % name) + MSG('E0006', False, 'Nie można rozpoznać typu klucza %s' % name) return False content[name] = [var, type] - with CD.open(str(appdata) + '/Generator CSV/format-presets/%s.fmt' % preset, 'w', 'utf-8') as file: - for x in content: - file.write('%s(%s) = %s\n' % (x, (content[x])[1], (content[x][0]))) + try: + with CD.open(str(appdata) + '/Generator CSV/format-presets/%s.fmt' % preset, 'w', 'utf-8') as file: + for x in content: + file.write('%s(%s) = %s\n' % (x, (content[x])[1], (content[x][0]))) + except Exception as exceptInfo: + MSG('E0017', False, exceptInfo) + return False return True @@ -936,6 +1016,8 @@ class dataProcess: return True def __checkIfCreatingOutputFilesIsPossible(self, files): + if files[0] == files[1]: + return False try: check = CD.open(files[0], 'w', CFG.R('mailOutputCoding')) check = CD.open(files[1], 'w', CFG.R('officeOutputCoding')) @@ -1103,8 +1185,12 @@ class dataProcess: mailData = data[0] officeData = data[1] with CD.open(mailPath, 'w', CFG.R('mailOutputCoding')) as mail: + if CFG.R('ifHeadlineInMail'): + mail.write(CFG.R('headlineInMail') + '\n') mail.write('\n'.join(mailData)) with CD.open(officePath, 'w', CFG.R('officeOutputCoding')) as office: + if CFG.R('ifHeadlineInOffice'): + office.write(CFG.R('headlineInOffice') + '\n') office.write('\n'.join(officeData)) @@ -2412,7 +2498,7 @@ class mainWindow: ############################### - # (4) Rozpoczęcir roku szkolnego + # (4) Rozpoczęcie roku szkolnego self.settingsOtherDRRSFrame = TKttk.Frame(self.settingsOtherFrame) self.settingsOtherDRRSFrame.config(style = 'layoutFrame.TFrame') @@ -2450,6 +2536,97 @@ class mainWindow: ######################################### + # (3) Separator ######################### + + self.settingsSeparator4 = TKttk.Separator(self.settingsLeftFrame) + self.settingsSeparator4.config(style = 'separator1.TSeparator') + self.settingsSeparator4.config(orient = TK.HORIZONTAL) + self.settingsSeparator4.pack(fill = TK.X, pady = GUI.R('settingsHorizontalSeparatorPadY')) + + ######################################### + + # (4) Nagłówki ########################## + + self.settingsHeadlinesFrame = TKttk.Frame(self.settingsLeftFrame) + self.settingsHeadlinesFrame.config(style = 'layoutFrame.TFrame') + self.settingsHeadlinesFrame.pack(fill = TK.X) + + # (4) Nagłówek poczty ######### + + self.settingsHeadlinesMailFrame = TKttk.Frame(self.settingsHeadlinesFrame) + self.settingsHeadlinesMailFrame.config(style = 'layoutFrame.TFrame') + self.settingsHeadlinesMailFrame.pack(fill = TK.X, expand = 1, pady = 6) + + # 'Nagłówek dla pliku wyjściowego poczty' + self.settingsHeadlinesMailLabel = TKttk.Label(self.settingsHeadlinesMailFrame) + self.settingsHeadlinesMailLabel.config(style = 'label1.TLabel') + self.settingsHeadlinesMailLabel.config(width = GUI.R('settingsHeadlineLabelWidth')) + self.settingsHeadlinesMailLabel.config(anchor = GUI.R('settingsHeadlineLabelAnchor')) + self.settingsHeadlinesMailLabel.config(text = 'Nagłówek dla pliku wyjściowego poczty') + self.settingsHeadlinesMailLabel.pack(side = TK.LEFT) + + # Nagłówek poczty - checkbutton + self.settingsHeadlinesMailCheckbuttonVar = TK.BooleanVar() + self.settingsHeadlinesMailCheckbutton = TK.Checkbutton(self.settingsHeadlinesMailFrame) + self.settingsHeadlinesMailCheckbutton.config(bg = GUI.R('checkbutton1Background')) + self.settingsHeadlinesMailCheckbutton.config(fg = GUI.R('checkbutton1TextColor')) + self.settingsHeadlinesMailCheckbutton.config(selectcolor = GUI.R('checkbutton1IndicatorBackground')) + self.settingsHeadlinesMailCheckbutton.config(activebackground = GUI.R('checkbutton1Background')) + self.settingsHeadlinesMailCheckbutton.config(activeforeground = GUI.R('checkbutton1TextColor')) + self.settingsHeadlinesMailCheckbutton.config(text = 'Umieść w pliku') + self.settingsHeadlinesMailCheckbutton.config(variable = self.settingsHeadlinesMailCheckbuttonVar) + self.settingsHeadlinesMailCheckbutton.pack(side = TK.RIGHT, padx = (6, 0)) + self.settingsHeadlinesMailCheckbuttonVar.set(CFG.R('ifHeadlineInMail')) + + # Nagłówek poczty - Entry + self.settingsHeadlinesMailEntryVar = TK.StringVar() + self.settingsHeadlinesMailEntry = TKttk.Entry(self.settingsHeadlinesMailFrame) + self.settingsHeadlinesMailEntry.config(style = 'entry1.TEntry') + self.settingsHeadlinesMailEntry.config(textvariable = self.settingsHeadlinesMailEntryVar) + self.settingsHeadlinesMailEntry.pack(side = TK.RIGHT, fill = TK.X, expand = 1, padx = (0, 6)) + self.settingsHeadlinesMailEntryVar.set(CFG.R('headlineInMail')) + + ############################### + + # (4) Nagłówek office ######### + + self.settingsHeadlinesOfficeFrame = TKttk.Frame(self.settingsHeadlinesFrame) + self.settingsHeadlinesOfficeFrame.config(style = 'layoutFrame.TFrame') + self.settingsHeadlinesOfficeFrame.pack(fill = TK.X, expand = 1, pady = 6) + + # 'Nagłówek dla pliku wyjściowego poczty' + self.settingsHeadlinesOfficeLabel = TKttk.Label(self.settingsHeadlinesOfficeFrame) + self.settingsHeadlinesOfficeLabel.config(style = 'label1.TLabel') + self.settingsHeadlinesOfficeLabel.config(width = GUI.R('settingsHeadlineLabelWidth')) + self.settingsHeadlinesOfficeLabel.config(anchor = GUI.R('settingsHeadlineLabelAnchor')) + self.settingsHeadlinesOfficeLabel.config(text = 'Nagłówek dla pliku wyjściowego office') + self.settingsHeadlinesOfficeLabel.pack(side = TK.LEFT) + + # Nagłówek poczty - checkbutton + self.settingsHeadlinesOfficeCheckbuttonVar = TK.BooleanVar() + self.settingsHeadlinesOfficeCheckbutton = TK.Checkbutton(self.settingsHeadlinesOfficeFrame) + self.settingsHeadlinesOfficeCheckbutton.config(bg = GUI.R('checkbutton1Background')) + self.settingsHeadlinesOfficeCheckbutton.config(fg = GUI.R('checkbutton1TextColor')) + self.settingsHeadlinesOfficeCheckbutton.config(selectcolor = GUI.R('checkbutton1IndicatorBackground')) + self.settingsHeadlinesOfficeCheckbutton.config(activebackground = GUI.R('checkbutton1Background')) + self.settingsHeadlinesOfficeCheckbutton.config(activeforeground = GUI.R('checkbutton1TextColor')) + self.settingsHeadlinesOfficeCheckbutton.config(text = 'Umieść w pliku') + self.settingsHeadlinesOfficeCheckbutton.config(variable = self.settingsHeadlinesOfficeCheckbuttonVar) + self.settingsHeadlinesOfficeCheckbutton.pack(side = TK.RIGHT, padx = (6, 0)) + self.settingsHeadlinesOfficeCheckbuttonVar.set(CFG.R('ifHeadlineInOffice')) + + # Nagłówek poczty - Entry + self.settingsHeadlinesOfficeEntryVar = TK.StringVar() + self.settingsHeadlinesOfficeEntry = TKttk.Entry(self.settingsHeadlinesOfficeFrame) + self.settingsHeadlinesOfficeEntry.config(style = 'entry1.TEntry') + self.settingsHeadlinesOfficeEntry.config(textvariable = self.settingsHeadlinesOfficeEntryVar) + self.settingsHeadlinesOfficeEntry.pack(side = TK.RIGHT, fill = TK.X, expand = 1, padx = (0, 6)) + self.settingsHeadlinesOfficeEntryVar.set(CFG.R('headlineInOffice')) + + ############################### + + ######################################### + ################################################### # (2) Separator ################################### @@ -2939,6 +3116,10 @@ class mainWindow: x[2] = '0' x[1] = str(x[1]) self.settingsSchoolDataText.insert(TK.END, (' | '.join(x) + '\n')) + self.settingsHeadlinesMailCheckbuttonVar.set(CFG.R('ifHeadlineInMail')) + self.settingsHeadlinesMailEntryVar.set(CFG.R('headlineInMail')) + self.settingsHeadlinesOfficeCheckbuttonVar.set(CFG.R('ifHeadlineInOffice')) + self.settingsHeadlinesOfficeEntryVar.set(CFG.R('headlineInOffice')) def settingsButtonSaveAction(self): if MSG('A0004', False): @@ -2957,6 +3138,10 @@ class mainWindow: 's' : None, } changes['schoolData'] = (self.settingsSchoolDataText.get("1.0", TK.END)).split('\n') + changes['ifHeadlineInMail'] = self.settingsHeadlinesMailCheckbuttonVar.get() + changes['headlineInMail'] = self.settingsHeadlinesMailEntryVar.get() + changes['ifHeadlineInOffice'] = self.settingsHeadlinesOfficeCheckbuttonVar.get() + changes['headlineInOffice'] = self.settingsHeadlinesOfficeEntryVar.get() CFG.W(changes) self.settingsReset() else: From 2af9fef0482140078ce4cadd237039f485d34f5f Mon Sep 17 00:00:00 2001 From: Mateusz Skoczek Date: Wed, 2 Sep 2020 22:14:59 +0200 Subject: [PATCH 26/29] 4.0 Beta (Build 20246) --- .../description/format-danych.png | Bin 0 -> 34606 bytes .../description/generator-csv.png | Bin 0 -> 31347 bytes .../description/o-programie.png | Bin 0 -> 72897 bytes .../description/ustawienia.png | Bin 0 -> 42334 bytes .../example-input-data.png | Bin .../generate-input-file-settings.png | Bin .../generate-output-file-settings.png | Bin .../input-name.png | Bin changelog.txt | 22 ---- configs/config.cfg | 4 +- configs/style.cfg | 2 +- dev-changelog.txt | 10 +- documentation/about_program.html | 81 ++++++++++++- documentation/content.css | 24 ++++ documentation/description.html | 8 +- documentation/index.html | 4 +- .../{how_to_use.html => instruction.html} | 8 +- documentation/program_documentation.html | 110 +++++++++++++++++- generator.pyw | 41 +++---- 19 files changed, 257 insertions(+), 57 deletions(-) create mode 100644 assets/documentation-page/description/format-danych.png create mode 100644 assets/documentation-page/description/generator-csv.png create mode 100644 assets/documentation-page/description/o-programie.png create mode 100644 assets/documentation-page/description/ustawienia.png rename assets/documentation-page/{how-to-use => instruction}/example-input-data.png (100%) rename assets/documentation-page/{how-to-use => instruction}/generate-input-file-settings.png (100%) rename assets/documentation-page/{how-to-use => instruction}/generate-output-file-settings.png (100%) rename assets/documentation-page/{how-to-use => instruction}/input-name.png (100%) delete mode 100644 changelog.txt rename documentation/{how_to_use.html => instruction.html} (96%) diff --git a/assets/documentation-page/description/format-danych.png b/assets/documentation-page/description/format-danych.png new file mode 100644 index 0000000000000000000000000000000000000000..d6b65f54350cfdbd4e24f4619679766f7f1e64d6 GIT binary patch literal 34606 zcmb5VcUV)|*EWpfC}RO+KtNh7<1mN;k=`w%j3A(NLMIB+MF^n<>>wpkks?*Zfb<$d zk4g!G5PAy`B|;#9Ut4@pSel9) zlszaUBqVa}pTDhzgnq3R68h!GZ~K8ek6c{d0hc|Y)~1()DhK43fj7VUT(r0-BvgYs zwBxxCcz@vTKTe@SLZY3!e|yqYMCF8po_1XO`=V`x+sdrS{i6#AH9qN=cAUiB$dRw+ z)+xu2h`)QBE(?8ARM4>BIdmC(6k}ojB_;4Wq%5tkVtRb;WZ?RQhlQF2Bqh+R?8zV9 z(^vf-9=T9It$Ee!@$9}%HvG1L@>;`(y+WLFO;1zUWU8lXFu#1!2wjvHrNl($`B1$F zvS#J?sRvB%;keW&L=0YJbknLR=3QaqJ&cg`kHOvMYAeYY0#1>*eh)C=KL6!DC$Swa zZZQdXCv@(|7h~>>%uB*v;LV5ofA;(WT;CjddcD_Y_hXOaa<$6&dw^H>A6!p5yL;=P z=hEvnMS&>=yu*sA@rkYV_nG1S*xs5*p^-`Bu{yDX)nLUqPoDr1@2b51$_L$@`+J$- z-67tr#qNYbM-g7*D~q^oc3gjwUvxB^KDm*qMDByn9kYrHM(q5+#bs708!+BUkT>!L zH(Vn4oRImOr@SVQKtJ!!aS&cdFve2g;Oi&G-T7Rdd8W>e(~JO%h5uIb5@f_E+}d8x zCO;*(D#WoTwmkXB*44iYy7Kj*4npwU+E&| zJ9QcFQs>_dZ&Zq(@*<8`Of?q;66dSGnc_be@7T)2zJ7FaD$bid0!evr!ntZFn&@f7V9N*1gP4lWf3UZ6bMe$V{j2eyI{n8$m1tVzAQyWW# zsP1)~iy|nOJronzd5S$Fhg$LL&Cq{wsl3WqTnVu<+_*8kzEJ=T?+WEm;d7~sRcCs# z2w^b^@gk21c29BjsW--&o5<8Ge2$sLo;0>q7EJz#sI-m!^h~L%(ls!jmuhmxHDAK2 zCu2JLe7j(e+3M1u*LDYLhMVjA=hmx8 ze0##f@|(i)@+MnV$k|`xMHOVpNVyCPwYClGFFA~n8Yma3`}O|bT9yf)fg<1caqnF; zh|<=M;TGsFUY72BRoZ=3DdxwcA-ez@`7}znZ%p;}XL*g&Mei0U;a!#AI><}+eEm5? z9ndWalf+lBG^1lCQS zXQYg&2hB7_Y%dp+SFxml28Hn6t*x(*KTpNT5sVf6$3edr=Z1QP&G}Zov%q%TW4AzS z?QKX%O1x&Li6B&IcF}Kz`8`x{j6Fvj98Os)V=ub$^W+qLa<*K2vxB=N7=8gjkPI2Qs8{xIoIC845emdT;| zGT}hWzjjX#?p3R15z)@d zCr6iCt=XR>J&gG7g0bKe;~9MK+XToh5#P!_N^dN`mFczgyZ~?zB`Eo6N(~f`)b-x_ zp*x>&iV2Q-UutJ9OH&pR3O6^OXjdW=$7V%MBx~By!84T+cP5@ia<0|T3f3|jEdAD) zD?h~2d;7rcmRla}7QTh4d5-S-iw77VH9;NZFZFTbug;7)1UcE!e~IumyQ~+BH9WD|3nRX$}2FvINov|ir3x6kQT57-RyRD+tx=h zz5#zBFYZ}Y+^YZ3w3rJXzo1Qx=HhJa5eBYi>VI##mZLOlG`l-EGrl-Fj3ZDytS}r4sK){ z)DP=(042J%C?;0TH5G(uIV*&2sN0=z>;0aW@+}wb1#1mbx=oCB!z;@-TDQz*51CIaIE!5Yma2Z#jhgOwbQH1j zWw_m^8?TyaJ*}NWOoJl=H15V6#970#?(PP=Xj6y1Eo`4mJDpG<+zIn}?#!;r3eq-t zYiF3|MTSo-p2p;E@;@_5Xnc69U{cogQ0i#cnsY@i_h~+-rt!_Nk18g|42yL&P(q!~8|8~80#p8Qe{iI_ z*ku2WwWLv-m5kWsjAp~CjaEJbBvAKHsMFca6!L_ff>-Ah;#h;Yt|A=2WwJip)or>- z(#wKVAvlIJTG$)%b+8~(d3QBeUQ6C$z4!M#AJFrBYgHre&w=)x+O4K5hrD(UkeON) z!xd6KYTF=20pYBa*X}c`c3?GLvBxOola7i75r4N3Nn!LNeY__3bj;s}D=0v_Q&Kcc zkx8k@8De4aZZdE#lU3?};^%Lvv{50y?Y`o^Jbnnc+`n|>*6x@$etYXS{Ld_x@{gBQszyg} z>&dlv9UrS6MbmtzSi_KSrgiv2rJNk2-6Qn^d^74fYx5dQyuZIDdVNb~d&%l8y=O1^ zJ{R_D-LEE!Nd;;q(!n^z zo9x9y;-<)i#fiL>Sz(Yu%Lw^_>Fqw4X>xJ|nIm&=wCBAFSZAh1TU3AkO++LODZ{Tz zHBT!RU@Absbnkx*L_I2Rj*&0_zFIdY*8(>LF{qUh-0FLG#$rCaSQw;!h1TLvaGZQJ>^Z7+U3n=K7)|J0Bdf{(o3l@w&eK@7u=*dE z5Bj$viVN(b%A?5~Z4_fW6imyGR}k+BU6sf-CuU?J-%%7 zoV6&d)LCuVIXzps&z?n2GKl-VUs&;|+Db&}g1V@_KC$X{%kVwF&sw*(YQ)tHSJNSV_U?i&J&<`@pcihs!%iTB`i7 z>{tW-P8DB2+>l3NEEuePZax-^7~?UXT>+uucqkEd3T2@8Q~4&;dn_$IzbOuy%u7Kn z3hyE=k8@?&dj8=||KLqk!n6^&P5t{v(BHGpYJW4UD_f4XbB2K=?#B!D`5y%}Of;5! ze^w2jeOa^*;)j0jKl_M;EYUEQ$quy7<=7n|5Zq`qth{h$5B7w?gzjY>T-LgoW{b+){m*;&c`oKt-nlI zHBdyE(z)11pUXCG*0(pNLKYT42~cL2ltFm+DO9XddreemKwr{%JEF-w?#sr&%T0~u zU?M!W{U~%X=4552(a8oQztvy0>jTh^`R9+%sHE^lC&ZphvP|X>*RzNIo>TIX}#$;iXJa7zmHSmUn=T)$A z6|)<;lY@WWd5>aNHtJV4{@APTd8_jj8bNPq(=^(S@SRl-*?f}GmEw@20PU57-#{eS z6Hck0{PT;kNu}dCb<*3y0}{<{4(i^+%DInCS1ppoitpaR59o$(61tB?`eFjJ+ih4g zpaj$KR|nySZc)gz+Tk!#VQ##moxUN#)cnaQl?k8NVtmjTFS0@B+b23T029c9n@zOU z6Q*);me9p7i3gOAv|eDjBC8{b4>8CACp zw{x|@=mE#OFqbfh--%63W6ef0=|+?#yIj=>JGR`E=Efx~R@4aH=Rv9-m18NnM}u(< z22?kM_EY&`?M~TyJwJ45ykhTyvGV0&d`owZq=1q|=wyR9H+OJqbCM=Ip9i+$RDu%o z1LD-BT(*XETkB`#)PhhI2JT2JmzbO3IvvypBseH6?C`No`_@Kpo7gZRj~m(NO;q(O zXVZB37%#GheT#Y?06((-T+BO97;?Ev74Bu3POKf4NrFP@T4p~c& zgjdu|x6)Ue@Bbq7$PBQv0FKb1&Zpcl{+^lsV9sXi)unRx?Vc&d?3zh>3WAlpW|U*Z z|A>kEofQ2wo8g}kbKaScxsspEZD(K{Be^DBL-BvhM|9ROY{)b+6B~Ql|3w-k3=?J( zL*`8K#;bLy6O`|OlkBfM>v0YIq*<2z*TnBHSJK`r4Mphs*TDRwT`5#(ukV7?-=u$L zg<3S#Elt4lFHEg3j7Z8UT6j9c!EM{73)}M1)4sX&z0-9}7u>r4!iTlDvf#GqrFVAY zOLgR&a-v7exT8_b?4J8PcfhJe=0L)t?zyL}{FuWf^*Q=qdm<`*&3DMN?5=4oQg3(r z_R3LQnvomh^A&a8UP^a=97k#D-od&7{twR7dT7U9>|_&KbGR!^IG zm7h{)8ZJ*LX}eceF%Esz@lIUb)7q+qylRo%_id&}e8@O|n#r7z$1cq3xA-lOxpD2C zd~#C7(n*Vu%|#fnz2I%4wN$zj2qvtJr$0jajhv({Z8I4Dxbtd7{W~{eI1RW&TJX-L z7@9jhSuPkN?hs3@ZCrsHOW*|2xZEdBq^31`fZlBXj~*x~7t^y{cGR6dc(RPnqP&fq z^b8pd-ig`u#JKR4mA-j^NDDH+$kF_`$e``D+gWeb1IMr3kT_+Pq)~MJOA%A*3reL5 zcXj}YC%50$>UQ*NRHOGd4`4uBhGHMX4d;R`Y;T^aXejN-yq7r|S@xbkF}NJzhAJnhQLhy90~{L5vG&ddFc^Qnq<&TY>>Gx9{a^;e<9A~v{o01>B&yD1G*TU{AE zNfYpRu8hIB899*q7LgI1;Vui><{X+2#c`iCep3rVn(VA7kx_z;s#+;0VEO(TKdAy3 z%8>=vW{K(RKUIKv7z!o?FF^`IllicyiBHkdAdk^|E54wFBvnI`>;dD|R|%##DTkQ| zQ{sq(;_i>nC`@g@9kVa6j5`#eqBMTjviG}t8bw2qg9ZisCP2ItX6uU4(oc@ z8FD+#EeTmsvt7h&@mRDuI4E@g(=sBdJUG^bI;U|aOH2*Hi3IX2aP4A$u7VGLL(j>P zRooZ!S+R;+NAN)jxZBya&sBg$*`&sOf!tDn-3^tuE4*UQtP^33GlM(7;dV4QLx5XD zeBj`KI|i!5HK%O2If~OY8q*Y;g%$->WeXj=gW~Kld|r60%T#LRT{%9OZoW6)LBtZs zKY>8JbyeE+ca>h(Mg8UG#&0$E66(bEBM9{^#?chRXkyp7;Iy>*;FL7Yl^*)cWZ6X9 z+oTo`Z<|ygS5JIDanZh&J9&q{)}4@%m!kXQeY%v>a^(|vMKf;W3H{yKa((}GtJ|mv zH`ekBqrsVf@=#NISYPed*BtjmRNP8y1Rv8J!9*)KC6#Z&!O2*+acktZsBa8DT7R@x=#his zA26+@sI>L_3$FW41*Qq#0|8KdYW1`8a*G z*qN=E93v*rNvC7fRXwGRwW%Re6V0yHUAb&SUUhIIh#eL>>=(&y*Gyo4QqC1V11$n} zyLa)PKAaR5HnN~La@W25x+~Gr0B7stHjZwUbMYl6S^JlB1r^x_%NWu9f|Rk`uwe5M z>OE{5+qymFAa*}h2&LCP&=GLD~+&$ER$C)Ft@E|s_t+hkGqqF$IIyV z3&h2E=bp;zYi7kO9P6*bHhl3LXpw8z+W1laWPH`#S)n*b_Y9v7vhR9lLsAf|ZN~K~ z26KiLLbk0GKygesyz2lI`HVXrV=~X8)Y21SRmO6cNm}}IEFuNpM}kQAb3*pq|5&L^ zZ(=MOjy9eRYMqy;x%+V{r2ADJ)8L4kf`dzhnRX95MJ3w!PDI~*vh=`O1I^_&@t=we zZP(DNRT9Fx|!_CNNU@I+NMm~Fl1RW*pj7`)+ z$8QV7gr2`U=YMS`f=iw{{#By3)L!^JP4x%Wj@_;37YFYyFI(WbC&^_XjD!3q6lw(n8vAbe=C{2m*pQu# z2p#WI&%MmqwP*@W37<|7-8->Yh+EwJy3Sh%x9P3MEU)L#hdW-SLfKD2-Onlc&t^^A zRI=RTRH>r+%@&zyiYh1UsNtEA08CuKftp?aRg8)_Y`{*Apk-s^sPNkp0}ARfojoxJ z0Kjyg_t8^WY^p=OHaw$fI8V7VF|_E`iSa=Px1LMo?`1X0)-x}aZa6N7JMuC`R`M=Y zI^+ZCegUNwNc5>s)2%9+obZ=+QQhX<j_vfqw`M{w9k4?zNmm} z#E_e`3&!aa;TR{&yRZ=;D! z5lNGOHIfJvF0O0P2AMPqAl(qYuov6;ve#0l z#L1@$vchhghZy6P4u$kn00Rk_nC}Tm3+9me?I_a4v5ab5|&IV)yCZ=MnNq zm?OX|p*he0U-IdH#|ixp=BnVo=_%=54#(rGUXV+h)W0;+qyHPS>wo^ET4PaO9-0>yR-@RF>z><)MVtw6q*3}T2hx+G2dtb$X zh1wR}fnxAq=fS`v*CHl9nLURszEm>*sX0oDRd8da9MP~60}qTX zf0G!DpAazC<~N=dFQ9X?W4eXspGdnU^hpcsUHG3#i8`bhjaVw;O3(`}u&R4Vkmgw@ zS-4@EH0H&)T0$c%jYA^*C*61lpQPXn8vxX%oi3a!~00NxAnx z1`2@09DdI($>DZH0{V?r=f$lh;EhBghhA|)$dB>!iD}u)hugYqiGA}cy~^~6<3X*A z_5{8B1Ql;o&!IBIZOoQg4v4bSs2Aio`${_fJF5sL4g0+=0EdHd!}6rNCQ1hi-lj4U z!Yg%BP90B0{54K1mz$liN2Bdu^t-egRVmD95S%qnR*J=vKf87r*obXV-YFFR@c{`Y`sL6m@}!Ko#Ghl7 zed186Y3HT_vF}hJ0lAyNBin+nxiF?;w#_BYXkO?FnbA{IH{Uw6@b)AP_B;&& zML$1d#HT<}_$vbPd_dF=F7|mEZ#0@DL5cZ6fqW`Adz1D{<#%>fbHnZYfg*R#)~AS7 z%tUQqwz*){+<)9izVOfdn@1rjNxrG4WX*&7Y9g-O;&^5cMqQZPr7q!c#|*G z|8$qvSbjAbE7&W33e|7E3nRr*0YF?uVPl`KFVMzd` z8aNK_xS13=_x;JGK(%M1?1Jz*_& ztso$3bBa=1ds)=zk06d9z3Y>^0OXW8)(#tw<5S3BZkTq3YfJm-jcJPFjHH~YUA#Ti zD=YvhVo{7AJr;TMdQwShmRHAgu|dOEGccyqIXER(dZNNn5qo>&w7P~GebU<3<%>IH zs4Ab z46Ra4o4E5ay%LI^qp2>*NIO)m4eD1ulBKv0kJU6sgNgKvhO$@@A}-I>7w4+Pg>mwH zRdNXnc59!tS?~PwUDsM$Ti6$F<1F1sZq9N~PB@(C^96m0B>OK`>cKvg#W%}i^)$H)j%@NCfXDbCc zJaXn(it=4q1mwC5C&F;%L_PJ+mmDW`fcnE6Xsxw(D$bRH){Z*@>w~Ia`*ZU3`0+qu z5uV9QssQjuvyJC%v%Pf=VxxJzmia&016oXW9uYb%;uuOH8$+g;19d=g%w~FJB-RX8 z#Hio@G;nCp@hGG}Q?gLI5@ld*UQxqAvB{?R_uc(9nsl1|xwhk;!-x*Su};57srco#*gA~|J9vMl{oZ$?!= zHb`aPu_WqRia#?d82L{T zL8M=-g4x>VJIR^nD|QqWOai@@J21mu)y1%I{&vvR2;140_*vTFQeB989Cb6D=GL~m zT-!T-f+^psGG-aim?%f7#gZsMv8#PpGKFZ`SyMQ?Q-c;LqC`Zp$=^&(U&Y#_Cl=+% zN+=@AXG+gZ)o3&a{;jpW;&{%WHt12{J9{Uh$q`UqDCByA@VuPSZ=JP2&KeXC$7Byy zGy){!EI*A1DZ&ZX0~u+!Hq33a+i_w2Yd%EuxD!Y(*m8&I3h(@qZ>*wX&W~8u3f%NH zLF*Aa}mvIQ%vSUk4$?5vuPstT%(5f@GSbVt-gkFxPy)Sxt&oK>h*CK0l9f}Cx zb%enCP&-AAFmpY5vxXT?|D>*;*LB*U*Sncd{$s9@gUr$G$s5}*h|R9VCuh*-#h<(m+~Uc4T@uT(oqD zv7_vi+mz&Jrr1^`D0F$Rn%I1Iy74>fq>r_3qf^T?=b=8fb8{Y;WuyQ#%x+yWdp&xS`Cw&ns%vd)!-y=B)kK8Pp4g*3_X3y^zT*qk{RAP zx**=NMZ_DQ#d|OrOF~SAG~3Yax9+qD+5|*s&aZ@;Q6L@&*%idO$=ZgEQ(A{AdOf?F zHIIakp@wS;X;5k!ff{X7`#`5{mCL7%&<_48yn%h7f68AaV$rf0y&a*>d+EyvR(S4NRG^Bc+wT?5IlnSCMQ zhDTJ&)+`+ZJD^kd>BJTuIRARG7(r1SKhO@~G(I(;m!3E|yjqh9IGk(5(5G}|kuPpW zH=@sQ0fmV!QB`ComTgo+i z+7?uZF9KCYHl~$@8JK~!WSxJh97ujPRM@<&PPC=RX24Y-jdy%kKX`PXcG7uQ*%ARm zkL%fv>SwIG(xp@($3j9_TgGjM0_|pQ&D_*BgitWvG-_-=QMJ&1P|~l>EscH4H+uz< z^{{a`azI9*P)^W5b=TLbj=S&6d| zXrGFT<}^scDv*qnw@b|X1S7en9rXd(tu)Vz8~Qy_O9d>jjcjIH)SFG)%^#-(uLIdQ z0U^Ir$%OSfg5E@_W7G`mI$iU5)K(mI239HHcR$dWj4s+Kg!M*Bm5_$#u$rzXJJBjPC0h-w!wVH(lBS1C5sF?S@7|-!CmU zy|Xu3jz?szt#TY_jltnaPE6l+Y0XnVlXwVVagCWi0!&=fQS~0^!JE@C>oEUg?#mBD ziJ0ru?>m02Zp%h`XWEpQ?By(&g*tmaRR8hawvxK?ZPsc(x58)Ic&(DQ#^;PhwM7?l z*-oSZbPL;!jR(^D{Vzvv2mAb;ec`W^RJ*A6iXfG#yQVHRD-U!xKixZ2643nKw)Mn( z@;(awv5dHnyBkK;;?QQf%GYBOmd{qfvjIX-WF_St(Zrub`vajv(>Cds~6D{ST4N*ze+| zx0q=czk;H7aPwW#?tUdY3gimxT%wgKzkh2~$wWN3o#VYT%H8zrz=p+RHpo%eU`dI? zV)UaW+z}B*Q#jaWc(z-rtG9Gd|40MnY6o=~HLF*gkpx5|f99Ss=W*<)du7W)d|+S0 zDz*=$Rboi;vSHPzL`91p1x`6kYfV3F$uIw2My>zw>Lmjs{2#N+6&ShkZf6_+H?oAL z{?MiC6O>^x6cY(f-Q-<Tim)22UCNe>fl zD^u!zn$BFR9H8<>V(=5#i8bsdejuIbhNDv2U~U!L#IdPwoYR4X7Mj03ZKqbGIWnKc za@pMEwnlfDCVv6K(wm6z)tMIrR`MEduXC_FA(7lY#y0LTvmSi4KQupNynbuF_o&kp zUixgJFpjtlZD>Af|C0W9CIo`ufds0S`4JE|NP4q$QMyxFRJ@3scJ;$@H#3t9#g*pY zuDYE&9=20p$&TVfif9JP<94%cAzzOfu04><93VD_-RB)EKXK?2GNpyf;E~k;wP@n? z=a{%<^XFs5ALnc)ZH-PVSNnmyM9!c=??ApfK~nq1vM26!zssCy&sOI4)Mx5MhR)b! znmi(V@zInpjr3!HSi*feESKi>aA0G#@(B#)L@UumkJ~Pks2-|?Wc1m*R1|zIJq>=; zDxl)yPDNjLJv7oR${k~^Zp`_Xum%F1*l!=qoQcm{V)(yq-e1%*>?&XL7UhbYbZ)qE zIY%PHwYnIZ!NRTZ#_mmY1F4FQgKOX(ArHZ;{fbrS7S>@m1Qimo_}ZtF6vwHm6QUO4 zwp18Bg$obn*y*I!6`gqv9JeHboT5X1HIf(;`p6sn++b2(%aF#nDIe%9 zi|q7=<-}UfY4hL&yrSUZ;gYhXS*_-yqTp5H9DQ;2W?Nr%!DYaQ{}(VxE85DlDP6NT za3OdrNgfc;mg;|@mYz;O?k`wJ0_A`8YD;5Irz4DQa*OQ6D$d{ezPvS67iaAL%Ny*@ z6;YuN7t%kKLuL=`18`*fuTIgLzu6tEvCd?5fcSp}ZH?d(9NZ#LyVtRecpKwek4#2& zb_Fe)4&9w^i#Ok2v5!{D4ZCz0WNhd@-4eS)F81PX8ktjt{$i?G@4|1X|3ar`IvxBC zOZmMXaf|=GGK zI{#k(C%^Dt8khgUXZ)8EXS6>@t0mI8LxVH_^*myGG)5E{^gd!an&g~8fYBR6SdR>P zns-uhX@p(gqgoBzvV4f+LfMb_SIVOv_FL`$qSpaZ48VRJE)p9#mA^ZNzo|FbV{OWqPWcK#1h z2WRRIK{cnJQ06y=* zKhO7_J7*jFp2y1w?o(8WT_7K-Gc}pH|FN5LawGY8(#K-?lNJLRC$qf9UZO+rHk^~- zP2C^Rk18|w3o0g~mb#H%n~sd{VmdykxR4P_3+EvOZeur1@|lGoW1FZAr}4|t zb*lSCE=oPJB9hQ#wZ=1*uUqvh+o+Y3FM}^mDGCGXjKj}%7rS1A1j@%PTQP?#0iilL z7JgJ68%Qk5QSVcmsHm7AvzIdq%K1|<+?IbltAyHAG@BZ!P(PzEe<1WMOs1PhwmLv~Po?}LrVD*SS4O5N z^QMaf14VvDJeOZR?q2g8d%GP?o?d~o=N*T_EM4j%APqr%x#mm~c^7ilJ*O=iY>SQ{*PFkKHR+fXY4?96aLZ@+H5yZ6Ts$?z6P}?;1=DcN);{rb$_& z0DgjT2p4v9XC5zg3pF+}gL0F>-Hnapi}COJYWGBE4aYQC__{V&j2Ziuj;2v9qef(t z9w?z(CtS~|x}UjLLLZJ)H#X0#O5|uPcEMEA@qtsLJW#T&dn9rA)tJrcvDquGT5R*~ z@K{CHe3Ku@NXF-_wTw9ho4M!+x54pAc1GM`?-8zwKsF8X*YzYDWRJALS&(A!&UIR> zPdXoILTwv?&^}Z3PZ0mC@K^V~6!YEZOaTP1uP?6zEAcG}y_DRaL|po;{UP==FS6== z9?2kTos({?!X%Wj(~T$A*Qdbwbr|D5h>7$FC79-OONna>D%QzQ5_&YVb!Z^vb4oTe za8BQUE+@ebTdytYR2OqQAV;%gc8xL|yplw7#zx<@8Pg_v{e+q0`eg`&6+TOtY3~fq zLmH$zXkEVsOEV_m74y~Bpa{PgY4kH&!{%ymeYBKaRr@wXTd=NDbqlBu+GJVX_H~^u zipr<@TG1VQKG>-2vvd#GWf?;A*4=g~hOytC`Jo3FOzOo|)?tUF5$faL{r%Q5!VroW z0w|(5fX9b2)~~IV9efK3{3l5DGSybJuJIn7}Z*$UK--s?%qV4XPP(?AyzBN zBBZ$Uih^$g??GeJw$Ur^#fLUJl*k-a>YXyoXid=nd!NboVI_C2|^F_#H-_7kgTmE4K zzm1-RVfc1K%;KJ~ij9^H>OFFz|DtABqP9^^mY08g!QJWo<{Hv!2{+4dwpzCarX8`% z8=3lrn$bJQ)X}~+eZk)cX#OfZWRUOSVejMHh+x#ia!sM=m;0k}8}sf-S8z4Qqg&r; zqVc4yd=Q~QZ|6q`q_?ctHhmmu>u4C`+EdM7N@>uWH(ZN8T(ilxjqJ-MC8rer*}N+`=*n@D ztzA2+R-~GA?S$nOR!lx?kqKs+0sTA78!CvoG-DLj zag}QA2-fn;B>TGden~0*Pa4u$!+X zYfkX`_@yvL$EIM5ceQYx!RUAbw(V<|!jQ}<=!UrR5=mTyIK1u| z5^-?!ZT-x$9_Pzm{nV#|_2V*46qS0~(v%q0$I?^v_kN*miP5wG)3 z6>JBtLjJ|`h~g{^XKCg`PToSFUdFrG^*vNS<;=cj((0x-6sd!gOvG?+vSKd#d7d+# zbDnK})2CnBx@ER}cJmW(rWLN`C!yCy)D&cutbE({bWgHd4Mc;dZwB2S>my(lKbsVH z+w#S~B}mL0vC^a<%SD!GRF87n8xt@{7CL9MeU5&1i~U;o(bf2xr{=Ylez_^3IU-zZ zK3L}nJyGXKg;So`i&QwP@9BV(8$U4X2?R~xm|;5u{EnIV1eW{j8~us(e_rFw5veJS zz1Yr6Q`eei=||nQD^DCJyGmsX+a{I&r@FhCx8QU+Ui4?hD%k5*Z#`QkdUNHX&F*#OR7$L+nxD*9G$(?&p(RN|BHW?o|^ZlhNQVa0h=y%}qy zhZ-0FEm-o}NN&&A4V>f({!S^&Y153) z$p(L?4L3TB5$(a)5^U|9k0J(bfXLJ0eNp7U_PAiSXAHZ&W96(8VG7M!qG$0bWoypQwT>L9-9&G6ubu6oo|HEu zRt6hTxtWwE7zNM7zauaMIJY|(nkNU+{NIX)-@y^2a?#`0$ZT3XgPS2Z=YZ{G%$yjmcp zDB6KQdTSj{PF%#udq+VSfqExkX6{~j-tV_Wt}EDl-2~b0b;dJ=_os|Td6;!R)dnj9 z{hcR!ra|?qr&BMzq;*#&5(0CbW3PK!A9PYpY1BJpIn6f?7n+xYvgcMk07+w=88P)gdnf*G|ALWTD}b)K-qQXJK#)=N zU2a?XY6@6TnXkv%xHGdI);Yc^o=WKH{%i(4g7WiQ`ol3x%Em@0)L~9;v4mA~qI50D z9WvqR!G!2Av&LB-OWYdG%JENEjPB^Jf0bq$NQmwnDSK;$IH>ZdEkoER&PXj9p_X=R z1rhh$`YW+APev|Xk|K5m^ETgPP+F~vEXbpm+G$B077{Y5y}i0RWvN}7_{`r{$D@j@ptsNurbayI*JN9bM5f!ZeNpDS z)&BeQ!Qqx4YMN)J>;^5C#7X8#qWj6~?YL{0u+kAbL?L~gSosdFnIh;9u z*8P~A61b;DD!mBJtPR2FXFxnL;g@r28BYW5b4Jm3?`5kdqJ0^8kD*s?wC`VSv7=XQ zk7Di~AKsNOylD&r|JpoEseH1F0)JSF|7>&FsM8!o__J}N3O`CS<6_z)c3K-NQ!7zM z#A4S-?ZH^I`E3}mNs@1`2moO{OBeRol-mYF8vbNs_{UIt^?+^cXaG z3_rs>8DvSTZPBw#>Qh5HzDTbtiBb9thO72xn2%J5Wgw%2XJUND9DTUYe<~#2-~wj> zWp-iR4dp`x+CfMeqd%Rs!oylFGE}142=$rzCCc`*n1tf1}T-1Y*z6&zCYPy3= zDJ<=7FoSwrvC#|wtA&?laLb+A&ud#k2Jc}%rng9hpP7YKW8~&vrRa3l3ir7lRKhO5 zb2j$Q5NDG{bIp{Bdak_$uE^NoLs9?k+x zPUL|LQz2w3=HI?3ng7uj)zjTGy-O|sHfXZT@c<0aIT==%Arxq~a#g?jlfrp&;{Us) z>VHEw{l6Q(0Itf*L>+HNlyuZ;Al+_U2;OkYGF zPe`KKpG6VRjLiFT5+A(%&C?R>51j(4r7Is}UKoAX2Np*C?wSE@RI}z6d`<1ik^5NtSBQOlcl@G<3Wo3Xu-?o;W)MS z4eOoNj9`?`T*N6A;u`umZ8#yjs#&Gs`<1}|;sl0cY|0_En)HY>{l)gy00sVUSo*s` zUlDmUsa?jumhXJ0ehju3FF8{QV@zs()3`lnnq6&GzeHa=wE8PxM_aEB3VQt#ip-U^2 z+NW|ar7Sq1#nk;6$hYx?F`P28@F`4zK%Dy=s!H3|ORJ9*nRY|Xxg;6Is@aaSmh|aH z|KYl2l7DjDW7+`sr|ZA{x@M(F?fdB!mG%xSP;ke7!l=+zVYC6jN4UjT>|I@5H-X;I z9C-&Gf30gFf2U%%Qt-mU*Ep?>@;$XS@C=bYkp}_E&F3_FFSp9%OWS2PZS`gn>uYBq z{+|~!@AtMJRLLp$r*$Mm;&pu;W9tc*(1wd++unn z0Sq}NSNEaVHZR_Bm)4OQBN1zPiABSqcScUvB6ztaFn*ii)$sN{azjaWb3}WEpX{3;?9VhS&*6T!vBFO+$5qs4?3po>-RRfYK0RJ3ey>9AVwL3Ca z=6^G5bE|DI>8J1Q{p@z2)>=jjkXmUM*k*x%035+x#gbVQq=a2@Pxf%k8G~<+;qXc0 zu@^-=!bk|-E{QyyB0y=#VYNT>LwmH}NxYSd<<#oSq=PhEPa-&|4H|NTo}eDgigH zwHondC1;&uuY9*OIC>?Wxf@17$QG4~y6L6Qq12t6=;$^!UuCQI)5chroU-7Snfd46doxyv`7*{i{zK)=?6>Ax zAnGxvUXCUf-xlH=Jp_SB!O(D`PS?2TY~QwQM7@!KuXoj&mK7fUrNLX(?MR`0TuEv4 z?7xURi@pf|7jb9PUj&XtS^A!11$Qs)%uLTIRAVRJ2CwaQh5nUgyvq*R`O250&H3jn z^%mNu?#X`PC9|!N%s7U0?ZUu2m<=Iw`1}v9vI#4%9Jvo9NDF9c#*Fj5`*PhA?NTED zdg*%xzFZb|$l2CE=It(SQjGZ0_y@oRHen2$#*M0<&IOTt6NVAL**cGLe74Cl*DCy{ zEpFI1Tb3lb?;>27TF!qX+=YtQ`eU=9XGq7+xe6K^w=G|^wMii&)PTue*s~h)JnbPs z>ry)<@OV%R&WB)6C=fymK|&Ei6+)ydSdeN9ph&R*LLk%t z1_+>1h86`v=ur@Y2>~ggm%HBJ%y?$+J$Lp#=iX1}>o}ynYyH>1{GMkm&#qYQ2=;AF zuGUNqG#0BsX0l#Byiixv>Ha1Q#0zjZHwZqs%JU&tr71mDi=%|K=9rzMb`kXDZXIxE zmZ`i4EhIKf=4n?qg8@@1GRa0^v)kv!vQdR_@CdBp#FUfA-lyhTFhk(lhlUK?!ZDqd z!--#-y!7NUnNaRRf4HQ6Y)qa2bEiXu=fxjE8q+fM#c=6@s5fL@BI52a$O!Q8S!Cu| zz29z@>S~0oYMYQYa3XIHk*qkp5;wqLBqiWd)wie*S7tT;jAILmTkR4#fb2}`hWB)J zoqBa#pe3Z8<686HIUKh6p+&oW=nY0_Ub-JV_2lPCOkDL)SGI?m zliRIc&2(>grCu+*2AqkXv3w)H5<4QeE6Sqh+7fk6v^-CNVEG4SIemU(hS46*4gSWk zisgQ!{S9Mj{TQb`GdqpIq)yiVd^fD7D)U^oNs-|vQ@^Z8Jg&$vMi4onF`l@7`Ng1F zsPRV4=BV3}M3V3pyM^V&){;w;Me?p2BIcck`gaD%^`SYN54dUbK|yrm~M>SqP>_~GJ(~mZaK^l2|pA-H0xrVHy&N!MYNPxw!*6oF0CHrT_{uw&3Cq@l; zmfP%P{0e?WS!*tXVh6Piaj}+8&I(lD&-c&1dv-_sNnB-8hrnjf3s&t80r{E_Eo^&z zIPW@gKSu{YDD@t4=(un@o1|ZzJaR-y9!{W-ZUg*mga3w3ADher9UF9FtR_ zWZE;YW^SHJg>B4zpf9lG7cg3Hh$Ydx?zGo3f>vVsf0fg2uD{XW3i4kYk5*!C`H`La z!rHzcw;)>B*x5sq12uexzd97Jsyh!QD1;>4l&AI%P6IVfh;`{Q*{xR>!MCu}PIA4d zqAXAztOc_jxn3S)AslH(QgD^dI1`^^-+<$hS!>}`3Q*Uh7jEnP4s{OCPaJVuE%-UHi^y3AU?B9>D&;jB6YlYFNK4i0@*i>fXsD1%_) zmK-2j(iCNA~R>t$D1U}eb`JR7=pGM=T0+M`+WVzK@mf&j+R`a;K1ZW zIFkcf;tBvRg*9qxW$OY7S`#H+kT%$Dt+gM=Z}knboJX?N)E}WpEc73&;O3N-?>G=Nj}8E#?-LQugw!l z0#Jf=FRJ2WrkP2wyH)8GPgz+5nJ3M6(E^4IG#{&Auk(?=&XJ;-s8Qc-YU!@-fkzJ@ z19Tf3X*}8BOC)-{nT2NO;}Mh14R12^_TYf@GcQLg=)+>xag5aYHKw5RFTEfE;(F|O zp7cWMgb6%TnL7dFZ3UBUDO;)>wmP=Vu4w|7=h^b|_;qGU5cepZ3pi_#-K|xUQHThP zY_dSFp-x}Row>jN>^tosjyr0kLrLzkiO$BC4@{P&f{SFyehgawYeo{KK3Z%3Rf)&4 zLhV?PpJ#)XQ)5=R9>TKNEdn{;u@VNlazkkFY&z(>909$fb*No#-Ztx3{Vuoehn&Qw zV!C{{XY#9KHohcQ-ztV%P_5*pwT-V|Vk3s}rmq7ZTF(C*t(LORKNyVl3OuCO8nRPu zqzy9GwlfHe-aX^k;2mm9)ya0P@RjA2Y8-&6T@d!A(?*F3WnXQ}ChBE;A<@C6BZR_1w1NV69Q7I@+$vUCD?C6bzx16KM1iU2K?R=@w)3TyLfV;85kWL&c*F7I4@) zN5e^g8#fF+EPPsIcmoDKfBd;_(^z9Mr)A5TWpn`J#!6lE{UT7gTn|hKO;$wUOqHkk zmnVaBJY&ATs<{gj#7+`e5F)m)rI6UAG=il$Dl+SbE|0SURxiI4ZT2<4;k}Dgw&U{o z`#HT@DGtED^G`i2R1XF?!(bZ-(%G-Y{YNVLcT`M%neEQ$5nst?CX6?^ z2We1+R%SWD;lT?tDyIPRfBhuLYQbp3C1A{-HSu7nx+ES*E(MH}W*uaar78*{9b1k~ z-d zFr&vS11$H*UB$}=4Mkv42uffnj z|C?j7F9)ibMAI~IQcM58VP0z3D<_C!e*G=kBQ+mEs@}lP*b~gUtXQFwTU&&o4ClcTHZrS!w=Bwza}$Uj&R?#9%GT?ec}O}j z*CukZ4M-*Y9V(;27Z+|UFM#$OkKZp5vLD4hA%BL*i=?8)_xsQvzlxD@0_Of{+7@;j z@~>lW@?Qo17zX#&PEj?u{Tcv2tox5RjGyuk<~n)Kg%5hpMF!--TgdS0#evD{AX=PP zFFgEOGx$6s6t2%)S@x&L0r$(9v%a3aRL}wqP+Bb^hxm9;Zj^>n6a9hc^oi#uQHt6# zfvDNA`*WheS#Q-&r7O)aS=iA)TRFCO>Q>p0DI);zu(0DK$i905I(4$qw<79WiJM=b z9LU1p9k2I&*oUup`AgYJjCS)i=*OjV)1&9nU=h%!(`9tO&Kc7N;S zt+1$Kc|dKYz4lp9?GvG;4Pc9w+6k5skfK1qpx$A>G{%k((UzQzAFi54|w_M_H0k0VH{s8 z=D5569mZ2;B8d>;r8gGp3jnpj4{yEL{ldk*n;U3ethI^BSRb-xU<_3M&QC`{+MUn@ z0rTnsjR1TSIatQ3G{4Wfc=XDBYV!X~1?%yB1-mu%A1he1<8qepcJgv_oBE2i__l*my1TLU5G?Ot(C4HEn01vF9FpI*RnPh@H2>XlH-kuYwv9l-tPK(G9k~?#6B>i zZxFVo1++O)ZM7Zp)9j!H=T_Ukr&kJA0K|+Mc&Q7kI2+Vs`h7yddxCd50=$P181C9#tRjcejb`nJ>L8 zWIGidTc|PCS9aDfAP{VN8~P4APJtf&F;u0(8mXZ6-+49rmA4YBzUa!8Mn`=k*7J!V zCyVzj`LATkyUB-&e1^}o(`;U_=VmBM4=iYf1*jqwukjEe;=DerceuTV1gRptY2*P) z*dc?2F7FtZ58F>eijRbEoUzSdW#;6-^biLr(jXA1dOd{TL`yvugKK((dGzsa#K zU)J4N(BdPrU!E*I1W(d;Wc0FVV6XFppS(v!f*vIeaZy|OAGUSZ|m;hs@LVtuEzcgi)EQL=uFHAfvUbzj)|NGl~*(?A2l>7RyqXJMXyeaVcdrp~yyvo{KSM~2N=ie=(zoK7mNWwdtxGRM)R_v$aAXYR zLMb^>81N)N69AEUyyTGZSy1MEag3rJ#joPdYLOl36mgKhP(|x6H>KMQ zP6Gp=P)kGix7vSdqJ<7>1Ym@`svYA#d%1F^mMoQo2nZ+_Se5fltASnfxycjIVu>Ju zka#@h0qR-s{B{~I2H5Lo$bkhFmgJ2?Y5BpRvBgIGzO=1m7B- zb!vd~RFriZ`P{$B^53bfKt_X#KBj&l`RLwgZ8s}X-?sbaos$%83GKOG+pk@l+&?~2 zK(V*4T!tnKiY09AlcpPA9;hbiECf@cy6B&zYTmF6qwZ+?pUt8SIaW{GC&%~;Pf!;g zmy6hKPD0ZpPPSXK2H?F2kFG-He#*)SsbqnmVOuTNFwmtvzf{s6ymUcTZtjJ4m}SLm zTqj9nWh!Q%QJHdZ%;b5)Ug+nmd}NGz^CG5t-sQ4az`%0YhKI}oNfIcuf3?<)HsZmN z)<^Hwl;+D_9(7+)2i}&xN~FT4HShd@>DrHR?!6>AK!k;KkORlIK<2=emGG>pzShOpA9C%S zdd5xc5%2~!L-5__Lfr_Ei{#TQFM3r@c+<)^#6Sj4juDU#l)yXfoy_K2<#_$>MzK$1erRb4c?rYEMtkcH7j6BdICU24_WsuBpC);Iq`Sd_y++_tCp&%b1#>C zoam)v=84%0bFUc=m~@m06D+suOK-VjsiEeCSOvk0f4|h)sVFJ9#H_XL$i3Xz`_oE) z8pY8*aGbF+qc8x;RC%`?O);5S%*wh7iG@3mzt5SlZ@Xg!JaT6?W9_PRp15`2i`#V$ z{;WY`Tg}e7E_N%+)M&;_;%RHyV6Ek3b&`nIVm!BssukBbOrGWhN7I)YVRG47T^hhe zw7+mB@j;XJd~%FqD7hHkIYU{tb9%ZGQL)v)pu0drRBK_xYYrMDjf|~Z1}QjgmX8qb zYZ+IQzkV`g&C7pUNR50Df|!pcaj6SO<@z{;Pn}0iXI8p-&CTIP9-Q7W%|`~n9F4ln zrqk>&(L&>dc#p1y{QP<)Xw9xD$qgCwiDEZbZbd6*+*BMhiFm$LO(wbdALpTt1Ita) zF7om`%UlNk67P0BL&sFFt@Y86hH>Z2hn0XV6NZR+Z>@y3ybN$Y1RC?AD`8&!`FxAh zy|kNV{tig1V#rfYV}rP_-LZRqOMp0GG&|b8>PYHbv&pmp2K>v(P7pK7?rHi^?YDtHyk3Qh zv9w#7%}BP(#)GbH$~Ah%pvLy;R6ZMfx(V&%da-zF`CzS~y*>0c?^%0T0ETWBSJR%j zqN-}@EXOI(R#ukj7I9b^S)Z?N+#7RzBH7%0Fdq89H(5h!Z$K^sL27>~0LPn>C##Yh zMiond9DPe@iQ|(5%i3Dz%LE&+sr=u}Vr$n^mOHbW*HHt0-X}L5IBU@-nBAQ z!|K1eqo(*r7#mjKsv11bpw*5ojSiY4DB3Hh+BH3EqZ4atK$zl5iFzjW^xA}k=FDQm zob6cZSLg1c+p~RL7<1&%ix@5G3v(GWHFJ~A5x_E6Y0I(CA6@Rv6w~{}pgu^Xh}wZ5 zhjzo-)QBKWDE19ZwMyA>T;|4&$M4eTyj^-`JxtTv&2oc}eSZIT0`UIpUciyQlF4|v zB?nP8x7E%A_+4K_A3y$ky;^pKVnW&wEeuP>YJyZ5_PCxdwJf{#@vm`A7lu2P4b%fQxtHJOLtToWUH5Y(8Yxoql69G} zA(=-Gn3B9M&yC)4=gWbX%`h>DpG%L}xDV5+`qoHC-iH#V-TU#^0`9m|TW8#Fq2cY_ ztoFnpk^N8?gJ)Xx?=%-eyP{~wM9K$|g?`co=nbx)#BZiC9&MC>|v8U%qLs zS)h=Uzy&IVyh$68H_eG`7K>{?e;wOaSQ1b@Q4(;5+>ue(w5sgi^W?BLpF)fG;M4iC zTpgBkED6ceC^k5`zN_egDadM8hlS2=mwwL`ynL0=bF2fKNXQD zsUWA%`lD+RXiHB%%O3j**&L+TyiG3O-|jg1o8Q44bLl)ZGa(?%-E%;%N(7$66_SLu zy@JAU%^=@Y{Xe9B;Z$jKvL}1~G=zLq?g8`I*sRE!WAOITel^gjaIKz*&B$kibH~{@ zDUBsBB&aM1$L@BaX?}$|Ed5TYk9`JRqdh?rI|85Tho|AIQA_o}$LHt0rjJ_-(WQ8{_9T1^c=W#53WdGF_8>=Fs&#tuBw zJ5N(F+<{m^(uXn>rP6m-;VtJR@t(^guY*yA`_Jb0KDK*rm2_h~8 zblojt;Q0=7^&Ll@j07)>jv%>|$R=mY(M6-I@aEbE=k8k`C(k!G$ow|>#hjQA=r9At(v4E@?44Uaawhou0bC0Z6zC`HoyoN-%xv{yD3j=sQIqhhwZj7Z&Sl4 z#Vm=!u`M)msVD+WQ_ieDwHfaoPnfU*i)LAytp`=M=lgoPh#nv6JK}TdAGN2;2W9AA z#;2pu#(|OI8VAm(xrVFcajC65 zZ&@zA`iF*knoGt{P(|pL9%TFxP3y!sYjak&j@^L0N_7ujqllC#1nOrPs|#}|>p{)0 z!kE6-O41Vi=q8d;xnS~+C{|Z-mb1I%kt1naWs&EL`gSFks@PuAkB4okfl%RHhlv{# zdhE}&8v;iN5x1Cd`)+pLa9 z6e)f}JxSF1Ay%$TR<*dWuKvx7HjcvB_ILx_8k)o^M)X#1z+n|5D#DH0m}Lyq#{=lh zY}$sL`TTG++$o`rhHELTTf_z zBQr4&(|g;6G&33i9eg%luSmQBvEKn{ttPj67^MF`r{%7x4w9^8-U>mef#mHzR+UVgfq`ZYOWtWiMSa6u zp|^D;r9mU!Q2^gK;uct?Aoanfuq*3^Ozj6I)^_m#{wr%w{?4~OfQF_CW5v%iOlJC>2BVY# z3dP(BoMq;Wi;G?2n8^X2XrXnXiQFSueO19_RqAPL)oRa|38n+poIGZ1sja^CgU3F) zR?71UumLB72w$(~O+M(@oXDt$2XMTZyC2$KJyM+sexT0K)B*XN%&{tiJS8T|<-@v= zJ3;%na}RkF3nY#=yGZWEZy~#vhYdKB3-7|Wgua2W9^TjK*PSh_v{Y?=c z7=ygJ3!@(F(uNNzG=e*KkG_wR!v-BPRY?*zmKlIGmHf<-?jY-5-k4?DOVZS8k)M*j zV>;&&vC}O(%DzEuut74_F9(8gz_17BdWx{-#lI1G3&C;i!&?#qd=Z{Jzl?q|_FVsR zb$e;QSMeEH=M4o=6BqoRE$yRsGgN4w4HELl3!I@ACvU!v>5(2mwH|TLO9IwUB|H}x z14*|75Nm+lkR*_@k}T;`Yso$fQ>4M)6V`xjDZ!g0lny8J0*7oaItlOoFVfEu8KAFj zkwJ~V3dpk?$adOU=1_`Q_{mdJZBOvDzcNx3)|f`kg~9$3tY-HDG6% z7t`^Msm8-uxmR`OIs9`CGzg5Jq&IuKpHY|1bXgze|Sy{@3R! zUw?BX`%O#y4{#^97}@&wg86SDkSpwPM_)UD{Bc;~--ZbTV~s2 z1lY(bSkC!lDhH0wn`Kc1RBP2nW8xJv%(C;RcLTa)bt4?fpPB?j;ZcD!pb`aLNn9m}B1!Dz9CSu{yVeO6 zZffj}2ZQ{83-6dPl~Y-tzm%ZnKplV#U$0hO(?UjBt_#UY${BW(NIAu&w0i+Urf&*{ zfHI}*X*CF7Cv5;OQ@*|c*De}PP?~2(_pUGPbF^`$ixC2`$4En6oTyTPAI5&d%KZr7 zo#}*9fts?CQKr}P@pdjMjN#1Z0W6o{8iUTsEX-vxqi_4?Q#9~H*x3(`wYa#LFn>Nj zMmgx^sVB3#xEup}uv9=`wu>`^VMT-228Xu$aSS}b?bmW8CZxyWCoKakrbpfa4nf}Y zLYk_`tQL+I@40XNIz%uA&}e{;5R`hSm-#lf*}Ir*xG{h|{RPv&trRRt&bSHnaXcM}hAh692ja{7dHipKjGZ-x%_H#QL{l%j$I_iYX8P zpOB)-U5bh+P&3FSWO{YOU(W~pv)ztDssA3;{{KGkwZb)*yy(V2h(!a93Q*$P{`Y^l z6FKK28sJvDCSxdqGmpK+8wMtJ&+Xgv12^*at(kFPsOQE-w|+pf1mrJOY{z3PlVv!b|{O} z;eb;WlBKj1a*fNjE8+@~-!Qb7?aL7I{V;t9Bc`qXT*TLiluc;^c%BKznqVBvaruf<|;&Tlfl5{TO=%bi!^p& zD_Wa&@5vaLcUGBp-|(oWy6{(nywT>{R~kT_K3WYCy1jmpBD&RpnfuOWZ|h@E8m9B(RV)>B-k2!Io|Fn?W&FP zj_i6AtN(rZohg7UzyCw2o|x0&6wL*R)ur$ja+=Fm#QW`;x|(~W*!g${>QSrh$=C!0;?d?!;sCezA zBC9S#Vq;MRTkxZgUU0OvTQkVDYvYtYXhte(`Yt33uCOmM*oHm`1&j z9=MEq=~?jnkZ@6~;6N2DPugOpLRNL`Qbg87hgCMCv$I$5+3?@bcye=Qw-_QhQ1)_?TT7ZZ#JgCSuj!>HR6fz2e^1QgqvKLg)H2 z*OYh**k)INL-CxI>wkfbf4Jz=Q{u-h`FIZgr%)1zEZl3FLM#7h2g*3n@wUt3bN$vd zI%mcNNr2`HP@X#Wa)7bw#(;AgFAS(Yp>**S4j6CAS{5FS)r?nY*+2tb?&~)U?{O~Z z?ro>;K)roXo#%1MmsE7wyRi`t-6nPMJ5syF-RK_1>0J}NcbDbF^CBn^fSKkcC-Nav zIj)oHkg-gL*Nac>V2}2D?Xwi)uMLN~WSs%OM(Xz$MHo^9_o?<-IGMM)(QAO2h4*4} z5&cO>k=~LdM63;>3pyW1#MM66?Q}Vv8tw87wS)&K>!HnR?--*2>fQS^iYJTUY~v51 z3>a{siqp$yY|Ex(zr|d6orXZL@|h@A0$EL%alUaHp~229akvoZt3&YQZ1$WP1|wWe zzh-*`H97GYLu5lHCWG7@L*F_%JuA!({L8GC$DSM0Sz?fT;8t+{>ocln%U=CH&uzE( z%>**$HAAPz%IQ}k@NgiuDa#KT_KMSnSOR_X9_Puk4>Dt|VG9mbdV<~BP@)30%GPS6 zP8@$H91M2DtL45}Hv9-K>6Zlg`>w1C6i_@mPsIlyzW5qwY6`0n#*VztSSs`L9U1Wj zSWuc9vtPY16O-L7F*y`3^l%bmIcjCmCzYeU7Oe)x0$E?Gy$G=29Bc4Zi7^$9(1Ye# zx9?=T#?Y6Nfy`=8cFkQ+17QigL5d0pa3G_@;@-ZvI;^6UMvR2PEU@}$RuU*Bl~toN zvlOVMWtoGNzv7ENoh_X=4wPfnR(H(b85q^mf40S0Gsjw7!(TjguwG?k!k0>F|3({u z1T@Gqup2cqd<+W@Rr^qa1Q5JDsdT9<447=rgr6f4O7=5LtE z451}3tV);r$g%c;uZN%&E*g>*X!lL9GJ6jatmq@i`5EnIIw;$zSI4&kXI(30UoSMa zFw5U&b9pbh_2Vlap@$@rL6W*0)EACoO|)xk8ga@FgEyqKk=lEIv`=Nl0f<}v7S(V0 zDY}i$qNP!jkdS53!7bn$+5=aa-n9c0w_b{NK`c;)jzM5!B3yIXm{`Ozib(l2LERmr zyd2`?-w~dkH9bA&3yazdV|Bc2G)SzO9Jwny&nwF4% zCKe`qS__FacOmqKrw>h|2ME+lqdsx`nPdlFtvWw)U;+y3wCV_e?XJ$QnUwArPJpY}P_kaHwP&Ze9hRsP<{dK3n+dqpYKAi#$tdibH`zSx*qn=JLUTuGahY*n)IQ8?oa_F z$}<%`c4X7fCO+koA-5o5_JjypUR__({m;1dz;cR-$;h^>Q(iku9sv?EnIJKv*z(>8}3aXTX_5& z6@$~Aawf;*{iOqQ9`EY5Bs07(bML(bKJ>k=YUgGz@CJH;lJ2Cy`(w!YOuHM#b|;Mm zPDp;y@6_3E>;EV%$0S$@y^t!;SAD_%ICKLeEzeJn%1WN&QM^1;rp0u` znRlK0B_d?BN`EGlERC<;i8&{H{RV&z-e-X8G@kRH?pfg%s|h@)))aiC$X?$d@6(5{ z0?OlfIUJ6>$=Z5Zut~;YEHcM#0EI~d*Er=(-SX_@^&A0S*udOi9nWy~XhrFW%TjGY zn{(L|!r_sCNhObD-4Da>;*V8`I@a>$UB!UwO_h|ij{Bb$)hO+GkSS^!6?T-}mKpr| zJXYGuXc^v=w5g>J+;deH?07!@;_|6ZPTm?GKfDwj!4nQ_Ox~ZIeFE9G>RUfL^Hm9EOZFzyU(=7o}-0z=EE_G1|)ZV?VHo9KW z7%}H!soA?FWeoHi*iZ95F1Ezww2$79YYn;U2`+ZD_1AA*3~xJKLDYf8xO8l*Eouuk ze_FnT_I%4sd&2#uU#}x>j)ZL;088`=5DvkTfZi*n;-g(6nJl#Pl(cZyM(@8uEWJZ`oJ9kC5JJ}Wga*4 zv~QH_(N3ipKl!A5@5*id%A|?J+c(}}g@;8Txc%?v&EI`E@K-#%lz(l9vKm|!KbEVG zo2&*xUSzKl>RX4V60NY>w-K6UCUwel8^|^vMGm(JamV&MPfXt1yBg=#k&BT5M%-9e zS&98RRQv4}{@Fc3vn$=o4`XlNI2gTxmda=g(bu`ywlBP-KP22j*19arQTv8OgW<={ zh_H=1RwsL;#hFI9=j-*i>9k6Eb~Wzpy43ol!Z%=ctv@YMSnw z-YX{>9)8+A_RQyI$`RlDsj73%f>+DBdN9Rh;(D0NB@!FU)ne)*RNfq zPoFjXuf4xWKkGcc`*iuyk-zL7)~JM(mj~Xounnx*#^YQ~aNV(kZ`6@&`-i&pPm@7b zH74j6j}E>JNIm<*zodWFJ$7o}hXHxY{cWcIN>j4QhyL>)5dF^3OeX~L}+2pHP{xCTr-?(0ANA;Aga$G*^ z5#e4C6^>uvyx+?jwav_I&@eV^Hfh$NTwrV*?ZBRJR8Q0%b~!R*zL6ENL`hW^-GX$) znT3o>QIsIEA*oKVCOuQ*So3NDWj;3QFT~d8z$w}pYLcO+rbk5oH=Ne;D2-Mg)7+&= z3Vur1j~u*z#$1r1#ByKc=UCz$iF{{7EOfhV%NKQ1G=cRN3=;wpRDp3~5Flj`^g?@L zsT);#_9t*wT8{F>!Tl)sg<OekVxDt1^UK=bvSjcAn1YEqkZT(Da&VQcx1+ahMtN*$v zBYu5+yyr*YrHaS+={7^0Xv5Zfz0{gT>O)EcGn^qNLwJH>0_7W!)191CU1h*B1tYx28iS z`MbTuH!inp>~c}*(CW0!KiXlFg9YnsGiH^E=-l#>ddX=c*UJo@@iHKyXww=tUBA<2Rrk#a?DN2$deq$ z&O|S$ar6AZLZVH={4m$xbePPPC3+Tz>Y~A=oCFg^Jez6OihsjuiuNqO%2bI}XyhHo zBz7X%4hYl2&GivlWfmekrg3BVDQ^}+EE?j(I7Ver=#9r@fx#N6y?s|^i!hC=3Mn+C z-?C&bQk7zlCxMqnXDDvw-5kdkTGFE({}1|aDrpjZ6}Ut(vzid^jV{rX<1dz-hAH4#AYL$ zj9d3IH&~@AUf9YX$DA5OZNN%nI^35)D;wJ03{o~i*ow%(Ax5SSpoe*a8xa;H*Tn`M zDvfV2$6_uch+?z(>&1_ihwYK?&h2tKB6+g2a>4%Dn5evj<`0^|TGc@}SG5AZ{T)2j z9O9M^+B}60q?H;hm6z1h@LOG)v*kI>zPxs*iemzuWiZFLEQv4v6sRnUZIqolY(CfE zaYX(1n}s)(*%g8{()eV1Oo99A%oyx0b3w37Jmu@*OY9;dOioSqp*>yE#}8D?69 zIVqea;8Sb`gwdn=q3(RnTuyhHDZ9cz#Q(?|2-A3a@!`*1X_~mBM$yYXgDYCW0SO76 z3&HruIV3_!Usmdum$qT}^z#Gmu3IqL^SM6o_F7KKW^QC?o-VsE7U_1GPZhPdjN*-p z7}4?kpcyS_$9!uJOd(BH-zQNMM$g=uoQ<>zgto?{8w%!z4XK&_v;k)k&BAN3u<2-6 zT*oxj>g{SU!LCXxAJkoBI;En^S~~?wQFg*Jzvhz5&s1&kb)j1mX=|_ z83(UBPF;u?eDi`res%78d{RQZ-7*d|a6Se5|J8cd9C{vrE=|GjxWwO)pA= zt2z=&t@CGJQLwZ}(l-6Xg}0S1*8^+H8YRXR2at)xu9(Wc1H>$6in}~USBV-QPl1^Q zC-Tja4KFiMaTS~0mV!Asud+mAP+8=#98p7aX1#GMb~1|+ZqsI(ZviK$606VUNEAtH zCbqY2)Xn9p+Y?(Rt=q`{7MlWB&TIUL@h--zqw!|U*u}ZE{gaxiR@yj=Zqsp+mUw&J zg*QAl2|H|8_Mp1${!eSz`qI+U1u7m^5Y1iGs*7Ci*CtjgeDsQuGq`gLM}AV%PWo^G zDIC6A{t81w=FpIbofFkXS2-*GRivyXMY{c^f)N`{kM)~A6GWw2LvX-Y@Y}|N@tidM zf%ie?dY%q}R28#9)7IIOa7cU9j$sYf=uT$qfhGlP7%h4T@u^-WX?URZ^H z+N}gtOX4(?(LhT4tStF;=^wgRk@oSTSxz zA#L18ywPl8r(uZ3A5Bs=a>x8PlJt^r5Ofs>*k;O_*!%vfXUZV7iCAGP-BJWKTP}JD zmyM-zW8?J5ii@dPZpvAx8BFm;$m-qRk7f3cV^v;4RpK%5r$CI6SVoc{-c6p`5E#S$ znQqcIIjw;A$!m_COJh~w#bG4tAyaf*3A&uj8kd3{C7P}WTjlAfaM=vmDNGn~kT+&) z^>hf!s29w@Wsk%75bDRiAKgAasP(V1_{r!^1KD1)Svb7T3WiDv`??_q-g>$o>N9dq zNX)S-PU@Fq1&>)(SuJvpR0!M`o3_<)hlx3qceT${;Ii4coG_45IN{UHswVqv=Od$u z^993A^iF?`8qLCd(n_zTvYVO$;yp^IK*b%H$2;K=<^8JUAN`ebpxfRfr>LS(N-%XT z3ZF6+iaYHnyyHR{L4Q$JP=qD)!7`*cjK3StUq78hr8rEJc?E#ehv&8GM4THiIEOho zXaK6^#&*W6IxAs8c5#!p_k>kW7PieJox`c*SytptruIqr?;K(5_IL!f^;A9PrALfw z6>FI5vNDZPuc2C|y{o8vi@u9`y4==Ca*_l)y^X+2vsm-tRTR6i7^oTJ%vo`mPJTT2 z#6;5j2X^A~b6uu_MOzFHH%#Y}VFGgS$wA@y@ZnvBQuWQuX*t;D)~5Fuh#yhy%DF$2oPe+qricX^DI+e(TQJ-T&_Z`JXW4cS;7(TA!R*W~NZLA!(s;>MO&TT0qj@}4_vd^3*~$Ek0WY+xh74_?dt ze&w67Gw?kZ|G&Y{f6=$(?>&_jA3dY@DHmXIM&VaDt}!;->507WWU9upaL z4DL+V4bk??1tGT&O3^K2wb2Rlj>ls!p)JY<3S?qpozc>P9O#9jM96 zynt44RZO(tOsd0b8c#_z+8rg;56_cRlc zkEcL<;ezl3;wnbxCq^zMZc7Zv){pt<93sM`rrv%01q3bxio%`#W=l15-RX19oQpiF8@fOjwy zW6qy6Uy9k=Fx+3==tpV~TTS1zR#2Rx##SYO#S4}h;GwBrU6HzKtAE8h?`)JewOXOB z=AdfL{U|=~=PuE@zgIe*-wsQ$)-+WVMN%KSy#9hW*|?K%d)}YcE^0kl%AZVbIA6ui zbJ0a|vXh+%@2DdCr0;F}?CiIA#!T&;ixDvq7U{-qD4!4p>97Ov)!$}9@@LCwJTnBX zy?Z{yXtul%)!jam+2s_Rd6+tR~&lN zU7OdjkfZN5zHzO;u>Ea9=vW+NwN+)+_4$Pu{ad}V-h-Vfr`JQ%B<0rzu`T9%k(=20 z3gu3H*%}HPH}w}T1~I<%Z6%yhOz4Qo>17>oz$2#baj%Ded3#%{m{|+a4|SD?WG84U zsU_yLPjnN}`EAd`SzmW+dB-BM!P%4#GZD;;xwr-_0@^u0kLb=_BzMn!h-sR#j}zI> zC`ZWD4jTP%uh?*PA#^CQr{c26n>RS9SxTwd+}~UJ$~M7gIe2;?Yoy1$k1CI%&k=|CM;2xkZG*F3j5Zs708#QwX%^~ zlsKeZ%=TqqisFrnDA`_j-Uo(Ll9M|bwkE$yCB(#4Ck3t4uC&r= z{_>t-;htQ(%lJc00OPV7ay)SN{cC!s`4cna&2b8~gxg`QtIOhsU-s_&)-Zc2%8lk` z8>)tA&wn1i*stZ{JQ@~@*P~J)DEtw(q#LaJ|K_e)E(6d4R$xEgjTz7-8-_qo==n!hlX<(y;48|wpO?+ zUf+U-J(vYgDk7SA9b>Qu4k4M_^Z`VKPk-*Z5sUSu2DTb@WmG|4-YEyBw0{Oddw&Rt zpt(V4L;mLw4agtp9*5~tCe0fzco|jwuZ0qSe%V;QF~UAxwKxz{%p001u9!lj*9UTn zr?lEHRTqYZ4qPff?23Ht>_!5~t0wW?6Yg{_qNAn;4I=I!Vpj@RwOXjV(O-}lZ@0w` z9XrS^1a0;s0{6^hppKl{)P4z#@yd*$)TT-kvYy5yO%Oyeb^9NvNyJK3AY@?S6Lkiw z6+G~&SCYDn$+mrUf*)ANc-_4^VT~0V$(kG=E^!NnBxoFSeQd>|CC@^#_{PWdUJZs( zDj#H87&HV%D8x)ZD_*$_a| z%iS3JrV&mVDM4;F`dNk~_)JU2NR@KtBJ5R&MHiYQMLZf)B>ZQYz8$$p-`>zhqKBd7 zdl&>OQ9?pOKwcmlM5{Hgf~_uM9cLhyF-Hxys|{9KFws(<|7cQD=YDGJcf)Qm>PB-L{^GxQVqO$O_%Y@yYGYJdJvDACov!eDdM4CAyNl8Rk zg-H|ncO&?ST`+0<^p>j?e%UTOVX-5|bLuEV53l6aGP|}M_+)G^p1B-#Hf1oyK9r}%6ffr5<;lj|ezDtwHWhrl&ibG%_|1*CB$;LM8Y=|p3pZnzpNDlZJ-5wW zcrpIM8*1D00`#{cSk}v|)P=Z9_^o%JCR5m|%egQ+el@~uro1%OKaDq% zxbybt`5@;FOUMaz({GSt>Rz$1>Qqy$#kK2N$C*ZrW`v0kr%2V;5m+rpWB7aIZral7;D)6? z6fk~~F!%%n_%Y)aDhs};@ZT68d;}_I&+`_|hEcY2DX5l`7tpq*6?Xld(mia!X z?ze!eop%7eL7kNHa(!evx7IrIFt2u^r9yjey_4T%cIpi7iAG}Pxv0d3GU@15Ve6CA z;fxL*c%al&YqoyFB+b~(2g!E(`zL4#gEa1(n zT+rutwD9XK>qCaWHFCxTTK=n8)-AV&M{D1Z?v5>%%`y(u_1X6}wH{H&BNro#Bm1#VylA%#LAiGty|< z?*v2T*9DIqL|}^I1Vdf4-n`7lCvAt-KT{p(;SLtV2K;;qBO~n!M*m5#F3maS%~NG| zvhdqOJCg|euo=C}T&nC_v0{GF(>YZ~%s45=`^z^C_|fP!rNH~^hGdHUWN219Vp2iy z#B11ZD?9ylg69UL%08^imJ_#9nUpxvQEoHY7>7B-kD7~U{8XpHGmKgkI*?G#!wLZ* z%SLLd`qS$DlQkWGxtx&bkc~3zd1}YIIx-@EvKf1LX#Fj)&jEE-%6Q{Wx?H%KjALT# zhGaci9>sgP%&s*OUfA1;i|_8(+InT%?GK6w?^tuR?r0CO(>qngw%ZHlK>!KRqhRZ! zRChB92ErQxc)tg6MW0`N)NXYg`6k@yPGe96rB!+5YU*t|oWgrQb>S$;%ktcluDKe& z!U4hBX|0orK@6jbl7yKm8;^W^@`1h!n;V9%jNUf470dgi8uEO@{O_0p8?JEKbE^Nk zxB0eK(gi<$H2lV5)}P&oUv=TH?U3Y+(f5b_)V+CQfY+nYN+@mG-DHm?$OEIB#M9U8ExNhYo|AZv zGCiSM%bjivezQsCtyHkm_M8_X4AgyZ8Z!qj%g5BE-) zt@oE#(ehMIZ3g~sORmWbGwm?+-Z~wATW^052;%(f-gQ=t`T+#!hdU#aTl2-)b594R zitomK@l@yHW})cc;1oEH$SL~K;O`{Y{@1xOd3ez3(l?byt$HByDUv2PK3a4rCu&yZ z(PDe9sS2S{cciNJq2v!bIkHJ=8q*>FBuIS5{>^)4mn`h!6zaw`L>9pDRUbYRPCjk^3XUy$q%%#~Fs1NeKJ-bPF zV>xcpz<+v#q}BL+AjH;iJC-0-9i0BX;iKhf)|)qp7B%{_t=&plWf*wYN){CO$W80c znv0hAm5^W;zP-+9;#pIZz-t}Ct;^{0j7_^{tu}I^n%MypJxYA#lE5c3YZK-ZTp**_ zIgBL2nbh|T*2eJg=4;cD7IgTR@-JH7`lXAZnwJb{d0#-*c zd+9yA7^gPTx*Pbj9M}RxT;k>6@I%s;AmW$sFLNJgHNok_{)VCvR>gH?)j}uvpKO<8 zW^B(2`iZunbuA>ZMQ>Hu$7~Kqk!kFgPrA*YNTntrK?iELSIORUN-|rL5>HF~v+{sl zr(*xK(0ZtbxEs9=#}rQ)Awd_}7gSUwtBt$r;-5I~DbvFP*}IT9Ezfvt#T-L^j;tTO z5qtnGrZCaj1cvTgSV!|#>T;1s>I5L7eDhq3Hx_)Qq30`V!e`~m2;rBLJ5vt%cl8WI z@Ok>PY&z_)6o36Ps|oHf2aa19Mp2&~eLWb0X3rdvSUJ&iPEvEG1Q$6NzVa^@UUXIl z`mSY&yMecJdiB&Xh|N)Hx>VvRCFtibjz2h9?#~~F9+Np`Gk%O3Umst=Guh>GeNO{U z7Gkk!vfc=$HNHVYvyT2+#%4&P!d&IE5^0QZvOak^DMZ%Pyl-=~w|ThLl1#`}>NXEq zY&WXei0H>P0MDN4yEqdPe?n=10yW&Yu=EHwLF6~SS%lW``|)xxQ4Tc&F?YF{Gs!sf z71Yp#-cmN&Lz1{xeEp*1z}9Z*d`dhf?Z}hGjY4D&R~t-!>R&vi>gG+q&P*!>ctBo) znd9kx3)Fyi4JGb$e#IEK$*^-0tsWf-GL3gX|9dkG!uSe{D=T)sJJKqlJu1UX;R_k`o-N9F!Y)p@IP z7}Dsgfgi+z&F5b*F&7={wKcWfJ5J7)obv8QpE1j##b4Pl2q%y1qsFyfEpJ+{sZpN{ ze6wYrIlh9-7>cx7WNS-%ch4$cwGRATs%T_M)4ytMO(rqLg3D)YAoF4Mi^E#Jw@I7i zD;TMLnJcKUtW{+t$E3V35Vzt|`o|mV@r$~T9hh%FRmP0IUHfp@58H7fFTUXD40JiY zuxul(q?UVSnLjBt957^m%pk*U1UZHV+LnmboGLr6`VsJ*dE?t7US`%PB{VG=Qo{39 zPAiqB$j$(gN?xWawzRI2o_c7Cno{a{#ypGCeP!UIpoI4r?A^@?9a`sWu~rpDH?b!T7HzZYF59Ny#f1AwpAHzIKeaV%;K@<6N&~flKIP zc%>-}PEHr0AZvtcHEvU!(WY!i_dTA0ON$ra(D&4FiXBmtX#y3EUK00V(DPqt zi2&mR{{EXn zjP1Gm{-Yh?iRnT5x0LSx)krv&&m8RRq-WZqjE=h<1BxC1`PZ<=;-Ve!w>;gtelIvM z`1Vc;`F|=#iG2+=^6^0h32Uiw5RogU_Jm&OWC4aF7JL3}>_^07PkiR^I+Z;^ZPO8c zUXlyn7&51ub5Q#;h?4p zykd(OB#whj^#zz&Df}JZ4&G{e_^Nr?SFED+f4HrwS9({L5ocWO^U_d>IcGASLBaAb zEdPZ66$~Vp%D1Xk{gc<2Gczor-RCtx6|NXJS zLTeYpTTP>QaQEOANOZ@IzvQCa1D)bPWUVsDo>MYT9<288v3_dW$`qzM`HXw6@=C7U z_or{dGd@*ip0%pp{M%y(mCirX#3J=QK}DZ-{cv#gs~gDOPTk5d_YP6NcD>NQND$Y- zooqG`G}?hb-)q~VNC_JZ`zwnI+4naQa%U7^36x0>*Rx3jcA(T4N}PUe$7~EONP9=Y z&y0m)$D!tC`#`F=w$EXJgh7=NB@oFCxWfTW8oJBZko#_Ym)+}~E;`2FynS^~dE3}> zTXgrJB1_f3=n%}*-;pFF%`yo!B3jri+{r8CKtum`xuVXj9J5GBD+4@$<035 zpAkL+;~`jE(yH~x`@EUr1&kas)#Mxy|4*{-_6^tE-rcH@0OV_TkPA=yd%8iVVcbak z`Qth#3=+vb3%72}07H$mYU?;ZzrDei%*;w&qU;eCM7z*JS5FGsn3+Ep|yg zS;vOmf~;C=5WC^OtNSiGo)cKM+Q%n~Pz8rzqL_$=m40noBDYAez0MMtXX{Bi!w;%H zw*M%KSV8D`Messkudz5_pM$ z%{0_NF&%1krGRcMSW>@^60MN%Pl>Q_n`RHeW6(!W7^lBs0_q(@Q1fX{8p89i;TZMWXqgy#JzJ-jj;xz_IzjKtzFYFl8 zx+uddjzQpd9D;dUt=~SO&BU;9FyQ91Cg2IFbXwddMG!?N=cycc?D~NI`Y9fj`(!_h zisL)|hYcSTvQTk(_LZGYuN#l7>E*7RuOIf&4eDnm!@Cp@7{s5C5$LLlzsIJi`TbZC4_$lOP)Hr6Dq%9fFSYWLFvZV z_dlU`@5G~PfG-rUmV{*9|CXHkPfq>LyNq->FP!PTUYJ)(8e{FUq*a^06LTaLvh;hG zuH>A6X|vPJoxg%J0loSuzyuFRC}-wIZofVHOj+fXn1eTAx*qP2;~oEX$FV5*03yqA z(uSf8JNcHQ-SeoOqK0uGHxVWSm~F>%=0&n%#B%umT1-VzVZGkc8N@LZh!bm!t%)*% zuX`M0N6|Ys`fSrm;uF+NLF>gWchw~BCYBG)t%*7=ZQ%h0Z1rTP2r0h&KTzRmSBXpLA&{iK;S$3Nf(kDK6TYKgi(2G(& z+6Ao(*S%>bbHc}3YGyvPmC)_?{7IeFZ@agTIVpNkIl9T2-{ZmG3(_zBa%7sA-XGFZ(;!-KEv5

CLg* zV_hjVH#W+1$!W4QORdmj3GW}0-cmiSZ$fJJaBUIux4|^S#kM|PCt<$zPepg?s&2B) zzk`PSJ3fpV`uUU`z;PLb8u0&q)@{oRNvKZ*vBz`*mV#dS%*A0vM(`_O8_2 zb6)%1&~Jace)sGYRf3gin3LfxBSQL7nNzo%Fu5l)c|=?RRMQkAE*}T?o6XL%6Izqp*p4#u-8HEq>GLG2{s| zdSaE~R;t&TGq2fabRqi^s)7-k8E3hk8SQ}2(!iAo)Ub5#rx`JOIZ4D=C z`kB!D2eMwH>z2d`=+D=5WB;m=bAg_D?9|{*=ywUH6{n=tE-m+047w^|VorCsaK>yN zIVV3qVMkjUs-hA2+I|g0ne?Ymu4cHj&kk*42$!2=b9Kels6GJaS=i>`j;Qjo(!<*y zs|P;0F@=N%mJWIBtxadewHGTayvp=|Sx-Onv~+8AKd;P~t_ZhWLY*_T0?+()0n}cb zakFy`dk#D{hYhJR#js6!PrtZcMTGlh4IIlU94T&OO{-3+XDEQ8?soRhu@WdrLR>zW z(2wd|9hylCXFkjb9~uph6N5d27b|wp#%kO!2J^Y#13lS#D2(iqnk) zk-gejzv*qC&L>^rP)|SE8}hOq3gOTT(KA1}{PHTpq1_;~ALa7Nz%VqoNp&MIkm zJ*M%{`&J0J+Szg29aKPVS=Ctn>mOrTrbbGlFEQZ7lC%c00PR4q z0G~;Q{;j()yw`i<$r!p?n_-pFUO(3M*NU8M1FAeDdG)NMqi;sdB+Yy2Rlv~Z0{=1k z?r3>JDS5TKVFJ9JRA6+@jDGsXdiLt=>dNyfduttQGkY#q-H*C)0GVA|*%q^>swh27 zC;86U&sRJ|1ZVO@RI6t|%6s`lgU~CGl9F@~cloF@{bQvA~p4IJ-;(@Eu;RMqJ zxc?u>UpwSWbqEg%dFJpvv^LGiHA4|fupux*~ zn*LBsXTM)_K2(u#_pfyMnWMJT1`(u&Y@iWan+`1D5SF(6Fj2bshw`WFgT=IW)S;_Dr?H&=J@mGAB&d$ti(zLxb)}a&rwke0oAf#4=_+ztZ*>O8i+~{l_Yc-QIakOVYS;a!eKf z5&E%Ba@I1&tUp-4Kd9<$oypw!3T|p@;P2#x`CBR=&!)fPqm8Lyfwo}yPiLcZrKK)zE2dY6_NfBh++r@Vp9`4?9Q8{n9w}-A6BKL=GXzRIIj!CV$ z-r3R_ert+R=xS&DZJ@Px(236%n|KDNAl;dj~+F+^-c-3JxLa+lTcv+IN4;3jx=Pxyzv*@gq?Uu z!W=)J6^9+SFy0#BpDyZ227eLZPBBBMM4;}sG+}mRLRc)?BF6#<`SnVS50~k2Buhw_ z%4hCc#!?cw^H89fJ9mNseSK)h1LNLIaRC`Al1agu>*%heV<_=P3=nt06DbTugS$D6KPPM~44qc3K9s;0(v!|G zP}zgq$tWQhsAN^gecOILQC;2JBm+$r*ou)G(A@aHTnb&51^rU#nfHH^?A*-en^Q)H zyMIqSNvd&ECh{YFDI+hsD7Qt2!~pJ2ogya8i+q-H;)yBL1~Pim1{4wy;dMbX1in6u?v1NK z)tI90Y64@G$PDKBqBk3{Y?7OcSNx1}q70N?r|T0mU4JWS8Xnd_9N;xf*pa)IUC3}Q z3W#;k$=3EkTlxX4j-n#=nmv%x2gw%4=7TJN;(`dsDishoD7n_)|3nvmsV{G9VrRw*T{x-z5feq4Lxz*FC`z3+1fEwuZwrYjmA zM^vk_{8uKA8oZtEe^3)_=GZ5(ADF;dKhoBi!za2 zd-tPH3}&L?QD^|J*k)Gdy=lVy%=zxy8$mu+qnpHdz|!uWd!V-P!dHt~+BGD43i!0( z#A*`6edRGys_U|rVdyom=a{CR4zb5o#lPD%!BYlzc*{IP8FU^(ESh{rHbktr_yoUk3zL{=_1d5dy1bV<6lLI45{H`broVGS$4* z4Qo3?iK7=*ONsBf@JH-lEBANT2L%NcS5&B&?Gue{ZNhjt9T=n+BHqQAO+)A)b}0YK z^^2q=6?LoI_L$I_dh*joP~^h1aWvSxL%=a&t+q4$+$L=k%L=2HLg{5%!(tc+$OJi~ zX9vnB0S~+0U%5Kd?#~nO$bLL?#j%n`MR=G=JuXtM@FnME@6kXpWxYq@sPt6!ms>Dy z%_NM7I@kLEjFETR}?Wi5!cy&^I3q8UHQRqmjEvnfewNR{? zwaO7&R*myrys(j z>L&NJoC6y-E=;kyTq5e%UmX)8lR9!5*6GJm77e5zTL4mV>oBQFgufM7wBC9#xyV;C ziT4q&(~-q#3!{wZ93p}b(fZPn<+FK>E?$9mp2#aIw=M{`XtQ}B0Zk3qrd8(}ssMMO zQfG0~D+namXkY<6)OSi7IIP5|DnJa@sBL-5oymw>L5Ok;R$IW|DeUnE@j=|xoj(Rc2#ok}`)4JN8`zC#yfVyH<*`B88# zC1HJFYbeW+;1-50giDo1sFEElcITMjjD>ed2tpfszY7SMsf2m+OB>@ProYc=qKPOi zR|s_?BzlQ3YWFNI+MiChn0pi>TF1IvgI}uF`AIbdUjH7}#w)7suVY6AR_plvTQCLe z#UiZ%&s*g<^9z$3qEA1wNa6xT<{A2w{!3MdZ#ldbjOJt_BVH^qhMfcZduLhVuAc$; zD(ZM+li!Z&3tYhcY}m^a+NJKXNhBNO{XokniJt`%55B47RvN?4U*}aY$H7^tQXL|v zdfXC2Bk%4-z5g(R3oDIpShjm*JHxE7EphiZ8mZS zf|})m$k4gAQ3QbHO3`9F``a6Za2>~lApn7io4oZ-G6`vz9u&eiH+^MN_>B&NX+T|X z@2Rvg-F~};r3!-i69u4NQg3DO^>}OWeuckeU!F-+!?8XWFE6HuXQnV=V>tJ~cA>xcCSH<2D3~s&6Z&Xb&2wPlZ|f4s9X;oh_F$ zg_lL)HNMLr!-ern@34F8&&@rgXNd0bKRlSKe{El(iFLG)gl;T6$Cj>Fl6x$M4dLq; zF${Mk4O-C4$f~vc4_p775=|t051H;NJ(E?s^*8HY{a9))T1Rh@FgZRa?0v&IjfhLt zPe*O&W$(^~)I6+-3`G_auJNdze2)`5k4Al&?ad4%*-r}BP$E4_5^rT;V!gZ?#)?tO z1k8Z~7>jnzu%XH`i~A0f!%0h)-7jFm9NsKQiR(zpA;j`lV(PVCj*6qJ#79rmbGZG3 z%8khPx0PdI{1FgU9zxD0zvtc(ly9N*J#%W_IK2J`ZqI?Oy+jb$FqWumO}VC@o1FOB z$%5vdU4w)_sOudUw^c`m_^e#di-Y(6f-2Z<-5S<~4f{P~Q*p-nGSl1m{GF3V_c+V! zV=gtw2|Ja7%2#_!yQW8yE`N9u45~?F1O9S;ic=l-;R#{6pb^oiB;NcIl`A>NRpAn) zuN}vb_X0)Rbgv$nFy>g5ze3dLjfrfI9W{a|@W!mWWr=M^;)vwi#=7GFrlY_6?t3!j zo*)ok^L*@iV=mN9OphfZyMBeV_~~PVM?$(*AF6 z{d-CKzXh!~{~ma(4eHrIaXS^5&gW~GsP)nWR0O}U9$A|Ng94!j5E#Ure8;f=p)~%# z7ySN{uJZXFAXMQJ68l3axP!9fx#R=6k;Ga`nKXIS`YGfN3Gq@ki2#hQfCEGB_jLlp^j#=W2p_X86WVT#MIgLo{ zIIB^>N3S_zbaCvjguB%-YPURot>ahM6k&*u(&YiMw4Aeh&I7Il$5hk2m@MN1_}A45BTHAv4n+1JK;>0ZaVY%(G3k0;A*5 zn}*W*-7^t6om~DkR}f^Nl8}H8?%%LHx!S6qU1sJ%U2uvLO!qyxipL3h1^G&;VYNLP zDmiM|F)>acb*}*73y<4lMYUm!yvqysezQ<1udJx}$cb5~4MfEN$R5qayJ zoPMZP|F$OVFicq1K*PWo!?`+(VAk`9Zr>@CH9sI>iLCjw0xwKGDy;JFved*YFvvN3 zFI9ElyZp~i=Eq)Pyo*xR#zv`~}d>5`Bb zRXu%EengWuP|fTI$`6RTnj}x%)scK!c~-&&Q=_KH8M&~|h%g0t6v~Dk94z8SqZSt$ zmhT=1kj%eF1Ty9N5B5O4{NJ@O{$jy|7TXRHprLL>iD%Tr(r02C1fC{PGn6N`Uhvy4 zClTk}Ll%fWm#ASQoulE2q8P;W2k_<)}7L zPm!ym^7GdfjVh3;+42`9$zD@HBsgK8cSCpnVF_&z#SbZnolSKSi3EhpEDS8h+*r`x z;L>hVw%Ich0{S^X)&cY60bogszC%%-ZO^~A>u2es<~=3m#`?3EyZh;rdaN+;|^>zbn9g+dOjC)2^t zu}R}d@Qi4?3I-Bx*b2`wyfC4O{Q1U3bU5vH-C>S9kKiz2|2(-NDFeygy2J`|gAPA@m08*;EIO+5G-484zMEILLC`4?>&z2pY z!QYozT>sLAXgSY-1`J{HFIO-9eNN7lU)@4qZLe2fIa5QlBq!1SxjKQib&tHL7u{y= zyH9*`W-t`~s{^UFjgJ9Io5XLVIC(+Uh1{^vJ}#%rOE^X)0BN^l=894Ffy;p|h^U>k z={3%Uo~0SytF3RzgC9Avn!-@y^?qW$zl(!C2f<}cw(w(WdzyjhW~%AI<7;}RbW!6B z*YQLyZ3wE@vxG=Ie}1aAco9P4KHhAHCEf=g7yteDas!n2`y5_ zxVM!ttJplovcRU2bg=a%q;RF{};M2PMt@n=L_i**wzDHVx4rIK~W zD`4Zuk%4gL-awFI`|)FWQ%6?V7;w*Mi*L~7gg{|^M+sPEBd=|BJ{P0I3~*xe7G|N7 z#9=7z&8nML$%b5mK&5H>`5Ev(fc#y8#IhRZyZfDU3&oH^Vd7`0${C)Vi^uazhOuctULTHW@o0$&dCNc#80z}>o?z%s#!HzR3! zZu#o9i!~}%mH+-e?&SXZ3vcd*9IJ9d6-|sUsyt!pssLxH=GN9)gNW3ai1q%raxtcI z=-c7bNz}CMp~;qo*_5dfG()N9(%-?=lU?E9q=5&7kKvb1^Hu&&lLZ5}9hxp`)irB5 zs#PV_I*MhIjQ#DkZW#2rak1S4rx6bTzgRH-99!$TZXZnPIGwM~MrSn_xixP7p0wN% z+^@sQ(*+H3&utnj?8k0AIEuX;rN*$D*IVtKk9u=KPs7jahP>3wjRV!a$K0B_yGB)p zikU;(d%<(%H@2-%Gg_OXdEQHem+3wuY%Xu-bOg`yzXD{nF*&1*+!H^VYz4Kld~_0aY8z6*->q zhDlrF(i6h3St3`?qkkG>p{|{Y`qtL%r0$>+tGfDcyui!08CkYmXTAAhWm6?hJRk7s zYZvbHYQMg@o3h{T82F;;SS9PMW5qsYI5 z|L>9wF3;f(o^Zc{rlN{w(_?z}u;0bgxYk6z=(t7{tu@7N&cx8(zO+d<6!|)m$l1MI z=K+RK6U|oGr|f5|ZUykWmmjoyCY#2gZl@oFsCq=u30pq$5OC9-Rk%wkwf4pP%*G?% z>?Lom?D{j%qtanJ>8d#xdz5huQWE<4UU?tzD+R9bK@diRi<&&*>|k+lq`GFmZKL#f zlh%iGkDZ;mo$@VzcU!vkv-k%Y?p&FuI<7iUWbwlYKv~iyMKm`TDsV2Zc#A)Vry8qP z0c`#H=?6I;rl+RP%{>rD0Z)GF-Ue`Bzm7Ntq;EcolIqM^P0yVO%^h>RejD0*`zBjj zdc>-BXyBilNgY9NHkM;?)Zrq+xnz5RChj;_znVA&B-xIg| zO`C9RiiJY528pu1MB;!91a;0MAyQbw`fFul9!)Ss*Etw|(SRNUDp};YbjIz|fiu+s z=}%;IAN0+p5*omfP~XR|dzWiFUdzNNH!)1IF_mQ}OPe|>f7IPET8Dp6!1|Hj%Gs#| zA6x%tD4gyj)KlGSrL?svNedHlII*j(ne-!t(UYHY|3`b@9oJ;Gu8XtB(QN?>O{$29 zQjJJ&Hb6lJ1rrEW2t^1*q)1C}REktlLshT=0-=N!YCxq4p&LqQA_7tZ0zx1Nob?6E z9OulLJ$KLEzjNq=yFPd~Y|M5+;Q zTtN~NFj+X|_x>bm=-pkt5U6=IJu?4RVUFBB6pM;z;`AOMpzJ=5Q^Wx+pXH^o-llfcHG6a)dJ+LH?Q?D z`zA6#n@t>|UG0Ch;5$DVx6znmti<2<4eyLPIO`m{TDnv?tMAxaHS*y&pUe9~BCf8r zwlMt4E^86WhS!QHb?yp}?&O(4w(xK&6)Sln)4Dc-9~4L_i-V}hTJ^OC%uLlR2eM+v zR@SiJGB4=cl;{h#UJG?QVb*mI6%JXbPYfHH-{La&f+4cK#muowIDE5@(dCOwv$jIl zmfhCLaqvU2R09Rv5BU7JfQ_2~tq*4Z2JP67NJ;eS!d>>W@Dq4;2S_6115gep`wD%B z*ruIUP}AMAXVd<=^yus9`3Dd_S1gyt0+eqWg1@u4cZ048`r)Gg7sL1Gu6FR6O2Lj0 z7f^Q`R@EHMw0`;6WJrfz9!m_VHrNVY6O0Lnj=Wqhg#|gsbI9066Ho58NS3_On}0@ z)ix`HU>~MzsI|f|Jl9^#wyjLcIaWVG>eBfv1#HKsSVb{Fpc4+;k|U#s!;u5IWv#N> z>%+On`QkVnkl^a-Q^x1|ZIcohHPw?7qP2m_b9Y`QaB8iIr|YaaJn?^Ns8dVY6*)o+ zm5L}*ec(HK7sd!#a`kvRH0-%{h^u;WvMg9@bQINO$}O&Cc8*gWiIAIcIBoVZQgrB+<4=fJTiL4Q5fsh7$}+PO+v?o$f_!aM@60ikB+r6L2neVo>hx-(~50?P!!pD!J#yPoCs zNJy@#8zv_%BLS)#HN|v?z>a>NE@_5K7xzTl>TTG03*p_?;$jcJk zBkr7z4=P}bR%2(Yepe(E?hUW#YEcnJ1CmI-vlfdUln(Hp*@w|} zNiE+#iV)mn!QJok!e}`}3xIDDuF_t|Gu(|QTE;I1d}SNbZW?Zq`TAh@b5_Ex@AM_# zs!?Vj!OLbqudd5J&u#lZKvKRX6!fL&3Pdqg(UpT4CNE(XcfS16li@;2`20%)&pL?n zCEwtEp~H;)tQnXJ76m_M_w@VOO#cbO`S!;D)TsZbJ^7|H;0YdlnYocpHMQiiB@285 zQ_Gaz7~M_DXl8oIK9?WlQs9BobmE8neY@FEj>Cu(R6DpCW%vPe$mJeJEGw__%0Gh zEXkb76qS$GVPqu;BsDS6sD`(x`x4mU6pMi;zGW5ew93@BBFSk*TgLoMY7CLbjW|K9 znmD;UIT$~?EIIH*<&;SYV}lS*abW5-yvP0m_PyB%@o$HNO-E*ynh7_!71| zb}Z&pubYd6UU&(qHG7;<9J4(Xi&@4k%d74hFpVeny15be~j7dY(+G656}BANRqw+faF@6~87R z;4`$cBY5+g6%PI+CqX7Pr@tBXFFasgD6|I`0 zz8BF)htgU3jJVqFP6>!BF&T3;31TnvzHE zSWh$Fs#=DN*MaK2fKv^Z2 z8P2Rn_#1Sla|sSywc$==TWw>(3RO)6?$m)~ShjS~1^#g00&hjlxdy$)^&6gADY|k= z*1fkbIB>pOab@BsYs{-ff?aLkNKQKSA-7Qvw|F#M%}{5o1gCeQQPYG7lY8Ak8{Ojq zhIDM_-*XpQTXdv@TX_++xlMQ(*?(=1%~;mXBhJQML?xY`7M<4nPOSGM(l=S;yi|I4+}8A@AB_;K@t!zI zTNBD#TBfR!y2U+Jb$JtfdAj?|x)ioOp_XU4>IGu>X6d9oo?YMReNuK1?x{!KV;GO| z{-3{VK$lLOd~Fux-_hpz@;${B<&kD<+>jx5j@dq&ehrRtc|qEvo)NyW$AtHf)|+esih-J^$1oy{S1HyhIxNXU*Yx$GB$Hyj4HFJs+1FSVuP7#Y-DQCt ztmvGz8!NOe&8oB$DqE^FLOEzKnh2o}Z1!t%=a<9CQ}bm*+k7xIP}fQa?O z4p`9ah4K$I-YnV6<6NRRg%O%g=Z5A5d*x$ug81}(j=AWR@>yAenAOK;HAaWs;93O_+;sJX~Miqq-jty;9ADOwV^a+lK|fP}qD47cv7hMOo*Kfw86Xc}o5c z)^vdLF_v6Aug}UY_r8_~_?fz*TyI#ncq%P<-^1?`Q{CZn#gOxco`<}v-O)!Kq-xz}D2Azg0#rx9A4EZB##li@r=5n$)fFLj4=YRd{XQwv}@ zyRJX*wGoIfFtvufbq9A_KOfwS6F1*U*t_+2gjmQp%U8G4W?r3#%j7R~RAO8GU)C7K zEI`8B`X8WJIi_suRc>rdW+*QEfyYXnk*k4?Djt*b@sZcT1GU@j$r+65Kw!}aYZ<;| zY?fCui~&!(Y(4w>gu)MAOk>!x!&FRmGo30wN==wmW%fucZ+4`yrcrZKhrTe(Kif;= zmAJoZtlEu(5Szrmc0gxD%9LWiKG?wO8I1cb`1RaZm<9srSop92^OhBcTDogie{QM% z|InU%AD87mLQGVp(sDnJJtElB)f>@fNF6yFvVov96;D51 zRh2QPU(qIqu^yY$UsFY+s%(ZyV}wFl+Y63?P!U&i@|jMveo;BO=B)8IfM{2A|8~sX z6gHVw3p!o~nAJ*4%ZZUJ5&3_ZCy$5NXOKPsvz_%Wt>4fCv#kO#Vuu=MsXFs^)xoO0 zL&spkThp1M@PC-3{tY{&LSTF3&U2L@!*M+$^UNBxogtps)-U9+ohWRv)-`56q1 z`;02hZcKIH96d9(^{q^FMNg3%VgXsx94{z?vdmcTcUu2szhi=_c@$%Qj$?3mh%`z$l)6hd9xXvX$H9Pq%zF6dR_)rXiUZE4 zvy(GTrD7vJrd}6`%p2nPQV7lmA0(M|Y96v$GRJ`&mHt9_QV&C5c-O0^lu_-xTHEl3 zCj0YLuPGcYS~DU^NzrO})=SOBZuauTBe=KnoK^4gMU?4L57Yjiz*r)zuj6k>1i+ES zy%3x0ZBac(@4MePTVDvO2}tQ(FqcaA9`10*^$+f zIzQ8cvJTy&*B#p#b22@m#^>WLQPjsSzg{7aWID%r^(;Q)?fmjb9$R8Oe_kuufV4P* zo$Mw&@-V{fVgDPN7ge2GKdKGB+zic{5M_a24{yCZ4_?5a_F)repfR#Xf%&o-zWlA< zk?RO>Xef%sXgI03YboZ4p&cm#Tn4G9LP=Vq(qXtKn!_^Xp6ZE>0uA(`-t#ae>8Sfe zcQzC};0*F|!4;2F0><0%p;rzTxWg&s21fJNeoqu{X2|UwXUNx-q)(%P4dgL)*s#Yu zVD@za`4U)SQMQnr^&?6Vw{rTGzFaRW3V@7pds+AFo)6FPOmL0f zqtCS>@7OClH{L6rx5vT4LFS`&ht|u$@%1q3G|+h2bXrY2;Wig$x;H-H9lGm;52u{i z;^~l~vqiZLQE9+`x`2yCGSqv{L_!nZ)@meOQ&K~H#;dS*JolPd03vy=IWu%M!*e47 zx2x(X8vSxN?MF{ZTchnE_FPH)FbAJGs;P{YMi}X_PkCma-DQZ4&^mCu<9YtnFNh(1f*+K3cG3Fr5HX-@RElj@o0$7z!V}@^QGzwsD z$LH^|G}Iq&&&sWK%rGu1F90BaC`jXyNv@`5XmbfZ@JFj$C`A{xcZqTqii!5x=%JEt zJDgneQ^oJkOyCG_ZL#uLu$ehE)D0^s&wX*$#?!FB_ilc)A^ah zwjXA_wM-zJV^&BJCK4SACPm&~>TN`VGQpf1Jm-VC98+C5sRbAjQ7zT(<4DV?fK6`~ zaKb}@thNavWA=euvREBvp>0jBv#K~PL$Hm|4tdhe^lx~q^^%p^$<5Yx z6eu(#T!Ic%Y5zj?fH|G+2PpqF;q@GeYbVYbZ+6m_c_bAanBW}YYf)D!#+;TUzLmL= zdS$!nvSj;gKV5tXw`w|!lWfX?YUjS16~Pd%A5C==#oXhjs&|Y5Ua?ksx`=h*iT2F{>2K zwZQD?2#kn8W6!#0lHLS%8jiW4@zb>pv{eJCkeP1poq(UMy&GUO=*;1tl;C1ww$PgJ&)s zOL*k1*#UM4I0?~-clCydCcZd8@kOuzA_#mYQvCAp#9aA8WAQ?YUo(^9V^KJjelopq za%^$+^Ia+os9Q5wHNUhn@AEqzQJ!!4!t|Kw|2>=4_UnVWH~6hYzGFFFLpM&b$GnVY zg54}ZNq{f!-`EcP0Z#B&4*Qz|ey(oz5nDL(nr2bB2jbjs`DHr+L83tS z@s3G##tlf*3qrl^`v*Q{gGaRd*11;L)4kvGT>pkv@>iWK-<~hH$gxhQ%W#6vf0I*M z#(+ijUq$z=QwAq(t;vhC^Dv_K@(EMZ7(u0rZK|a{Nk5w!$REm5tem#E?a{>x>OgX2 zX*?0~uzza7#!Pwat%WW+PoiQpx%3cZwjyH`tlEa}S{=haJ;xJWmD$6cCJQU*WJ`2fl~ zy-bjovo^d_bw~4P<5R|Bpa!vqHd9<=sqIuOOthrj-T9;mkm1S*$*?_hF&g)MZF)Vb zyS?qI)^|qlCzan%>ivi{)_CoO164KNxVWj)Xfr) zC$DM?A-09sqvYixA2jh}s&O!q%26eAkc0*qG8jZo)? zdbUwhUw{0WU*IAt@v0IX?~-r1P2GyUuWNp0Wwg4+w|-?S$|$U^rl)TTY|BmKTN=kVO40z)R6VO zWkj2Y`HmX$o0NzlG@Z8P2SUb;Nv%I(#th;-DDn)rnL0h!vNhYK;>ucz*4qT}T%CH6 z(BZy5ie3X(e1)~4Q935KqPx8F4`L6~0!-NjXR0?+~25!k$TtQd+?haoe#ucZ(BH z6Qmbb8*FQr$Y{+#3|ksv{Tyr>@yB7Sy~mR~RdMWvlHzDG9h{ceZixRiz3K;*f)6>w zWrHmy6N_m9H>{|QomCo3Lz~+THYIVJcwjYF@4dL79jn@U7@h6nN(3;ll0W^aTr9-yc4JwrVpnDT$hMA&D>-b z5Ib++VeQ$G4a=&#+)O0)59r%9jh!=Q!M2U4no9%S1v4flhZwT+zgJz`hT0VX2eTQI zvb^ZR;i}m&Xl-#_BD5U(uFq}$?v(rvDExAVsBiVb?(KhdNNWXtDfwDtP2^qb z=}NT&$4$n^>4vbs%oa0I{vNUbZ=4&-N=U6~=>wq=nvMVPSfW|Eo@iED0(&Rl8@hCz*!?Rza2pM}J%K#>j$l35ctGO;;pj41zIzh~ zGRq}K8X-`9Bo&(Mpso?4Bhx9dZZusOeK7b;j9$owrKl73M!SgSz9uSNw~1^l#}QH6 z;&_M>u>w&i(&Cmp+sB_(kbD9u*PcxbaBrWO9dU-F>u)X>U|ZR&bO$WY`&Zi8a2R|G zAK+_(I{qZbqII%BPYTCsUIue6%MwbFiUr!~DbOt9`8+(~zEBYh3hj-pmzh%P;i;oj z;p`CiaI~hx(@q04&75c(kD8)bC6Tv} zl-sDtSD`cS+t}~nPUZy()ZNJDimnZ5lYZlBCID5zU3q`x4@x7KjPtIfXC?UpmwzYG zqS(J(c&3q~TV(i7_3YHtcaCh5>mj2_e(yY$tC zk@%HUx8dASh=|kcNZxzI3@980sK9)QiN3yrM&=Pl$P3sJUo`VhD+d$*5nqrN;*}A| zJiibQIt z!UZ2*nq4k$#h7 zmg#}iV$ZAd1jMMF5Yl?kNDiCw0JedJ^*UIy5Prg^y|R3&LO0=wqMpMuRsvQ2;_IB? zR1(*KpO{tEGin)eyEt|i`mx@}?1ml|mW>C1{Z!$ee#>imS*%*}jl&M|1?SaOtbni5 zsKUXi>cb#xuX=6*i1V&WDImNci-R1ZV_>+KPiIDyW|gaUY^zWj!Ge?heA3mcPeFYd zta9JMORS>`+>!w{lnzcX3`VZMcc=O-MW$zSGt03k==xB1Spfr!N-cSG!nn`#YZu5J zk-Fa{%FvZ)egh)k^p&Yw@VPULs&`U2NiYv2Xc+|QuRJX(Dwa5$iJ^#sAk|o6&+Sa_ z`P1a`g_PL#vOWPFx$U8OJ+ZHtRlrcy0KQ}s%Q08x5R*9)9$VO`W=LD`-<9hZBuY7u z*`}Py!IlIj;m1qF51Iyj#+;=_#gLTxpw`x%LVKP%k3SF3A5!{|E8Rd?>3fi zL;44|rZ+kCj=H?{SK(WA_<7Ig(v_v(2hId2DhIP|8#LF2!9f+ym`o;bbZo;>j|pfw zfuiaIaj0PQ%PT~fuk2p`=jty-@P9Q#1CoE&MC;Hqzv(v)>B{o`iZslOb7UJ=;T%n2 zDykM$8|?O$)?aRAH|7-?D5=@K1U>8txSJ0AJsW4tRvgcJX|i}<6L8ADFX?SjOx3s zb1~t=D5pz4YfCdeNlvbl{!+9TYh0<}bW@Lkw&{gd)I{^pds%c|$KbmUjIx*76;e%M zDO{x8<%#|>(;0WBToI?51*OnS=F0QSZar(d_UXKWE=jK1j?&wWR$b`CIpIxR*j1Mh z^&5QLzVrdA0L*$)>j}1n&|3a+nbOd##;Jp-xG&gaHsL$Dn&&vt`7UvlUqX3xd7zH2 zWH*gfS72FK7#Pd?r1qL2KwDXDJLI&s+Gf8zU(Uz22j7 zN5ju2tGDy}U!WxQ$X7MVsPnk6Dad)=WB91ijXs+C>yI`q+nB6vms}xzcpf$5IYwo_ kFyF;E%Udy^^=WN`wA?|-^Nuim=;%D7@$;#y6Bn=l2O&87>Hq)$ literal 0 HcmV?d00001 diff --git a/assets/documentation-page/description/o-programie.png b/assets/documentation-page/description/o-programie.png new file mode 100644 index 0000000000000000000000000000000000000000..9285df6b0b1e5e6645e40bb71dd7d2f8e2b91ccd GIT binary patch literal 72897 zcmdSBWl)^m(l#0cf(HoB-~@sbe6S$F65Ix7aCg@PcMTE<5ZpDmTX1J!a0xcJ>mVOw z?>u|Ir|PZxPSyEyRuv2bbI;w~tNZHitJe@BFDrqLLWJ_<$rE%bNioGIPhM0$dGhS= zCE~+x{Is;PA0D1MC`yPtDIF%+dU*1}L|8`n$&-pG)Eh9u!*gU?Np*)OPcXV3zn;c3 zU=Tlfa-Sn5CamnLv%7%Q1Tab!JT&Rw+B{E4Swuyq@gG$ue}299`0ihNVu31AX!s`wY3ysHFoPb_V1-)L2~;Vu9CtDJ8rJl1BmnIVY+%dt8xZ= z$<^y7&n8Sh^+=Vehgy`@J)5u~0;d!o{v&dyu&ztY0WrJ|ii-%gAq2n25W+|4DTS9JZ168Zvfy-L5FNSIvb{VPxv z3jhE>;v})KZa$lOzMsfXI}o1jR~S=WorkJ>e{^xw7Il}$Pg`!gBErGR)xjdPzqR)( zPL5x}`-Rqd`^B=C&8@{J>B+B~yP_BGZhoKBj;VMDU;nDRE)2+L5F%M z*U9oeir&Ni*M^L-2|r8k?(RO$^5RP}isYKfTe>-~b?w~IZH2Y~VD&3N7?C<=i4XD| znODP&X)R(kmCKS>Evag$R*22*T&?ZZ4hxGAYH{5vX)$b8-Mb1C*rgFfsHPjVQ;Hgx zd~vl5RwY}O7EpjVt^>Jo9Ap9m2wcXhl z?;FWI55v!DxpBoLo-$Yn-&!}|MbBeKoU$^E{p(p&CZ4aR5=Hj)gmiN^CU#% zUcHjtn6J|5DQSb+3X2>q>;3O7xQ#d+XQKD9$IaDN-qGoVK)x>ZfOJWL$CyRU%0+9d z#E=gW@!%Fu2Io=bjwQ_P>Bam+?&jIN2ZMF1M1q2-oB*E8fx^Iigi|jVj=I8MZQ@~h zV0H>&@g1DvObfJhIP_|o_b6f?J=qL85?S{S6C(*g_vtnE|rk+j}Ch7q7DW;;lZ{qxlVvMfE-RKXK2blegG^Xpo&(pNe8E5C_6x4L13JbT7 zv5^WgRbx-jr~#iBfYVhr&-Y6#o$Fq^x|dsN;iv5;>!b-i*On8^O29+X$!ol$M$FW@jnu4kn7#NnT`QW4j@1|dJ+isbfaH} z3wB2k2boRe8Th(eKLF?o`}&bJ&YMqka(klC{k!sQJRMwr>GdAizHsde)!Ib4%0{Tl zDVa*tFK{rsRf9$zF%a(d&B^#vNqz)1AY&&^%^bWJw5ao8qNO1K#F2&C*jPbSIV6pH zV70Veeptv#G6yi8lu?n;TM$2MI8NJEXpQ11_Lyk~aj#|yBpd!a#IgG0Rdu|7#3;>t zFR%Qz5@RbbtSYKRWju`1Nx$3-Z9!VPyoMfKu0|Q0ZF63(0(`n`yl*Xbrz=c)_Hk%s zJ1(Vg2BHGHtEy?LVf~hR z1C6-$%F+Lepq(65a>W^5#t~J1D3!zCIG*E-@MYb+1hshbl6svg@ZXGfy51c199|kk zffr_-K$V8b$tfu-Jdry4{!qsDw`V2p?l!_mYjTU;HudVuuYfQOwK0xEa*|zOd}@h{ zO;^M1!ev=gbNl;355hFT_nSM7li-z1LXeumgtXEQOI!~z#MKtRu|i?5Z9iHn8NYDV z{vxg|ykL7wew?v_GQZKiLc@ivw8cC_L-l%(p4)3aYtAJ=2p$H^N(fm6zrlb-S8qYku-H84tzmx68$Tx7S%% zS+@!}g|~p0hBMMvqAfh9?hm%7i{DPJEMU+y6}WoJ?>HLFuvquRRm$I@NltWKhxu-D zJ;eCwKI|+PeJ1TRy*eGbkeC6llvq|C%VzT|iPn+3z6Q3{h*s`4w|(%NE1Av@LMkS zl7Lp`SKk)aRKxI_F3KuSmB_%sf2}4)wf)1wP-S*>Eq0nxwd zLI%xqV6hiUG+a3TEf$CdFbd2l{;4k*)}xd0{<=O^bL{`?(&LLT-rqNiKtZegM;B$* z;a1y%R<{9++aE0zQc)6FrIF{y5STaFU#G3ARi2;Mrn0CC5<)BcdtIaruotK!LLT@} zrPIT59`ld3SHAQoAN|h@ds^TL)olM&BK@%7{jW;_|Kn0`f&cmw zf~qs1fP8@Uv^P~qD(WLX*21^>%*o`J;@f|DuGMp zpJf3c1+o-Jam3AvLs239?B7NxYF& z1m^KN+`HvwU_`H?inSIom=)>y92T4?-1liKSp(Z%;0)HxvsWB;$PI19S-OcL+hP59 zJ&Xc%X`I!Ytj*jQM1Xl&ez=;%EH26doJGj2Kc;t%sc_x87m1AD!VqL4)_L1X&s9Kn zOMZ0*GsDN<6UQ1}q~E1DjA?lj%x*fL|74uyE7xD|tUHk#sA%T5$nTtk;xRUydTR0e zFefvF3N}J^_8Sfo>y%1_E*pX>qV#Wa$hv5mO}k2wTEfci%!J@7M??&F7jJHC)mc4S z5fcyEyq0cU=EqdiS`TIF)|2Z!tPOnNEVK9Fn%4CPpW$*3BK8C+&--&a&4v?)4Yywi z8%?P_wNRF#n!xDtYkaL5sCK;{P@w^mZS}snzuWMt+g!MiFmt*iOC)m!3)7#7|<20(Wt zp4NGY+_F`koVWvh7%oG4dnlaSz%NE6YE!>`D{mz>j;Ywy@%iwrR zP429dwD_0oc>-T`MWw+*-Ct_(34Hd8DjdxfWsE9TjzYbxQlmcNap*()pOdY&gFD5G z7h~SCNIu&0XTBAQd-VEdX!-9qp6tjhP;~YRzOG|?ZJ>YbsI|&&{X&@EgS9nT)_qwcjjJg2A|{Og!*YCEYhwZyAtiU03dH z;pxyU8LQ2i&Gata{jCoFuuZV=#}B<#(8|VB3&A3tpFht$zw@*r$Mnlf@fLe&U%r`l z9n#8Hy(6l<8Q8mwYnqzXHq|8FU!`}PeS_$1{eCW*d`JG=ZnOjS@Eiefxp%>pwLYj6 zQ;K#(9{^VJKvGo7+x797-M3}T3AI8ZHdNWa-~U4KkPyc}6v9&!7cuKCcc1FqFuMgs z7}*t~*yU354isG%328IHH)x*gsOcT-3{$JEcPF|P&L5HPc0BGwnHsN;&MlZFG?>(O zmjbK+gr0{2Hj^&w#k-daVUrzIfYbZ+`s1QUwqURGVcSbi{PawHWR_}J;JnsuKb|M~ zAS^bQ^PfaB^`urtpoW{i|=cpn4Pr$a#WR)BSq4t~)8-kLQg# zZ{(;SpoVBW+K+LkHmdzSmUpIgd6o-uI(1jWarMW;X?B+FmNHWB8#jTwdHmMEo_kOH z#iN#}?VbbI0|~pO)Yfjfy9<|mnI~g~#)IExeSPlle0GS`$B*E85yko}{5|4Bjm%7~ zzz0>w79FR3Rq9N9Zn@*F?k75r2IPpnBRK zkQbT>;xX|nU?w)-%3Pif9rR!qcRV2TmCgW$jc$rxBhJ1#*&W~-15D-u{a%Q+kgzkW zI&`v0#`Ds$5%Z$g9{;;gwyG*G53w-;)UzFKH!#G#%ZAqajqYLlBgBJrsCHEHI#`HU z5rOB0F=x6$aHcfRHPWWN1+tENc*;)sDW}@Xf#8*}=Na+RuO*+2tji#u>zdLSr^Q(4 z_JdcAc^o`7H@vFQnC4VCy<3j@*9HOI&tJ;kZQM546yd_R<_g{J>Rop@)nMShoBGjP z(8969Jez)X9){jZ%K1wd!^=}ab5X#!V9jukZ*IBzigklrNv?xk-@pq=;U!H6JH>VT z2CjtJ4X+DyhVZ%Sl{GS{UE{ajfnnQS(3}}1L{i=kpIEKcB>1OX<;0FpFyCcYH zDRC zU&&wndQ>Zr{(`1@Y4{gIJUkybz2WBZf|d`n5^qeF|2EF2$7d(k$%bT{?RsU`Vzk&M zeW#`Xigs~QA9+0ZoOe_(^w`3Hc@dbM3uWTg{eo}|8E6Sgty>i=XjW!ktM#sbay07PKTa)pVm$mY4zWk=#Y z0{a`t>H^g3(^Af%HuM%9+xThif_Kn9Od!^}RBYw|qUu$K@SU2&sI(9%+O*g8Dtv@E@_-ytpbI>@TDO>^JF zD0U?;j^HwMT_(Q}g)XAhkWC%B*Ee!Lc4~m$3?%j6_i2cDBmQJ%ek;5C6Ve@wS<_*T ztJKr`Pl&ol+Bns;LY?Ls4imUrG#*3+K(!*j<99k8xX10oZc}aC1i1EKzhWI^Cx_5- z>N8K9*PUzHPb{+&(h#{d=~`JI0ax&AN`+Lq>VZz(fvwSFeSw27xw+ojjTtmowI^S- z5R}e8`gE{We*~is(?mD%wn-~&`}XoQhZf^xI1Kl^KB|$=u=PX^Y*j_kneMw0z%sYR zm#{i~e4gV|;pH6rSd}@xEH0QZo(DPJJQSz~VvKqpX$P|rmLfV>c1ynnfJ#W=V?^4b zI|0>mtS;9){D-|idE6FX&!4s7xZm`6khV)XyxHlQ=!Sx4P>DZ_MCmN`7VN2TXRL^- z{kSl>5I!G%sdH_#Ua3wUTX0PE2*$+kKqoMIFlxG4v1l#KU72U6eNLR-tvhAh&wjD`WZ=AP$e`obNx7adD)2yE zeSc^B@Vt7@t=f7=nUTi}Yq-T#5U)m7w8SX$`YS@+X|dXBEOaQ~v@O11K3#OCXNui+ zJ+`lx|H|ib!g~qM3@X`N?JAI3Qxgo~d~QDFtwS~M%?>=gv>|($z*Ll6PhcX4z{Rh# z7-;%Xik-rJUX26->VxKn*J5hN36^Fnd-Qgv&yLgIK4PT1!W_>j`A3$mD^}v7<+BYi zo=O3gYF5hgKo_>+;tZMio%&v|S4cgUsu#N;CIhW%?TnFbeST{+PYQ3Y80IV(F@ zKX&_ZmV`Dp@N2v{jFn>sG%U*6u8FU7Q??yzb8Z;~tOY040z#`}YPby!ROL zW9sWGRD2c)MMC`E1O*=b_28_ULuZD;LPev$R=qVqUmZWaP?WI~#d+ugvt)+qL8dr8qFP@@b>{#R6KGfQP?^PK!lU&$vPphXhBXOS@eD(d1@( z^ClsId1uQ`!5*A7T+|Hoh_8QEPtgjJdZ^eZ;GnxH!XR(lvLOk`WEg$lczOw$N&}TD zs~cMtm!_&Ph++tnW^jAv=~8K+k;mb7GFJUQ=b^O^s0LBj3RGi3qIdxM`<{;ce8uD+ zVXOnJ%gt2?;)8+kX?hU!J7#O)(~V-)%eR~f<@6fpJnEMZsqze&pn6qLyYgkJ0cC?D zmUQhM+w>30A!I`EUv(97Ix#B!DQ-c8h&R4jWR3T5^5Xiq4$927?bH_RtL3DP~4@dg~e9k5gB7cnj@Quja)rE;a@TfH4ub4gQD@)b?8I#c& z{D<86Us<64yJ^|~N?Gas*DZYQ?gBpE4rw}nCHsdS19-fVu>?o_hk^QU8tMPTd;f1F z*Z;Uh02Xp3TIXl8WC_D)tN#oSoug@l;|}15{0G5kl~(_u(F`Bwl0GL1fc2nGc83i| z$mC=^Swu0DW{rMNWa^(r{*UE3@MMyw-`$Wi6o;IpxwHbv?&mP6VkEz&6ROY6IWHdw zdQg$xolyN}ddD#(8O>Ro8vqWb;j9omyBBos5@BR(aUyB)oY)+QddTH}mT57OY6Rn! zzxH(*@b>h><&2dOWP;o4s>s!D(mW~f>Bz;v$NeG2ofZ=0eiG;}6}1*po2FxXK=M#> zWBCo^Uw4b(Y%p61z;B*isxz~2n`%e)EB+Nm(|i2|BaPXoGo?a*9d(^Xyc=oJhe`0P zFDe7X2;RT>uwMVyt2m}UeR~Hs+}EH}EXc;Fqr}pT3c^S*+B&+y*;pB5$2)L|*3cPO zR!E5G`RzWpo=E<02hC*LJv28=b*8t5_g$Pe)i&QYyB-&qIO@@iNIA4gzgu$BzS&T_ z_luMLuAA0qlg9|e_ww?(Oo`HpYQ2J;(Zx$AIXtuYXRaHe5~NKvP_rk1NCA%b2Z3dn z{suy3jBPJxWj2?pE3~7qkYi&gHEXlA${pR^1I}u(f%Ykx1M6bm<6bXGMV)WQA7>-8 zp|J4A)F$mhQfR?>hC(_Idj6kGfYx z-j`YF+iu`Pr}7Tt2VeQSQIcp3y|H|_f!Q7LX1ms@Fn7|8p>F}it%vHjmzXX$$lUXn zxTmM57=h}=|KVkW%t69a*PJ7Xc^IF7o{7f6b2YUq>^>I={Kv3WV6VQ(!QTyV%*SZ_ z>GF@ZS?fur8gxU*oy=AI&|k;6Eam?pr_ulGG7fsQBD0KBj|b-tw=gfX+aL@(lUGwK z+B9NUZ#uXoo1{p10TLBuEQrA^b?Dy8+p4h4vu~`#`0FJf#`YN`5mK&=Zu<;1MrWgSLL=> zj%d@yR~Hvs1QKHS@cMaldv~mU+T;IKj&M`A4zko_ByqLU9y6aUs>KFZ!Bz{~cxt|IaXsdsBnP(u)FbOXhU|-W_1J zlI-^0O3qXoF%0V-C;um55?N695~)szAXiWiYBV|vv|P(Az2{0{5@J8Dv0exqxxM`t zOZZ1s4psCb@hN$B<4>Xw(Q!4_J6mPO?!0HtBJ_`qD@>#356-+#WUZa3t@f9GF~S^`ld(<1XfxB>_1?$eO>N65lC`AL=r7i7JIj;x01c( z`WxOVLXk3Y!jS4b^C8J?^4D}hjY)D1My{3|x_Js)HA~>mc67fZf4-W?3&hO8fMEBQ z(i+6!_jqy(a8~EixtCPBA{j1Eerb`W&GvY{K}vl32olXA7<|1NO(5T5vNi<-;(}{I z><80cSwVmI(Gou9q%j!9&^>kCJ0{ehvKpVKa7+yLHe~49g5qT<6RSoI>Kd9>>svIY zMga-^>Cc(iLC_1W_qbm6HJEY&n4EPDB^gn{M2a)mxyfe5_d{N`p)|J*@p#(aY;Sbx zU+{(URu{fdH%~EdDbndZfqY#lx{ZUX_=2r_WYp{x_0$+q(T{9GbS~DH&(Yf$ zc?fiuK2Jt5cZRm|hv--2bHTRvD!z+9IDiD|zj7$R|2E!fHyB+By&%~R4AQ2Cu7^xGEy)LM=pqbfAlJYs*Ri02gv2M( z3Dwko^zriA&vFTni*JUS;g)yYn}Y#K9zzzRDdmuXd@CXa0&`-7>WHS<(z3;%CLB?j z0q@>{e($RmTHN+ihqSzN37P3_sksv@1wR#gaHIL{;?p0rVIp#5?zPnho7?EHgY6G& zDWX!(m>)nclgi)gPZa?8Ty}?0F=9Qg#j2nbvE2>q(j*=;Y3Q96j1JvC%v*D{HNqYE zj2p7G;rmXvd%R0dgleM7@iX!?@C^3s%jh0?-NIDHw&U!PJ30|zP9^6Wywrp0d&k zhHMLrihCxw`m7LVNO!dSz~nDQQ_b^dHD#a(9hL6Ei}NgHy3E!xIcJ+u$tl4Q20SxXb(C17Ky8^jA$8)&srSO z$6StI6J}A@Bk1NDQinJ{Da#{%=qhX_N?(7A6|8U^qCu|7c3>H*5Eo5NAFE0tJRr1cHsv`fMgSe)~;Gz`q5sM`u_`r+T{g>;2URI-%&enyaztVe!7w6gVj4ZXw0#j`Eptvc6xpDfjZ2Tef)2&Jk4^6w=iC zozh(tx7r_w3$Zl$QTZAhWBLqZN{axNTuBUgPs~UOLbh0Y>;tO>0R%IQMuF3lpj5O*XElja==Df)e~B6 zS1zCjT>di>i+Sg#u4UDN3zhiKW^FwtHpwKU|R*1LO1&Jyj z^PN0hOS4i~x-+qo3N6(bH`tgJ{N~v$##-yqiPJSA_iMD_yTy-uuBi{kgXcL63*rb} z9hI!t!)x7#G_Rsn$a?3N+yHmSy#~8fkJb~1j2=ph{kDTdT8OzH zyPoDTzFy_}o*$x<_k;l(Vjeplto+&D%Z%Anl1f-W6%%|#0}8|(sKinjn2ALF3G$6` z&@0$VXmBS8z~*W#$GiH3!M2qmJv8FzoTWVbGo2wMkNOpA-`Cxr!zJex2XGuj?aC6p z)P-iRhpF<{bvr=k^WzDLYts?un;uzOSJ{tQb9E-s>+x@_+i2FyCH{UgP4H&*%-FsSS zmDooLnCE+hhl-7IzhWi)v~V)dgSiGDgt^6sx*vMxD1<2|bV9Ot(t^3=wsb#YV=z`g ziz&#hTvuYT1gGHtxy$>fTH~N}G-RiKSy);so|%JT>;O-CWc;G#J%Ow_9B>Gj5KEtO|{_C*CWqLEUA)+ zps&7b_vfxWP_+2uf~Sk+tP*R)KO&JK&y)R5dHqhAG)vw}o5?))(|bp)=ebORSRsKL zYu_oulj-X!@&yIQYgKEap%cO*dVwt`nWsp2mW!h@%my_f{uzs%?=cowN`>K}oUpkX z)IH7Fb5rK}veB4_9q%rsnw@D}mF&r=3@<^UAU(eFO(Toyfp{w6Dw1gbh`FOZ4@hl( z=;6mCN1sN}fAzNuejz+o$bo%Uou3Rr%cBSoR3X`#s4mMiM}F?z7f_NbFaZTeTZ(p% zU#RfoiFR?XteqSJ@UceTJGFm!7boFkLiVY8|GgTwySw|@&K7GohY@8GDfVD5*`A0Z zb7i(E!W z)4IVxT1e-ZbaGX{;DuTC9y(zyDM&{7j zAr&6>q)NFG@^l{$iZyb*hypI6RdX5y9A`VgmMzCtjm3FB$)#lOqo|H_WyM#qw}oF} zu=nP5BV0kbrxx^8r}x2h!OwPgQy(58E#li_{KZ1LHL|;F$vG`3uc;8-Q%O+LQ04Y_ zX%)i2?dRwOr5Ynh(g|69Zw8KHLc8THBw`j?P^J1zu1^k;Dq-ex0gH>x2fef*ioBXc z)bQ9(Z`q3j&wJ>6sPuchkd8A?wEPR|x#0Zu^{Wg= zAJDp$TI|K-J11JxbF)V`0+>`2>}%88XNH&$vk}u`!lU8EPTv4X_w~hrC+3zT5wvBe z?CeImauCz|R7pjis0~TzP)EBB1#0xC@pk0Np8sJ%;AURjkA*F!!U@T6Kc_~6bjK7$ zX8q`=$L{UbzSUZqyw#I(;2(*fl`^pLlWIY;B5QJg*=4C}o(-MK44 z)RpU?uo*G-6qaFl-Y2GftJ^R4J^}(#95AjVe2wbHIRfD zDK9yU3|@by;MNiy{V@R*>86{jx#C(5;@!USBr;xg-fuqUw zb?Gek7Tw|`ZYN~NJrdZBXj!c^-kP*4L5Ulb%JgG3X0yU#hJIJ0@o9jij*VtqEnyS- zLsdp38fj{Bz`pt27a6B}wH6^K3-SrMjG83YjL;DH^1H7Y!1^tmo*`gBH5MBGf^I&I*`&{aQ! z;KN4zd?rwqS1gN7Sxt>1O;Y~{OCB6OOiaHQW9_UWPQ&n5C?6(b54GgCwKJo9i+qXV zYSOI&9C;xN`a;_3wz3itOcI`T)&W~c3$LIh@++C=ZmplisD0@t#JzW99r;@k)!&w# z8lA^K`vth$|e_`0kEJwI!*4 z`<=|p@I90P=t2H;*x!R^?PSV{3SSW~y~q;`&~fe65R|TgK#otfpPvEXYa8r}h%vSg z$5PeLRxLMdhhF&#IKGtNFLjhu^1QIH@I~maXVSl#tlxl?=Z1_N zfOM7F8}UR%oQ^+#{pfC(CA;zg$1xkma7+j(-r`Wg)5 z4z!)tg`x_8*4S&tPiDh!DbgV0@3a?A6&g0*snox6J9B#tp!Ps@-Aa=v_gdF@J!uzj zoG7~Z^1%dv9`UN57t3WK;}rQfG$Ta2Bf{7EFE>?~X>ZviCF3Qj&o$J_|Bz+wyrQCM zj!vAhamg{?*^#wXupD38U%S2i*<0B#9AlLsKS=R_Jc{d44-Mf|RNKsfq?;3Rx@(ZLp4^?<+_1jhDsIe8$3@Zr;U{E9Ct$A&rV&ap58cXdrv86bl z7+#L|V~soogb1znS+vaWpt#wz%O*$57JG6gL`wssqw+6`Q7pG_adY&7u4jqP7A?^X z9fHNztfR(VN8j7n_>{9@d5f<{e{6_P`Uw-GRsnNN%?1~KQ-TP~WaLi`Zkz-cK~Eq+ zw~;Rv-^#PL;~WGCW}Zn%H%wV~R4y(0#X7!ZfvaQuet>@1<*(tzN8CGo=PNJi zTWCrd*&wy5SIb4Inev_sOhKyH96>wmJ30HL=uJfC4Vw}Z zIcK$}Qga|j^;fsjsH9}shj3k*iD>E3ec!{*qQ8o2;VbmH-;O!R=%M>{yon(nNCsMU zM>KnREX8cRcLaZm8D1>&g3j4>6Xt>E&bdy{&J2EcA-h8PmU1_(7HR;ux9v?XttVxV zj44uL+}+Q`+fu#yjGcaa%^K?omwsg3$C^|Umej!#BwYA?^j#@o_0KmQshp$&)yQa) zF25@Xv~&|#vA#f(-|6M|(41*GQTy7Z1@sQAEt;Y#>lbZM|E?heZB>j@ z+2Y8=lh=)x*L<6yk&L?G03(D2V1cMc@-2S9E|8e8-`%&t+@56 zsrjK@SZ4Wwv@&dyKsTpP+Gp3L8XwKg`a)-P&fXR|nS8f@wym+fyeUn0dWwl?{AhL0 z!U9=LmfqMmUld3Awz?25w}-g5mbbnj-G>jQ)Gn{Z$#$vTABDLu`r)2fGmoGq@~y8e z4D4Afz{y+*G;1sYo`2YV8Q)i(?{Y6UYE)1yYQ2*cY_SSZfA@ogdL4bHl;df5k3qgPZA|2xIqC;M_hn1a zmrt@t%ElbepwY7`=CiS*5?rqwhpp8E_aE{HF#j$ueLMnr((urB#D2*8BX4`51mue} zD9zn##zq%ds}uAI)P5MkIk5_{Vpr7LN>pov?4QeV>JEHd%2?i=e>TNjp=F z5kK~c<7VoE>BWq|FiP;mTEK~d*RWLYNO~{;-L8uGGu1DmPDKNnq|LrwQ(N{6&9f;BtsX~ z)kHMof(?|sa0<@Z%wMGvPDgt;qI3^|D@=@Ek8@CN-%9A!Au=vOTqwc|-Ewh-6#$q8 z5ea)_@7cJ2bhc*`5$#yWt7&F&3%Pgeb680Nn-B?lNl}{IJmdNU_HF+Hdr1C6R-L>tqx)1S z>Oz6}JfZYRiS@JZRy!kaa6u1UynIe~wY^ev382?%CS~b#q2pCoVPsO|}*l+r(r;QSpKUghX)j?X1v;AnQKgDlSDKP45 z@dqTQQNCk+V~>8qaKp$rpgOTVy}4c!LrSTDQ)33Z>(j5OyZiJ^_%dn4aIDq}>7i{HQ`Yo5 z!qJRz`FOa_96ath#Ph^n`EeAyU-Y$)e zK5%UB-(e}O5}<55B(ASJe3d6Da% zk=&t{qVT|yb{PXhO?R0er8Pv8=70&8Yz+KkgtI2Hrr?auB63q?BzpkHhXX{lQFKQhEilczIuoMbzz zl$22D&8Z>V(QyQc7`DEL@kHSq{} zKD1;8r2lHo)F66$Z#&!8;x;liPI>7#v*qWMQER!wMBX&=t;iels*7G%Z`&Mny!YV# zuKzYk7o#3Kga8{0hi&fjCa0KwWXG@EG*bK~`4~ zt==u$Q|GvDZJQ69sI*9s4xf{g(_9MYmp$$N@yth)Ljk^7r_DP!IwLQ@f?!`HS7qXQ?$&o*&V-<dNkJOZ)Gu1043R-_GNqwNlr869;E2#6jFm&r z5lZhD_LWfc(%DYWQ4MbM-Fp~b1Z_zRnVI))B1TJsOpD7@EM8E=e@BAnqO+6iJoC0k-5AQ-!30GS@YR;!<^jW@H7+DC{3-#S6BDiSHe&A z@54_;@K`il!(lVz$F~MP_0eL|ppEhy~*1y|IBV=RSb^S(O>rcH663&Ze&n+xPL@m`qaS&&C zSMU-APhN3AN=VCiUc5MiXsg>@`PXlP>B*dfXsJi(NV%9Ec~CX6h62)Rlc6B?M%?;Y z>gzCG>P$A!74~@AmxuZxGxV)&#go@0b-&2JW3SXT&_`@5zivsC;2@ejq3(*!4ShJ9 zg0W=usfP@?aP1`CLAX1>6E6^msgXkTDynDTW4x4J&M<0yBY0kQN3zvyI~_O1LG*g) zIHnd-&((~;q^9**$|Nmctq862F}qH;6FrW?9U+p3(ZwYq3$aS0OW#GhE2Fv5>uPdR zFmoxfrezh7)T>or?{Du--^`oWDZ+D`T3(<nRZMAI9=&X`7t*qS{yBCt z(~ywvmldRzLr=>J0OYb#8hBCt_(^rxbX)G@`7cLh$=BpD5jgJ z>b9P`5tf%N{)o((kwHf^!~rHygP!9_TN0CUGj&EhQ9t^&w)i)O=5C$u+uv@uX;aw@GrE32Q}H*AjRjze6w!z7z%)DutE|kI0-OM> z&>DftD9UuI?zor7Vr3Isj768WpW{y*lRU%&F!0a+UC5+jMynutQ$y*!&%c`=_G9GT zGGT)I_-CV8R!>X?sA!2zMg~LJxsXth_6~PvJQ17G_}B7`Qk3Vn+Q6h}zU3{eBD06K z8%ce;f`bzWjXcul;rNuF<8hgwqw9D`BV^oOI=`T7VpG0gelz|%j&@2-=fZ5rMz^>a z_So`G+#;7285X;5?8*=FTVu}2AI$0jo)Va$aG1wqGZ!iNugoGAK%u(2rkX@6Bdn&4 zHd7Z6Y}0X0Ef_5riAHe6um6odRudSbTaBBlIp=#eup(SSU)4p0`0m?ssXs6?_KB$Y zb-Fh}6L|z7wsC(D%b1ZZ#tY6P|FS!`97_E$I+C(^-lpHY%#{mXbo;v^UP{lgtIfRd z#g?j8K78WYPS2}Zm7ogdoTLDRfb~51qyv0)(jSD=;r(;e2o@akO8UCE(85ai1~Nle zJw-{(DbMjmY*BQuX61Dl>q{&VEWT0?b5LKVExw_6a^+O+5M`1IF_bI&A$mf@2KVKf3raHy`N>(d zo_EolB+V2~zz&tU;M}-iZsjaym56=xIeGecMBSkOGG3SDGUS~RrTMVN=hADsYsNNq z^vkrUDx{=w?ih_X+RqB1)mgWF_-vz4vf?7ddbpm2#dGO^T|Ec3kqh5fACXhs0%TCk z(CB>*-;Kw$0Q0EZ6gNXp<8wx^ILm(I0*EE^s0U}Yf13Yj#P>>>9hKKaYJ3wQ(xJEm zx}Ik!bmDiicdH3dHekw?OD%-k>`YgDY$h1tdBhXn7wX4I43F51MFUovxWuEDON~rL z#{*`O&S#@{p2+V_(g8~t;Mm=_HX;^M@Vf%>$+Ue;^!Tqg3c=cS& z_Q2NX$v)gy>{g&abX_ajxX1_CC3QNJ;UV7ZcQlj37hp z#+t2B!!5BYHZ-VX-->xX{{a2)R-XdX$vXEpkTs&Q?Y*W%`QGvQ1D^zg>HRs%NyG*= z)%!r1&*z30y1El1)fMgFl~nqr&Zd1TjK`Fj&^E!UtJDPgcxL5{`g5>2m86)AVWk&P z>(EQ86ymV1`C8f(Oyq&5YZ%wgFag3#L;62FePuwDU9|P9gwi1b(jn5FlG5GXt)w6@ zbV!3pcXxNg(4mxccRMh23^COA;Jx4d2}8`B=bU}^UVE*z;RYqIYlF7=!X)i)v{dTH zhc0t#t5DCZD%uez)~6#PuZ2Q-oeyG|JY~8AN|i+Bxm4LOE{VzR=R6hKE=e-3z!AtN zaf8l4mbka~Kbq!H`<3E7k%Wmdb z*!jTU05b$>Au6!tfO_ktho%!*$&cP_ApRC-qQhLeSSYM+w)CG zf*PNx(chr-HD~H~_4rRV-kd~v?3ay=ee@qapP705bqfv8L_ExhAOu|W{ydxp7Qq*V zrHX@^1F;~|7N;|Z)G%Fqs$1MLQiQc79y>o@?sg|D3gCU2-m%F2B9SvfaWWQLtYUi8 zNbQAaBoQh%MvhV`7sXF21ad7Ma2k`7K2=>7qnwU_FX7>ayL({dq%fN7I-L05P2u-P zM6s|56jGJN$?nM0Sj?b1=)L*S{+CH1O@vk_ocq}eCkpj{+L_+FF{R(;G~7A$C*MTp ztTVp^aJ4?^A^L7d0|`#aejXPy$T>HdN1>z&Of*yyZbpwaaBU(<9TPV4L+8|hRVQqZ zO$JE%w3w>KhJHAYUj$r?s6)OKRTDE~GN3|KlM9uyQPaEJK}qhE3rPcoC5>5D#T`Id%h zaQ$$v3MzvgEnM9=;h_chz}|Do&`{eLO%TNPqpA^)#3(h)E+lMxdKj?!j+va{;6&TE zv4Tn_|3GiJtZZXfdfR&VAJ77b9U<6kams%|ert#6>xPUG*?9T}h0lgYK6AS2Kzu#) zQ)Y9l%gb3UJR#!d6P$!+EMqXfDENOahzVKLoEc>+N!ZrMK`xp}j90pS(!(`4tgSL- z!BhwyT*t$v1L~}KDXfSu0*K6N>Fl}?$C>vYgA0(8I@xpWv@tv{z#(Xpn`q(MG>T18`37BMZn>u`TL^V>5V2WYGU?S)#S@8ODZUF z4HK;5AkQWz%-_;>a=J<0cqWv)HjUU8l&zCwzhBV}GU%^(-L5uK+7yMA@^V6T!+p)< zI}N1R1(jU6Xrm2jrXYT|FejbnSg#G`=voM&zTUVJ(mPvdg`if(OeB0*%ADdp^v)`k zN7g|e`QNhrz9nFR@j5K3`tsEkn4Hc#v>|KmH!wtpAQ>Y(ARyfA+doABEHB9O^QWUE zfI#y)J+8PGXcZ)iYEc0io>>2PX2k{$B#Bv$(N2`>$>fQvE%^D5Pw&n0bE}AyD9c$M zNddk_QsYLlgUQ}V(JYu_j=O$F@ZEw+1g5s*SEOmLnVX9OEeaPuJ16HCD0L*(pLLvK z#S)`6^$A2gJeQd@iTGJZTMVQ5W<19sIO{Olr1o><$xPeIY7uD+F&_!l^C%a&1DIr=6A{ zt~eb8@#<-<_zkZ;$Ys!sYt zEWQF$bwBm6gU_xGLM}Y@nGT+j9dwjBsRHZVAk}kfL)~D#qGF{?^m&jf9Udsr{+)&1 zU&VAD%j#WAVvgr+Q!8mBC(H0Q;+KhnjTC+5Z!kmRj8*J^|JBS~XnsaKt6~E4G$kIG zz3Mt^Y_z_FfhdR~yJHx%-dg^bryZA(jmx!NuhytcI0bn)}?O zF7WVW%Qt3Ey);}2s@QXr1CR3zChBRb737mkG2l|No9sHViCI5g@=#Z`k)wtrnv6Jr z1zEe+m0m~*N2iv0Ch#1IQ6^8-i--Sv$= zn%&6sZ}+%~4q;f-Jj6n9R%gP?tVkr+C*9b%W-f}n$<^2!vV1twXrt#xIEVke&$$_E z&aa0@#B?HpT<6*!C?8BZ_Mwcm9rmyI`T0SFje9fdr#%Pjp+&!uAM(jrh?RJGc+3FP zparmfTb7917?by*gK!Ya_Q3Y-E+caCb9Y%giL$Y3)_oUQ-iwzo3$g07UqikAJPN_DEbJmX?Y?h`*~ zo|nc^sDB$}C!(;2ZDeQ%Tf5j}Ht567G&>)T`n+7jOsj(gbN}!8T)oVv2V%FJl#v4v zaAED8&S(^eu1jm_XnMjgOIx$X`?XOwxg2Npc7KY-L@sb(TGlLYC6iP0O4r3F4fGxeCXL(^Ac7ZV z{Qc0Lho%isK0OZ)4;ffdB#vHxHgNe+X5@?vegih8+=|L=`+mWURay~PwJsb0x)#Z` zwLD7zR+e9IN_wuw)*{$P*JnQ>d!W75S%oRdg;NxzNy)&m*W@*gd1)@qgIVw>G9v=Z zKDO^%#XC>ECd%gBuQqo5f zgY(^5WBs@J)o4_+%W?bpzoLXo;NFTkLhP*rboTb8?bU^-wuxg|-{M3|Z0JKtu4mmS zf$n94>e5P-!P9q^6X1YPp2|j6P(<=ez**Iyp?4Y1WBuhxER&Rpm+6!i{B}wdJGD#> z%S~j!Q4h9QJgv18FG@D@rW_NvwtFC|6MTMDFJ~m14P1H0)1wwvWCz6_SjyL(_Qhpv ztlwotFu;}#r{l$4N3{~pJ%o%Oku|k51N8q;WQFY)>2av@4<}hEu>AE9QXXJv-sz4} z*9y>Ho`O`*kR|Aa#fAl2&3w}2*EAy9J30Ja6`Cz>vCYyiD4uB9vEj{Yk4LJBnY9wI zPmt*kh1ziI=RKR3@RRhF#nOoMTEh??VSbO&T)e#dFRcmrhWIitA=SF|n9;&lh=6LN zoE$h%dyxK#-M+K-*7~#M^;Lf}_M3=j#xto8h#8{>qTEx(w;@}}05UapG1P(T3EAtF z>Rf*(5+X)KFPj3-&>K|`}fQTG8W7>u&ouVjQ-#qj8I z(MYBN`rM`_;_Qb+x>T_;d*o+qL1phnlhM`MpyXX}R`+TBweV=)Lv_y>>r~qD;1LX( z<%+5*F^*KGaZ2D9^CemS>|T2S9LqmOPRQ^wD!Q`lyEtnKAiUY|+bot0-#QvCL>V45 z?|IAh$A8l(w6}=RCB$jWfmfd=uc-r?en+n-_#0B_kuqD$ zzi=Q3$!Pw1_4Jx?cZqqtBdqrv=sQ6S*8?lFrbm|X!S)nBa6tt_KJp?{%&KRfmr$h# z2CQh8y~ilG4svC|+Xl?6-pA@7Jm1|{@bafijC;tnZ_wJB@Ty-Dx{y$Z=i2<^ZF(Tm zuF67!N)!EZ(mbH0%FAcGC#`9Kz< zR}iO4Sd^c1t-_QT!1W!Zj6C!WL;U9{Uo?K=o!%{36fbVmG0QBn!pDJGle@A%2PGHF z^ps|v;3q!Q64vfd#v)B34=>xV$hfKbSa4v5wdik<8oy@qe;%6qEJ20y(Gl+PywO~M zo`<0f9nI*I-aw;*fF)^XiHQ?EG zv+HoW8SRUkX}Ie7@%hhycW?!WB75yiMfI1YSTZG4M-$U%ir_-gU^1vz#Gpts*~5`x zU~Wh!X`QwNWp=3jqK$Y6(THT5AfsZbMXTu(#!(9{U`iCUn=|Z4mm#b*_$bp=#F2Ea zx2Ll_9;+g3h_GAB^)vBv9Tek0H0g{KYsg9=^DmEFR8SN0xzD@)88gtf{)U8o3-sfy zezj=|Jmi%WM?i`#MRUD#w6NuKt-C=2Xmltu>yza$k6W)_Jm{dBafBsnRg3bZPa~0_ z)QWL1U+jF;mC~r9U5>L4$sBlQBTmxHA2)8N<7D$9J)IW96G_@lgC0XDXQ6AW#7p}X%lM6-bx2h z-ijOZDU0==e)u>rOmRo+lRd=8)Jz)Tzbq5~Lz6>>epY3mhZlnodJ)%IT@apwqMdn% zyEOde0H9vy9EHE_j?j-$TYuA9o=dw(-4HSM>7jpa1L-+J$B;Vv;U|g# zF#rexrrmK>O4RI@BeZH=~Bq?bW^~vyz>q$Q~^BtHnY?wXqc9FJtX^%12~@j)td|jm=bJ zwngf2_d(+OY*Rt}fxpb@wa$>So^hkG;7JO?_h6c#5?UEvce-tJX2X|zEG1hai?coOv*CX*7HUlMvz~Qx z(kXD10KPEgexg9ZAnVbcZ?bgO+QXDEug6^tQ3ZWv|2Q|?z^$oO4C4dYiF zT&Ovw?^whjcok*htJ+_bUM&6V*!O;?0-Hf^9G9$%O?v{6tnBS2#l-`Uu?>c028Pdo z&Y6sM;aUCD?YqwFjuXmEr30VCgM;Z4*wTTuVaLbiVaD z;-7Z78u}ly(6z9L7CpWs=a_TFet9x5xj{(dwP1k1f)q39CB<_<@?F+h7 zW;gA<5$dJG!T)U0xsrK$WqhyahR$kZYwI|gz80qJdruvBpn*TtYj3~YM5=A{FyHPB znD|YqQgsbf(vJJLx$`uodceHCQkhC^@<|rVTrjWFxQH~pTwQi8LvsghR{EY0Lv2mm zkqB?|vLK6XPke8q2AopfhN!AF#adG~E*9cz+1oQGu^9dsF}ltRT_8TPAZEI4RTvAhWc7vReG1#(Wn|*I8w7<@jp9c2$A7j?1 z4<{w#Jgmkabf6vMMov6sECO!?-jL-L1t*yII(AU}cTCt_w0!dG_ZRKswX7^4h~_=S zQrNn|UBaANGcb#L#dK-tYlbNNA|Kk6{YXTfvdOoNyUw_URvzo0dW)ad2QHmAoy3QD z?%;3`<#RVRs)Eg;#Qtm0$5xa~-xrWPqaSd@U*hDAH`ko&AbpFaV3X;>kFtDe zQo3%h{g81NyGoiGdIb{@nF})2TKJ}xjIj4L3 z!%;qp%wnyUZAY_p+*t?@6}qC5UEQ8sKgN&7yW)tD#H5>Nn>2J^vtB&xyy0FrZU+-q zEH#9*LIYW-62tZ(GjI48Y`w0KOtzJNmX}Eu6m&8c!Ic*AIiU5-JTp12YcB8AWIK?% z=$|*n7ah^0dyu^eMPT*g^gPJFqHcmgWJyiaXi(6D38*u!>f~W;=D)dwSPhJlFrESk zzDhTFT+DYyDSQyPNfvy*lsH@l^H_dwS4Ps!YWJv$bc9%TYjh1IF);LF5W5ci=VX5r z*tO_1X_%?h&A79%6YFMcbn^=B?+J-*dTW2Yl{ssG4 zv;$hq)FZODbm-kt!J_Ka^vQ|CyMib?MWZQE~YJ zkMoRnozWU7M1%!@GB>Q;(P5jpE-CJKld0VGy1tTFRe3aFd$W8yuTx!jT(WP-S%uRy zV~wm!;C~i^J{C`=fZ`%qnry((ZM_(cu~1E=1O?5G(t~M4-k*snVW>TZiC8$ve(-Yj zdL=sa=|0RCL-(K84oU0xsQH4}6iy@r9GFhPYw2SZt8VSCRYk3ru3pH70!y&qHYQ_j zb&M{J@8E<`9Cp?7jPqchkCzmNA+eN#AJi~^3~aajO|v}(&l{5kiAnD3Gtr$$n6dY7 zu?sP8Ce8T95uFb7<~XdG4?g0Xme-}V7uWpc^}Tn$KU#leUc1P;-n*vS)U?z+X~D4C z@%fkq@4PuxM5yv!E2PpW)!`+*)Wp$RyNmjwm3KY4TjHu&tDgvhT{9A?jvy?S@%=UK zM2y_l8zz1Rcak{|h3g9bqZS#PE>`|U+SO*bZYaIte1fph>P)b9{;-BtmC+r%ycW>n_WaAhVvGHvEyW(zc(U0Le%1fY;oUkoUa|)z zlB!<_4M=V``Ql~iMqC-Vw z`Je7D&aVBQuEEjtt>X1ADJ&PeEN-@1)xQnhR)e{1=Kx~ftL#yBu`6S-764~3J(#Nm zYb`Q2uLO8pD1Q=8pB~3RN8fPkx+Cqp$UMm@E|$JV`q0a!{5X{P7)21`$>>CmNL$uP z3(ejHhuuKc)w4@U<;%|;Q{J!wdGdiawEOS<;Oe7Kh1Fz+ihmz`f^zazeXlImVjQ_h6Errdr_}} zdR+JShE52K`oL;G<)2*bJ3wq}ivx;)@^%E}y}OLI9CEI*bI?ibh}P%tRt=n~Dyyy@ zpcQ^ZX+OqD?o%Dmei>C$0vJe>|8DMtF_YrGE$E`>9;bT}J=NJP^^xu027g{mpjST~ zf4Uz}5ps(<9+AINvoV#WyFL}i+B{gl0otf5i29kzP0Vg4E}Q(Kip=z{hIy{8UyVlb zQ$1K(+ice1cs7>;NTrUl5q19$?-(b_+8xo}dVEDDCM{j;%QTAAZ0{h>kl5=1$!e6Q zq{(<3KY1c_yFXLvs0h+0%{25Ru6t%c^4ImQo4>R9tR(hr%pH9J->y`1S4NQvAzTJ= zHuEE%(ZWwLmX_|QGLNC4%DH3~1OuHSBz60~YfJsQ{ylV>C_MUY8Fy;M3nhb^{~UZr z^i|aL^ZPJJA4{Aj#Ic0Ks{OW!bz04WF@F>!Y5j@n)-v}n8kPgn%+2^9O%Mp=K1-9F zlUpmAEe@v7h3{FW?sFEIPwfopbbQ8u!{ILLsGWk7v0G9cfNr44d@dwC?xY3QGSL_*c z7Rj^NV+6o8PWH9)_Hzb69)@>>1|k{)^ttFlJH9kcDMxrpN$rdr)88GIXi1Io6K!_| zcvEwq{XxJHoXyADsVu6{k~*6;M<-cg`jQ?_IJ`+B*!29NlL6)Z&VUxGd7=oIqvPB5 zXG5LvUwQEA2srCc8@<2w3pLi&yP;p79b%))3!xWb6u$IFr)%ESxoa~ZrM%0K-+?2G0xUJoo#vB-n9fe&mJY+ZPn|29M$ z*CKde*FCq3e7CMbK>+Gy7D8X`xiUQ)*$L8*9ZSH5&9Da`DEoZ7{FvA2HcP7@uIOy; z@y$0Kc=K0ZB+~GYm6aW;$0$fAWF3&;Q$So&4Udju@u3N%h&FcVCH`e}uv$Hl~F$s=|iIqnH zPW!ZfvdMwikp_01QdbrV4kh!%mgH7`85x#FyBO+H$LiKBFgGRYA)@A6URgP-h(=6& zP1-IaLsNjVWnG+E6#eUbm+?~PD|W)@XHX4I&+3^wSt_)Ch8g3MTE0u!;{?I}NM=TZ zTDTSmNuE@GebzMD1=ry>Q-UkV+{CNRDCzlg@|Y>2T}vPTz6d0LL|nh0^V=})f{_Xh zHvEZ)0I%=u5uvKvQuoM zoN50c!w={fGzy17Am`5)Iyy-%LiOh#VJ!tlvxs5L!;T-aN( zz*BE@a&oNRB~Wciumn|0eY5xRq8(-?<9zSoeyF{Cq?EGWQQi*!DY308rx!zBc#zSf zJ3krl0I2uFkaejJNnG|m0bfZQPefp0M@q-0&HYegAX*U`ZBkJyd^zu^s(ESJ49jWD zmw$yzWtb_!WJZ}hUOp8#^P7K5GEB9aBP_;5JPjQcdExDV#`??|+l05O)rl_+gztVjvLAq`vRC zFV4nnR_l1J4nyeO76LB{^pf5?X7~ZT##aSV6Q9nha(lDUYEzdGBd!H*B!4#VPn9ET_LbIluPS;AeTnp$h}bvKY2t38-*?*$R^w08mL+-%!dV1$`U-_#q0GQ8aZFp>Yl4lBz4r9p00d$WAqy-&QVXykWsk-UtHD?=IUU zZ|pjO_D_TIeK3Ii5mEO#<}|h9NZAv0f^+8F6eAYs@JTgv*(H`Z7p|uCNY#d>YOU}l z!x&iHg)`#v1$@YfIAkiB^OE0$XioLBWsjS070BNFS$`m1^Eqk+*L$ISw|Ixr-a7$m zh&_?gVU%x)T1lJ?g5a4h_IRB?Y_zU5>ic(QbVlS?cj~LI&-7>;Q$_$66}B=B%t<`` z?D?sI{`FUXe`h0%!Ope$i)dj>^=T;3v>eR=HvWT12Ja#aW(jkeN{*H(+zK18BQ*=A z6qhO5iCa;RfQpXOEMbA7ba3=Tg`DQaRQ0tZnmdmwZziNg0(3ZX$0*6GiUsVaM}}_k zk;SJjPj#?bdLQC_un>$FPt1BmdwEsUmCAzyZUwX|M90k;O)TUGCGGK;8lyzRgk+(YT`Lf*NtyTw4I5+G@1Do4kS#{z+gBN&zq)kz5#T2}l(y%3 zyG@Fk6unQ{fdx(AD|Mp6-ff7dF@WHny1GzX>#;phG+dbTaD2s?4AJ9KOf5-%E=N}X z8R$pioGmfzl;?e-LY_Ydb!j=7Hx$5)8)zd|ej5fh*6z$bg`AW|*p|lb?nQjfi~Ru6 z)6rMyWhxElwuIt&KVD=HBszHKALALlWP0Z^+N~9rr$086bAM{NgfAM}_hcM6TV-HsgVl!MH26lxm^C@>XFYyhR^Utlet%rgD%!uz8wXf2P7-2=|BA7){v^yWic(22=wzsg&4%;N(SOW0 zQ#v6Zh5Jfn@UO|iUxQ5pqVo4_aWS$s0NHxzbs9oaHgj}nb=~KrpAo@SW>DDL-P-jQ z{GeKzKT|4KWnw&(yTP^Vv6j_LI^c1ypsT_RCq{si!D$CB05)~~f=$nVUO5x&Jnk0D zEBGFwAZB=b{7kFFt0_`S|7PxBJV7qyWBvwrXr_e3X>r4K6L_tm*#bl~3SsU~TKR89 zhos25M}=U-x=+gUi-@Q3LYNs#adKs8sT7qB<@EY1fijlv)toCdQgF>&4El2#iO;0N zM%XPwSnqx7B>1?n4VKN+tortT$4Wl3)jYTzYf-wCnWixUz_V(1@**ZyNP?K8x9(%Q znUcb```;)_$jhT?RU1VD5+EO@-H-30LoU=_R)pD?*22k3t*yoOdL*iJh<^TOt4=@j zC+)jgtFxQFkh9aV zBk|WI{`~Bwh&jS|`7(A0gCc%kT#zVO-3d9}WpaVWUzL!E1{cM}#U-bs!%&3m zn-fu)v#L}U{}bS8lx*Nh|FvZk85s2?%}TY?o1>v_5>7e-TEJ)C)A7q~@ zYA6lsQUv-bYfY{aiU>``pJhM44bP)<`$(c4E%Rgad3%U~Ej-(UKK+}ObyaC<#>npp zHkBxl&3)Ci^B;6;$yHFoz<9n%lu)Cm+B-)Eb|o@TFe& z{&q7ocApr~4_G{gp+mFnrq#qJNX94pXH&mq2m1m3$KCr81P2c>X{NZt!~6qvIq!zWmfiK-L%@*Z_)XcuQXVrtbsMY~+QgGkd3V>u5c3v?dP06jyE`6M|u zV`w+{Rn~XY(g;7S5+xcO<)yumOUk0Zsa4>NCc6^NJ#1M%N*^j&N1DEM9{{TRJ37Dj z;oG?Z7tt>O!SQqs?}8yc zfztBin;vuVmC_wrh^=EN#?j}q26htg<~vWjlHvi)yczA{*r7{`q_FN0f|=k3BxSLe zjhW`OM1-U}DP#_!T7-B*6p)A*A52IvY0XG`?Cbd6ZQA9E($bzFlsA{IeUG&}EkK=* z(kKPxmd;qB(Pj`UJIOdAwu#Y;!Y6?xm`hGh&sU3lS-hBQ*4Cnx0t%6f`Y`81uUrIZD*k-SlY($FU=yzsA zS0?90+y!^_}86JW6OVNR&ZJvp!dw3cE)Np*X1Vc_|>+`FDKxXT5 zY$xp|%?oeyf(1WOtoH#GK}&t^W>3>jlq2+qepOMbS)@l|Zia%qJ55i-%+EM%sQ`TI zM*pqaBl*+$nT4>k?ScHJCVY%Y44|(v%iY(@rWIn&+>d$#WqdE1^T<sTFa?T#InO|=5X&J=OkDXs zs_B0*3cqo}(;R)UIz12Hb*?nC?YZ3R{6XF;iT_VN2$JyA^0hegJEvFwqA4h! zP|WOmcuhS4GfUHMvqBAuf!r(Ya zF>RXr4-ygdp=Tl(^qi=_q8d$Sn*Kzu{av#&#Z?h2s+6VZwqDUi=l!!g=kUC;4WHa9 z$8^)=>~x?8M=3%DTLTf-O=cw_@B#J)5p{7jg8eYi;Cw1bru&dj7#yTp7+6nr|2)*9 z9wn0^EQI!S1AEsG+yz76P`q|KGF2ESGe*C{^n{z%h5HLdmxUeu_moLw8fVQVojb0AgwTDzFh2A8_&G$i7U;j&D{_wsU$_7+Q1jbDC!XnKRp4_cA?2)i%U+e| zn?p*gjz98SeUaHckgW>35v4&C`ht+^+3zwWty0)nbV|I>3>fe?@qdI#QnAgR)Z3>( zdzZftVIPFOwp|bMyN{RNq=?e6pv?X|A$lc={J?kLK5|DOjCq|LB8|EtjKJFhY!V1MK)mW4Yxfm0`Lc?eJzReH*L;WBqbazd~Ol)mtHLr;5_!Qqpw8qcqn=tiRFIxxcIxd6~Q`p zvFk2ptb5e9bOzv|rjW5rV5|+tla8QqpHN)wVHOv6g1K!fzFc8knrYuRgN~<39rXHf z0SpXzCFzk=v{+$l%nJ3--DTCiLHqHw&bLG@?GZqaF;W$PF=GMQ8WQ>~JK6lyPgbAJ zlhaj)Cp06U{a;qrU#F1ry*t1A`@aCXVv-&0g4b#C$y;-NPyAouygzzWE< zxOhliZQciyqU0#?pCNifmYetflSh?6NJW2i9LT&`!7%=9+;#Bae)QI1wr_dOBQuM3 z90r3OSkg?yogVZf(Xu^HoFbw>dwt%Cp!~pT*A0^ci(k7-2+<*N^Y>!st1$7}#|?N^ zkRM?hTS<8M#(W++ORkG%q|74<6<;XclfkMY%Agb<`WC~8A2SM3(U{KSmww-uFfL3K z31u6Jy|vNh_vY^cGN=@|bMp29xUfe>Rf35)$x}E@sauXFMxj_WAfn129e4J*zPGXy zc=lx8yawcu+v&~s)M-S-m-hlEomjkUmzwu*H@|)`j2PI^#pQoSD5g11{6ilD?#S;s z%gW0DpJ5lkHpX-a31YhFJZ?TPW*8{46Z%8(Id=^Z;ffTvkhr9=w_paui|@ip*0tmf z#iTv!9OoJ-%YEYmEoU9Ta3NOS59l^Xi9<7|awzY6>*CO-8XO-3kWN=O2<5*6-6;MF zgI2XyQkrE72?`$owee+)%=;LXXlbXICAaVdJGX0!>O`$-=4#ex%WT5|NuK!0#(hhX z?X#N=ua9Xv+`9(CMrrszdZ_8%*cc0Ot1`6GCAq!|o`p4bUuxmaLSi|6@Mhg+T!nO; zwhOqIC))Bm+TQ~u*#ea!TIBy9i&Y^$KQj|X{s?JysTaGIU+waP+}%tn`xa%R*=k`t zpsh{Zo_OA+pY>q1OOHF)E{D4rU*iDP0ThC%sr|}t0ay;EgCB;}U>9ps1Jr*7rxsU>nEw2^ImSoQqd%-dV{qoU>iK5X7 z&2^ib@blH zWZzHPR3xN4g*7KXA0eIB;rDj5NR-!c>#KAg`+&+Z9F*kuTyfW+f|qX>t4Ol9DF@3| z@=vW~Z?$dy4&AbUy_Yy#nav*_)FpM2HQ_sOSP$7s(7GhVbb{*ZK+?l@FG_q$F~+^0 zk{#vyp2WF;Eo4O_I+x|=BqU>+yIb%Evb`0-GI4R-agy7EE$^FR`8u$y)%o+L$kR44 z!0Tgo`KWtc<#jwiZs?`5b_Pv}sNLuYqoJ6DV0IQKk!(`Z zdCB@2w7o)6vxu2bAktM+mnkZ2-M$HbD!*9*LjCoUYBEgt z%Z|{L|NgcHnJZd)X4#49uVjTWG;6tVyB8tw_V=ehhp#n~G}j^ROFvc&6P|NhA}u*Q ze4Q%r7O2KvUR(6(_}aGuLK^jSbBr~)E|T*!buHCs-_r|#WRtn&w$@^@@ELaI6sOz@ zmN1xP{Q*VpUzBiF3nO;z8Ol(>tC0I+XbH`rz7!1#Qg646OpG(vF#e{kj6Icy4@y{p zItb zG>;t~d5^t1fE`VXE#E)eIk;`&XVFDvGXV+Q`EC>QUsY`_j<&H6xA6TYR(@%#Zbw(n z;(Jp5+vA~C#=>d%pHH7p`QW<_x#=T)%pc*W@+malAia;OATnC>9`t9_d@ts1ux@8e zx+=7OT#)qXT-kVzyH5D-+IVjCi<(2extR{sp``fc$Rf%d;yvM(5ui z6Y^&{argba!FO&>8|=vUsu~2pX+SUR(pmCW5v)ASIOjy#)L03ke@&{SOE@SOW3ugF z$?UF})yU@Fzr5ox$BX%_V~S1DOQd?Jz4>%eLc zw&wzOQiqn{@uFJq2WV84ua=JlMttDOqSh4NKW4OXt(@19ul{sZq$b|hX5BcC19KM)gHhgm z*xUZ_B58&r-dUok>kL-CJs<){kfC?7kJQ1(dtqxK8b75NQ+}^ZjzQK!SJ{qVDeP+=Ajx$BlojUAR4Gywhzvb~h zMVN;^)*5;J(!YMEBRyK9>Q2lj!_W0>R zK%^VX*Bfw?B}0EM47uiHFIqopGxc13lfCROKn=y{+RHJDEO)y!n^ry!pf&vgj;fL? zoLEu|tkiGXqmcJamzKw_dDS?Mi=LA7lZNlyg^=^h7>R-VX0T3+Bc4iLh&gnq)sYx!L9wzukph^v7Xzc2{@)zD z-nw#YAGAej34CAztq@nxdHO!RHUb}*c91-85vQ4e3UwvnZ_yM7i;yJFXIln-u|pX| zPS&pf1U?i@zR@IrLEZ480;NDN4CaNubBnZ7%gB0DWXk0gmZB~<*@;u(kfUQsQKMMD zFNjkbK7c}9jFqZ=j%-2pR49gf8?qELi;Jaw71e$Z2mA`){i1Y7AalS9`Nok%blC;p zt#%v{dE$iM-GSEisNa4dJrUKqd9(l=GOeuNMQ3LEfbny!QuuMU*Ins7Kk!mOUWq2{ zC#WExJ-7+}n00%SWCaQUt|XZl(4b!&`d#K8IpM^%%=;g|Lp+5#x&i|KBy~1eEEq3R z88qj18WH@E$Y#I}AMRv%u4KEZpfA@G`L&_iM#o_vGCR=rztMll>}1?Lsk)5Z;g9 z@@-)L3^1f{nDH!-{Qken%1ZdxU}Yg#f^gP(J05grMc4D}SBjABl}R@ZM2k=Hm2@xv zK3`WI7B8=HQ2fZNU|7w)9TA-e%g++StY`PbEA+M*FlgBo7I**^Sb28+9bYz%0kQRh zAb+xQ1DO`D<|>yj|MBwNlkD?fD;wSs)nK4if%&f98^pv=0p>!lWn)wU$Q=h{DrnR9 zs&Rd)Lh&VG>EuIFbGsyOV7%j}wC-ST;~+#fjt4o6bNIj#n@iVDtuQMpl8{d?3hl;o zcYr7TmwoeU$no)4AG_VKVJA)QS)jv;6BiyNyte68h!Ai79#Vvki;4dOs(+Al=?rfx zK~^R;264eeOjDrs{#X&*=;2{j=mI+wjE61{zX*)Qw6&8TbA|5y{PJ9r;k&EgyyUT5 zX06i8hmhJyp8%mB5fp2iU*Nk*lMoYO>Vs0jlD3;i7%Y?yz00b$rI%&PWzCYNv{s^{ zOu9UBS+q>v1XX*jZg|Chmmj=~ule-bROdntBQbZfmIHlN`s3I0P)QFZ8*4+y9whg1 zueHE0**^GK!Z$g>YjqhbG1B~XA_p1upf7OKNM275r+bM7=~NPd@#@$A0f6wuOzkC2 zf)@3vn5a+m?33cNe@Py@oenp^M`Z@85&ntFjEs}YHVqt zD?OJfXifD9U8%q1$@cuKdwB)JcpUskT8>p3%SATk7j_F27#(UTW(nIw^dmv_i!L-~ zy2Zfs>yutSsY28eUD(P6gNcfng)snYPjHdSjc@O@#z$c5WD7tE<^Q95FdxErQ9$j! zKP}tKvaT|q^N=q1E$=v)2_0>JH`uh?7!#=wR9)FwOsU*ijwH>D&BQ1#A8$(Ep?{|H zf0>E={W-7S1kx`kl)>QX(H7S3gK{^38v4-s2zEOdTeD2>g2Tu!1@ZZS$^COvI>X8#jAX09m)<` z9Z`Roq&juKi9{yNm9LCB?e*k}tqY(_B2mcV(sh#~3qfEKN6*I88Xn+biXLLmUvA0DEq9mc1L;kN$_l3}c4-Yy@;r6pIW2l;)s7ho7u(@ov?=|iC9ILmyXLg+=`?iwtrc|&}@OWVQR zYZ=q~V!gAxne|yHOiCqhcnQ_xa>^z76Bi=;D=$#2RZ45HE^vv*R75C0_|TRxEy({9 zN2-KGnnuzH*7?1ar}9AEY05BrQfEf*0G!1Oiaqiz>2omEy}@4hPRbCQ&RXI|>-j?I zNujp`jl~+;-M^iFSR+StzP&s-89u%ajOe+>+6x#76}b`izWqIJHoOxaC_1aM>8<;B z#Q2^>*!#f?)&#KL&RU5(1ER)Q9#Vj48$w;uiWB!q-ZxSaHqGGkIe4dejw3*XC47?b ze(+H)6jtQvJjL$n9+*;7!A^)f5fpgMeE7)^Oda#}-VMEO1qS%$lRpXAE!HWHcL8l5B?zS+pCr1q6Wi+0oy4p>(-oMa1)0q&T=_kq($2_mZufsLNblB|B_Vd8l zNdh;TmzC6Aja3JGr#H;hX0iNf67&$2k$ay387J zw~w8j{QX7z0~U5A<5Yrw&;97FhD}P7h1!+3Zqz-#LNV7BOLH6BtXOg@XtI;5h1HFP zm0|D7HN8Q57%(58bxu@{$vB9smJ%C~hp2M$G%;{CAhKEsk+p&?&9}?VkAaT{)_#Xu{BO@#>E=RG!qL;d-;g-Ni2HZr-mEOTiL z6Js^`S5|}TPY2V$z(<i%d z_{A&I_Tg=s+_*~w8uL{05OG{Q*+r?}rqgZ1*1E#zX%<2rBvo&aE$cEJ7ug(9jrIQy z)rhFMq9SnWN;ff^FJ>V+a4;i`=;TLmCBq_}UY9-wlO7!WKdQdPpQ-=lk`N{&0*cMBDw7r-~~@(Cu%B7YxQ^V7=*Kp`HApjcf)|K~IU(fA$xo zX1fcM=VoZvz7K@%TOuiA0&@n4X-vvFHut2-E2UOc2pqyXM%X+J33Wpo|8SySsJMx}RA!{5k!d9vVe<4I(^3Wn8nzJWecvi+ zt+)EEhD5)jn&|3IhQX(*7ZennHx};7N!IDV%^jPS03QCA3Jm57^&BDkCu%6=ueJRSU zJ-ZIHB=U;kkC0w!tjg{cAe4vcN!Fjj;c#J&3OvM6`k`5!r}g>jBEpdUp}TbQ)*r)e z|KEpPtW16B3LMaXRR&b&rF>rDGH zc3Evg=zHA!E%lgFP~u*v!ZAVoOTO&oxzd)SK;&*!R>!fF6FJK$@GX62TcnW$FKaKU zn?Vzjc7*ogKd#IKkvnP#HXrn)n!r-DV_{y_exLK78A>zvaj#tZ^%pCWP9ti2d6ls{ z9^*+Xvs$$jXy^`<3ZIyq-0_cy4@NGgI{%45G(IG!oYGIR4Y_wMWN)tZnf7kDW2wE> z0>LQQ^KV8{EB?@d36Q#+KBslZJCCB>t>k&hq;)-*2?aEU+yiD~cF-36s-C_clbXCtg)UUD)omJ-V26@cY(_ zNsl@^LaB98Y3W!^MqYB_6FqL$ZU``reAI;293nC*;5w2~8c z9Et!r=$a?7ncvzPOP&&!@B967{Hck_rLj_LjN9vQkebv>eH4#Kneb$?T~DH@B5((4 zTc#4{{lz})Gt@5COsN}jmAj*D=0wubTV^$d9iBkdDIZ%8;05f50D&(^$50qy{UT4` zjQkI|c3>7!&dtZvffr%Yy%hYHYimTC9gNpIgoL;)Q2EGI6K_rE$9$06prwTFb5Hq= z=r?rRTsfmNqQX+{kP3(=RU{mc)Rn?DN1^40wNmV#3^Gz%z>Ote$UWZP1_~`?M1w*3 zDJ5uh+vj_}+h%Qr$Cqv-BqWp`B`oFdPf>T)cI;oOiW_wMR$8Mrg-2v;ZPox$$@e^+ zgwrY6_Iid|{yWZ>&aS$xeO++IBOpW;V^5?1zL*z?=V~=0>-dxNfueU^>DO@_0Y(>P zHe5-b-0x*7Yi1pTw!oUHS$0er@0e4*Usu*KgIM_dvQY zo-kf+7Z!3Z_11jH2X2ntSYd~kO0{pZbWmo-#HTy=RxB@`K0EoAr^XT3$f*=cu&c88 zF2$t40@P8wAO6RvJKu-LVWj-q86KWf_YjjWs>p=)%SDFgc&>^J=oz+^+ZzCvBZk$J zy~-v~%)Xi%Nd;r>e@RXki?ia8$g7S!Fb{0ManR?(aRO zELU&BBh-$tokLM}wmP%NT%&jWpU+?HwfF96<@&^cN2`sA&`0^aj^6F3_eFYt7z^0l zNlD>3HpPqoWS{d_$RQ^JBYTG?JTXz;};1 zrST3J2+%}tSo7{5Us`^k^X3NXI_GIkAOu4nGYAVs#lMt!y!D7j?^QPKmM_1xz1~=Y z^qsSx9TYdJM^4kG;|2B7WSjSXeF#kd`?7njC**CgS9n&|sb`g$A068cbM3VkmTqJP zUvUW^K+#d6I~|bRKghwFjZtLm@p_M;EB~~E%ZeuPs!5TV%@6IqSHo3eat??Jvz@&e zO)07GM!!d%vP)@raILrkawm%O;^ntB*$yASt;}G_Lp77XD!#HaaZ6bJM`)ztm0bSQ zwpaQuQtQoPke#%!UE(RIhLTT;I(0n$3+m&*d~Mdz+p#D-2L3;J6$G9xVVfL2jO{a2PyMAi9Jn<-E*kJ{^ZnOq0ROa3ObisH?Ko_*qT_ zg7p{$9Q!u}P^rIY?fK8lGOrPGkYrwh%X?`nltrFoq6sR`MoS8l8JB*Ql|}h?PM|m| zvF?<*#x<{*YI8zQ{CVAo2dgbBvQAgQfsR&6R)O!@um({2 zyF9TwUSa3=Ic|c#qxr0Ecn-UX`^YdZ?stKUaIdz$Zy0;+TdT+1Z0e!P>K-2}_>ey; z&a?~gP_}o^`X^r-bBRwgW7bPf`2$<*O9fPvs;{9ffQf#LSJu@%C&iK}ZIZ(IQy+X%9-12@rwo|X*86W*@W1-8?K_}Sn4TYw0f#EzuFMj+`Zpac2 ze!spiy@QuF^yB{y_sxHGN}h4t{e1jhX0y?RS@C-ILlC^NO;GRjinz+z*tTZO-{F@* znkA`>aU0##VIC}S0T+8X`Ma$GE)ghh{5j6X-q(%Z!cHqXLqWD%h-KY9`U^LdH2?la|$!F>6d0GUgVK7 zIJB%?g!Z8om_~=|PkT}Kg zx&2Hf1A`(P|CK9-ch5pF+DA^~u&NuFudo4(YL!vkLT(J$!cJtqP^T@y_fAjcp@B^T z#gxS{$(T#d{FOr)5zyYs$;hp@)II3tAU&RVQAJK@ZF|4y>KgtlfIsiUMXVH|DCEimTQUVyR}y0AW3MJwiE zV0a~`B!m8vX{_{ECk}fehGwbjs1cp`A^QE)l2(9lQjep63%+Ay@ZwknHIh^8^4n}R ze=eot?j!JEv#TrY7SY)V^*c<6Py9jUpYFDNq3<$x%c83irG4f*wX}l;cW2y)g&(Av z&2HYj8NxqyxdMD!6n*#PWBcB6m_*RUYzZ}%(vUx40?W75_TOi9qRZD=-ly0EqKLH) zHc!0#OOeaVM|$R1n_H-SF~^gdq=t$u+d7!Ig7x&ZUxq#eUQTm{SH4&12z~|z=WGiw zWNi8ZVOrpW+#7H%qc4*yB&?xwJSwZd6Edw|*`i*{xA0q%7v|+gh)_GQTc{lGtngje zc^!Jt6`kPb+yA}2}@*^`r1|x8l5a< zNy5yF|GrZ8^Tlx|1n5}y1$%4TPf7=Qjr^=8*oGeNRd-@#a&3dNKSSmW|r{q}ZrpEZ^ zPi{5e`0iT_~%hW7g@z z>ZX$X;SfbA5LDDPoSh4g@GVqm!(l?*+AlpxQ(&z3;Gc4t`r^M;RZiixQ$pW)IYbSZvF_qtFCI>Ss8e@^awXC> z+g+zVulxwn^gnZO=Y@cK`2~4QqFP zS}ICSbG6h|l5#3NdD$wFdkgOpr#&Y1{p{mL;=@(O-VP`^g_47a3fF#pj*{+*(D2;n zGHhD2CIb^=ZFA~NcDT=gPxNGrKOU@>onmMs~k*egRU5~bQGu#gRAZY{hgbt z_dVP;RdG~LYN~U3Yid`sI*r*jppUEJwbiIYECxE&*X?M}uSZC9p&PlRY>ELD-T;E+ zxwikaOLcKcUucfd{k*F+rV5gMxLv4*XIA}lqUF%^Uh*mFP)c1W25^z-7B%y=+J&FZ zW<&>aK$kLdOUHigF?M6T$*nFlJ}*36=OaRfLUYim{m}*9WX7abi-d9cLZ&!M%|Nux zdMi_3XtN1k(--1%X_Aqt*xVa!fuFLlm^@mffw+G$-vz#8%*qN0ygISwS>!c6CJ=iP zFt`sdxh0KOgQ3gcraY6-@{T6iZ(y3f6Mt9vP_FHap*i4Z3)kU}zC3WaBbblRq|`Im zH6P+{Yo1~;k`&I>dGajAkjkC4$4%`cAGqa@L=P6;JELTz15bdMkIR%i^mjWQF#GMq z#&}g1^x?Q%tt4BZcwgnKLKRY2ACR{O3hMf2Ht-;Hl>CM26^YN4f_-Cl*OPJfYfs)e zas3>Zap!Z-sCBn2W3sP3Yd@bbyW&t0(z>olZxuLreY9G3q)XpaJJKdPr-a|(a?-4M zoYk~+?GQ&iz`YG=nqi+*m<*ldQ0{M`Vp`BdS`%?={_sVQ)ArvFQY(m13ZmxamQ!e- z$H8y=yTF9^a!gIV3@*lUqBKJ~ckX$fPyXhsS0r=Unq@*M)OCCxv?n?Z|9Fm*odZM0 zF$Ny##iESG_q_mvC5YRMuF(;k3=E&cb;H_WVua$~J4M4rsI* zfOa2Ql=#H!b^N)pRBW(wk_GwYK+wT6@^LS$cKGw>C)<>a2%-N{K_4r~K|w*m^2rcK z3G>SDPzUqdck$bwn&0^8;&t;#q-zM9s*uka?d3Piw%6G;d4isVnUvMrdRmVQ4lbu3 zzBL2F(6xVI2=&WM-L3-t1%|&DT>yh4OspbroP)5EPB%;rr*(cObzEB!cv|$UVkWx4 zxvEX*nXa5tM-0(`N_u2o^z5N=l4I8ZsM393=Yb>VgS%$(Nf3!~Ote8k1;L@5oW_hn zSGG1+3HE?u-LEC|$)<+8bO)vp+$J64J(QBN$KE=2rMpPoS}MvaK^oXa*LCt2`!yNe zGmBXTC5g>1DUVEN2RdWbGW~Jm-)k&1Q+@QqlwCAWFfjb@(S5S>X21RSMi+t#yctk& zCg(B)(vv3_)P1n|5p5{$8sPHCW53Z$(g`uNnoiRn!8d^#(j^i`z943wedR>AHgri@7Fdu z9GP^CC7tplp?$i>o4i?g*8yl8@dVuI8m(nfZNgZ0PE>aXFnU z@UMohXmb2Ogr)Y4{SY*vy@c#gZok1eRIoFiz0{#_*~_Tv;UuUOG@&W|w=4GUi)^|PmF-)*#ImVb0; zaYRQXjXweu6SMCM6h$eUQs)>L`k#+AO3!<~VMNTk;}VK>5~igKWq202`-h4<*nWvF zJd6~~Fvccp0?gTd*vW@whf)= zU6?*^^a4~J^hbYw{e7hWIpL*KEXvn#!8r2;i#+$cg+m%fjzQnvM8lbV7l$=LOAjfN zRU+vj)mbXsc=^2%1<3oX0G9uEyyoDMAK$JCZ0Ah{u~iqV-NYKj6#Izq@A0`L_>g@! zJeoCxIP<{13X{9rZJp73+m4f7=h*7p$8)T){T*bduegXD{5w{K8*MWRK zzwwn|0w=mQsk|JDo&Rc(O-f&(+NK57+;kv#SW1#Za^w+b`C3~4Mm565i%(Lj+~ii^ zY6NgzEm#XH@dFJ$R0KYq<0*3>cy$zO6nyn~u&-;r@CEH-20iaIrSGHrkY9cwrO)-r zGAUPEOAG!i;L*mkwXWvtHdtqq#YlTtjnGJVI79a{+RyQs>=MhF(M|ysa#uICFOpf{ z>Z9G}&CBYvsIYjEw9XiVi;sXnP*->(?Hu1-7XXB|sCXXk)c0u)Qw}42o#>Fge}BED z>Sa7kLhh&hmC-7abD~LNsAmbY4$}|TbGHJQBXyqq?k7R zZgv}Rbs=PSRs*jw|F1vm#lMSW?b20^jU0xXi`((*6B?8gEfY_fzc5Q;#pCfM!)E59 zYhSvYbeq!)#P3&xq!vG4Cex3CCUzH1i4i6a25)>PJ=NbZ$*5*kcz?L+c2e2j|7?QO zYn@k)VH}t#4FBY<%m58w%PTpAf;~*Qu)X>irDC1(HnQIy36I2ixGSXTdQ zw)~BczMU1zbM>d7v*1WHZ>g!A+t?Fb`uua5>bx;sCGlc4T zxI#b3;uX6*C5ealm5F%p6DDJ@tx$J6tBa>Z*7wY=92YT*TQ32*j_ru;L5J;fyTDO- zX)_h378@SE8w|=AmL0f1kEwM3%)zl?)?S6d@#~mhda{<=A7gYk!N{?AwR9lf%gJTf z<7LtzBiGbskv&@n@#il0a3z97oukaELOt^~7l0Lu{J+DW?9q6|7wNN~L zf3l?{bZ>@8TP=PflIt#6D1C{l`dR#y-D8@)U6Qu(;mHeh)eJOM^Lf$s-zyj+GPHmv(4D+=^&)AJ^~ssvGw|$R>Z~&I4g{_sGM}o z>!iH3`G1brpIj{Xc}z3*O0`&}So^0s4P&J!>Hl*Vr|DQUl!3F8xiN@v7- z+y}g>)_U>VYsO>P5h<%b8N&gvaMT@JI^5cCNVECPkAkYfK~Sv?0DuJV5~ttNI~O38F*Dl#qegx{WhcJWU6`^r+LsLu-$j#&HFSn$3|4-r`^V zp*{TZRu1~|zMwG^-m9YMtB~LYd<}gP2a>QY-tdrz$Y!_sSZ7bh7N<+nwtJ~7gqB>N zJESYu0EhhfOS9=;jAUzjqQyP4ZL`t*tY9}n)cj`t6N?_KLetH-)pX(@ACb8H@FW|% z6K;9%$8z}_q?WnZY-s}@$@R+lt1;m`Tm?d}BgrYZ(>;*2A8)fKKSk?AbZKzfca5aU zThxqnv%Y*2uKJ=IiXhBYh^7qy(vmw7xw|fqAk-bTQ0}B(W2B>7#%MIx(x-XtA0boYG*E!er zFk6%9X>j2b4vs)X@cB$MIhV<-R9CJHQZ`3m;#L)Zgjr0H-m^9a{-bsm41*)XEgsvo z#)jW%*>!7K47<+e7|&X=@+~ITqtGM4qcGfTYY{h6Y+iLR!lIvVyg4E?_nE(oMsBe^ zh9jCU`OqZIW6s@rc=2y_;73P&)?RhpeG6T?dh~9ptlzWhoYuGt^uzV=U<+#88sw{c zGq6V{X=-OUsT-xLi0&{d5}?!R;GnuKFE|6k+2c^U^l|(HchLqFQyuDG2ug0uJE@6A zO9;3ujvY?y{3KG3V#Uw=SgKrI>BoRUx6$C>7S$4NdwH`5_H$hUgU@eHFD0NBxs+bM z53%-q+?F+fT$H6QYpbn3ypHKllXR!Wm*d_o3f^Wpv2n$s#e5ewV)5S&Ku)HBqM}7J zbg}lZ;0N*r`n$9ZCCfMBlbO#|jDsW$RncyJ8MMP(O#$!mWXXUf;WQeCJo01s99u=Bw)4`rCEduB+UTJ-l4?ZG409rZSq-mV5fn{NiPsg-40lD|F#|d0y z3hrl0L(E+a9q~wl#?hLsNSJq6VUTjO{*!fLXUNRI8NzUBbM?KbU-TnD~iay(nt9rGv}Z6*N*Snku#g9 z6ImhpEr+28waxoDFY4RQ0+z7B0sCG>e)Z(Zt7=hY3!|kZk17v+to=o&%4V>-hNor?YM0-PZ-M<3 zM)SXveldocJ;jV=nk=^Mr`12p%-D@tC>8li`G2FG$ zZ0vExp?Ta$tVK>T=(pKDoHUhs4@B2tC8b%+4#Ix^(K1(%j5zz@?Xl1npU3A&+)qrKgZ4K8DQ`#!2Oh|f+>k$q zWDKt+>#u#J^lzJjRm2)tr<8-QSgIIEYE7G+d=t&VST7BirTh1Yg)&93WcQKZs+Bho z;Nti}Do@Z#M~a^ULAP9{u!WgAoP7!)+RUYWUXpczJoZ4x`8K_M108@D15t zg492MAIarzTH-Unzch5G zlP#&qyE$@q`-B^w2k&4IvhK5FM|GS33Pxcxhe)fHm7`9kGV zn8$|v%Sf11ykXdd87mKdx&(bW}`&6L1(B_%StkYnP~*^Ro(p9z?_R1XlA`0S&P z+FiuwX=qRMWx4GH3Epg->kJGgH-4N1K;-9}@0mCm)ecj+@?_E6A3~5l(~nco@(`s7 zPRSJrWold(&xgb5ElybF*qb@sy^{R^y|Kry9H<(deVbuW`|vn`xf)|4EYNcb7cWrVlol-qpEribcCyhoKU@uSbu*HVp&(VtH# zgf@hgxC9B(8eFR@k%UnN_glJ-@i9)&R?|bC0>R|=zlu^FrC~n4K})PicfoGfMq*&r zH%U&)RRNg|$=i1e^a_KQh>jF%1cDOK!H&PzCBkCL{*IUAjkV7xS1oE2+~$9iwQ|O( zfl0kq!hb5B^^QWDz%9BpvU+&sn{{q9S|h!7UondXw=@}Yb2rU@>FerUv2iK4+HwEQ z8+a#wP-UT71Myi;e<&F0tTH%Tkmce{R#lS#YWK_k%bP+4x7nj%zw;FmOCB}bW`0RF zwG=69pcS?rx$Cy(+&g-QC3$||IE}0}1XqAJ=L;S_8)Qvm9_->P3imSHFO)Ti6mlSioi^Ut^FN`a(F>0~%zR&Kh3%E<$_iYbV)j zCZD@#sprGx8Z6N=pA-^g+_^iVZ#dbkjSZbB>O|A1z(Y#&{CmhNY$x5X%+#9H4@C+y z>uSp{z)Ytvx_G%hu~5|Bl4=U5AtK=+1F%my+FNM5OhxFuEP1LOvg0B8Y>-=@=G{d*Vf9)vYy8=)@ z-(d=4s&Vwy)g%cpjs3=XW`-H&)*H?p+*@5G)e5ynu*g#e-bp7QwhS8WVr7}0w8kVr zmd*aF@glY^K38uTavlRF5>`KJJsBNk*6}p(KK2h@QT0#}VXo*{?7DWazg2_W|Jzx! z$o>K1l=}K8WCPxXk9lEy&as_mzCRTbF<$#~ z3m5p&$sMb&KZu5w-qMPSd4Zpdg0!(GgZ|opO>h#$*Y1L9q~p{)H=oUQ0(VluSNOJ0 zL^DB>V45GeB!+hsroQmVI>UYjbDIZnoE_PG2uKMRya$&f)@xTGs3ns(JJDQddH z{&Tp5PWkiKox&;BXh2Ctd(|0}zmr?NeUQ)E{-CUB*W?@r2gXVgV^vbb3jzBt<*gT} zIKQi$^r|E8&SA!-7Os4KGO&zkokuEz7Yu_}uk#GKiDo5uwMKyTGMBeoPQ%-p2p}F7) zH=-^50oH~HyTr)wsvX#5{{OQ8ODD18yPQJ(ZJa4$TU%sssA@?ipQF)(v1tcqx44GW zdXbqg%^B5RP8+xZjg4`G>0g77AHZ94kh@8{`Z>k%XD*Fb)IR7uiH@B*xNt~r*-t+9D8S3So7V<|s3`BQB$cV64iB*h&-XY_Fi6*QdG-PC30hhd{ z=T-^pa_IimktQEh>m}pT)zJo0em9PMYFXEa3Igtw9zJV7Z5!=Kymj5w=e|J-u*`gE zuw*PvTWh75{cN;+vo}@J4BgRkU;`P*Dpbh^pXJLK9(WRwR^gFVdpKSv|EGsM0lD?iF^tJ%?0x7*R(WGI z#_;xS{=8{9QR}_e6j*;Wrdk0q!|&EbgRKMD(Wf*f z^Z%8g{n`fkm48(-(A#h^&UeQ3AWhLEW4NR8)~X*_OnH~O_=5D*67Q2|B{^S!0?m7l zu92S3+&D#Q<$#y#78UI7vNr+Z$!4`;U;ye5wP-1p^5T=dr`aBf|EwhV`gnKcTw^x& zzc2o?`>UJGkGH(b0)r{n4&sjv2I$DfFobf52upR}%9$|tbEdB$zAaz!e2<=df@jI{ zUgLp)yn)R^n4p=fxTam+=)R3XvkCpR-CVGQ82_leeO^wmfePkulEzX27~bhIZLWXW zN)ZdMofQJ$vc=(j(CCeDQ@aIEG=|ixTY|oe2+QOHxdaew)Mh}o6My;91W@z7-E`Zh z0Q2aUkBV2G<^g3%^RJ$^;$y~;SsvJ;00Ed^4hMG&zIcYN&J5o$?Cv`LjTpwIGTXS+ z1(+U6ZC5*ZsImzSU1(9z`RI7v^p{__dWgh}kB)PeH~;H{6`SoCp2!r|o8Em?B}WfEUq*fMEu1W5EwH)I#%XwH@(oiJ=qEXMxH6 zcsVszd1|bb>ui^oTq$5)-r)N+=NTBDNZZ?GIT80HH%R?O?gnr4@*yZJ9~q`!C1yg+%nDN>-3tncr}+21i-^Dj)dQk&oE=C zh%Z>Je%?*J`(D?EPm?L(H6!yVkw_LJh;LUFyyarBc;!gQf;`ADk7l2A^9`3x6NkTLev9mn6d%=7 zf*S~t2V59_-@WehQ0WQw`h36j7e?mO)BAqT{T_sUvPJewg%(@Jjn}LsL>yvC#u8N@ z{9)9j2u7jr)z*3h3fo)(Jb>#MKzsIClaY(1Pt_~?FBxmhC|kuU>j5e>)+M<06q@uz zM~Tw^vTy9q#X_!%?X6UeM>+Y`qAaeNI6xk8d3S_y@Vd_>T#XXym+-&89vfRUwfQ~= zHdSXl+rsvXy>A0sO+!Atb{p&et02t(Y7`Sv$HlSh);*iM4+I+~vU;$(qnru~U2Z3-@8s``9K3?_SN}_KHH*G=+)+9tsdsY00AHNP|@m~JdGQVz=_D2V=@Jn<%M^-obl zZbO6YJx6q$N}B!xSic5tY<}NBhW-1AnBq%ac_1^cGiz{O98eNx7@1N+eNd^=O3w1z?P&imC3( z7i(trv-_^avee$tkbJJ0Jx~50rz%USNvx2v`j;>`eAEQpQfZC7d-xriq3>RFhJhi5 z^EfIiKB_PrtaY|E+@nfLEPp})BLeh*=;GA4>ba~v!5B`k`qJOA)#=q$0?Icm1`%em zr4wE#>@#OQa|yl0I0%a`sKi^~nax)OOJe%3onw#bGcq!xF>K}2?dtASEh0I^<^^H zk^gZzRPyqQqOD4!pjVxgmGWagYiV^}emBMwj8mv78M}#FOU)i#W1QG*>y2K^A~G;A z+&X5V8LqYw-LZVz!CpIV+Z|c1TJ@z-9QmM1QuiAE@px+v2+JI7=s0rg1fHZqOT^XE zLMnfIQa7~J9v22fK9Kt+4VbE98m!yxigfdT(b_=CtIf8G_Jo#-2%)odzx2NyX|$5B zp$=H$;}WpZ7>;nycx?7V8A^IMK-cbK$O(p5^IaxvCxE^wUK+E0lrhM48fK}eonMsa ze!i&gw`XTo=tD1$zz$|poWfG{{zS`j3|G@7xw$-vpmulE6OlAlsHQ zDU^&w&D2MeKl{|@sD$OB>qGtD8tL=LsZh!*it|$KE>rjum8#2`6iYxmkyy!TP786- z`O>bm%844pplzZ5OQ)@uINO>h9tzgEsV&|$P7>=|1UlHx^luQr7T)6c7gU@Q#PklF z5oOKZ3O7%ZWC=n*c0N7k$jEi+x$r&^yDtQVolf zS2)#QY$2$Ldc5MJuP#wImz~3rha6mOCN~{C#~me6+`Q++A!UjNzFt=OrT40k+N1<~ zOm_=617K3eBGFfuo-1tlhqXBDFRa=Ghre^GbF(!ZnL&PO&6kyT44qZswYaZmPYTp8e3&%N)KGR4GvwA!IN&qge>w+54V30_T8Xjr z@y%i#Mi-dymRwHqp`#xY#QRvmaQgS3#AMSK9mW!Y1etJGilfzwnjng5G^ZQP&5P3d zx&l@VME+#E5uF;awJW9hLduE4cTAQ389YH6+2^Vng5;{pz6=e$5%7XOBdnXJ(O%;x zLUqbcSGnf5M-9h;qgO!?bfi!An|@%xN@o~QVSYnB{P#dvSo6Bc9TiKW@zS?6O;F=CJ1kMO4><3ZKp{>{wd)}fS#c`CcW7C3cG4+nv zR%SLh71xHc=Y)e)otIB6in$i>%Td8&54rW-lOW+fK^b;iooKclHy)2Ls&7!q zkj`&Ne=e6k_G3yCZtaU&l(haG_BzkTk@(sU0(;GI9&H5ZO<7aF>UI|7HZT>&TRYob z>BO52A7$sTb^y*zu=#|8wW7tU>2J&@2$`mdEXY@!^`YvPpEw%`>m){9`k%sy`4HxJ zR{%+oiI`d7QFs>LRo?di2hDIbYpC^lQXScp_(oNe-$R$M$~uJ$sW&xIIwS9dE1B3z z8&NP=Q3>PayBf2UsGA=N#G5zOmm!#t&nh&Wg4mOM-Y@~S&qy6Z*rD|~ZZrtWOZd|5 zB-^k}mFKm5nSHuJ1~eIN7BiaJzbEpx-cOe;qPNEjX%#0TtRZ$wbm2`gw$xPx2u(p&3r)06O)rc%I$RkxyeI1^Y(q9&J>u&ZreM!gTRy z-FoO61^vtOZ|}HaE}JtNiDycy|5|pZ%-#q;JNQW}GA^(IJAF&9Vk$I_Rw}~e`9x_W z`L{^%nD{EJFTsE0l&9dTQXT)w!6f4DTar?bWtO{|(#Dx^z(4c}Wm*cX#`Qzr&ECO5Ii za>>h?ntsCFF5g2Zn?8%xDAqc9|IVnXKrm&8zsK@F$GNvnylb}CmaZvpbU#~Hx*{;; zOwXY8W)|>pNq|dGzf>=~al&!SL(Q!@ot=>(HkTUK{JOEpt`6N+O%PDgVriWbVk3V8 z^)_`-Ex4-0*o3xr$V+S016^i4QfI2Y2AqH3F?0DUVKtiD#)#(YRYWUMUuZMT7yOGW zoe29}*>gK`mA9!Js?y>e?X=tfVF^~=BhIveE?A8`8Kh*D+fr-=h&sx>F^fl=!)MsV zi_1Y08sb-D2JTJUCOy$@=Qjokpk*uu*E8=;)U^~>Xyn=>>s?QoU-7Vu6(s z7+{H&n)_j>G$mbO+=@W5%|y<$B|!V;R ziD-Oe?3ZCUMHK?ADbyd&d&;%_n|W4;$<*iicd2Od)DHATN&;jT?&s&`@O8bc>JgwO*CRCQhUM@(4Zz0Zc;x`jcZ0x0nj@CkLeA)cFne%w$c2(Q&e+6}gZ7IGaO z;L#-wU|P_WJnvhy!^tE@RD7wJPpA?mZqZ#}c(5)DpD8iPw?%CMPI=`Y?DCX_jb+{7 zvgQPxYAWGQHucoP_S8qQt~_3lHvFV3c1b6071Xyn7$y8+^q$4opVFof9xe?q2$F=6 z$EB(0Ib!_+b2-RBqu_*O~ON2|Bld8zuckmKl&$ zqW;xggRXi*{9-z##ZL20SqxD#CG(yU~e9 zkAvdR_mV$=^j?}dz1BB< zjrIJy?7#fhyjMXmhk#K7x9g^=S%lA9T&Sg{UU~mbNgl6Gk^k-Q9U$ki9dgP+P%?^LFJ zviLM?resXi)Jqz0f=nyt{WWWiINu4X8n!dd$k;%Gdp+3|RMDatkIXjx6lvyP9bPBN z3vWZrNb+Y-E^Gnw1_6%X^##rknxXnXAwz&34`I`yRTVnN8!et%!nhx$yOQ=MP}gJS z!Ae>XxgZ0gk6OYt@$O@nYUc4-^*f{ghrRcVYHDk{hCOiijecthWzrG*e zJI3>laj#!n9m3vg?={yo=XG6k2@MJYB>JH8@{@UZT8)A|9jbeWM=4Q*>E) z>*@o?ACkx&#PgWPj~DI+^eYuu_m&+&rpgdmtwU$#;);-K-b?1_nPx8f?-Q|>zgxK# z#oT(|sCzD+sE$IY*TvE?yv6SKBysrqga}7dT?SDE z`LM}Gkt&SFnTZSU^B(B1?WqILZ0m(@?9c&isaRu}-caoF#Z%-*j$<*gn2uR^eMS;@15LVPfH3_yaBt?sq6JaJoA)G7{;&FMO9@ zv@e~}wqDnLqx^od=bzun@!9#a=i7x+N9IyAGL{!*E0Sry-x|nX!EP>z2ig->e*P-; z?2;oju00_&IHuMxejfoz%Y1V-N{9!4^UarD;2m<3L8Mk@ zqcc&xPmq68Lo!NWvV%$0)v zQigb|QGV+O@Kj&S%)^;{s7G?~#$FW5$aD62mzs99Q=^uzFXbeW-PJLd&s^@PqnFr$ zrqjrejXi_&%QkbDQ*yQ|CnA>b^6>GL&X%1MI(zug)yRXr%;PglRcNBlx|*yOM2OD2 z1KDr`oiLG^?IQ|iC@~D`REplFWRi>OBG)7GlzeKV!lbOk5)HV0*k=5-0kRc% zen;TN)?G_+Fm%Ms>C=YdK>De)^|lrX%#Ji|$I;8>JPk&Q#*82~U@Y#bfECj;*Arve>^K z)!Bgv=BA1D=B~@KC9D_=?2fpKr@KR<{R<@*fFJzV{S)1tj|ass4VQP_2F=M^eFQ82 z%5Ey|aH(rzD-PT+mw#CL2!s6Tx~LwVukJ>AbQK&(E(FO4jGqmwT{)rZT`So4|GJN7Dl?wYubzTOn8U z^|(3V_F6MWgMg%wS-oq*Xz+On=~U>M=z7$0)$IHW?cW0|o*|dB^i!*5&B-*FQ~E~1 zAeNqCC?p)b$H6gna6mL^esiCwBH~rG*CtzjagNX~n^R2Rrl9mPdwcT@a33LrAD`ZkIXt`0wK~fRf71pts~b3 zg)M~n%>E>;_|Yy-I{pd1bbTAG5hi#vLpk+=2ldB0ndJu7`ZOmnC>YY!@_-VwblnA7 z3*K>^#unR{eI~&>UJ<1|)Ip|j>xghO%C_wd3Y&F>KL=*yqs@Geq(GeR9y*j)WceqXP1%Q^>rOB=O-oa`4=$yv@B^?=Yt9*0LROk%^IC20;#725j`lLZ9e$pdw zwcPGdo&972n-;x%X=y?H*YOPdoJK#_ZrWp{SgDE3<=pO4n=3mG5+|Ei8AfGYUrKu; za+WBuDUNy+m_e$6ZL_)3OkvE;Bn|z@Hv+UiX1TeyYwTu7A@)dGkgaWQ3Cl$FgT~N8-F%qUwN$!0qc~clQ@VlwAL>XYa#xYN8)4 z$1RUq5Q4Q`HRd-%|ZAM8Sa~l?XPZu!#fL35Y&}?RuAK3&6Yk@wCc6B>dMhi zMR}o1DxX*lN5Fleh6W~7r^6fmmrZyoEYW0m##y6hF9W?`M%!$V~E)%<5#ubxHg78_NY=xmCak=K1 zsVDmDyvy~7dsTLv)(|CZxV?mGbt+J0kap+bIR@VU)ts3_TBL)5+chcO@ZypXM4V_? z>J5+7+w>ioqCgU}Y}&5qVco~g+l4ie5+^3ciq_~FiWNbz_6Ye3yYjkpET2A%KsZ}? zL8aeJS|Fv}RmogV=KJq)V^!~eG{bzW7BC;$2Kvu?IC(!y+R=FJz8J1@C8ILut2+-@ zaV8Nhdu`Np5Bu%ID38Ljt5-uzV0h)!6GO%1>~*$f(P1XSuTbc7p|#!F?P5Oj@$L2* z|G2T=c-O=D4M3prMJJ~L)r!i9x^<6rM%{YOGJL9QM`WhF_EAjTO^M(fpcT2h8iGN3 zNuppB49djB_#LRt;TFRJUs}6REVQ)%k3ZDem71-2=ul4;`of$2C=Q#Vb_0l0!^=!& zGdr;oGLXa$ufQ@;uvd!kGu4q;V@l1xFM(W<}ohxE0KAO(N8Yp2Hcc zu9>~Vld~n#Crt<00 ztX~*=!y{0Dy`A#J_yl$d7kRH{I4LgjY zEmQ8f;L(}FRV~O7SrExzI3r|E9To4%sG$G?)O%G+c5eCKHlB z^oJTc?BkA&oQ2tC3(SDNG(O9w%+Nz6`aB}?AJY;uvyhd@+9{Qm3Cp9FTF|&5%^~V$ z8;P!?^d&FN&sRlgY-jc{K-^B-&dX*)qOuYf-!qpL=%@~sLMPDl|A$G(?79qyhD7uU zT}e@Log?v>%g=Pbv_^oaB-QAoG-7m-|DX+W@=|qwE)w|z;mNJ*f-$%f7a{Jk00sel zliDF=u6!1ShSpeS#yQ3;B0X~M7bnwCU;DW1Z-;Jr7I*+I%uk`K8Dm)Id zOWY(u$&bud`860k_{B&5{nG%zNglcIk3wA$TG&H!MZ7%EKRVV2c z9&pO7#VUS0uua`pL14D-xCsTwFhklJ&7@^Vhs$g%ipSjRObb40-SQ9Z@Ag=jV0N^fJ%{$9|hxquIwv7#ERknS|^sYpeo>J#}7mCefJ>AICQ zUf@y_t7j%MiEm|)cTQ-mAqn1V>^pU@BmNSozWPn2^1;Wvt(L9TRpP^tGrE7{N%5kl zYJoeCFw!%dTH>!d+^S>(b;9(iaa~lispU08TH-H+DqmFt54d0)?>tUg* zKDEdD@Li`p24}yl(z6>aTO0U|e_|*fNVl`E!;e(o+3&mwuqvW~UOMJog%9L3hJ#Q8 zktOEk6p(a!3J(_>raUk}gBMF&zfr4Iz2vBiu^T z#QnqQ*;5_IcD!`g)Q&&RY?e>{{dPN3QqZmSNQq?o+IttEi8eItWP7L+dD{;v2JcdT zfgd=OXaqHw&^N8-PCp@uRB z-7z#No|v~|e+SQ9egrfZ2DS~nZz?f2kwlh8M&HWTh)s!#yjdU3Lhvq5OvzS6=(w%= z0r8ld!T}2jt`w2%x&cTgeOaB|7EDSykQ_GOk#jg+hq?w64?V1XB77?c;P)l%*W97)1{8&*^mSr%({R zy{CD2dFpWesqL0Uj|Wr&vXHEfiP0+gdj^MP;F5yxpGt3M-9va!T;T>>cxGKLd1YQK ztXlE9hj@^djqdabIfcYD+HTtzNjTk2l?~o7L8hQOoU5UGKZ|+Qi7k2ZQoOqlE!>I) zi0e;|&M~DkDMe+nS9%7%ygLSZJnRit_r8@oiYO`bl7D2asP>{5F_jOUyiul;dAbB}0`-ORtNjsM{5T2XJBp+v2 zylI}6;oT2!J!SmYN6E)D z)CpLhy}T1GA4hA@GEf-2O@{Jk@b}1Q^T$)FL)X*nV3p6Uw2)VFiVQ4_dK(0hK_MkS zm0NO72_A*k>!KFpBowIKX(=Id7tafVFg7Ok7Ks|O1u(jJ4tZYK$HTW<8{UU0KkX<= z7zES~uLJF{>$reBNh7+_U_8uaqVCQ%_c;jG@=+!DaY}p1le=Ib>o`yhGGnrDzFZDy zXAunUZ(l9g%YBs zc!BxY=(V8K;Kkkq;K-s*d>DKX&?ma&5A+H2Yz!Y=XTf3lshfITCUJ+2M8@SJa2(OOV!3Lo3 ze{@#Zm!Jd9?V!BZD3mT5|9BmwQahC_Z=NbBFB{-s1_*wA_a2vXeuYKzdgIO{0#Tdl zxdTNZz`U&5HbB7!onc{D*!OXGB$p4{WMihpU($#n#p(@1z_^<-NJQ0L@GaY0GMGF` zmjZ(83#%E3bn2$)qZ+fM&65y4d9!r*3MG0hPb*y5a58fnBFX(~QRjg>Z}amDsI00( z-=Cww)5lcGeXgHZlxg)+GY@j138ZcNem8WY`^IP~U8}hIJs$t`d#j4YJHwVQ*HrTD zSIj0V7qc^Wy51FMVpHIMm!BD_ zPF3!ekXhgNf8OH*uuq1$&QQ%a4{$eqpVxQT4g+qHxFwMP$F+?$Jfa> z)M^RSYn>?>y+v_F-T?%D#%4)>PSTj)AO31bhkIZtgU=#LZfmWL($f~txjm@*LAtFY zUQ00{o{&9hdIAvpPX^G08YD%TE-wC# zK+VmgG7)Jxv((bNGE1W7e$=SDMaU}XM2){;Dt;3Z$P|l@=ifEuO9WNrG^^R%1>G2R zqwAomS2s_~I z{dusgvj+c(L8q0R&ULO9B+bK%Vd|KikgVc9w9(6KZv%#9t}%$;REbh$1&YDSC?^8d zY*FUoSMCmc?ath@0;NV9$~y5|KZAdZEVEWJ$CN#6L{uyya75(T+-6`&myMm4=u)Oe zxv?;DRb^zGHK2DhIG`e{6$d>y7l&K|+Ec+&wbT+~@mqzZ(7!!h!#`z_Mbs6gT2J zpf=r%Jf}cuKwTN+Y+~a?t?oi;Uo3AGT`dAz8>M=+hCra#ObY_GZaNl~84$`oMu$3( zxNOb8@fXVPmhSM5n+gP5om*fKAlp48Pt)3<+OX?TL2n9S#2`uF>;?98;-1aR-pAHM zzo+m0?3N8-D~;ro61H#SMU_&=Ve($I%ZRa>T9;9ACgeBJxcOvFB@MBf`uLVXzE)i& zEg|NpC3NQ_!O+JRKdL-h%{4A|ho5Q{>2|)P+1mozT|&_BkReu@`e-trLta)vHZR^$ z0Uj{xq=57P_R6{p{LMq&%C|?=a0VcXiIpxpT(Qa}*1(6iHs5~SWno;_{IV0jNx`L; zl&ae*kprakN}4Px&|r`sfq5^z1m{PfKu4hw?)TH+-Ium z%Q_FQaY5#A9P;d4=nlPuwNxV+1Hv~BY+S=!!G~{8ekAD0s+T*xB2b0;d#GB)9f;i0 z%|7L&u;sQ9#?&B4NQ_~8IScvZB=G&-Vf?!w!&ft6U4#2+rt^9jV818LkD#de)U3@h zJg_%V2FjT4Z=n=SH0H@IkW%JKkttA`b5jW~f(ouOszHymLy98oZ3SE@DBx*KN^ZY< zue4ZAELQJG5;%8(z4f^bl>JjWI^Aw>y@QH1(7_~JPURKOw^7GH=4}7= zNhbRu=Xa>@Z>N6gVP~6<7`~Gh2yA()23ux|nfq<^AYQ()uqoT*V`l{iBlrgnyC9*3 z*1<~2CX(O2K?-;M<=y>P$O8Ekp-302B=b`*I$j9|=|Oh~#$|k{m`d;ZKnMM%)>nEF zsUVUMP(Ai(;a@NzwAFEOr&~SCe+2_B4QW4YyZ&Ycgpnxc9Ep*MHA(3#6#=k}d}lnr zPeA!@zKpgXu(a)Xbg zeYcpl1C{15*QZvR(}Z!{pcss=x>C?_LREojIII|idg z!imj3()a0UPW0w+?@-tJny*vOrmCEi2GC#>AEv?+ts*w;Flf2=i5nKoPTbfSSXvz_ zmGd8@DS?@vW!cvy?T}DGs9JY)z|SDU#%w)f7%oJoN3XqMeV@_?a~#n`-Ndn)?@d~h zE&%NLy*bDDbHA8s3U|*FYKP(*$9kFk<$Dp%=vlHKAg@aOC`m*2$#`x})HOfdy`+4fR9$o^&KFYIhrJp;Z{_v;5vedao^ zUGbT;YpTd$)I`CXB~%wLzp~hzpRIp3XPZmG9bLb-!=eq^Q-0e6qz%=_TI1RK9X?N; zoc|us5+97)DqyM5^A#ekjKzyOoJ-+8<5F|<;rc<-q@XwvuVWzsW*kMOwi|4?7$t_! zOZB#rS;1}J$pi&@R~sexiX1t0pN7CGEPoC9N@rh4o`qu zYzBTJa^qB|*D&E0^URY;OUnS_>MxI*!s48d+5umIe;6bu8lR{{TT!|AuFSobcxCB| zbgA`ZuKIX5?Gk-jl)yqwI)uAm+y#$6>#Vx>i`#ws4#-9UIAHGUsW(O_zcu+j`P{cV zh5pLg5xL9Jvv119SoJTsd;mW6f9zNvz z=K%qgu&y!^2a7eWL>ut^`DD_}vQc^Y{P^(3Jk}$Qxls+D3TY%jC?B`jN%FQqGoNoR z5A&=7xbAzsM<0M~Rlo~PJ8yLAFM7%*qk8{q-+7^pZNM)a4>&@jZagPeMzJU1uavZG zmw#;5xs0oMSPk?tTYWs(Q~I7r0gcZ~YS<ZAVe$0?4npFSp_~w zBJZ2}X3R?fU#>`t48{xD3XYDuEf;Puer|ai3(Y+xB*+ZgW)Rpp^+AI)C)BIe&jY%q za;S0(StzSkJDe&CK9i`Pm5kSle>AmuaZK&SQ7GShQn+>*PDLJm{z-;2T%2Hy7xApF zar(jRPUtlp1m5Apszknd?vOI|)~Ni1nq2+fv9l}QN=F}h4Zey$vbtl3X>7}0KcgLG zNaDsll(lH{;fr+z0+%e4|JE^`*Zw}0mFlVRsJL-&n9TAK4&LgON-kfloB7hJjy}8~ zPAubUVif|bte64Lx8}Y=3!(-gS7=Q4g=MLYn09&L@GE4-s`BPa2h9RnO_RfCNfm<= z$ipa9_b7@HJDiTM*Sc{fI(H&EiKK<>6_nkWc%z3~o!tA_y*rr;VcR$4X}wH&g1=Q#i z8w%V1gQq{ST^dx&lNuusSv>HjYrBjGy73E6XEwVHsZ9$ztXzB6$dk*pe*_(v@&cXz5dBhO!b{#(Z@d|NGYM`07P5SzYZkV`z8{6&O()|p{DZMOqp zL^OkoHF7$wfT%sA9JSK(Zt|j6_vfd@WDLR-Sjlgfqb1d#P{A`_vZ168p#A`RwlQp) z-o0>%IbIWL%XKVdzKDlPMl34D&ji`!2_`?UcCLSpc(j1?iO@CnF-*I*5TUe^s}v4Y z?{9pgHmEyh$45p?I{eX(-@W{7ymE0aNuFJ2AD4cqadTWpOA)R4w=Qf!W7!7dKi8o! z;yrh_lfmJ$pW2)Y;_(XEp%-f$8*N5-N)>5EuV&S3D@a-wMX$Ao5;!Y#lzna>1}ICw zw>+226aX%o!GvE9)zutP{rl(8-H2E*)x}>g;e6h4cvsuC6aoo0U%{64?qTz_i9*F0 z?SAG*18TKjE4#Ckx!`a^iXZbnHK#ziZLF+VT*u$u%v!l2kFym?{o>?h7j~YD+0Bag zZ(FFKkC(MAN0|vv8Ov(dE`KvTY3x`{#YGYpbH+5IPA-j=*DjHXb+gx0EV6x< zMDoKXX)|28%XO>zZ9_WyjgDdmZ}Ozt(4P5Tvav{=LcVDI&%b_NHcl7?&J+u#jfn%W?A#RGs*r`lg4R7cDCq@s*)muCn)6;6c(#2AP{AhTgQ9@b2t4{FGDYn z@ed;-_$1p+$zDU&yey7th;?=8+vYsYq57J1bO3;U@!O z9^G1ewKblk#rCoZL)ek*&o^a%@mrb8s0ac~27hRetE=_UtOY_Q#G|#dFgktf?f318 z5pi6{6m`x|ItNydN1nXJUEke=6`%#=QJmyiHZ~HC08d zKdc8IPEBsoIx-R_7dfS1Rb;)4{nQ~pn3EDZ+7`59*opm;^C9%xnWM&IpV=C%OV_7T z-SkgfPjm`v`>~{)tBG8_Nq?-Bw3t=ir9P7UH$3-+>fQA(vw6SZv7Tusfa?7#2RdFT z=~io;XTq5Ap9$3WL98#pt~xSf7@5TI8E7?xGhUQKc0OuHuY4vVyUa(nTZHNZNLkHEfgDYvO= z`g)Om8jUTC)3>Q4!pKX)-PYg)4F>__0+J%0N zchyE})PtHci*Vj3&erZ~~?OlHe?=*cUnbC2Y06)N9a*`Vi@xwy?RAFxI=-N1+FH(oC(S zgJAgLx>70#g|O@DgRcxM-KRd#2VUYaICI7QR_}#zfBQA(*59-$IU9=U;jCx+xNFBp z7e&Sb@XPA7Yj%Z7D%8oYySvwv_YlVPq#ZBuM8zgJv&-1i`~6ulCx?&e{tnTb{@`F1%yAN2Ypf}i! z>^J89M@>25X}T-*H*Mb6S#^TB<^T6Dh{2G{%0vat#!07tT%ikV9o`! zDDI7(Tcd9%aAwLEa=}GC2-3*VVEC4|@Ba zTg|(}?u?Ipc^Pq22tSd*H3(m%`b6Ea7`|wM$F10r4O`R zyTu40u)`9gTC!tvHC9YWc7GDQZRX3WQ(00?ENvBzIeR^^QQyM8AJuz&PFuS>yCzY2 zPoZx6oM;x+wqWn8eSx^|V1VWuWqN-7p2_G>WBvZXW!6~TDyylyJck8!T@!MN2!KirRDSKIdkLSbI=H5Tw;%t>K`!}=QN zzKiWq_G(zujZ5^6ms<lsGvUu1(W<>Q%%mn)$ z2ovYhRi^s1hreW=8w5hDa&|L*UschXc#yyg}H1PZluleV8Z@LO+Su<(x^Cjg1%U z6PnSzBg|`L?GNyC zkaO?8r`o|E{s&7ss`}_YTRyG4dh<4Mzvr2W1&vMCc)3;khm-D9K${ZD?Z*v{KcW&a zIvk*t-4}wXTmLQ6eeN71qYdBYR0z;#Om|u6t=y*YMu6gDS-e_R{>jit2VKo4>J<*N zQ+>@LG>+TP$7d9Y_LEe;`PbnqWG8EO?ETE*8~T|w8KjVcU@hrarqK)nWv8Crq6aSm+csz5QqWHoTkibvw&2kwxz;YagYA?#BV;fcH-6 zey+ZFklYP5r^W%$#F3K+b_$cGSr13eR!!~M`i$B^#fCI=G9NLC%u|$L14FMnV3R$n@5&_HiL^S5jaiw|8 zgjP3!z;zKfyM68js!Ap$LAtfPQjrv3}?0U#-ZxU97Um$(xdj0r0D& zYl>=bb@QZ@TXLnW{o~8}Ou@Nk5UuqWIrBaf#S%l657?`tucS8zF?-y^+ocwsD}GbS zzh+f8ok#?FQM``2C1b$ri}FKt^E2AU9OwlU)SzaDTcta(&B!HY*$4S_q-ydggW2Vi z)g&R-De*mB^?MyywW(Cd^Z62N5q<<*zua>ZP{$uC9Bh1w;SV(6M{+2g6#S|_?7Iw3 z;0ole^>zD5wt~CvSc(@ZJ9l8@nZ7Vckx9%$Nt*F8AWK|DTuT~MMZeT#f+6fOpp;e( z4__Fs@=w>!eYpP16;r!(k75dC8QYc4HDT>EeqM{UR5gw~q5;4CacSh*sV1$sB2-z{ zsjG%{P1b|-8+aXL{C)mZzJLTb9RUwVp8!?W-f3^u(*uEk(^YXRe(siS*ozZ2_cIfz zt0JAc2(7g`u%GpxSVMIqfoVhDufT4IJ?NP=s$LqY@N>|_t{MIj;>Eo6{HJ8B08-?n zln}+r7@dgnt>N73rW;^{%Kz}EMwXWP$d_Ai^O;`judq$j>QPJty%zcL8wpq?MfdOA zG81(a<4!9Z*-Dkz6vqWRuKaEIcgUh%X8tMXArC!6=@`&5qKaF{6#I+0ICN@?spF33M z7OKz4yhwHgnd3ENR+GP#(CU!11ju4X?(B>~!oVjAzojSa>WwG(~ z#nT>{qlp4f0X}sj{-CKS>b^UUC1k5qAr25q&eYF!2 zZbtBq<7AmrdqWL=Zmpr)C(HyE()g|*JYsfNRhD^Y5H@*jA5Wj*{zkp`m& z(VDy>1ky1S&9#o5CfLzicQ{}aPJO6*@HED5c_8=2Whr8leiZWOW0?d=FI}e*l)2L3 zJP8FAzxm+uy7hbfPv{$^+tf?~j0ONl#ky#79RxNr*{xgkS`0$x({gPAFMk8=$FW-H}*y)6s({mbei0z0=)}~aj_BV;4 z!-pJ39@vJD^Dr=@tf@p`hjWwVu1%v4OXofbO{nPV#n_ih9P^s{c$Y9kG`?rA4o(IE zTZg3lze~?m^(|XSe`jasI@Od7#t4`9yTLPu@>dI+aYv{Z09%E;qX&i+T~O3Yj|5&m zzy}DZ0_uc-Kq;@q>h6KX)&8!E(Ap>M`yF8xosDlBVUe#IK_X78Ue^z?=k zDVuM8b7qyRdg1Xn>(i^P=D-?2stYU#o(Rts8x=}~^fdr#Nz~)qu!!)fW}3#vH{b-- z(c@F`dq%MNO$GWwX#eTr+_E$@`P_1yNw@gykO*yBtn-ZeCg2)00&XZtkK6Z0KmZab zQ!6jEd!p3ZM@KaxYAzl&O$-7H83zRlJRieYr+pPSPF4yL(+;it%WZNiN(;$>YoGSX z9W@s>GZdq$3M8UaltKp7t^OmhuY4h%lvg`}^z_0Y6@|FstONm%HkQQiN&sY6y8UFr9%v+A*rIgeo{$l> zBaoN@9cf!!$7VpiSpAL>Fdve8hEEodvPDw4AD*zP+yCFctw#m}Z@Qo8jWf82YP~Px z{`0029j1lyM3i*I$)^AOK`)~SJdd=I?MLC5wSFOG>r^UEJ!i}^plbC*dHcF`UNCdZ zO=qmG!*~Ie-`YmaSBfgctU$;8*TInk19QZkFOQGlk7iO;@WerC^o`@drOIsf@2#mUOMuj0q!`4 z=9iuN(PCBYpP?KT&{(teX@*RIOr3+zrUQi2es?&n+KO+RQ)j+#h1zE7M6ec5(^eF# z+JG$W8m?}UX!Rc6BHtf+ld{DvsG%Ur>Z)Vs*4jA_N}S!^a;PTTi@a*tXcNiil1++e z84Rr@qlnq|eme{otzgZz>30ErmLs>^mMw|dPWxfixdT~1kA#PO0Ct_G-B#%xnW|Hr zU(m;c7yZf6yWdhZy1n{ZXFA(ZQRbW4MGS#5OZNf16;<^i7gYd|hia1Gpa^P zUyD{xY!fedlRE6Q_DJUn`OTMHKY!TRedRoQEOSCdgnUhHL`_YiZSA5+IN;TaIf%M$ zxW^2v*pykPdG)aOLy-6HB#u7;){FuBp7k!)Fhc5Q2<=wn7h|3LhVC2yq9}t&ZtguVi?=;fzDeP$bM8$`Va} zp0V{o5#a$FaeP8g+-NB4M_v-3HRkbj56y*v-9P7y%}FCC8=SNx+X9V@<|;MA+JH*8 zfA@#J9a-Bt(owm)6ItwsOFr*F}->Zhdye6A_2@Tw@dys&0 zFBS-dvKoQ68*5Y|2u$uFgv7x|D*(~NET8sNW)?B@SEhke8xP4pLjVbe{qbSb9Sm?8 zU_4Dbun0T2IuP3azg}JaEHlpNIRcyR0Kqjbjh z5oU>;F*JJv2h@xC*6iCxA5#7Llojrib>XR1QIHCwbtQ57PyaV3rNtLpT?C3;OXl9R>w$#d#4 zis`du+LX>MRU7V9Y~)M4GVG$^z#6~Y8x}Q!_HOj0wXJB>Uhy8#jT$C-jobIH%W5wj zDSDlsQt3bg_#|mGsFAf)Ezg`N?hmmJc(>_ci(7ruR>7k`+SE=51Yy-{`+@PH*wqbI zaX<5O5STVmZWYqVYO1rd7P1#roTwv!97S8-Z_^&gV7*30(@K3f&$l|8_nB0uPMN~P z)1jC;^6h+J!`)iHGs+km4{6Qs|9X{oP1SCcWZvLMpz1T@PgQ;K~EL*#OCuP8x_laa+8RxE6uXti^ z-)u~F#i%c~qYy)92&#<7u)cn!At@pA!CJ75N8d<$Ka!7tE3bQWxMlbAiD=!_5HN){ zPj0SUECVWOvepk?0Iz4g*v{ycq` zi<^SYq4Rll^GgTRpb`U*yN5F0Kt|75ug?9ZwO6#W@h%hD z@N-FhD8fHm8>?B?Q+2958tI+$M3}ce+BJ>=e9c*T|CR@q{@G&!iY#!*N9r*^ zsjW4;(_F>L9)Prs-!~=Bt_ZX5VQ_~~)Ew6#RJJ`3jAnnL6361H8}0?_?MVD`o!yj` z#r26y$%z^{vAML$G5O}S#tM&y@?cg~irkIfy%g=}kbnUF(HewgLc2O*#IOK7jab@QRc>nIIxlmH4z%)VISKUx(!?Wz^Hlg_}}Q`>MC z#AJEqUpHfXI<^z1w`%tC&SV2C`^U89zk|D{rXHPB%!Pr`6-`ymhT-^ebgbN0aFvmL zEkLZdm-n%QaLrz#0rGZlH@R=bxQyJl1^3g>5(j?B;sNLN9RUO%yga!zb_!Yga=m}B z>4C;>NY?-ZwwSRMnJk|e{XlqUe*C(T zuP+vsdxTUv5?VelV5EPJ6D}R6da(*bfLnZ8aNt0o`c-B;2&ff~uI+VRxn(=tZ%?^n ztaD0*UeA8)Z4)*1`G;#2AcB^{3#A5p{?Y%yL)hC?pl$Q#YCmzGK2pIshn;*!Eag=J z&SGd{9H4}0NueEvowwnzn+;{%*&XkleJ~VgLEElfn8NTq5GRBs@E2j%<@bPX<;R`<;($XBYn+KJR&F1w*1tn#$+JN#C8%Z@^*&R>Ef>&} zwx=0=$@Uo^3pM~UwtP_$!dtMPSI}!AyZH$9=!TRJrftDrBR)`T>;e2R*#?lCSXpYZ zey%kavxiY=>Gb3*m#nw!26ExM)!{4frkf)GA~mi@+S_@WZ_>7Xh<{;fTO;iXLCZfh zn;gTPqJSAYb7wyX=KB#{Hx}wf2RK}8*r|;^xK2I}X27Q%_2Mr#AWGhv{`5Hxf#{m> z(V`{)k*zzcWBvx~8y71hmaX;owP9@)%Q=R1bm5#!WEiHeV*;2z!5vLw?sS_1lp^S^ zAh51i4_?d%FS-$fX{5SGonZLrcHqZ=?xBmriP+^t?61+ z(>?qc(_KMk0#$Wz!*k z0Lq{0o(yEwt=4;dxt%8)g{HzbUbeb%+b+WrH$wPmZ;W}j9s~J7o=BT$Wlk8Z^2d@z zkxrvUqNaG_musVVw~$>Z1mK?sPL05mBVKc~MwEP(Lobn^&!l?{QrR(zA$FPw3yVfm zm>)UtWaXbw@se}uF8?k64*W+|3oI_=>k8s^(lyf7Hq(QXCQd7sXoOJ-ol30!#3K8s z*yqJ|eMYuEb#Jj!`8$c5y1h;=J%1sv)MhK6zhs1jbd{lO@rEA|!e>bA2q=Vq>@K9Co`%jaIFl zT?UIS3tebqB}Hm|P9-qPIyZi@nKV2{L1nizkZsrqZFs4k>%Xo!bbjTPUUE~jVKjPn z{@g4_HHXPh3g5#K^c3ck_ULrj|LOjc`e&Rc3U6(3kN(uubWXi1cr;BYarC74N31DDUfIk3F|ehB$nSNg`siGoDn z{7nr*YMpic#I_jw%a|6;Con=ODYCqi*IUfJb3}`>)%-BJs)cS94B8FjD9p1$ltJ#< zCEA|vYm0liK9ql1bK+}iayz}Rr~u`dK=W&A%gv-uxe>;C>? z{!JtMw6nU8Kepe!zu6tSnEY6=A~?$eFHV~^dCI3by1-;U``OFc{_`@79jh1Jx872@ z=kwRiuGw#{@3VE!Q{Gt-s4oiY?E~BBI*DiB-21;;J+3uH_Kdpkw{vRg7wZB+=c|G` zrmxlxPl_WH(<6mat%*skkSI&rAP33QM&2+T1> z6mpk}&+WIV1CDxtfKU9(oM?glV55O0!ul;!y6!_7gAA9&YvGd-Oa9&b4_q>N|Nra# z;6rCYS}&gm_DRDgiXMZMO$-e$wf|l_eg1epr2W|NvfB<;#8T9h1Q+JEpm8A(@Cm;3 YpLq$JXjzSvcNa*&)78&qol`;+0AoG>;{X5v literal 0 HcmV?d00001 diff --git a/assets/documentation-page/description/ustawienia.png b/assets/documentation-page/description/ustawienia.png new file mode 100644 index 0000000000000000000000000000000000000000..1c335c57e670bee7b01a2d972ab8e25768cf8704 GIT binary patch literal 42334 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 fFTi@^u?Bv(oag

+



+

+

Generator CSV

+

- Autorzy: + Wersja 4.0 Beta (Build 20246)

+

- Mateusz Skoczek (styczeń 2019 - wrzesień 2020)

- dla ZSP Sobolew
+ © styczeń 2019 - wrzesień 2020
+
+ Mateusz Skoczek
+
+ dla ZSP Sobolew

+ +
+
+
+
+
+
+
+
+
+
+ +

Historia wersji

+ +

Wersja 1.0

+
+
    +
  • Pierwsza wersja programu składająca się z 4 części
  • +
+
+
+
+ +

Wersja 2.0

+
+
    +
  • Podstawowy tekstowy interfejs
  • +
  • Scalenie programu w jeden
  • +
  • Aktualizacja formatu plików z danymi
  • +
  • Wstępne przystosowanie do działania w czteroletnim liceum i w trzyletniej szkole branżowej
  • +
  • Aktualizacja kodowania
  • +
+
+
+
+ +

Wersja 3.0

+
+
    +
  • Wprowadzenie pełnego graficznego interfejsu w środowisku tkinter
  • +
  • Wstępne obsługiwanie wyjątków
  • +
  • Przeniesienie modułów do oddzielnego pliku
  • +
  • Stworzenie pliku 'format.py' ze skryptem przetwarzającym dane - przystosowanie programu do ewentualnych zmian w formacie plików z danymi
  • +
  • Przeniesienie ukrytych ustawień do pliku 'config.cfg'
  • +
+
+
+
+ +

Wersja 3.0.1

+
+
    +
  • Poprawka błędu - pole 3 i 4 nie mają już przypisanej tej samej zmiennej tekstowej
  • +
  • Przydzielenie wersji, autorów, tytułu programu i lat pracy do zmiennych globalnych środowiska graficznego
  • +
+
+
+
+ +

Wersja 4.0 (work in progress)

+
+
    +
  • Zmiany w poszczególnych wersjach developerskich są dostępne w pliku 'dev_changelog.txt'
  • +
+
+
+
\ No newline at end of file diff --git a/documentation/content.css b/documentation/content.css index 3b7d964..19859c5 100644 --- a/documentation/content.css +++ b/documentation/content.css @@ -5,6 +5,13 @@ h1 { text-align: center; } +h2 { + color: #C0C0C0; + font-family: 'Segoe UI'; + font-size: 25px; + text-align: center; +} + h5 { color: #C0C0C0; font-family: 'Segoe UI'; @@ -24,6 +31,10 @@ p { text-align: center; } +#description-image { + text-align: center; +} + #about-program-image { text-align: center; } @@ -39,6 +50,13 @@ p { text-align: center; } +#about-program-changelog-main-version { + color: #C0C0C0; + font-family: 'Segoe UI'; + font-size: 17px; + font-weight: 600; +} + td { color: #C0C0C0; font-family: 'Segoe UI'; @@ -77,4 +95,10 @@ th { li { color: #C0C0C0; + font-family: 'Segoe UI'; +} + +code { + color: #C0C0C0; + font-size: large; } \ No newline at end of file diff --git a/documentation/description.html b/documentation/description.html index 824ae4b..290677b 100644 --- a/documentation/description.html +++ b/documentation/description.html @@ -7,6 +7,12 @@ Generator CSV -

Program "Generator CSV" służy do przetwarzania plików danymi uczniów/nauczycieli i generowania plików wyjściowych w formacie pozwalającym na import danych na stronach szkoły oraz portal.office.com

+

Program "Generator CSV" służy do przetwarzania plików danymi uczniów/nauczycieli i generowania plików wyjściowych w formacie pozwalającym na import danych na stronie szkoły oraz portal.office.com

+
+
+


+


+


+


\ No newline at end of file diff --git a/documentation/index.html b/documentation/index.html index 685195b..915b1cb 100644 --- a/documentation/index.html +++ b/documentation/index.html @@ -16,8 +16,8 @@ diff --git a/documentation/how_to_use.html b/documentation/instruction.html similarity index 96% rename from documentation/how_to_use.html rename to documentation/instruction.html index 1d3a51a..6f7635f 100644 --- a/documentation/how_to_use.html +++ b/documentation/instruction.html @@ -10,11 +10,11 @@

Dane dla przykładu:

-
+

W praktyce układ danych wygląda tak (* - nieznaczące dla programu dane):

[imię] [nazwisko]
[klasa] [szkoła]
[login], [haslo*]

Uruchom program i otwórz zakładkę FORMAT DANYCH. W pierwszej kolejności należy nadać nazwę presetowi formatu w polu zaznaczonym poniżej lub wybrać preset już istniejący z listy rozwijanej

-
+

Aby przejśc do edycji presetu należy wcisnąć przycisk WCZYTAJ


Typ osoby
@@ -97,10 +97,10 @@

W pierwszej kolejności należy przygotować pliki wejściowe oraz stworzyć presety formatu dla tych plików, według instrukcji w sekcji "Tworzenie/edytowanie format presetu dla danego pliku źródłowego" (jednego presetu formatu można używac do kilku plików jeżeli pliki mają ten sam format i to samo kodowanie)

Krok 2

Przejdź do zakładki GENERATOR CSV. Możesz przetworzyć naraz 4 pliki wejściowe. Dla każdego pliku należy wybrać jego lokalizację (wpisać ją w polu, oznaczonym kolorem czerwonym na poniższym zdjęciu, lub otworzyć okno wyboru lokalizacji za pomocą przycisku "Przeglądaj" i tam wybrać plik. Następnie należy wybrać preset formatu, odpowiedni dla pliku wejściowego, w polu oznaczonym kolorem zielonym na poniższym zdjęciu.

-
+
Krok 3

Wybierz lokalizację zapisu pliku wyjściowego dla serwisu szkoły, wpisując lokalizację wraz z nazwą pliku w polu, oznaczonym kolorem czerwonym na poniższym zdjęciu, lub otwierając okno wyboru lokalizacji za pomocą przycisku "Przeglądaj" obok tego pola i tam wybierając plik.
Wybierz lokalizację zapisu pliku wyjściowego dla serwisu portal.office.com, wpisując lokalizację wraz z nazwą pliku w polu, oznaczonym kolorem zielonym na poniższym zdjęciu, lub otwierając okno wyboru lokalizacji za pomocą przycisku "Przeglądaj" obok tego pola i tam wybierając plik.

-
+
Krok 4

Aby rozpocząć generowanie wciśnij przycisk START i potwierdź komunikat. Pliki wyjściowe zostaną zapisane w wybranych lokalizacjach. Przed importem upewnij się że w plikach nie występują błędy.

diff --git a/documentation/program_documentation.html b/documentation/program_documentation.html index 71ad1e4..0efebb7 100644 --- a/documentation/program_documentation.html +++ b/documentation/program_documentation.html @@ -7,6 +7,114 @@ Generator CSV -

reset_appdata

+ +
+

+ Informacje o programie
+
+ programName - Zmienna definująca nazwę programu
+ programVersion - Zmienna definiująca główną wersję programu
+ programVersionStage - Zmienna definiująca etap rozwoju programu. Wersja stabilna definiowana jest przez pusty string
+ programVersionBuild - Zmienna definiująca numer buildu programu. Zapisany jest on w formacie '[ostatnie dwie cyfry roku][numer dnia w roku]'
+ programCustomer - Zmienna definiująca nazwę organizacji zlecającej stworzenie programu
+ programAuthors - Lista zawierająca nazwy osób rozwijających program.
+ programToW - Lista zawierająca informacje na temat czasu rozwoju programu. Format: ['miesiąc rozpoczęcia prac', 'rok rozpoczęcia prac', 'aktualny dla rozwoju danej wersji miesiąc', 'aktualny dla rozwoju danej wersji rok']
+
+
+ Dozwolone kodowanie plików
+
+ allowedCoding - Lista zawierająca wspierane kodowania plików. Są one wyświetlane w rozwijanych listach wyboru kodowania pliku.
+
+
+ Dozwolone znaki
+
+ allowedCharactersInSeparator - Lista zawierająca znaki które można wpisać w pola dotyczące separatorów osób, linii i danych
+

+
+ + +
+

+ Biblioteki główne
+
+ W tej sekcji importowane są wszystkie biblioteki niezwiązane z interfejsem graficznych
+

    +
  • sys (SS) - używana tylko do zamykania programu
  • +
  • os (OS) - odpowiedzialna za polecenia w terminalu
  • +
  • time (TM) - używana do pobierania aktualnej daty i czasu
  • +
  • codecs (CD) - odpowiedzialna za otwieranie i czytanie plików w odpowiednim kodowaniu
  • +
  • pathlib (PT) - używana tylko do pozyskania ścieżki katalogu APPDATA
  • +
  • shutil (SU) - odpowiedzialna za operacje na plikach i folderach
  • +
+

+

+
+ Biblioteki interfejsu graficznego
+
+ W tej sekcji importowane są wszystkie biblioteki związane z interfejsem graficznym +
+

    +
  • tkinter (TK) - główne środowisko budowy interfejsu graficznego
  • +
      +
    • ttk (TKttk) - biblioteka odpowiedzialna za kontrolki i ich wygląd
    • +
    • messagebox (TKmsb) - biblioteka odpowiedzialna za wyświetlanie komunikatów dialogowych
    • +
    • filedialog (TKfld) - biblioteka odpowiedzialna za wyświetlanie okien wyboru plików
    • +
    +
  • PIL (PL), (PLitk), (PLimg) - biblioteka odpowiedzialna za wyświetlanie obrazów
  • +
+

+
+ + +
+

+ Lista komunikatów
+
+ Słownik MSGlist zawiera listę komunikatów. Format: '[kod dialogowy]' : '[komunikat]'. Kod dialogowy składa się z [jednoznakowego znacznika rodzaju okna dialogowego][czterocyfrowego numeru komunikatu]
+
+
+ Rodzaje okien dialogowych: +

    +
  • E - komunikaty błędów
  • +
  • I - komunikaty informacyjne
  • +
  • W - komunikaty ostrzegające
  • +
  • A - komunikaty zapytania
  • +
+

+

+
+ Funkcja odpowiedzialna za wywoływanie komunikatów dialogowych
+
+ Wywoływanie funkcji: MSG([kod dialogowy],[True - Zakończ działanie programu po zamknięciu okna dialogowego/False - Kontynuuj działanie programu po zamknięciu okna dialogowego],[dodatkowe informacje wyświetlane w komunikacie - opcjonalnie])
+
+ +

+
+ + +
+ +
+ + \ No newline at end of file diff --git a/generator.pyw b/generator.pyw index 71f33fc..347da8b 100644 --- a/generator.pyw +++ b/generator.pyw @@ -2,7 +2,7 @@ # Generator CSV # Wersja 4.0 # Autorzy: Mateusz Skoczek -# Styczeń 2019 - Czerwiec 2020 +# styczeń 2019 - wrzesień 2020 # dla ZSP Sobolew """ @@ -10,14 +10,14 @@ -# ----------------------------------------- # Zmienne # ----------------------------------------- # +# --------------------------------- # Główne zmienne globalne # --------------------------------- # class VAR: # Informacje o programie programName = 'Generator CSV' programVersion = '4.0' programVersionStage = 'Beta' - programVersionBuild = '20245' + programVersionBuild = '20246' programCustomer = 'ZSP Sobolew' programAuthors = ['Mateusz Skoczek'] programToW = ['styczeń', '2019', 'wrzesień', '2020'] @@ -32,10 +32,9 @@ class VAR: -# --------------------------- # Import wbudowanych bibliotek Pythona # -------------------------- # +# ------------------------------------- # Import bibliotek # ------------------------------------ # - -# Główne +# Biblioteki główne import sys as SS import os as OS import time as TM @@ -44,7 +43,7 @@ import pathlib as PT import shutil as SU -# GUI +# Biblioteki interfejsu graficznego import tkinter as TK from tkinter import ttk as TKttk from tkinter import messagebox as TKmsb @@ -57,9 +56,9 @@ from PIL import Image as PLimg -# ---------------------------------------- # Komunikaty # --------------------------------------- # - +# -------------------------------------- # Okna dialogowe # ------------------------------------- # +# Lista komunikatów MSGlist = { 'E0000' : 'none', 'E0001' : 'Wystąpił błąd podczas inicjalizacji katalogu z plikami konfiguracyjnymi programu w katalogu %APPDATA%', @@ -68,13 +67,6 @@ MSGlist = { 'E0004' : 'Wystąpił błąd podczas ładowania pliku stylu (style.cfg)', 'E0005' : 'Niepoprawne dane w pliku stylu (style.cfg)', 'E0006' : 'Niepoprawne dane w pliku formatu', - 'A0001' : 'Czy chcesz zapisać? Zostanie utworzony nowy plik', - 'A0002' : 'Czy chcesz zapisać? Plik zostanie nadpisany', - 'A0003' : 'Czy chcesz rozpocząć przetwarzanie plików?', - 'A0004' : 'Czy chcesz zapisać?', - 'A0005' : 'Czy na pewno chcesz przywrócić domyślne ustawienia ogólne?', - 'A0006' : 'Czy na pewno chcesz przywrócić domyślne ustawienia wyglądu?', - 'A0007' : 'Czy na pewno chcesz usunąc zaznaczone format presety?', 'E0007' : 'Wymagany przynajmniej jeden plik wejściowy', 'E0008' : 'Nie można odnaleźć jednego z powyższych plików', 'E0009' : 'Nie można odnaleźć jednego z powyższych format presetów', @@ -83,18 +75,27 @@ MSGlist = { 'E0012' : 'Nie można przetworzyć danych na format wyjściowy', 'E0013' : 'Nie można utworzyć plików wejściowych', 'E0014' : 'Nie można zapisać plików wejściowych', - 'I0001' : 'Operacja ukończona pomyślnie', - 'I0002' : 'Aplikacja zostanie zamknięta w celu przeładowania ustawień', 'E0015' : 'Nie można usunąć wybranych format presetów', 'E0016' : 'Nie można uruchomić pliku instrukcji (documentation/index.html)', 'E0017' : 'Nie można zapisać pliku formatu', + 'A0001' : 'Czy chcesz zapisać? Zostanie utworzony nowy plik', + 'A0002' : 'Czy chcesz zapisać? Plik zostanie nadpisany', + 'A0003' : 'Czy chcesz rozpocząć przetwarzanie plików?', + 'A0004' : 'Czy chcesz zapisać?', + 'A0005' : 'Czy na pewno chcesz przywrócić domyślne ustawienia ogólne?', + 'A0006' : 'Czy na pewno chcesz przywrócić domyślne ustawienia wyglądu?', + 'A0007' : 'Czy na pewno chcesz usunąc zaznaczone format presety?', + 'I0001' : 'Operacja ukończona pomyślnie', + 'I0002' : 'Aplikacja zostanie zamknięta w celu przeładowania ustawień', } + +# Funkcja odpowiedzialna za wywoływanie komunikatów dialogowych def MSG(code, terminate, *optionalInfo): try: optionalInfo[0] except: - optionalInfo = ('', '') + optionalInfo = [''] # Błędy if code[0] == 'E': @@ -2859,7 +2860,7 @@ class mainWindow: self.aboutInstructionButton.config(command = self.aboutInstructionButtonAction) self.aboutInstructionButton.config(style = 'button1.TButton') self.aboutInstructionButton.config(width = GUI.R('aboutInstructionButtonWidth')) - self.aboutInstructionButton.config(text = 'Instrukcja') + self.aboutInstructionButton.config(text = 'Więcej informacji') self.aboutInstructionButton.pack(side = TK.RIGHT) ############################################################# From a27a5f05bebd700f50c7d68aa51fb2ab473f1f30 Mon Sep 17 00:00:00 2001 From: Mateusz Skoczek Date: Tue, 8 Sep 2020 11:01:14 +0200 Subject: [PATCH 27/29] 4.0 Beta (Build 20252) --- dev-changelog.txt | 6 +- documentation/about_program.html | 2 +- documentation/instruction.html | 6 +- documentation/main.css | 3 +- documentation/program_documentation.html | 255 +++++- generator.pyw | 955 ++++++++++++----------- 6 files changed, 724 insertions(+), 503 deletions(-) diff --git a/dev-changelog.txt b/dev-changelog.txt index c4b921d..81bd87c 100644 --- a/dev-changelog.txt +++ b/dev-changelog.txt @@ -147,4 +147,8 @@ - Przeniesienie pliku 'changelog.txt' do sekcji strony 'O programie' - Dodanie screenshotów programu do sekcji strony 'Opis' - Rozpoczęcie prac nad sekcją strony 'Dokumentacja' -- Lekkie poprawki w kodzie programu \ No newline at end of file +- Lekkie poprawki w kodzie programu + +4.0 Beta (Build 20252) +- Kontynuacja praca na sekcją strony 'Dokumentacja' +- Przeprojektowanie sprawdzania katalogu programu w appdata \ No newline at end of file diff --git a/documentation/about_program.html b/documentation/about_program.html index 780ad5a..39ef8e2 100644 --- a/documentation/about_program.html +++ b/documentation/about_program.html @@ -24,7 +24,7 @@

- Wersja 4.0 Beta (Build 20246) + Wersja 4.0 Beta (Build 20252)

diff --git a/documentation/instruction.html b/documentation/instruction.html index 6f7635f..7e1a31e 100644 --- a/documentation/instruction.html +++ b/documentation/instruction.html @@ -84,7 +84,7 @@

Nagłówek dla pliku wyjściowego office

Jeżeli chcesz aby w pierwszej linii pliku wyjściowego dla office znajdował się nagłowek, zaznacz opcję "Umieść w pliku". Zawartość nagłówka można edytować w polu tekstowym po lewej.

Rozpoczęcie roku szkolnego (DD | MM)
-

W tych polach należy wpisać datę rozpoczęcia roku szkolnego (z reguły). W pierwszym polu (po lewo) należy wpisać dzień, a w drugim (po prawo) miesiąc. Te dane są używane do obliczania roku w znaczniku klasy (przykłądowy znacznik klasy: 2023a).

+

W tych polach należy wpisać datę rozpoczęcia roku szkolnego (z reguły). W pierwszym polu (po lewo) należy wpisać dzień, a w drugim (po prawo) miesiąc. Te dane są używane do obliczania roku w znaczniku klasy (przykłądowy znacznik klasy: 2023a). W przypadku generowania plików na nowy rok szkolny przed jego rozpoczęciem, należy wprowadzić datę dzisiejszą lub wcześniejszą.

Dane o szkołach

W tym polu należy wprowadzić dane każdej szkoły w zespole szkół (po jednej szkole w linijce). Wymagane są trzy "kolumny danych". Są one odzielone znakami " | ".
W pierwszej kolumnie należy wpisać oznaczenie szkoły (dowolne bez spacji, najlepiej jak najkrótsze). Te dane będą używane w znaczniku klasy, jeżeli w trzeciej kolumnie została wybrana opcja "1".
W drugiej kolmnie należy wpisać liczbę klas w danej szkole. Te dane używane są do obliczenia roku w znaczniku klasy.
W trzeciej kolumnie można wybrać opcję "1" lub "0" (Prawda/Fałsz). Wybierz "1" jeżeli chcesz, aby w znaczniku klasy znajdowało się oznaczenie szkoły (przykład: 2023bs). Wybierz "0" jeżeli chcesz, aby w znaczniku klasy znajdowała się litera klasy (przykład: 2023a).


@@ -149,11 +149,7 @@ for (i = 0; i < acc.length; i++) { acc[i].addEventListener("click", function() { - /* Toggle between adding and removing the "active" class, - to highlight the button that controls the panel */ this.classList.toggle("active"); - - /* Toggle between hiding and showing the active panel */ var panel = this.nextElementSibling; if (panel.style.display === "block") { panel.style.display = "none"; diff --git a/documentation/main.css b/documentation/main.css index a4d8e5e..74bac21 100644 --- a/documentation/main.css +++ b/documentation/main.css @@ -72,9 +72,8 @@ section { iframe { float: left; - width: 99%; + width: 100%; border: none; - padding: 0.5%; height: calc(100% - 220px); overflow: scroll; position: absolute; diff --git a/documentation/program_documentation.html b/documentation/program_documentation.html index 0efebb7..c0b9031 100644 --- a/documentation/program_documentation.html +++ b/documentation/program_documentation.html @@ -7,29 +7,9 @@ Generator CSV - +
-

- Informacje o programie
-
- programName - Zmienna definująca nazwę programu
- programVersion - Zmienna definiująca główną wersję programu
- programVersionStage - Zmienna definiująca etap rozwoju programu. Wersja stabilna definiowana jest przez pusty string
- programVersionBuild - Zmienna definiująca numer buildu programu. Zapisany jest on w formacie '[ostatnie dwie cyfry roku][numer dnia w roku]'
- programCustomer - Zmienna definiująca nazwę organizacji zlecającej stworzenie programu
- programAuthors - Lista zawierająca nazwy osób rozwijających program.
- programToW - Lista zawierająca informacje na temat czasu rozwoju programu. Format: ['miesiąc rozpoczęcia prac', 'rok rozpoczęcia prac', 'aktualny dla rozwoju danej wersji miesiąc', 'aktualny dla rozwoju danej wersji rok']
-
-
- Dozwolone kodowanie plików
-
- allowedCoding - Lista zawierająca wspierane kodowania plików. Są one wyświetlane w rozwijanych listach wyboru kodowania pliku.
-
-
- Dozwolone znaki
-
- allowedCharactersInSeparator - Lista zawierająca znaki które można wpisać w pola dotyczące separatorów osób, linii i danych
-

+

Aby móc uruchomić aplikację w wersji developerskiej należy pobrać Pythona oraz wymagane biblioteki (wymienione w sekcji 'generator.pyw - Import bibliotek')

@@ -65,6 +45,36 @@

+ +
+

+ Informacje o programie
+
+ programName - Zmienna definująca nazwę programu
+ programVersion - Zmienna definiująca główną wersję programu
+ programVersionStage - Zmienna definiująca etap rozwoju programu. Wersja stabilna definiowana jest przez pusty string
+ programVersionBuild - Zmienna definiująca numer buildu programu. Zapisany jest on w formacie '[ostatnie dwie cyfry roku][numer dnia w roku]'
+ programCustomer - Zmienna definiująca nazwę organizacji zlecającej stworzenie programu
+ programAuthors - Lista zawierająca nazwy osób rozwijających program.
+ programToW - Lista zawierająca informacje na temat czasu rozwoju programu. Format: ['miesiąc rozpoczęcia prac', 'rok rozpoczęcia prac', 'aktualny dla rozwoju danej wersji miesiąc', 'aktualny dla rozwoju danej wersji rok']
+
+
+ Dozwolone kodowanie plików
+
+ allowedCoding - Lista zawierająca wspierane kodowania plików. Są one wyświetlane w rozwijanych listach wyboru kodowania pliku.
+
+
+ Dozwolone znaki
+
+ allowedCharactersInSeparator - Lista zawierająca znaki które można wpisać w pola dotyczące separatorów osób, linii i danych
+
+
+ Katalog APPDATA
+
+ appdataPath - zmienna definiująca ścieżkę do folderu %appdata% +

+
+

@@ -87,7 +97,204 @@
Wywoływanie funkcji: MSG([kod dialogowy],[True - Zakończ działanie programu po zamknięciu okna dialogowego/False - Kontynuuj działanie programu po zamknięciu okna dialogowego],[dodatkowe informacje wyświetlane w komunikacie - opcjonalnie])

+ + try:
+     optionalInfo[0]
+ except:
+     optionalInfo = ['']
+
+ Sprawdza czy zostały wpisane 'dodatkowe informacje wyświetlane w komunikacie' i jeżeli nie, definiuje listę, w której znajduje się pusty string (ma to na celu uniknięcia błędu podczas próby wywołania dodatkowych informacji przez dalszą część funkcji)
+
+ + if code[0] == 'E/I/W':
+     TKmsb.showerror/showinfo/showwarning('
[tytuł komunikatu]', '%s\n%s' % (MSGlist[code], optionalInfo[0]))
+
+ Wywoływanie danego rodzaju komunikatu na podstawie jednoznakowego znacznika rodzaju okna dialogowego.
+
+ + if terminate:
+     SS.exit(0)
+
+ Zakończenie działania programu po zamknięciu okna dialogowego, jeżeli została ustawiona opdowiednia opcja.
+
+ + elif code[0] == 'A':
+     if TKmsb.askokcancel('Pytanie', '%s\n%s' % (MSGlist[code], optionalInfo[0])):
+         return True
+     else:
+         return False
+
+ Funkcja zwraca wartość boolean w przypadku wywołania komunikatu zapytania +

+
+ +
+

+ Klasa checkAppdata odpowiedzialna jest za sprawdzenie poprawności i, w razie potrzeby, doprowadzenie go do stanu poprawności. +
+
+ Główna funkcja programu
+
+ 1. Czy w folderze %appdata% istnieje folder programu (Generator CSV)?
+ Jeżeli nie istnieje, uruchamiana jest funckja __buildAppdata (odpowiedzialna za zbudowanie całości katalogu programu).
+ 2. Czy w folderze programu istnieje plik 'version' (przechowuje on numer buildu programu, w którym został zbudowany katalog programu)?
+ Jeżeli nie istnieje, uruchamiane jest okno dialogowe zapytania z pytaniem czy pozwolić na zresetowanie katalogu programu i kontynuować ładowanie programu
+ 3. Czy numer buildu programu w pliku 'version' jest zgodny z numerem buildu zawartym w zmiennej VAR.programVersionBuild
+ Jeżeli nie, uruchamiane jest okno dialogowe zapytania z pytaniem czy pozwolić na zresetowanie katalogu programu i kontynuować ładowanie programu
+ Jeżeli tak, sprawdzane są poszczególne pliki i foldery które powinny być zawarte w folderze, i jeżeli któryś plik/folder nie znajduje się w folderze, wywołuje się funkcje mające na celu przywrócenie/utworzenie tych plików/folderów (odpowiednio: __restoreCFG dla plików konfiguracyjnych, __createFormatPresetsDir dla folderu przechowującego format presety)
+
+
+ Budowanie katalogu programu (__buildAppdata)
+
+ Funkcja ta, jest odpowiedzialna za zbudowanie całości katalogu programu w folderze %appdata%.
+ 1. Tworzenie katalogu 'Generator CSV'
+ 2. Tworzenie pliku 'version' i zapisanie w nim numeru buildu.
+ 3. Inicjacja funkcji __restoreCFG (w celu skopiowania pliku 'config.cfg' z głównego katalogu programu do katalogu 'Generator CSV' w folderze %appdata%).
+ 4. Inicjacja funkcji __restoreCFG (w celu skopiowania pliku 'style.cfg' z głównego katalogu programu do katalogu 'Generator CSV' w folderze %appdata%).
+ 5. Inicjacja funkcji __createFormatPresetsDir (w celu stworzenia folderu 'format-presets' przechowującego format presety)
+
+
+ Resetowanie katalogu programu (__resetAppdata)
+
+ Funkcja ta, jest odpowiedzialna za przywrócenie katalogu programu w folderze %appdata% do stanu podstawowego oraz stworzenie kopii zapasowej starego katalogu.
+ 1. W przypadku gdy w katalogu istnieje jeszcze starsza kopia zapasowa folderu, kopia ta jest usuwana.
+ 2. Nazwa katalogu programu jest zmieniana na taką z dopiskiem '_old'.
+ 3. Inicjacja funkcji __buildAppdata w celu zbudowania nowego katalogu programu
+ 4. Skopiowanie katalogu 'Generator CSV_old' do nowego katalogu 'Generator CSV'
+
+
+ Przywracanie plików konfiguracyjnych (__restoreCFG)
+
+ Funkcja ta, jest odpowiedzialna za skopiowanie pliku konfiguracyjnego (.cfg) o danej nazwie z głównego katalogu programu do katalogu programu w folderze %appdata%
+
+
+ Tworzenie katalogu przechowującego format presety (__createFormatPresetsDir)
+
+ Funkcja ta, jest odpowiedzialna za utworzenie folderu 'format-presets', przechowującego format presety, w katalogu programu w folderze %appdata% +

+
+ + +
+

+ Klasa CFG odpowiedzialna jest za zarządzanie plikiem konfiguracyjnym 'config.cfg'
+
+
+ Odczytywanie pojedyńczej zmiennej z pliku (R)
+
+ Funkcja ta, jest odpowiedzialna za odczytanie zawartości danego recordu i sprawdzenie jego poprawności
+ 1. Inicjacja funkcji __checkIfFileExist w celu sprawdzenia czy plik 'config.cfg' istnieje
+ 2. Zczytanie i zapisanie wszystkich danych z pliku 'config.cfg' do słownika content w formacie: 'nazwa zmiennej' : ['zmienna', 'typ zmiennej']
+ 3. Inicjacja funckji __checkIfRecordExist w celu sprawdzenia czy w słowniku content znajduje się żądany record
+ 4. Inicjacja, odpowiedniej dla typu zmiennej, funkcji mającej na celu sprawdzenie jej poprawności i jej przetworzenie
+ 5. Funkcja zwraca wartość zmiennej.
+
+
+ Zapisywanie zmian w pliku (W)
+
+ Funkcja ta jest odpowiedzialna za sprawdzenie, przetworzenie i zapisanie zmian w pliku konfiguracyjnych.
+ 1. Inicjacja funkcji __checkIfFileExist w celu sprawdzenia czy plik 'config.cfg' istnieje
+ 2. Zczytanie i zapisanie wszystkich danych z pliku 'config.cfg' do słownika content w formacie: 'nazwa zmiennej' : ['zmienna', 'typ zmiennej']
+ 3. Inicjacja, odpowiedniej dla typu zmiennej, funkcji mającej na celu sprawdzenie jej poprawności i jej przetworzenie, dla każdego recordu ze słownika changes
+ 4. Nadpisanie zmiennej ze słownika content, wartością var.
+ 5. Zapisanie zawartości słownika content w pliku 'config.cfg'
+ 6. Funkcja zwraca True jeżeli operacja została wykonana pomyślnie lub False jeżeli wystąpił błąd
+
+
+ Funkcje sprawdzające istnienie
+
+ __checkIfFileExist - funkcja sprawdza czy plik istnieje i możliwe jest jego nadpisanie (w przypadku zapisu)
+ __checkIfRecordExist - funkcja sprawdza czy record istnieje w słowniku content
+
+
+ Funkcje sprawdzające poprawność recordu
+
+ Funkcje te odpowiadają za sprawdzenie poprawności zmiennej i w razie potrzeby przetwarzają ją na pożądaną formę. +

+
+ + +
+

+ Klasa GUI odpowiedzialna jest za zarządzanie plikiem konfiguracyjnym 'style.cfg'
+
+
+ Odczytywanie pojedyńczej zmiennej z pliku (R)
+
+ Funkcja ta, jest odpowiedzialna za odczytanie zawartości danego recordu i sprawdzenie jego poprawności
+ 1. Inicjacja funkcji __checkIfFileExist w celu sprawdzenia czy plik 'style.cfg' istnieje
+ 2. Zczytanie i zapisanie wszystkich danych z pliku 'style.cfg' do słownika content w formacie: 'nazwa zmiennej' : ['zmienna', 'typ zmiennej']
+ 3. Inicjacja funkcji __checkIfRecordExist w celu sprawdzenia czy w słowniku content znajduje się żądany record
+ 4. Inicjacja, odpowiedniej dla typu zmiennej, funkcji mającej na celu sprawdzenie jej poprawności i jej przetworzenie
+ Funkcja zwraca wartość zmiennej.
+
+
+ Funkcje sprawdzające istnienie
+
+ __checkIfFileExist - funkcja sprawdza czy plik istnieje
+ __checkIfRecordExist - funkcja sprawdza czy record istnieje w słowniku content
+
+
+ Funkcje sprawdzające poprawność recordu
+
+ Funkcje te odpowiadają za sprawdzenie poprawności zmiennej i w razie potrzeby przetwarzają ją na pożądaną formę. +

+
+ + +
+

+ Klasa FMT odpowiedzialna jest za zarządzanie plikami formatu (.fmt)
+
+
+ Odczytywanie pojedyńczej zmiennej z pliku (R)
+
+ Funkcja ta, jest odpowiedzialna za odczytanie zawartości danego recordu i sprawdzenie jego poprawności
+ 1. Inicjacja funkcji __checkIfFolderExist w celu sprawdzenia czy folder 'format-presets' istnieje
+ 2. Sprawdzenie czy podany plik formatu (preset) znajduje się w folderze 'format-presets' (funkcja getList zwraca listę plików formatu w folderze 'format-presets')
+ Jeżeli nie, wartość zmiennej jest wybierana ze słownika content (zawierającego podstawowe wartości zmiennych)
+ Jeżeli tak: + 3. Zczytanie i zapisanie wszystkich danych z wybranego pliku formatu do słownika content w formacie: 'nazwa zmiennej' : ['zmienna', 'typ zmiennej']
+ 4. Inicjacja funkcji __checkIfRecordExist w celu sprawdzenia czy w słowniku content znajduje się żądany record
+ 5. Inicjacja, odpowiedniej dla typu zmiennej, funkcji mającej na celu sprawdzenie jej poprawności i jej przetworzenie
+ Funkcja zwraca wartość zmiennej.
+
+
+ Zapisywanie zmian w pliku (W)
+
+ Funkcja ta jest odpowiedzialna za sprawdzenie, przetworzenie i zapisanie zmian w pliku konfiguracyjnych.
+ 1. Inicjacja funkcji __checkIfFolderExist w celu sprawdzenia czy folder 'format-presets' istnieje
+ 2. Sprawdzenie czy podany plik formatu (preset) znajduje się w folderze 'format-presets' (funkcja getList zwraca listę plików formatu w folderze 'format-presets')
+ Jeżeli nie, tworzony jest słownik content z zawartością podstawową + Jeżeli tak: Zczytanie i zapisanie wszystkich danych z wybranego pliku formatu do słownika content w formacie: 'nazwa zmiennej' : ['zmienna', 'typ zmiennej']
+ 3. Inicjacja, odpowiedniej dla typu zmiennej, funkcji mającej na celu sprawdzenie jej poprawności i jej przetworzenie, dla każdego recordu ze słownika changes
+ 4. Nadpisanie zmiennej ze słownika content, wartością var.
+ 5. Zapisanie zawartości słownika content w pliku 'config.cfg'
+ 6. Funkcja zwraca True jeżeli operacja została wykonana pomyślnie lub False jeżeli wystąpił błąd
+
+
+ Funkcja zwracająca listę presetów (getList)
+
+ Funkcja ta odpowiedzialna jest za stworzenie listy plików .fmt w folderze 'format-presets' oraz za jej zwrócenie.
+
+
+ Funkcje sprawdzające istnienie
+
+ __checkIfFolderExist - funkcja sprawdza czy folder 'format-presets' istnieje (inicjuje checkAppdata)
+ __checkIfRecordExist - funkcja sprawdza czy record istnieje w słowniku content
+
+
+ Funkcje sprawdzające poprawność recordu
+
+ Funkcje te odpowiadają za sprawdzenie poprawności zmiennej i w razie potrzeby przetwarzają ją na pożądaną formę. +

+
+ + +
+

+

@@ -102,11 +309,7 @@ for (i = 0; i < acc.length; i++) { acc[i].addEventListener("click", function() { - /* Toggle between adding and removing the "active" class, - to highlight the button that controls the panel */ this.classList.toggle("active"); - - /* Toggle between hiding and showing the active panel */ var panel = this.nextElementSibling; if (panel.style.display === "block") { panel.style.display = "none"; diff --git a/generator.pyw b/generator.pyw index 347da8b..9cd7004 100644 --- a/generator.pyw +++ b/generator.pyw @@ -10,28 +10,6 @@ -# --------------------------------- # Główne zmienne globalne # --------------------------------- # - -class VAR: - # Informacje o programie - programName = 'Generator CSV' - programVersion = '4.0' - programVersionStage = 'Beta' - programVersionBuild = '20246' - programCustomer = 'ZSP Sobolew' - programAuthors = ['Mateusz Skoczek'] - programToW = ['styczeń', '2019', 'wrzesień', '2020'] - - # Dozwolone kodowanie plików - allowedCoding = ['utf-8', 'ANSI', 'iso-8859-2'] - - # Dozwolone znaki - allowedCharactersInSeparator = ['`', '~', '!', '@', '#', '$', '%', '^', '&', '(', ')', '-', '_', '=', '+', '[', ']', ' ', '?', '/', '>', '.', '<', ',', '"', "'", ':', ';', '|'] - - - - - # ------------------------------------- # Import bibliotek # ------------------------------------ # # Biblioteki główne @@ -56,6 +34,37 @@ from PIL import Image as PLimg +# --------------------------------- # Główne zmienne globalne # --------------------------------- # + +class VAR: + # Informacje o programie + programName = 'Generator CSV' + programVersion = '4.0' + programVersionStage = 'Beta' + programVersionBuild = '20252' + programCustomer = 'ZSP Sobolew' + programAuthors = ['Mateusz Skoczek'] + programToW = ['styczeń', '2019', 'wrzesień', '2020'] + + # Dozwolone kodowanie plików + allowedCoding = ['utf-8', 'ANSI', 'iso-8859-2'] + + # Dozwolone znaki + allowedCharactersInSeparator = ['`', '~', '!', '@', '#', '$', '%', '^', '&', '(', ')', '-', '_', '=', '+', '[', ']', ' ', '?', '/', '>', '.', '<', ',', '"', "'", ':', ';', '|'] + + # Katalog APPDATA + appdataPath = PT.Path.home() / 'Appdata/Roaming' + + + + + + + + + + + # -------------------------------------- # Okna dialogowe # ------------------------------------- # # Lista komunikatów @@ -85,6 +94,8 @@ MSGlist = { 'A0005' : 'Czy na pewno chcesz przywrócić domyślne ustawienia ogólne?', 'A0006' : 'Czy na pewno chcesz przywrócić domyślne ustawienia wyglądu?', 'A0007' : 'Czy na pewno chcesz usunąc zaznaczone format presety?', + 'A0008' : 'Nie znaleziono informacji o wersji programu w katalogu programu w APPDATA. Nastąpi zresetowanie katalogu programu w APPDATA oraz utworzenie kopii zapasowej dotychczasowej zawartości. Czy chcesz kontynuować?', + 'A0009' : 'Została zainstalowana nowa wersja programu. Nastąpi zresetowanie katalogu programu w APPDATA oraz utworzenie kopii zapasowej dotychczasowej zawartości. Czy chcesz kontynuować?', 'I0001' : 'Operacja ukończona pomyślnie', 'I0002' : 'Aplikacja zostanie zamknięta w celu przeładowania ustawień', } @@ -126,94 +137,233 @@ def MSG(code, terminate, *optionalInfo): -# ----------------------------------- # Opcje deweloperskie # ----------------------------------- # - -dev_config = [] -if 'dev.cfg' in [x for x in OS.listdir('.\configs')]: - try: - dev_config = CD.open(r'.\configs\dev.cfg', 'r', 'utf-8').read().split('\n') - except Exception as exceptInfo: - print('DEVELOPER CONSOLE LOG: Nie można załadować listy aktywnych opcji developerskich') - else: - print('DEVELOPER CONSOLE LOG: Pomyślnie załadowano listę aktywnych opcji developerskich') - print('DEVELOPER CONSOLE LOG: Lista aktywnych opcji developerskich: %s' % str(dev_config)) - - - - - # ------------------------- # Sprawdzanie katalogu programu w APPDATA # ------------------------- # -appdata = PT.Path.home() / 'Appdata/Roaming' +class checkAppdata: + def __init__(self): + if 'Generator CSV' in [x for x in OS.listdir(VAR.appdataPath)]: + if 'version' in [x for x in OS.listdir(str(VAR.appdataPath) + '\Generator CSV')]: + versionFile = CD.open((str(VAR.appdataPath) + r'\Generator CSV\version'), 'r', 'utf-8') + if versionFile.read() == VAR.programVersionBuild: + versionFile.close() + if 'config.cfg' not in [x for x in OS.listdir(str(VAR.appdataPath) + '\Generator CSV')]: + self.__restoreCFG('config') + if 'style.cfg' not in [x for x in OS.listdir(str(VAR.appdataPath) + '\Generator CSV')]: + self.__restoreCFG('style') + if 'format-presets' not in [x for x in OS.listdir(str(VAR.appdataPath) + '\Generator CSV')]: + self.__createFormatPresetsDir() + else: + versionFile.close() + if MSG('A0009', False): + self.__resetAppdata() + MSG('I0002', True) + else: + SS.exit(0) + else: + if MSG('A0008', False): + self.__resetAppdata() + MSG('I0002', True) + else: + SS.exit(0) + else: + self.__buildAppdata() -if 'reset_appdata' in dev_config: - try: - SU.rmtree(str(appdata) + '\Generator CSV') - OS.mkdir(str(appdata) + '\Generator CSV') - versionFile = CD.open((str(appdata) + r'\Generator CSV\version'), 'w', 'utf-8') - versionFile.write(VAR.programVersionBuild) - versionFile.close() - SU.copy('configs\config.cfg', str(appdata) + '\Generator CSV\config.cfg') - SU.copy('configs\style.cfg', str(appdata) + '\Generator CSV\style.cfg') - OS.mkdir(str(appdata) + r'\Generator CSV\format-presets') - except Exception as exceptInfo: - print("DEVELOPER CONSOLE LOG: Folder 'Generator CSV' w folderze 'APPDATA' nie został zresetowany z powodu błędu: %s" % exceptInfo) - else: - print("DEVELOPER CONSOLE LOG: Folder 'Generator CSV' w folderze 'APPDATA' został zresetowany pomyślnie") -def checkAppdata(): - if 'Generator CSV' not in [x for x in OS.listdir(appdata)]: + # Budowanie katalogu programu + def __buildAppdata(self): try: - OS.mkdir(str(appdata) + '\Generator CSV') - versionFile = CD.open((str(appdata) + r'\Generator CSV\version'), 'w', 'utf-8') + OS.mkdir(str(VAR.appdataPath) + '\Generator CSV') + + versionFile = CD.open((str(VAR.appdataPath) + r'\Generator CSV\version'), 'w', 'utf-8') versionFile.write(VAR.programVersionBuild) versionFile.close() - SU.copy('configs\config.cfg', str(appdata) + '\Generator CSV\config.cfg') - SU.copy('configs\style.cfg', str(appdata) + '\Generator CSV\style.cfg') - OS.mkdir(str(appdata) + r'\Generator CSV\format-presets') except Exception as exceptInfo: MSG('E0001', True, exceptInfo) - else: - if 'version' not in [x for x in OS.listdir(str(appdata) + '\Generator CSV')]: - SU.rmtree(str(appdata) + '\Generator CSV') - checkAppdata() - else: - versionFile = CD.open((str(appdata) + r'\Generator CSV\version'), 'r', 'utf-8') - if versionFile.read() != VAR.programVersionBuild: - versionFile.close() - SU.rmtree(str(appdata) + '\Generator CSV') - checkAppdata() - if 'config.cfg' not in [x for x in OS.listdir(str(appdata) + '\Generator CSV')]: - try: - SU.copy('configs\config.cfg', str(appdata) + '\Generator CSV\config.cfg') - except Exception as exceptInfo: - MSG('E0001', True, exceptInfo) - if 'style.cfg' not in [x for x in OS.listdir(str(appdata) + '\Generator CSV')]: - try: - SU.copy('configs\style.cfg', str(appdata) + '\Generator CSV\style.cfg') - except Exception as exceptInfo: - MSG('E0001', True, exceptInfo) - if 'format-presets' not in [x for x in OS.listdir(str(appdata) + '\Generator CSV')]: - try: - OS.mkdir(str(appdata) + r'\Generator CSV\format-presets') - except Exception as exceptInfo: - MSG('E0001', True, exceptInfo) + + self.__restoreCFG('config') + self.__restoreCFG('style') + self.__createFormatPresetsDir() + + # Resetowanie katalogu programu + def __resetAppdata(self): + try: + if 'Generator CSV_old' in [x for x in OS.listdir(str(VAR.appdataPath) + '\Generator CSV')]: + SU.rmtree(str(VAR.appdataPath) + '\Generator CSV\Generator CSV_old') + OS.rename((str(VAR.appdataPath) + '\Generator CSV'), (str(VAR.appdataPath) + '\Generator CSV_old')) + except Exception as exceptInfo: + MSG('E0001', True, exceptInfo) + + self.__buildAppdata() + + try: + SU.move((str(VAR.appdataPath) + '\Generator CSV_old'), (str(VAR.appdataPath) + '\Generator CSV\Generator CSV_old')) + except Exception as exceptInfo: + MSG('E0001', True, exceptInfo) + + + # Przywracanie plików konfiguracyjnych + def __restoreCFG(self, configFileName): + try: + SU.copy(('configs\%s.cfg' % configFileName), str(VAR.appdataPath) + ('\Generator CSV\%s.cfg' % configFileName)) + except Exception as exceptInfo: + MSG('E0001', True, exceptInfo) + + + # Tworzenie katalogu przechowującego format presety + def __createFormatPresetsDir(self): + try: + OS.mkdir(str(VAR.appdataPath) + r'\Generator CSV\format-presets') + except Exception as exceptInfo: + MSG('E0001', True, exceptInfo) + + + checkAppdata() -# ----------------------------- # Ładowanie pliku konfiguracyjnego # ---------------------------- # +# ------------------ # Ładowanie głównego pliku konfiguracyjnego 'config.cfg' # ----------------- # class CFG: + def R(self, record): + self.__checkIfFileExist(False) + content = {} + for x in CD.open((str(VAR.appdataPath) + '\Generator CSV\config.cfg'), 'r', 'utf-8').read().strip('\r').split('\n'): + x = x.split(' = ') + try: + name = x[0].split('(')[0] + var = x[1] + type = x[0].split('(')[1].strip(')') + content[name] = [var, type] + except: + continue + checkingOutput = self.__checkIfRecordExist(content, record) + if not checkingOutput[0]: + MSG('E0003', True, checkingOutput[1]) + var = content[record] + if var[1] == 'S': + # String + var = var[0].strip('\r') + return var + elif var[1] == 'Sc': + # Integer + checkingOutput = self.__checkSc(record, var[0]) + if checkingOutput[0]: + return checkingOutput[1] + else: + MSG('E0003', True, checkingOutput[1]) + elif var[1] == 'I': + # Integer + checkingOutput = self.__checkI(False, record, var[0]) + if checkingOutput[0]: + return checkingOutput[1] + else: + MSG('E0003', True, checkingOutput[1]) + elif var[1] == 'D': + # Date (DD.MM.RRRR HH:MM:SS) + checkingOutput = self.__checkD(False, record, var[0]) + if checkingOutput[0]: + return checkingOutput[1] + else: + MSG('E0003', True, checkingOutput[1]) + elif var[1] == 'MSAs': + # Multiple Specified Arrays - schoolData + checkingOutput = self.__checkMSAs(False, record, var[0]) + if checkingOutput[0]: + return checkingOutput[1] + else: + MSG('E0003', True, checkingOutput[1]) + elif var[1] == 'B': + # Boolean + checkingOutput = self.__checkB(False, record, var[0]) + if checkingOutput[0]: + return checkingOutput[1] + else: + MSG('E0003', True, checkingOutput[1]) + else: + MSG('E0003', True, 'Nie można rozpoznać typu klucza %s' % record) + + def W(self, changes): + self.__checkIfFileExist(True) + file = CD.open(str(VAR.appdataPath) + '\Generator CSV\config.cfg', 'r', 'utf-8').read().split('\n') + if file[-1] == '': + file = file[:-1] + content = {} + for x in file: + x = x.split(' = ') + try: + name = x[0].split('(')[0] + var = x[1] + type = x[0].split('(')[1].strip(')') + content[name] = [var, type] + except Exception as exceptInfo: + MSG('E0003', False, exceptInfo) + for x in changes: + name = x + var = changes[name] + type = (content[name])[1] + if type == 'S': + # String + pass + elif type == 'Sc': + # Integer + checkingOutput = self.__checkSc(name, var) + if checkingOutput[0]: + var = checkingOutput[1] + else: + MSG('E0006', False, checkingOutput[1]) + return False + elif type == 'I': + # Integer + checkingOutput = self.__checkI(True, name, var) + if checkingOutput[0]: + var = checkingOutput[1] + else: + MSG('E0006', False, checkingOutput[1]) + return False + elif type == 'D': + # Date (DD.MM.RRRR HH:MM:SS) + checkingOutput = self.__checkD(True, name, var) + if checkingOutput[0]: + var = checkingOutput[1] + else: + MSG('E0006', False, checkingOutput[1]) + return False + elif type == 'MSAs': + # Multiple Specified Arrays - schoolData + checkingOutput = self.__checkMSAs(True, name, var) + if checkingOutput[0]: + var = checkingOutput[1] + else: + MSG('E0006', False, checkingOutput[1]) + return False + elif type == 'B': + # Boolean + checkingOutput = self.__checkB(True, name, var) + if checkingOutput[0]: + var = checkingOutput[1] + else: + MSG('E0006', False, checkingOutput[1]) + return False + else: + MSG('E0003', False, 'Nie można rozpoznać typu klucza %s' % name) + return False + content[name] = [var, type] + with CD.open(str(VAR.appdataPath) + '\Generator CSV\config.cfg', 'w', 'utf-8') as file: + for x in content: + file.write('%s(%s) = %s\n' % (x, (content[x])[1], (content[x][0]))) + return True + + # Funkcje sprawdzające istnienie def __checkIfFileExist(self, write): if write: try: checkAppdata() - file = open((str(appdata) + '\Generator CSV\config.cfg'), 'a') + file = open((str(VAR.appdataPath) + '\Generator CSV\config.cfg'), 'a') except Exception as exceptInfo: MSG('E0002', True, exceptInfo) return False @@ -226,7 +376,7 @@ class CFG: else: try: checkAppdata() - open(str(appdata) + '\Generator CSV\config.cfg') + open(str(VAR.appdataPath) + '\Generator CSV\config.cfg') except Exception as exceptInfo: MSG('E0002', True, exceptInfo) @@ -431,151 +581,82 @@ class CFG: - def R(self, record): - self.__checkIfFileExist(False) - content = {} - for x in CD.open((str(appdata) + '\Generator CSV\config.cfg'), 'r', 'utf-8').read().strip('\r').split('\n'): - x = x.split(' = ') - try: - name = x[0].split('(')[0] - var = x[1] - type = x[0].split('(')[1].strip(')') - content[name] = [var, type] - except: - continue - checkingOutput = self.__checkIfRecordExist(content, record) - if not checkingOutput[0]: - MSG('E0003', True, checkingOutput[1]) - var = content[record] - if var[1] == 'S': - # String - var = var[0].strip('\r') - return var - elif var[1] == 'Sc': - # Integer - checkingOutput = self.__checkSc(record, var[0]) - if checkingOutput[0]: - return checkingOutput[1] - else: - MSG('E0003', True, checkingOutput[1]) - elif var[1] == 'I': - # Integer - checkingOutput = self.__checkI(False, record, var[0]) - if checkingOutput[0]: - return checkingOutput[1] - else: - MSG('E0003', True, checkingOutput[1]) - elif var[1] == 'D': - # Date (DD.MM.RRRR HH:MM:SS) - checkingOutput = self.__checkD(False, record, var[0]) - if checkingOutput[0]: - return checkingOutput[1] - else: - MSG('E0003', True, checkingOutput[1]) - elif var[1] == 'MSAs': - # Multiple Specified Arrays - schoolData - checkingOutput = self.__checkMSAs(False, record, var[0]) - if checkingOutput[0]: - return checkingOutput[1] - else: - MSG('E0003', True, checkingOutput[1]) - elif var[1] == 'B': - # Boolean - checkingOutput = self.__checkB(False, record, var[0]) - if checkingOutput[0]: - return checkingOutput[1] - else: - MSG('E0003', True, checkingOutput[1]) - else: - MSG('E0003', True, 'Nie można rozpoznać typu klucza %s' % record) - - def W(self, changes): - self.__checkIfFileExist(True) - file = CD.open(str(appdata) + '\Generator CSV\config.cfg', 'r', 'utf-8').read().split('\n') - if file[-1] == '': - file = file[:-1] - content = {} - for x in file: - x = x.split(' = ') - try: - name = x[0].split('(')[0] - var = x[1] - type = x[0].split('(')[1].strip(')') - content[name] = [var, type] - except Exception as exceptInfo: - MSG('E0003', False, exceptInfo) - for x in changes: - name = x - var = changes[name] - type = (content[name])[1] - if type == 'S': - # String - pass - elif type == 'Sc': - # Integer - checkingOutput = self.__checkSc(name, var) - if checkingOutput[0]: - var = checkingOutput[1] - else: - MSG('E0006', False, checkingOutput[1]) - return False - elif type == 'I': - # Integer - checkingOutput = self.__checkI(True, name, var) - if checkingOutput[0]: - var = checkingOutput[1] - else: - MSG('E0006', False, checkingOutput[1]) - return False - elif type == 'D': - # Date (DD.MM.RRRR HH:MM:SS) - checkingOutput = self.__checkD(True, name, var) - if checkingOutput[0]: - var = checkingOutput[1] - else: - MSG('E0006', False, checkingOutput[1]) - return False - elif type == 'MSAs': - # Multiple Specified Arrays - schoolData - checkingOutput = self.__checkMSAs(True, name, var) - if checkingOutput[0]: - var = checkingOutput[1] - else: - MSG('E0006', False, checkingOutput[1]) - return False - elif type == 'B': - # Boolean - checkingOutput = self.__checkB(True, name, var) - if checkingOutput[0]: - var = checkingOutput[1] - else: - MSG('E0006', False, checkingOutput[1]) - return False - else: - MSG('E0003', False, 'Nie można rozpoznać typu klucza %s' % name) - return False - content[name] = [var, type] - with CD.open(str(appdata) + '\Generator CSV\config.cfg', 'w', 'utf-8') as file: - for x in content: - file.write('%s(%s) = %s\n' % (x, (content[x])[1], (content[x][0]))) - return True - - - CFG = CFG() -# ---------------------------------- # Ładowanie pliku stylu # ---------------------------------- # +# -------------------- # Ładowanie pliku konfiguracyjnego stylu 'style.cfg' # ------------------- # class GUI: + # Odczytywanie pojedyńczej zmiennej z pliku + def R(self, record): + self.__checkIfFileExist() + content = {} + for x in CD.open((str(VAR.appdataPath) + '\Generator CSV\style.cfg'), 'r', 'utf-8').read().strip('\r').split('\n'): + x = x.split(' = ') + try: + name = x[0].split('(')[0] + var = x[1] + type = x[0].split('(')[1].strip(')') + content[name] = [var.strip('\r'), type] + except: + continue + checkingOutput = self.__checkIfRecordExist(content, record) + if not checkingOutput[0]: + MSG('E0005', True, checkingOutput[1]) + var = content[record] + if var[1] == 'I': + # Integer + checkingOutput = self.__checkI(record, var[0]) + if checkingOutput[0]: + return checkingOutput[1] + else: + MSG('E0005', True, checkingOutput[1]) + elif var[1] == 'B': + # Boolean + checkingOutput = self.__checkB(record, var[0]) + if checkingOutput[0]: + return checkingOutput[1] + else: + MSG('E0005', True, checkingOutput[1]) + elif var[1] == 'C': + # Color + checkingOutput = self.__checkC(record, var[0]) + if checkingOutput[0]: + return checkingOutput[1] + else: + MSG('E0005', True, checkingOutput[1]) + elif var[1] == 'P': + # Path + checkingOutput = self.__checkP(record, var[0]) + if checkingOutput[0]: + return checkingOutput[1] + else: + MSG('E0005', True, checkingOutput[1]) + elif (var[1])[:2] == 'FA': + # From Array + checkingOutput = self.__checkFA(record, var[0], (var[1])[2:]) + if checkingOutput[0]: + return checkingOutput[1] + else: + MSG('E0005', True, checkingOutput[1]) + elif var[1] == 'F': + # Font + checkingOutput = self.__checkF(record, var[0]) + if checkingOutput[0]: + return checkingOutput[1] + else: + MSG('E0005', True, checkingOutput[1]) + else: + MSG('E0005', True, 'Nie można rozpoznać typu klucza %s' % record) + # Funkcje sprawdzające istnienie def __checkIfFileExist(self): try: checkAppdata() - open(str(appdata) + '\Generator CSV\style.cfg') + open(str(VAR.appdataPath) + '\Generator CSV\style.cfg') except Exception as exceptInfo: checkAppdata() @@ -643,69 +724,6 @@ class GUI: else: var = (var.split(';')[0], int(var.split(';')[1])) return [True, var] - - - - def R(self, record): - self.__checkIfFileExist() - content = {} - for x in CD.open((str(appdata) + '\Generator CSV\style.cfg'), 'r', 'utf-8').read().strip('\r').split('\n'): - x = x.split(' = ') - try: - name = x[0].split('(')[0] - var = x[1] - type = x[0].split('(')[1].strip(')') - content[name] = [var.strip('\r'), type] - except: - continue - checkingOutput = self.__checkIfRecordExist(content, record) - if not checkingOutput[0]: - MSG('E0005', True, checkingOutput[1]) - var = content[record] - if var[1] == 'I': - # Integer - checkingOutput = self.__checkI(record, var[0]) - if checkingOutput[0]: - return checkingOutput[1] - else: - MSG('E0005', True, checkingOutput[1]) - elif var[1] == 'B': - # Boolean - checkingOutput = self.__checkB(record, var[0]) - if checkingOutput[0]: - return checkingOutput[1] - else: - MSG('E0005', True, checkingOutput[1]) - elif var[1] == 'C': - # Color - checkingOutput = self.__checkC(record, var[0]) - if checkingOutput[0]: - return checkingOutput[1] - else: - MSG('E0005', True, checkingOutput[1]) - elif var[1] == 'P': - # Path - checkingOutput = self.__checkP(record, var[0]) - if checkingOutput[0]: - return checkingOutput[1] - else: - MSG('E0005', True, checkingOutput[1]) - elif (var[1])[:2] == 'FA': - # From Array - checkingOutput = self.__checkFA(record, var[0], (var[1])[2:]) - if checkingOutput[0]: - return checkingOutput[1] - else: - MSG('E0005', True, checkingOutput[1]) - elif var[1] == 'F': - # Font - checkingOutput = self.__checkF(record, var[0]) - if checkingOutput[0]: - return checkingOutput[1] - else: - MSG('E0005', True, checkingOutput[1]) - else: - MSG('E0005', True, 'Nie można rozpoznać typu klucza %s' % record) @@ -718,6 +736,186 @@ GUI = GUI() # ------------------------------- # Zarządzanie plikami formatu # ------------------------------- # class FMT: + # Odczytywanie pojedyńczej zmiennej z pliku + def R(self, preset, record): + self.__checkIfFolderExist() + if preset in self.getList(): + path = str(VAR.appdataPath) + '/Generator CSV/format-presets/%s.fmt' % preset + file = CD.open(path, 'r', 'utf-8').read().strip('\r').split('\n') + content = {} + for x in file: + x = x.split(' = ') + try: + name = x[0].split('(')[0] + var = x[1] + type = x[0].split('(')[1].strip(')') + content[name] = [var, type] + except: + continue + checkingOutput = self.__checkIfRecordExist(content, record) + if not checkingOutput[0]: + MSG('E0006', False, checkingOutput[1]) + var = content[record] + if var[1] == 'B': + # Boolean + checkingOutput = self.__checkB(False, record, var[0]) + if checkingOutput[0]: + return checkingOutput[1] + else: + MSG('E0006', False, checkingOutput[1]) + elif var[1] == 'Ss': + # String - separator + checkingOutput = self.__checkSs(record, var[0]) + if checkingOutput[0]: + return checkingOutput[1] + else: + MSG('E0006', False, checkingOutput[1]) + elif var[1] == 'As': + # Array - separator + checkingOutput = self.__checkAs(False, record, var[0]) + if checkingOutput[0]: + return checkingOutput[1] + else: + MSG('E0006', False, checkingOutput[1]) + elif var[1] == 'I': + # Integer + checkingOutput = self.__checkI(False, record, var[0]) + if checkingOutput[0]: + return checkingOutput[1] + else: + MSG('E0006', False, checkingOutput[1]) + elif var[1] == 'Sc': + # Integer + checkingOutput = self.__checkSc(record, var[0]) + if checkingOutput[0]: + return checkingOutput[1] + else: + MSG('E0006', False, checkingOutput[1]) + else: + MSG('E0006', True, 'Nie można rozpoznać typu klucza %s' % record) + else: + content = { + "student" : True, + "personSeparator" : '', + "rowSeparator" : '', + "dataSeparators" : [], + "loginRow" : 0, + "loginPositionInRow" : 0, + "fnameRow" : 0, + "fnamePositionInRow" : 0, + "lnameRow" : 0, + "lnamePositionInRow" : 0, + "schoolRow" : 0, + "schoolPositionInRow" : 0, + "classRow" : 0, + "classPositionInRow" : 0, + "inputCoding" : 'utf-8', + } + var = content[record] + return var + + # Zapisywanie zmian w pliku + def W(self, preset, changes): + self.__checkIfFolderExist() + if preset in self.getList(): + file = CD.open(str(VAR.appdataPath) + '/Generator CSV/format-presets/%s.fmt' % preset, 'r', 'utf-8').read().split('\n') + if file[-1] == '': + file = file[:-1] + content = {} + for x in file: + x = x.split(' = ') + try: + name = x[0].split('(')[0] + var = x[1] + type = x[0].split('(')[1].strip(')') + content[name] = [var, type] + except Exception as exceptInfo: + MSG('E0006', False, exceptInfo) + else: + content = { + "student" : ['1', 'B'], + "personSeparator" : ['', 'Ss'], + "rowSeparator" : ['', 'Ss'], + "dataSeparators" : ['', 'As'], + "loginRow" : ['0', 'I'], + "loginPositionInRow" : ['0', 'I'], + "fnameRow" : ['0', 'I'], + "fnamePositionInRow" : ['0', 'I'], + "lnameRow" : ['0', 'I'], + "lnamePositionInRow" : ['0', 'I'], + "schoolRow" : ['0', 'I'], + "schoolPositionInRow" : ['0', 'I'], + "classRow" : ['0', 'I'], + "classPositionInRow" : ['0', 'I'], + "inputCoding" : ['utf-8', 'Sc'] + } + for x in changes: + name = x + var = changes[name] + type = (content[name])[1] + if type == 'B': + checkingOutput = self.__checkB(True, name, var) + if checkingOutput[0]: + var = checkingOutput[1] + else: + MSG('E0006', False, checkingOutput[1]) + return False + elif type == 'Ss': + checkingOutput = self.__checkSs(name, var) + if checkingOutput[0]: + var = checkingOutput[1] + else: + MSG('E0006', False, checkingOutput[1]) + return False + elif type == 'As': + checkingOutput = self.__checkAs(True, name, var) + if checkingOutput[0]: + var = checkingOutput[1] + else: + MSG('E0006', False, checkingOutput[1]) + return False + elif type == 'I': + # Integer + checkingOutput = self.__checkI(True, name, var) + if checkingOutput[0]: + var = checkingOutput[1] + else: + MSG('E0006', False, checkingOutput[1]) + return False + elif type == 'Sc': + checkingOutput = self.__checkSc(name, var) + if checkingOutput[0]: + var = checkingOutput[1] + else: + MSG('E0006', False, checkingOutput[1]) + return False + else: + MSG('E0006', False, 'Nie można rozpoznać typu klucza %s' % name) + return False + content[name] = [var, type] + try: + with CD.open(str(VAR.appdataPath) + '/Generator CSV/format-presets/%s.fmt' % preset, 'w', 'utf-8') as file: + for x in content: + file.write('%s(%s) = %s\n' % (x, (content[x])[1], (content[x][0]))) + except Exception as exceptInfo: + MSG('E0017', False, exceptInfo) + return False + return True + + + # Funkcja zwracająca listę presetów + def getList(self): + self.__checkIfFolderExist() + filesList = OS.listdir(str(VAR.appdataPath) + '/Generator CSV/format-presets') + formatPresetsList = [] + for x in filesList: + if x[-4:] == '.fmt': + formatPresetsList.append(x[:-4]) + else: + continue + return formatPresetsList + + # Funkcje sprawdzające istnienie def __checkIfFolderExist(self): checkAppdata() @@ -800,185 +998,6 @@ class FMT: return [True, var] - # Funkcja zwracająca listę presetów - def getList(self): - self.__checkIfFolderExist() - filesList = OS.listdir(str(appdata) + '/Generator CSV/format-presets') - formatPresetsList = [] - for x in filesList: - if x[-4:] == '.fmt': - formatPresetsList.append(x[:-4]) - else: - continue - return formatPresetsList - - - - def R(self, preset, record): - self.__checkIfFolderExist() - if preset in self.getList(): - path = str(appdata) + '/Generator CSV/format-presets/%s.fmt' % preset - file = CD.open(path, 'r', 'utf-8').read().strip('\r').split('\n') - content = {} - for x in file: - x = x.split(' = ') - try: - name = x[0].split('(')[0] - var = x[1] - type = x[0].split('(')[1].strip(')') - content[name] = [var, type] - except: - continue - checkingOutput = self.__checkIfRecordExist(content, record) - if not checkingOutput[0]: - MSG('E0006', False, checkingOutput[1]) - var = content[record] - if var[1] == 'B': - # Boolean - checkingOutput = self.__checkB(False, record, var[0]) - if checkingOutput[0]: - return checkingOutput[1] - else: - MSG('E0006', False, checkingOutput[1]) - elif var[1] == 'Ss': - # String - separator - checkingOutput = self.__checkSs(record, var[0]) - if checkingOutput[0]: - return checkingOutput[1] - else: - MSG('E0006', False, checkingOutput[1]) - elif var[1] == 'As': - # Array - separator - checkingOutput = self.__checkAs(False, record, var[0]) - if checkingOutput[0]: - return checkingOutput[1] - else: - MSG('E0006', False, checkingOutput[1]) - elif var[1] == 'I': - # Integer - checkingOutput = self.__checkI(False, record, var[0]) - if checkingOutput[0]: - return checkingOutput[1] - else: - MSG('E0006', False, checkingOutput[1]) - elif var[1] == 'Sc': - # Integer - checkingOutput = self.__checkSc(record, var[0]) - if checkingOutput[0]: - return checkingOutput[1] - else: - MSG('E0006', False, checkingOutput[1]) - else: - MSG('E0006', True, 'Nie można rozpoznać typu klucza %s' % record) - else: - content = { - "student" : True, - "personSeparator" : '', - "rowSeparator" : '', - "dataSeparators" : [], - "loginRow" : 0, - "loginPositionInRow" : 0, - "fnameRow" : 0, - "fnamePositionInRow" : 0, - "lnameRow" : 0, - "lnamePositionInRow" : 0, - "schoolRow" : 0, - "schoolPositionInRow" : 0, - "classRow" : 0, - "classPositionInRow" : 0, - "inputCoding" : 'utf-8', - } - var = content[record] - return var - - def W(self, preset, changes): - self.__checkIfFolderExist() - if preset in self.getList(): - file = CD.open(str(appdata) + '/Generator CSV/format-presets/%s.fmt' % preset, 'r', 'utf-8').read().split('\n') - if file[-1] == '': - file = file[:-1] - content = {} - for x in file: - x = x.split(' = ') - try: - name = x[0].split('(')[0] - var = x[1] - type = x[0].split('(')[1].strip(')') - content[name] = [var, type] - except Exception as exceptInfo: - MSG('E0006', False, exceptInfo) - else: - content = { - "student" : ['1', 'B'], - "personSeparator" : ['', 'Ss'], - "rowSeparator" : ['', 'Ss'], - "dataSeparators" : ['', 'As'], - "loginRow" : ['0', 'I'], - "loginPositionInRow" : ['0', 'I'], - "fnameRow" : ['0', 'I'], - "fnamePositionInRow" : ['0', 'I'], - "lnameRow" : ['0', 'I'], - "lnamePositionInRow" : ['0', 'I'], - "schoolRow" : ['0', 'I'], - "schoolPositionInRow" : ['0', 'I'], - "classRow" : ['0', 'I'], - "classPositionInRow" : ['0', 'I'], - "inputCoding" : ['utf-8', 'Sc'] - } - for x in changes: - name = x - var = changes[name] - type = (content[name])[1] - if type == 'B': - checkingOutput = self.__checkB(True, name, var) - if checkingOutput[0]: - var = checkingOutput[1] - else: - MSG('E0006', False, checkingOutput[1]) - return False - elif type == 'Ss': - checkingOutput = self.__checkSs(name, var) - if checkingOutput[0]: - var = checkingOutput[1] - else: - MSG('E0006', False, checkingOutput[1]) - return False - elif type == 'As': - checkingOutput = self.__checkAs(True, name, var) - if checkingOutput[0]: - var = checkingOutput[1] - else: - MSG('E0006', False, checkingOutput[1]) - return False - elif type == 'I': - # Integer - checkingOutput = self.__checkI(True, name, var) - if checkingOutput[0]: - var = checkingOutput[1] - else: - MSG('E0006', False, checkingOutput[1]) - return False - elif type == 'Sc': - checkingOutput = self.__checkSc(name, var) - if checkingOutput[0]: - var = checkingOutput[1] - else: - MSG('E0006', False, checkingOutput[1]) - return False - else: - MSG('E0006', False, 'Nie można rozpoznać typu klucza %s' % name) - return False - content[name] = [var, type] - try: - with CD.open(str(appdata) + '/Generator CSV/format-presets/%s.fmt' % preset, 'w', 'utf-8') as file: - for x in content: - file.write('%s(%s) = %s\n' % (x, (content[x])[1], (content[x][0]))) - except Exception as exceptInfo: - MSG('E0017', False, exceptInfo) - return False - return True - - FMT = FMT() @@ -3154,8 +3173,8 @@ class mainWindow: def settingsButtonPDUOAction(self): if MSG('A0005', False): try: - OS.remove(str(appdata) + '\Generator CSV\config.cfg') - SU.copy('configs/config.cfg', str(appdata) + '\Generator CSV\config.cfg') + OS.remove(str(VAR.appdataPath) + '\Generator CSV\config.cfg') + SU.copy('configs/config.cfg', str(VAR.appdataPath) + '\Generator CSV\config.cfg') except Exception as exceptInfo: MSG('E0001', True, exceptInfo) MSG('I0002', True) @@ -3165,8 +3184,8 @@ class mainWindow: def settingsButtonPDUWAction(self): if MSG('A0006', False): try: - OS.remove(str(appdata) + '\Generator CSV\style.cfg') - SU.copy('configs/style.cfg', str(appdata) + '\Generator CSV\style.cfg') + OS.remove(str(VAR.appdataPath) + '\Generator CSV\style.cfg') + SU.copy('configs/style.cfg', str(VAR.appdataPath) + '\Generator CSV\style.cfg') except Exception as exceptInfo: MSG('E0001', True, exceptInfo) MSG('I0002', True) @@ -3178,7 +3197,7 @@ class mainWindow: selected = self.selectFPListbox.curselection() for x in selected: try: - OS.remove(str(appdata) + ('/Generator CSV/format-presets') + ('\%s.fmt' % self.selectFPListbox.get(x))) + OS.remove(str(VAR.appdataPath) + ('/Generator CSV/format-presets') + ('\%s.fmt' % self.selectFPListbox.get(x))) except Exception as exceptInfo: MSG('E0015', True, exceptInfo) MSG('I0001', False) From fd82416b85b93036a822fe20f902be0048127046 Mon Sep 17 00:00:00 2001 From: Mateusz Skoczek Date: Wed, 9 Sep 2020 00:01:02 +0200 Subject: [PATCH 28/29] 4.0 Beta (Build 20253) --- dev-changelog.txt | 6 +- documentation/about_program.html | 2 +- documentation/program_documentation.html | 75 ++++++++- generator.pyw | 189 +++++++++++------------ 4 files changed, 170 insertions(+), 102 deletions(-) diff --git a/dev-changelog.txt b/dev-changelog.txt index 81bd87c..c99cca9 100644 --- a/dev-changelog.txt +++ b/dev-changelog.txt @@ -151,4 +151,8 @@ 4.0 Beta (Build 20252) - Kontynuacja praca na sekcją strony 'Dokumentacja' -- Przeprojektowanie sprawdzania katalogu programu w appdata \ No newline at end of file +- Przeprojektowanie sprawdzania katalogu programu w appdata + +4.0 Beta (Build 20253) +- Ukończenie sekcji strony 'Dokumentacja' +- Dodano informację o ilości osób, których dane zostały przetworzone \ No newline at end of file diff --git a/documentation/about_program.html b/documentation/about_program.html index 39ef8e2..f8e85f9 100644 --- a/documentation/about_program.html +++ b/documentation/about_program.html @@ -24,7 +24,7 @@

- Wersja 4.0 Beta (Build 20252) + Wersja 4.0 Beta (Build 20253)

diff --git a/documentation/program_documentation.html b/documentation/program_documentation.html index c0b9031..790af67 100644 --- a/documentation/program_documentation.html +++ b/documentation/program_documentation.html @@ -9,7 +9,7 @@

-

Aby móc uruchomić aplikację w wersji developerskiej należy pobrać Pythona oraz wymagane biblioteki (wymienione w sekcji 'generator.pyw - Import bibliotek')

+

Aby móc uruchomić aplikację w wersji developerskiej należy pobrać Pythona (wersja zalecana: 3.8.5) oraz wymagane biblioteki (wymienione w sekcji 'generator.pyw - Import bibliotek')

@@ -242,7 +242,7 @@

- +

Klasa FMT odpowiedzialna jest za zarządzanie plikami formatu (.fmt)
@@ -294,13 +294,78 @@

- + Klasa dataProcess odpowiedzialna za przetwarzanie plików wejściowych i generowanie plików wyjściowych
+
+
+ Główna funkcja klasy (start)
+
+ Funkcja ta odpowiedzialna za zainicjowanie kolejnych funkcji w odpowiedniej kolejności oraz zwrócenie informacji o błędach lub pomyślnym wykonaniu polecenia
+ 1. Inicjacja funkcji __checkIfAtLeastOneInputFileIsFilled w celu sprawdzenia czy przynajmniej jedno pole plików wejściowych zostało wypełnione
+ 2. Inicjacja funkcji __checkIfInputFilesIsReadable w celu sprawdzenia czy pliki wejściowe, do których ścieżki zostały podane, są możliwe do odczytania
+ 3. Inicjacja funkcji __checkIfInputFilesFormatPresetsExist w celu sprawdzenia czy format presety, które zostały wybrane, istnieją
+ 4. Inicjacja funkcji __getData w celu zebrania danych z plików wejściowych
+ 5. Dla każdego zbioru danych - for x in data (dla każdej osoby), inicjacja funkcji __checkLogin, __checkFname, __checkLname, oraz dla uczniów __checkSchool, __checkClass, w celu sprawdzenia poprawności danych
+ 6. Inicjacja funkcji __processData w celu przetworzenia danych w linie gotowe do zapisania w plikach wyjściowych
+ 7. Inicjacja funkcji __checkIfCreatingOutputFilesIsPossible w celu sprawdzenia czy możliwe jest utworzenie plików wyjściowych
+ 8. Inicjacja funkcji __saveData w celu zapisania danych w plikach wyjściowych
+ Funkcja zwraca listę zawierającą zmienne boolean potwierdzające w którym momencie zakończyło się wykonywanie funkcji (z powodu błędu lub pomyślnego ukończenia operacji)
+
+
+ Funkcje sprawdzające istnienie +
+ __checkIfAtLeastOneInputFileIsFilled - sprawdza czy przynajmniej jedno pole plików wejściowych zostało wypełnione oraz zwraca dane z wypełnionych pól
+ __checkIfInputFilesIsReadable - sprawdza czy pliki wejściowe są możliwe do odczytania
+ __checkIfInputFilesFormatPresetsExist - sprawdza czy wybrane format presety istnieją
+ __checkIfCreatingOutputFilesIsPossible - sprawdza czy utworzenie plików wyjściowych jest możliwe
+
+
+ Funkcje sprawdzające poprawność +
+ Funkcje te sprawdzają poprawność określonych typów danych. +
+
+ Wyodrębnienie danych z plików (__getData)
+
+ Funkcja odpowiedzialna jest za wyodrębnienie danych z pliku (path) na podstawie określonego format presetu (format) + Dla każdego zbioru ścieżek plików wejściowych i format presetów (for x in input):
+ 1. Pobranie danych z pliku formatu (za pomocą funkcji FMT.R)
+ 2. Pobranie zawartości z pliku wyjściowego i podzielenie ich na zbiory danych pojedyńczych osób (.split(personseparator))
+ Dla każdego zbioru danych pojedyńczych osób (for x in file):
+ 3. Dzielenie zbioru danych na linie (x.split(linesSeparator))
+ 4. Dla każdej linii: dzielenie danych na pojedyńcze segmenty separatorami z listy dataSeparators
+ 5. Wyodrębnienie wymaganych danych ze zbioru
+ 6. Dodanie danych pojedyńczej osoby do listy data
+
+
+ Przetworzenie danych (__processData)
+
+ Funkcja odpowiedzialna jest za przetworzenie danych w linie gotowe do zapisania w plikach wyjściowych
+ Funkcja zwraca listę [mailData, officeData]
+
+
+ Zapisanie danych (__saveData)
+
+ Funkcja odpowiedzialna jest za zapisanie danych w plikach wyjściowych dla poczty i dla office

- +
- +

+ Klasa GUI odpowiada za interfejs programu
+
+
+ 1. Główna funkcja programu
+
+ - Stworzenie okna
+ - Inicjacja styli kontrolek
+ - Kontrolki
+
+
+ Akcje przycisków
+
+ Funkcje inicjowane przez naciśnięcie przycisków +