From 48f8dc221af5061219d3f863a7fd39a055907be0 Mon Sep 17 00:00:00 2001 From: Paul S Date: Wed, 30 Jun 2021 18:25:01 +0200 Subject: [PATCH] i18n and ini-file added internationalization, clsTableWidget and INI-file settings and pyinstaller-scripts --- i18n/de_DE.qm | Bin 0 -> 4690 bytes i18n/de_DE.ts | 308 ++++++++++++++++ i18n/hu_HU.qm | Bin 0 -> 4616 bytes i18n/hu_HU.ts | 308 ++++++++++++++++ requirements.txt | 5 +- scripts/pyinstaller_v2.cmd | 30 +- scripts/update_ts_files.cmd | 22 ++ src/clsTableWidget.py | 258 +++++++++++++ src/img/icons8-add-row-16.png | Bin 0 -> 447 bytes src/img/icons8-append-clip-16.png | Bin 0 -> 416 bytes src/img/icons8-clear-symbol-16.png | Bin 0 -> 506 bytes src/img/icons8-copy-16.png | Bin 0 -> 663 bytes src/img/icons8-delete-16.png | Bin 0 -> 751 bytes src/img/icons8-eraser-16.png | Bin 0 -> 672 bytes src/img/icons8-insert-clip-16.png | Bin 0 -> 469 bytes src/img/icons8-paste-16.png | Bin 0 -> 612 bytes src/img/icons8-scissors-16.png | Bin 0 -> 777 bytes src/main.py | 565 ++++++++++++++++------------- src/utils.py | 15 +- ui/main.ui | 52 +-- 20 files changed, 1230 insertions(+), 333 deletions(-) create mode 100644 i18n/de_DE.qm create mode 100644 i18n/de_DE.ts create mode 100644 i18n/hu_HU.qm create mode 100644 i18n/hu_HU.ts create mode 100644 scripts/update_ts_files.cmd create mode 100644 src/clsTableWidget.py create mode 100644 src/img/icons8-add-row-16.png create mode 100644 src/img/icons8-append-clip-16.png create mode 100644 src/img/icons8-clear-symbol-16.png create mode 100644 src/img/icons8-copy-16.png create mode 100644 src/img/icons8-delete-16.png create mode 100644 src/img/icons8-eraser-16.png create mode 100644 src/img/icons8-insert-clip-16.png create mode 100644 src/img/icons8-paste-16.png create mode 100644 src/img/icons8-scissors-16.png diff --git a/i18n/de_DE.qm b/i18n/de_DE.qm new file mode 100644 index 0000000000000000000000000000000000000000..d8fb6a91c30c1f44c07b6a60445c8d3f77676bc9 GIT binary patch literal 4690 zcmb_fU2GiH6+X6S*X#9W;}}Cy8p1^~#wcL{H&ks@nUb{wfyj=tj)NkhGTyzrJ7jjo znwgDbs+Na76fD01YAf{z5G_p=A@LIl5dx?p6cAMqDlN3MFCg^+0WV1_+O+C-?(EKA z#2bH4MP-+Sg8&(*fP@#&8r{_Ry;U%LCz(|>+~h%z<#t*PnVL_IGP-Tn&h z93z_l4N<z}{G^C4PUdluh1{qf@mpx4`Tp00%5iQd-X5uy#B_3eD@ zb=-fg@4lZ&7(Lzh{{0`|`I>h3z)7OqHQI?Cz@>j{XOBKa)PG!i_xtbQ{;KxTzs?a2 z3}$xBz|O#Xnd+CJU;J8TZO<8^zRj7n6FY%>Pv%c7SNtGz?(qrW{w4eE@4!#mnO(j8 zQUqcy`|I=9Kz=rN`D4J(F6U;RJ`cQKYhObARtwL6 z>H>GTaO%kW(4Q*2{zwt=8`*H*iBDj6qWH~ce+oZuDw=N~4%+_W*#{@_|L-;yZ$!Rw zCpW(E^><)@a^t!0B5wIn1Imp)Fc_jc$fhMSNRorg%HbwuacO0HX{8@O2IuufQywyE z4e9l;&Q>}MNrO!M6?9}B{T%BH$6ijw*h05Zn_PUV)Feyc)nfMh%55)Ibu?yP|1U88 zfLWJCk@nHwIvk?DXQ!koJ(+5LOqn;~y#d}$((%hf9Ly*Z-5kVm#K#gqQ`Q9xbZV1!}~iKeX3!rK2Jz^tvaV zRQys5-=@X?5q(nxeNonJCq2|dG`$YJ%zoWnCqMD(T+hD&1+Z>m48t-9tF9d4@(ddz zdqB4Aqv?#d#R3AYbujWi#^>0Nw%80(RtnPIf52WAGfpWrBO^>TZZuMhMb8$CQaH+i zT6z_3rLj)fT~#ey>0CMS`WP6iNY68@hAKR*r5l!-4cynP!Bsnj-r#!U*%F3t;1={j z-7sZBbC}9hp#{1w)C*kBl`9L^u@3tj8aqt80nKBpJ4Cn z1sA_;SQ5$>bBfpsENq@KTGDb2+Y)wNc+D<)+}DK^T=pWwX1@g;RbI27JQ?GD%8rlx z^g}-a0gJJKh1;cJFWw`oo-6#KaXqwYEl%n(RLY&m+cf5ATHf;0cb2F3rDV>bJ>b?< zg~D;+A>@0)-xo*l|LN&hrX9z2gkh;z*Sf*~0&Y*P%RZ4~c*S8!)I$*SD`HGKH5Cww zhq{0uAHzXyqe8s^Av=9+$#&E^8MeiER21Q~Pzu~2B9g5V;S<0N`!Jkom9bJuO_vaI z0v>ap`D=op1FE`OfDAL2@I3(Evd58~R_O-acQxr?T61J?Rhq|?fQB`;2P8Ts9mlO&+ z7?RBG)2&7u(#W2%Mz?2}f*YsLk$Gz0GKK&WpP@tgAE=emcI2sk?>cl5*aW-OchyhTh9jmhm zt3`D(1k*3HJJ&W6`J6~*2v&KI36v8o<{~VvV7O%3#CXMX8r$a*lz5rp$Vs4Es2MjP zg@wXxn+aOOD&5?1D&0^Gk5H|m`};?jqtT1S6TB-C-WwR86S_)*=LniBdQj+KTMM{m zDqg!@k7sVT@{BveRCV4Zoj!SZ=L`lJ+~Wg09BA7r?P^uJZoO@qD?+O2CTqc9iW`0# zhPlNWfLTOg3Vgb&ii>9H5>Y1b9}hsDS6sp$s#I`^&7z&Sef`aWcZ9g6MLY0}6X)*@ z0*%~VabDrz;E5C1<@3uwgH1IexQy7!JY;NGzE{cf84mtnS!HOuQ=a4cKylchar8%8CkH+c1v~_i?s1y z`W(P$IVSg(OXASMyHMz9QF(f?ciBTJeibk{{DTgQVp#Qf`%TWxJP!8GejCVX7w@Q- zsn{lKDvWVPi)C7+#>K*n1F|F>{t%%J1 JpxxWk^B?FhtPB7E literal 0 HcmV?d00001 diff --git a/i18n/de_DE.ts b/i18n/de_DE.ts new file mode 100644 index 0000000..bf35da5 --- /dev/null +++ b/i18n/de_DE.ts @@ -0,0 +1,308 @@ + + + + + TableWidget + + + Cut + Ausschneiden + + + + Copy + Kopieren + + + + Paste + Einfügen + + + + Delete + Löschen + + + + Cut row + Zeile ausschneiden + + + + Copy row + Zeile kopieren + + + + Paste row + Zeile einfügen + + + + Insert row before + Zeile oberhalb einfügen + + + + Insert row after + Zeile unterhalb einfügen + + + + Remove row + Zeile entfernen + + + + Delete items + Zellinhalte löschen + + + + Row Nr. + Zeile Nr. + + + + to be removed? + entfernen? + + + + Remove + Entfernen + + + + Delete cell content? + Zellinhalt löschen? + + + + main + + + HELLO + Hallo Welt + + + + &Settings + &Einstellungen + + + + Language + Sprache + + + + Stuff + Gegenstand + + + + Length + Länge + + + + Width + Breite + + + + Height + Höhe + + + + Weight + Gewicht + + + + Dimension of the garage + Dimension der Garage + + + + Dimensions of the objects to be stored + Dimensionen der zu verstauenden Gegenstände + + + + Result + Ergebnis + + + + Volume of the garage + Volumen der Garage + + + + Volume of the items + Volumen der Gegenstände + + + + Free space in the garage + Freier Raum in der Garage + + + + Total weight + Gesamtgewicht + + + + New (Ctrl+N) + Neu (Strg+N) + + + + Open... (Ctrl+O) + Öffnen... (Strg+O) + + + + Save (Ctrl+S) + Speichern (Strg+S) + + + + Export to EXCEL... + Export nach EXCEL... + + + + Information about the application + Informationen über das Programm + + + + Quit the application (Strg+Q) + Programm beenden (Strg+Q) + + + + Garage + Garage + + + + Quit + Beenden + + + + New + Neu + + + + Save + Speichern + + + + CSV-file + CSV-Datei + + + + All files + Alle Dateien + + + + file + Datei + + + + saved + gespeichert + + + + Open + Öffnen + + + + Export + Export + + + + EXCEL-file + EXCEL-Datei + + + + Error in the garage dimension + Fehler in der Garagen-Dimension + + + + Error in the dimensions of the objects to be stored + Fehler in den Dimensionen der zu verstauenden Gegenstände + + + + Successfully exported to EXCEL + Erfolgreich nach EXCEL exportiert + + + + CSV-file + CSV-Datei + + + + EXCEL-file + EXCEL-Datei + + + + There are unsaved entries. Without saving, all changes are lost. Continue anyway? + Es gibt ungespeicherte Einträge. Ohne zu speichern, gehen alle Änderungen verloren. Trotzdem fortfahren? + + + + Garage Space Calculator + Garagenraum-Rechner + + + + Calculates available garage space + Berechnet zur Verfügung stehenden Garagenraum + + + + utils + + + Idea + Idee + + + + Used icons: Theme + Verwendete Icons: Thema + + + + from + von + + + + Version + Version + + + diff --git a/i18n/hu_HU.qm b/i18n/hu_HU.qm new file mode 100644 index 0000000000000000000000000000000000000000..5bbd152236a4d45e06e113b81f4f24b1232c4e46 GIT binary patch literal 4616 zcmbVPU2GiH6+Vt<*X#9WZGs!p(3qQwrlB!g8dB5(A_d2Rfa?&}aUvuHFYjE{ap)PDnIYJVik-$9ur1HZ>9^Rr__eUDQ1Z(k!So}uhN z?!mf$)8xa?60Q0v-F@_RqJly9{Qnnt{|YTGy@>by^wzsSf&96y^K=FD4y|h3JxH|r zcir2bJ`TLsx(_}fq4ZSuI}iN}?;kYo>wSr+=e%)b7`SxCI5~e9_p`>EKR!j2n=t2%vRu@$o}8~*h$x9 zAHMxda6~=(r}JOO`g?Qhp9X%mo*Vz|dEgz+9eM+Ljc0nTy+e5a>)sEmd-497{PW-Z z5A1v+|I1@1Vdu~DuP;KLewBao@F4JB%71*_w_$IkZ}!q3LGSiH=jd+8pXqyH>C3SH zhlN+(_kp{$aCG(^$PX8eKUu{3mscM=@*ec&i{E?kap?K9XrF)|47+&p&`q%W@&4ks z5U6> z)@9Je>%SUbPl8NbKJwrLng>Fiu{27KQH19%q#_l8v)#+fkiexCX||x=5HH{8(Up|Fs(o~{8X%MbTi_>5wYRdDSRvy8yBnv z?j-Kn5~<=9gse6Gz%Iv^p7NeBYk~Aqb!>$WK{a4Fus*^MQZ?|ZD6W^pG)&2w>!rJV zE!~nvRTItJY5Et4)}|Cu#R2TXJY`=*`Pr+WE|@Sw%2)w5HjG_X`DW9*>c3B>W|X~4 zHr)AiYc?UkaY@L53?rsf%7<7NGpk2bz`JR+b`jrE>TDb_?Ml-&1L+HM-n49PlcH{VW?c$@ z&a8AW3UeurD!}Zt(u>xsU3b|It05iVavkB;L@?dPOxky&%1q8l581%YiJi*4T@*T+ z8!QW`2@MDfp2jrA3UZUy<+qi&Q!}y>_##XLKR}UIld1=4l=dSYqcoNhkNoKVkX!~h{%+V!WZPO+L)ugb?vCpFNPBUA=o#A0miu)jG?Yf7mINQ&u!8%r?3k91=iJq5DzFW#uc%~+`r*c14z|078E{#f zQ!a)Su55E|&bBJ*Y0@cq8YgZyHqC3wxiQnJH_=^^OV=QOnF9B^kMNzaa!fk)U^*#a z&|5U4QM<>b8qOl8t>yQyXftX&Kn!i#0ybA^i2@|V=8?d&w~SwvgdT*yc?5Jo-h!~2 z)3l(mdrmq@fh(1r+^EBC5mR7?=G`&P0}OyvZfW1$p}nYjJ`ZDa+|eVwdzw~9q;I-N zq}416t6MNIDCvtmF-990x-*nwb2;$pTlRF&8$iwSCR0_-7%tKTF+TWByMtmu`I3jM zge_$=oHL?$4aFCBa4pBUu4d7e>&hM6@@h!&5W0v}rW`bDwPd`$gSKIz2BfltXI|dL zWk-kNS+Ju&X>V28Vcq z=Ti)iA>6(AIgHalgOje^Y{>R-kk;xqwe0DwVC*UP)H`;(p_WDR03x`Ml zdVzdusJ + + + + TableWidget + + + Cut + Vágd ki + + + + Copy + Vettem + + + + Paste + Beillesztés + + + + Delete + Törölje + + + + Cut row + Vágott vonal + + + + Copy row + Vettem a szöveget + + + + Paste row + Beilleszteni a sort + + + + Insert row before + A fenti sort beilleszteni + + + + Insert row after + Az alábbi sor beillesztése + + + + Remove row + Távolítsa el a sort + + + + Delete items + Tartalom törlése + + + + Row Nr. + Vonalszám. + + + + to be removed? + eltávolítani? + + + + Remove + A eltávolítása + + + + Delete cell content? + Cellatartalom törlése? + + + + main + + + HELLO + Hallo Welt + + + + &Settings + &Beállítások + + + + Language + Nyelv + + + + Stuff + Tárgy + + + + Length + Hosszúság + + + + Width + Szélesség + + + + Height + Magasság + + + + Weight + Súly + + + + Dimension of the garage + A garázs mérete + + + + Dimensions of the objects to be stored + A tárolandó objektumok méretei + + + + Result + Eredmény + + + + Volume of the garage + A garázs térfogata + + + + Volume of the items + A tételek mennyisége + + + + Free space in the garage + Szabad hely a garázsban + + + + Total weight + Teljes súly + + + + New (Ctrl+N) + Új (Ctrl+N) + + + + Open... (Ctrl+O) + Nyissa ki (Ctrl+O) + + + + Save (Ctrl+S) + Mentés (Ctrl+S) + + + + Export to EXCEL... + Exportálás EXCEL-be... + + + + Information about the application + Az alkalmazással kapcsolatos információk + + + + Quit the application (Strg+Q) + Az alkalmazás kilépése (Ctrl+Q) + + + + Garage + Garázs + + + + Quit + Kilépés + + + + New + Új + + + + Save + Mentés + + + + CSV-file + CSV-fájl + + + + All files + Minden fájl + + + + file + fájl + + + + saved + mentett + + + + Open + Megnyitott + + + + Export + Exportálás + + + + EXCEL-file + EXCEL-fájl + + + + Error in the garage dimension + Hiba a garázs dimenziójában + + + + Error in the dimensions of the objects to be stored + Hiba a tárolandó objektumok méreteiben + + + + Successfully exported to EXCEL + Sikeresen exportált EXCEL-be + + + + CSV-file + CSV-fájl + + + + EXCEL-file + EXCEL-fájl + + + + There are unsaved entries. Without saving, all changes are lost. Continue anyway? + Vannak mentetlen bejegyzések. Mentés nélkül minden módosítás elveszik. Folytassa mégis? + + + + Garage Space Calculator + Garázs hely kalkulátor + + + + Calculates available garage space + Kiszámítja a rendelkezésre álló garázshelyet + + + + utils + + + Idea + Ötlet + + + + Used icons: Theme + Használt ikonok: Téma + + + + from + a weboldalról + + + + Version + Verzió + + + diff --git a/requirements.txt b/requirements.txt index e190d6a..ff357c2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,4 @@ # project: GarageCalc1 # file: requirements.txt -wheel -pyside2==5.15.2 -XlsxWriter==1.3.8 +pyside2 +xlsxwriter diff --git a/scripts/pyinstaller_v2.cmd b/scripts/pyinstaller_v2.cmd index da1ed72..5892f60 100644 --- a/scripts/pyinstaller_v2.cmd +++ b/scripts/pyinstaller_v2.cmd @@ -1,36 +1,36 @@ @ECHO OFF :: -------------------------------------------------------------------------------------------------------------------------------------- -:: project: GarageCalc1 Window-Exe Generator -:: summary: create a GarageCalc1-executable for Windows (version with no-console-windows as well as version with console windows for traces) +:: project: GarageCalc Window-Exe Generator +:: summary: create a GarageCalc-executable for Windows (version with no-console-windows as well as version with console windows for traces) :: file: pyinstaller.cmd :: date: version author :: 2021-06-27 1 paul salajean :: -------------------------------------------------------------------------------------------------------------------------------------- -SET version="v0.1" +SET version="v0.4" SET release_dir="D:\Temp\Prog\ownCloud\profp@uberspace\transfer" SET prev_dir=%cd% -SET reinstall_venv="true" +SET reinstall_venv="false" SET do_zip="false" CD .. -ECHO Creating GarageCalc1 Windows.exe-version in folder %cd%\dist +ECHO Creating GarageCalc Windows.exe-version in folder %cd%\dist PAUSE REM pyinstaller --noconfirm --log-level=ERROR ^ REM --onedir --nowindow ^ REM --add-data="README;." ^ REM --add-data="image1.png;img" ^ REM --add-data="img;doc;ui" ^ -REM --add-data="LICENSE.txt;changelog.md;GarageCalc1.bat" ^ +REM --add-data="LICENSE.txt;changelog.md;GarageCalc.bat" ^ REM --add-binary="libfoo.so;lib" ^ REM --hidden-import=secret1 ^ REM --hidden-import=secret2 ^ -REM --icon=.\img\GarageCalc1.ico ^ +REM --icon=.\img\GarageCalc.ico ^ REM --debug=imports ^ REM --key=N0T1me40pp0ssum5 REM --paths=.\src ^ -REM GarageCalc1.spec +REM GarageCalc.spec REM The key-string is a string of 16 characters which is used to encrypt each file of Python byte-code before it is stored in the archive inside the executable file. REM This feature uses the "tinyaes" module internally for the encryption. REM @@ -55,19 +55,21 @@ python -m pip install -r requirements.txt :PYINSTALL IF %reinstall_venv%=="false" CALL .\env2\Scripts\activate.bat ECHO Running "pyinstaller"... -REM pyinstaller .\main.py --name=GarageCalc1 --noconfirm --console --clean --onedir --log-level=ERROR --hidden-import=PySide2.QtXml --icon=.\img\GarageCalc1.ico --add-data="LICENSE;." --add-data="README.md;." --add-data="changelog.md;." --add-data="*.ui;." --add-data="img;.\img" -pyinstaller .\src\main.py --name=GarageCalc1 --noconfirm --windowed --clean --onefile --log-level=ERROR --hidden-import=PySide2.QtXml ^ +REM pyinstaller .\main.py --name=GarageCalc --noconfirm --console --clean --onedir --log-level=ERROR --hidden-import=PySide2.QtXml --icon=.\img\GarageCalc.ico --add-data="LICENSE;." --add-data="README.md;." --add-data="changelog.md;." --add-data="*.ui;." --add-data="img;.\img" +REM --windowed +pyinstaller .\src\main.py --name=GarageCalc --noconfirm --windowed --clean --onefile --log-level=ERROR --hidden-import=PySide2.QtXml ^ --icon=.\img\icons8-garage-32.ico ^ --add-data="*.txt;." ^ --add-data="*.md;." ^ --add-data="ui;.\ui" ^ + --add-data="i18n;.\i18n" ^ --add-data="img;.\img" -::MOVE .\dist\GarageCalc1\*.md .\dist\ -::MOVE .\dist\GarageCalc1\LICENSE .\dist\ +::MOVE .\dist\GarageCalc\*.md .\dist\ +::MOVE .\dist\GarageCalc\LICENSE .\dist\ ::clean-up img-folder -::MOVE .\dist\GarageCalc1\img .\dist\img>NUL +::MOVE .\dist\GarageCalc\img .\dist\img>NUL ::Remove temp dir and files DEL /Q *.spec>NUL @@ -81,7 +83,7 @@ IF %do_zip%=="false" GOTO END ::-mx5 = This is default (compression is normal). ::-mx7 = Maximum compression. ::-mx9 = Ultra compression. -"C:\Program Files\7-zip\7z.exe" a %release_dir%\GarageCalc1_%version%_portable.exe -mx9 -sfx7z.sfx .\dist\* +"C:\Program Files\7-zip\7z.exe" a %release_dir%\GarageCalc_%version%_portable.exe -mx9 -sfx7z.sfx .\dist\* ::Disable Virtual env CALL .\env2\Scripts\deactivate.bat diff --git a/scripts/update_ts_files.cmd b/scripts/update_ts_files.cmd new file mode 100644 index 0000000..71dfb33 --- /dev/null +++ b/scripts/update_ts_files.cmd @@ -0,0 +1,22 @@ +@ECHO OFF +:: -------------------------------------------------------------------------------------------------------------------------------------- +:: project: GarageCalc +:: summary: Update or create GarageCalc ts-files +:: file: update_ts_files.cmd +:: date: version author +:: 2021-06-30 1 paul salajean +:: -------------------------------------------------------------------------------------------------------------------------------------- +SET prev_dir=%cd% + +CD .. + +mkdir .\i18n + +pyside2-lupdate -verbose .\src\main.py .\src\utils.py .\src\clsTableWidget.py -ts .\i18n\de_DE.ts +pyside2-lupdate -verbose .\src\main.py .\src\utils.py .\src\clsTableWidget.py -ts .\i18n\hu_HU.ts + +:END +CD %prev_dir% +ECHO Done. + +PAUSE diff --git a/src/clsTableWidget.py b/src/clsTableWidget.py new file mode 100644 index 0000000..98c5bb4 --- /dev/null +++ b/src/clsTableWidget.py @@ -0,0 +1,258 @@ +#!/usr/bin/env python3 +""" +project: clsTableWidget +file: clsTableWidget.py +summary: Implements my own TableWidget class +author: Paul Salajean (p.salajean[at]gmx.de) +license: GPL +version: 0.1 +""" + +# Standard library imports +import sys +import os + +# Third party imports +from PySide2.QtWidgets import QApplication, QTableWidget, QAbstractItemView, QTableWidgetItem, QMenu, \ + QMainWindow, QMessageBox +from PySide2.QtCore import Qt, QItemSelectionModel, QCoreApplication +from PySide2.QtGui import QIcon + +# local globals +SCRIPT_PATH = os.path.abspath(os.path.dirname(__file__)) + +ICON_CLEAR = SCRIPT_PATH + "./img/icons8-clear-symbol-16.png" +ICON_COPY = SCRIPT_PATH + "./img/icons8-copy-16.png" +ICON_ERASER = SCRIPT_PATH + "/img/icons8-eraser-16.png" +ICON_INSERT = SCRIPT_PATH + "/img/icons8-insert-clip-16.png" +ICON_APPEND = SCRIPT_PATH + "/img/icons8-append-clip-16.png" +ICON_PASTE = SCRIPT_PATH + "/img/icons8-paste-16.png" +ICON_CUT = SCRIPT_PATH + "/img/icons8-scissors-16.png" +ICON_ADD_ROW = SCRIPT_PATH + "/img/icons8-add-row-16.png" +ICON_DEL = SCRIPT_PATH + "/img/icons8-delete-16.png" + +# def resource_path(relative_path): +# """ Get absolute path to resource, works for dev and for PyInstaller """ +# try: +# # PyInstaller creates a temp folder and stores path in _MEIPASS +# base_path = sys._MEIPASS +# print("base_path", base_path) +# except Exception: +# base_path = os.path.abspath(".") + +# return os.path.join(base_path, relative_path) + +class TableWidget(QTableWidget): + def __init__(self, parent=None): + super().__init__() + + self.parent = parent + self.setParent(parent) + + # self.setSelectionMode(QAbstractItemView.ContiguousSelection) + self.setSelectionMode(QAbstractItemView.SingleSelection) + + self.setSelectionBehavior(QTableWidget.SelectItems) + #self.setSelectionBehavior(QTableWidget.SelectRows) + self.setAlternatingRowColors(True) + + # Context-menu + self.setContextMenuPolicy(Qt.CustomContextMenu) + self.customContextMenuRequested.connect(self.on_context_menu) + + # self.setEditTriggers(QAbstractItemView.DoubleClicked | QAbstractItemView.EditKeyPressed | QAbstractItemView.AnyKeyPressed) + + self.headers = self.verticalHeader() + self.headers.setContextMenuPolicy(Qt.CustomContextMenu) + self.headers.customContextMenuRequested.connect(self.on_rowheadercontext_menu) + self.headers.setSelectionMode(QAbstractItemView.SingleSelection) + self.headers.sectionClicked.connect(self.select_row) + + self.row_selected = False + + def select_row(self, row): + self.setSelectionBehavior(QTableWidget.SelectRows) + self.selectRow(row) + self.setSelectionBehavior(QTableWidget.SelectItems) + self.row_selected = True + + def on_context_menu(self, position): + menu = QMenu() + item_cut = menu.addAction(QIcon(ICON_CUT), QCoreApplication.translate("TableWidget", "Cut") + "\tCtrl+X") + item_copy = menu.addAction(QIcon(ICON_COPY), QCoreApplication.translate("TableWidget", "Copy") + "\tCtrl+C") + item_paste = menu.addAction(QIcon(ICON_PASTE), QCoreApplication.translate("TableWidget", "Paste") + "\tCtrl+V") + menu.addSeparator() + + print(ICON_ERASER) + item_delete = menu.addAction(QIcon(ICON_ERASER), QCoreApplication.translate("TableWidget", "Delete") + "\tDel") + + ac = menu.exec_(self.mapToGlobal(position)) + + if ac == item_cut: + self.item_cut() + elif ac == item_copy: + self.item_copy() + elif ac == item_paste: + self.item_paste() + elif ac == item_delete: + self.item_del() + + def on_rowheadercontext_menu(self, position): + menu = QMenu() + row_cut = menu.addAction(QIcon(ICON_CUT), QCoreApplication.translate("TableWidget", "Cut row")) + row_copy = menu.addAction(QIcon(ICON_COPY), QCoreApplication.translate("TableWidget", "Copy row")) + row_paste = menu.addAction(QIcon(ICON_PASTE), QCoreApplication.translate("TableWidget", "Paste row")) + menu.addSeparator() + row_insert_before = menu.addAction(QIcon(ICON_INSERT), QCoreApplication.translate("TableWidget", "Insert row before")) + row_insert_after = menu.addAction(QIcon(ICON_APPEND), QCoreApplication.translate("TableWidget", "Insert row after")) + menu.addSeparator() + row_remove = menu.addAction(QIcon(ICON_DEL), QCoreApplication.translate("TableWidget", "Remove row")) + row_delete_items = menu.addAction(QIcon(ICON_ERASER), QCoreApplication.translate("TableWidget", "Delete items")) + ac = menu.exec_(self.mapToGlobal(position)) + + row = self.headers.logicalIndexAt(position) + + if ac == row_cut: + self.item_cut() + elif ac == row_copy: + self.item_copy() + elif ac == row_paste: + self.item_paste() + elif ac == row_insert_before: + self.row_insert(row) + elif ac == row_insert_after: + self.row_insert(row+1) + elif ac == row_remove: + self.row_remove(row) + elif ac == row_delete_items: + self.row_delete_items() + + def row_insert(self, row): + self.insertRow(row) + + def has_data(self, row): + """ Check if target already contains data. """ + # sel_idx = self.selectionModel().selectedIndexes() + for col in range(self.columnCount()): + item = self.item(row, col) + if item: + if len(item.text()) > 0: + return True + return False + + def row_remove(self, row): + if self.has_data(row): + msg = QCoreApplication.translate("TableWidget", "Row Nr.") + " " + str(row+1) + " " + QCoreApplication.translate("TableWidget", "to be removed?") + reply = QMessageBox.question(self, QCoreApplication.translate("TableWidget", "Remove"), msg, \ + QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) + if reply == QMessageBox.No: + return False + + self.removeRow(row) + return True + + def row_delete_items(self): + self.item_del() + + def keyPressEvent(self, event): + super().keyPressEvent(event) + modifiers = QApplication.keyboardModifiers() + + key = event.key() + + if modifiers == Qt.ControlModifier: + if key == Qt.Key_C: + self.item_copy() + + elif key == Qt.Key_V: + self.item_paste() + + elif key == Qt.Key_X: + self.item_cut() + + elif key == Qt.Key_A: + self.setSelectionMode(QAbstractItemView.ContiguousSelection) + self.selectAll() + self.setSelectionMode(QAbstractItemView.SingleSelection) + + if key == Qt.Key_Delete: + self.item_del() + + elif key == Qt.Key_Escape: + self.clearSelection() + + def item_paste(self): + cur_row = self.currentRow() + cur_col = self.currentColumn() + # ask_confirmation = True + + if self.row_selected: + cur_col = 0 + + col = 0 + if len(self.clipboard_data) == 1: + data = self.clipboard_data[0] + item = QTableWidgetItem(data) + + self.setItem(cur_row, cur_col, item) + item.setSelected(True) + else: + for data in self.clipboard_data: + item = QTableWidgetItem(data) + # if item: + # if len(item.text()) >0: + # if ask_confirmation: + # msg = QCoreApplication.translate("TableWidget", "Zelle enthält bereits Daten. Überschreiben?") + # reply = QMessageBox.question(self, QCoreApplication.translate("TableWidget", "Überschreiben"), msg, \ + # QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) + # if reply == QMessageBox.No: + # return False + + # ask_confirmation = False + self.setItem(cur_row, col, item) + item.setSelected(True) + + col += 1 + + def item_cut(self): + self.item_copy() + + sel_idx = self.selectedIndexes() + + for idx in sel_idx: + item = self.itemFromIndex(idx) + try: + item.setData(Qt.DisplayRole, None) + except AttributeError: + pass + + def item_copy(self): + sel_idx = self.selectedIndexes() + sel_rows = self.selectionModel().selectedRows() + + if len(sel_idx) == 1: + self.row_selected = False + + self.sel_ranges = self.selectedRanges() + + self.clipboard_data = [] + for idx in sel_idx: + self.clipboard_data.append(idx.data()) + + def item_del(self): + ask_cofirmation = True + sel_idx = self.selectionModel().selectedIndexes() + for idx in sel_idx: + row = idx.row() + col = idx.column() + item = self.item(row, col) + if item: + if self.has_data(row) and ask_cofirmation: + msg = QCoreApplication.translate("TableWidget", "Delete cell content?") + reply = QMessageBox.question(self, QCoreApplication.translate("TableWidget", "Delete"), msg, \ + QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) + if reply == QMessageBox.No: + return False + + ask_cofirmation = False + item.setData(Qt.DisplayRole, None) diff --git a/src/img/icons8-add-row-16.png b/src/img/icons8-add-row-16.png new file mode 100644 index 0000000000000000000000000000000000000000..df7f82193320ee4486fc83f7844fce7f60120dd3 GIT binary patch literal 447 zcmV;w0YLtVP)Eu|J?_O@s!O6zxj_8_KV==NI~Z`d)*Gm0Ce-XFHd3&%O6t;2-2*_dLnl zOj5T{sj-SmsOeYD1-0GD49sq!R5qV~_GZS4Am%KyEJB>k{zY3=*01!^s zvsIK21c1Wu9jBdL$Zs|t2o3;voB4B91L%DM?Ro}^)&`9f0na=jNqq)%kSlpeplnT} zIH$m{AfV{40XHF_#rm{VQ&pm5UwgydF1cOJplR=%<&{REQJ_4>kQ pOSejNm=t7oy0O1@Jxu#2yaUF?iW?7PB< zg9)*Nn}huuj4Vdulc|F1{{jRRbUt=BtLt@&F!$x&#R5oT!CsnAj%&VdT`mFuDI|vPH~<7* z(0+aQG42Fj&~6sp93x~IA=@mvxfsDD;^yp$lKuezFf+9kBjART6w$~ zChHVT)+tn+d@n{2hJ(ia+0_gwW@*h#g?%|_06=NK6i!M!u|5F@XNriIVmfC40000< KMNUMnLSTYD;2p6`6; zyBsd?zdOyO0gqXQwqB$O!;=I=+$`2=tq>(%0J?{)OndTL1+L0N@Sh zK$#%%62V})mf^KZN*Cp^L^v{t-fuyb^YBLpL7srA?<5VDrk5U-dd$5vB?INi!2N8( z8ybLV{O+2CFf8AF)^fXB(;LnK0K(Tbs5^O3;3o|x=O1o;)0nCo@%?y14rV~UgsyFa zwMG1)0l#!LVITqE?Rxc7`R0A{D71j`7;@fUqDcfRjC z=L$rE!Q%Pk>_f2G8_iozBCUyolfuq}`%mv}N8e-={Bmk;zQy6O%=n0?jHGGe%1~cb zs&s(i4ws~J=l%T34FJ=jg=nbbBIM_f7##Gg5=p|an*r7!>423FXs*O&5cPbxu!N)? zVEF@($vO~Zfpq|q4z%(h>;~A$WVUV#wSndOTwK21^-nf5h!W7s16V_74c1D<8d$d7 zoU*IauC3tsAi&0I0fHo~1fjb(jqR-x2CntREK`+9SYE)er3je?2_%vZEL#9tn_Si9%Xf~0}cCWEAdgTfIuR`(#t z42#cQ%)!mIKvgQ`Sc9Yd&cw>Aw^Eog-QS%WWaXJ{xdya%U#er&l`;a~JFVTt{{@S$ z-|c5R(`SS*003@h_4gj`o`SUxt$ZBqmh!`QM|wg9F$N93Fwj%S-0E6Tf0geQ*8P}; xXzb;SQ?oCYZboxTvHJDX`&PUEF8K?!^B=gM*#P^Pc4`0s002ovPDHLkV1jK;Iqv`f literal 0 HcmV?d00001 diff --git a/src/img/icons8-delete-16.png b/src/img/icons8-delete-16.png new file mode 100644 index 0000000000000000000000000000000000000000..c57ba92bb2b529536d3d5e49dce0c276a0f7ed44 GIT binary patch literal 751 zcmV2Y-=Hgq|k&+G4J#oXfT!9vG^l8{I-S=c1+ zBQN+oLA9KP)to}V8b_w;zIi7!;KY2ev<)DqLUZZEgoT+mOSW6m)!~0f3GTea-P8iI z`rmscSh7T;eNzIE#W&$Uqr-OCnAHp`tLnkfMD1+jYy4aPgE2(FMB2i2ppewU}0H7l(21YYBxiukApSi)~0lHv1i3TICoH+CP&vkxN+_e~a zV4+7UB#c2xLy!;@vo@`(1D-r_%!fDZ*nW7=MVL;aK$uf6PPGWsqlRUbjuo9grLu)r zl+@Q7ys$!D7;yKl8+enIo!bxZy*P8+51G4gwD+%h^TkP(h%!*kpFR|1a^jKU+f0fi%&jorvJl+-H$(^7_w%bDK3U`t5 zgo}ukN3I8Y-UqhM2!GqGl-fdx`dX7(q=PcQ1Z%|sWJOK&g;VEChd#b|wZ3o8LeDP*fm8W2-!S~2@KZcZ$ue|Y}o_T_Zd|m zj`**2H*Q2J7K`jvO_fbgh#hMq)6koBpmY=Bgo^t4S$vKyD^$(gyxQ%Rw%(0qD2BtI zW;wuNY&eE=QU+r@FPSBU&1+-JmwjE{r-i8z0C>|D7&xe`Hj>v z^tC+O?q@c<23w`)hcfadcY})n1gj&{_^C5S!&%za+}HeiJ8>QW0A99Aw+Uw#wN`^A z9ZSXIRQ#rJ$N<1OiQj$L)O+vNV28ljaQmYx<2yljRet~=v+Hw9x>+;;00008!0#Ou(-+N~!4H1LFEHH#tZ7Ny>^%sJujr+i*Mo4Z0 zgBFG0qSo1Oh&Dm{KxmQCA}WyV;x#iW_&G#X>-DCCj#LcP6P^WtNFk1ld0l*z2Ne;wRifV&wZ1LL9T zk?!WdT)ao=q8O9`psMPy%JmjNQ?2O0~m59PBk$5`X;F2)NYS}<05hF6?n%|qfJIQa| z=JNeEQRvTxxds4>n~Qq0K-bN+tAnF163hhvOrTWrd$XTS_G7&O*6f=jbfHvp00000 LNkvXXu0mjfahun( literal 0 HcmV?d00001 diff --git a/src/img/icons8-paste-16.png b/src/img/icons8-paste-16.png new file mode 100644 index 0000000000000000000000000000000000000000..0e3db7da518e8bdbf0de89492b4c5f15214c0c5a GIT binary patch literal 612 zcmV-q0-ODbP)MaWVYK}Ve&Iw=S$>fq$&Dx#!?U2K3b4D|*|_&;?hJqt0lXNtpH``I-Qbun@G}xWBm0|AW~{3JIDqgopC7D>W2fW-Q7T)sX<`A@Cyc_VSO4rsR&VbD|p9S4wu0Fi{U5mF(f zLKC1&hcIX(keHU09QuVw{l-a(;7o2a$?}ZGPn|2n4npVk~6UoBmM^t!WL6M3yS*bzz1qoghQqVqJA5&)p5VK9@^bmx8XfJ`$lh zRZ$=w6<@bGPS5?#3;=+euT?&4xM(Z#zFjOd%tQ*KeqLbG*{_UAzYQY{6ib&lo19## zr7!>h0+eVSI^%QaM5g0qo8v_tUo(+K0-lz`r*dwEhqwyI3xQ8K5@i~g(~Ii<8-`w$4M5%}oc zlo1pHYZ5A&@3>jdCq+IOY!7*YK7zoi@5}ntmhP%|sQ^C#n-L^hoGSGs00000NkvXX Hu0mjfL%&vX literal 0 HcmV?d00001 diff --git a/src/main.py b/src/main.py index 7ca07de..de26027 100644 --- a/src/main.py +++ b/src/main.py @@ -11,27 +11,34 @@ author: Paul Salajean (p.salajean[at]gmx.de) import sys import os import csv +import configparser # Third party imports -from PySide2.QtWidgets import QApplication, QMainWindow, QTableWidgetItem, QStatusBar, QAction, QFileDialog, \ - QAbstractItemView, QMenu, QMessageBox +from PySide2.QtWidgets import QApplication, QMainWindow, QTableWidgetItem, QStatusBar, QFileDialog, \ + QAbstractItemView, QMenu, QMessageBox, QHBoxLayout, QVBoxLayout, QAction, QActionGroup from PySide2.QtGui import QIcon -from PySide2.QtCore import QFile, QSize, Qt +from PySide2.QtCore import QFile, QSize, Qt, QCoreApplication, QTranslator from PySide2.QtUiTools import QUiLoader +import xlsxwriter # Local imports from utils import show_about, resource_path -# Local globals -APP_VERSION = "v0.3.1" +# my own classes imports +from clsTableWidget import TableWidget -APP_NAME = "Garagenraum-Rechner" +# Local globals +APP_VERSION = "v0.4" + +DIR_APPDATA = os.getenv('LOCALAPPDATA') + +APP_NAME = QCoreApplication.translate("main", "Garage Space Calculator") APP_DISPNAME = "GarageCalc" APP_AUTHOR = "Paul Salajean" -APP_DESCR = "Berechnet zur Verfügung stehenden Garagenraum" +APP_DESCR = QCoreApplication.translate("main", "Calculates available garage space") APP_COPYRIGHT = "(c) Paul Salajean 2021" APP_WEBSITE = "https://gitlab.com/ProfP303" -APP_DESKTOPFILENAME = "GarageCalc" +APP_DESKTOPFILENAME = APP_DISPNAME APP_ICON = "./img/icons8-garage-32.ico" @@ -54,24 +61,93 @@ DEFAULT_GARAGE_LENGTH = "6" DEFAULT_GARAGE_WIDTH = "2.5" DEFAULT_GARAGE_HEIGHT = "2.5" +TBL_STUFF_COL_COUNT = 5 TBL_STUFF_ROW_COUNT = 50 +TXT_UNSAVED_CHANGES = QCoreApplication.translate("main", "There are unsaved entries. Without saving, all changes are lost. Continue anyway?") + class MyMainWindow(QMainWindow): - def __init__(self): + def __init__(self, language): super().__init__() + + self.language = language + self.is_modified = False + self.opened_file = None + self.remembered_row = None + self.load_ui() self.init_ui() self.set_defaults() + self.create_menu(self.language) self.connect_signals() self.create_actions() self.create_toolbar() self.create_statusbar() - self.statusBar.showMessage(f"{APP_NAME} {APP_VERSION} by {APP_AUTHOR}", 5000) + global APP_NAME + APP_NAME = qApp.translate("main", "Garage Space Calculator") + self.statusBar.showMessage(f"{APP_NAME} {APP_VERSION} - {APP_AUTHOR}", 5000) self.calc_voluminae() self.ui.efWeight.setText(str("0")) - self.is_modified = False - self.opened_file = None - self.remembered_row = None + self.retranslateUi() + self.ui.tableStuff.setFocus() + + def create_menu(self, language=None): + menuMain = self.menuBar() + self.menuSettings = menuMain.addMenu(QCoreApplication.translate("main", "&Settings")) + self.menuLanguage = self.menuSettings.addMenu(QCoreApplication.translate("main", "Language")) + + ag = QActionGroup(self, exclusive=True) + + a = ag.addAction(QAction('English', self.menuSettings, checkable=True)) + self.menuLanguage.addAction(a) + + a = ag.addAction(QAction('Deutsch', self.menuSettings, checkable=True)) + self.menuLanguage.addAction(a) + + a = ag.addAction(QAction('Magyar', self.menuSettings, checkable=True)) + self.menuLanguage.addAction(a) + + menuMain.triggered.connect(lambda: self.store_selected_language(self.menuLanguage)) + + if language: + [action.setChecked(True) for action in self.menuLanguage.actions() if action.text()==language] + + def retranslateUi(self): + # menus + self.menuSettings.setTitle(QCoreApplication.translate("main", "&Settings")) + self.menuLanguage.setTitle(QCoreApplication.translate("main", "Language")) + + # tables + self.ui.tableGarage.setVerticalHeaderLabels([QCoreApplication.translate("main","Garage")]) + self.ui.tableGarage.setHorizontalHeaderLabels([ + QCoreApplication.translate("main","Length") + " [m]", + QCoreApplication.translate("main","Width") + " [m]", + QCoreApplication.translate("main","Height") + " [m]" + ]) + + self.ui.tableStuff.setHorizontalHeaderLabels([QCoreApplication.translate("main","Stuff"), + QCoreApplication.translate("main","Length") + " [m]", + QCoreApplication.translate("main","Width") + " [m]", + QCoreApplication.translate("main","Height") + " [m]", + QCoreApplication.translate("main","Weight") + " [m]"]) + + # labels + self.ui.gbGarage.setTitle(QCoreApplication.translate("main","Dimension of the garage")) + self.ui.gbStuff.setTitle(QCoreApplication.translate("main", "Dimensions of the objects to be stored")) + self.ui.gbResults.setTitle(QCoreApplication.translate("main", "Result")) + + self.ui.lblVol_Garage.setText(QCoreApplication.translate("main","Volume of the garage") + ":") + self.ui.lblVol_Stuff.setText(QCoreApplication.translate("main","Volume of the items") + ":") + self.ui.lblVol_Free.setText(QCoreApplication.translate("main","Free space in the garage") + ":") + self.ui.lblWeight.setText(QCoreApplication.translate("main","Total weight") + ":") + + # Tooltips + self.actionNew.setToolTip(QCoreApplication.translate("main","New (Ctrl+N)")) + self.actionOpen.setToolTip(QCoreApplication.translate("main","Open... (Ctrl+O)")) + self.actionSave.setToolTip(QCoreApplication.translate("main","Save (Ctrl+S)")) + self.actionExport.setToolTip(QCoreApplication.translate("main","Export to EXCEL...")) + self.actionAbout.setToolTip(QCoreApplication.translate("main","Information about the application")) + self.actionQuit.setToolTip(QCoreApplication.translate("main","Quit the application (Strg+Q)")) def load_ui(self): loader = QUiLoader() @@ -81,46 +157,49 @@ class MyMainWindow(QMainWindow): self.ui = loader.load(ui_file, self) ui_file.close() - # self.headers = self.ui.tableStuff.horizontalHeader() - self.headers = self.ui.tableStuff.verticalHeader() - self.headers.setContextMenuPolicy(Qt.CustomContextMenu) - self.headers.customContextMenuRequested.connect(self.show_rowheader_context_menu) - self.headers.setSelectionMode(QAbstractItemView.SingleSelection) + #self.ui.tableStuff = TableWidget(self.ui.gbStuff) + self.ui.tableStuff = TableWidget() + self.ui.tableStuff.setColumnCount(TBL_STUFF_COL_COUNT) + self.ui.tableStuff.setRowCount(TBL_STUFF_ROW_COUNT) + self.ui.tableStuff.move(10, 23) + self.ui.tableStuff.resize(541,268) + + # create layout + #hBox = QHBoxLayout() + #hBox.addWidget(self.ui.tableStuff) + #self.ui.tableStuff.move(10, 23) + self.ui.tableStuff.setParent(self.ui.gbStuff) + #self.ui.gbStuff.setLayout(hBox) + def create_actions(self): self.actionNew = QAction() self.actionNew.setIcon(QIcon(resource_path(ICON_NEW))) self.actionNew.triggered.connect(self.file_new) self.actionNew.setShortcut("Ctrl+N") - self.actionNew.setToolTip("Neu (Strg+N)") self.actionOpen = QAction() self.actionOpen.setIcon(QIcon(resource_path(ICON_OPEN))) self.actionOpen.triggered.connect(self.file_open) self.actionOpen.setShortcut("Ctrl+O") - self.actionOpen.setToolTip("Öffnen... (Strg+O)") self.actionSave = QAction() self.actionSave.setIcon(QIcon(resource_path(ICON_SAVE))) self.actionSave.triggered.connect(self.file_save) self.actionSave.setShortcut("Ctrl+S") - self.actionSave.setToolTip("Speichern (Strg+S)") self.actionExport = QAction() self.actionExport.setIcon(QIcon(resource_path(ICON_EXPORT))) self.actionExport.triggered.connect(self.file_export) - self.actionExport.setToolTip("Export nach EXCEL...") self.actionAbout = QAction() self.actionAbout.setIcon(QIcon(resource_path(ICON_ABOUT))) self.actionAbout.triggered.connect(show_about) - self.actionAbout.setToolTip("Informationen über das Programm") self.actionQuit = QAction() self.actionQuit.setIcon(QIcon(resource_path(ICON_QUIT))) self.actionQuit.triggered.connect(self.app_quit) self.actionQuit.setShortcut("Ctrl+Q") - self.actionQuit.setToolTip("Programm beenden (Strg+Q)") def create_toolbar(self): # Main Toolbar (for all pages/views) @@ -159,115 +238,12 @@ class MyMainWindow(QMainWindow): tblGarage.itemChanged.connect(self.on_garage_changed) tblStuff.itemChanged.connect(self.on_stuff_changed) - def keyPressEvent(self, event): - tblStuff = self.ui.tableStuff - - modifiers = QApplication.keyboardModifiers() - - key = event.key() - - if modifiers == Qt.ControlModifier: - # get selected row - sel_rows_idx = tblStuff.selectionModel().selectedRows() - row = sel_rows_idx[0].row() # there is onle on row because of singleton selection mode - - if key == Qt.Key_C: - self.remembered_row = row - - elif key == Qt.Key_V: - self.row_insert(tblStuff, self.remembered_row, row) - - if key == Qt.Key_Delete: - self.row_remove(tblStuff) - - def row_remove(self, table): - # get selected row - sel_rows_idx = table.selectionModel().selectedRows() - row = sel_rows_idx[0].row() # there is onle on row because of singleton selection mode - - stuff = None - item_stuff = table.item(row, COL_STUFF) - if item_stuff: - stuff = item_stuff.text() - - if stuff: - msg = f"Zeile Nr. {row+1} '{stuff}' entfernen?" - else: - msg = f"Zeile Nr. {row+1} entfernen?" - reply = QMessageBox.question(self, "Zeile entfernen", msg, \ - QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) - if reply == QMessageBox.Yes: - table.removeRow(row) - self.calc_voluminae() - - def row_insert(self, table, source_row, target_row): - # check if empty - item_stuff = table.item(target_row, COL_STUFF) - item_length = table.item(target_row, COL_LENGTH) - item_width = table.item(target_row, COL_WIDTH) - item_height = table.item(target_row, COL_HEIGHT) - item_weight = table.item(target_row, COL_WEIGHT) - - if item_stuff or item_length or item_width or item_height or item_weight: - msg = "Es sind bereits Werte in dieser Zeile vorhanden. Trotzdem fortfahren?" - reply = QMessageBox.question(self, "Zeile einfügen", msg, \ - QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) - if reply == QMessageBox.No: - return False - - item_stuff = table.item(source_row, COL_STUFF) - item_length = table.item(source_row, COL_LENGTH) - item_width = table.item(source_row, COL_WIDTH) - item_height = table.item(source_row, COL_HEIGHT) - item_weight = table.item(source_row, COL_WEIGHT) - - if item_stuff: - table.setItem(target_row, COL_STUFF, QTableWidgetItem(item_stuff.text())) - - if item_length: - table.setItem(target_row, COL_LENGTH, QTableWidgetItem(item_length.text())) - - if item_width: - table.setItem(target_row, COL_WIDTH, QTableWidgetItem(item_width.text())) - - if item_height: - table.setItem(target_row, COL_HEIGHT, QTableWidgetItem(item_height.text())) - - if item_weight: - table.setItem(target_row, COL_WEIGHT, QTableWidgetItem(item_weight.text())) - - def show_rowheader_context_menu(self, position): - tblStuff = self.ui.tableStuff - row = self.headers.logicalIndexAt(position) - - menu = QMenu() - row_add = menu.addAction("Zeile hinzufügen") - row_remove = menu.addAction("Zeile entfernen (Entf.-Taste)") - menu.addSeparator() - row_copy = menu.addAction("Zeile kopieren (Strg+C)") - row_insert = menu.addAction("Zeile einfügen (Strg+V)") - ac = menu.exec_(tblStuff.mapToGlobal(position)) - if ac == row_remove: - # tblStuff.removeRow(row) - # self.calc_voluminae() - self.row_remove(tblStuff) - - elif ac == row_add: - tblStuff.insertRow(row) - elif ac == row_copy: - self.remembered_row = row - elif ac == row_insert: - self.row_insert(tblStuff, self.remembered_row, row) - def init_ui(self): tblGarage = self.ui.tableGarage tblStuff = self.ui.tableStuff # clear garage - # tblGarage.clear() - tblGarage.setRowCount(0) tblGarage.setRowCount(1) - tblGarage.setVerticalHeaderLabels(["Garage"]) # clear stuff # tblStuff.clear() @@ -291,8 +267,8 @@ class MyMainWindow(QMainWindow): def app_quit(self): if self.is_modified: - msg = "Es existieen ungespeicherte Einträge. Ohne Speichern sind alle Änderungen verloren. Trotzdem fortfahren?" - reply = QMessageBox.question(self, "Beenden", msg, \ + msg = QCoreApplication.translate("main", TXT_UNSAVED_CHANGES) + reply = QMessageBox.question(self, QCoreApplication.translate("main", "Quit"), msg, \ QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if reply == QMessageBox.No: return False @@ -300,8 +276,8 @@ class MyMainWindow(QMainWindow): def file_new(self): if self.is_modified: - msg = "Es wurden bereits Einträge manuell geändert. Ohne Speichern sind alle Änderungen verloren. Trotzdem fortfahren?" - reply = QMessageBox.question(self, "Neu", msg, \ + msg = QCoreApplication.translate("main", TXT_UNSAVED_CHANGES) + reply = QMessageBox.question(self, QCoreApplication.translate("main", "New"), msg, \ QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if reply == QMessageBox.No: return False @@ -317,8 +293,12 @@ class MyMainWindow(QMainWindow): if not self.opened_file: # if not file already open options = QFileDialog.Options() - fileName, _ = QFileDialog.getSaveFileName(None, "Speichern", None, - "CSV-Datei (*.csv);;Alle Dateien (*)", + txt_title = QCoreApplication.translate("main", "Save") + txt_file = QCoreApplication.translate("main", "CSV-file") + txt_all_files = QCoreApplication.translate("main", "All files") + + fileName, _ = QFileDialog.getSaveFileName(None, txt_title, None, + txt_file + " (*.csv);;" + txt_all_files + " (*)", options=options) if fileName: # if not file already open @@ -360,7 +340,7 @@ class MyMainWindow(QMainWindow): garage_height = 0.0 if garage_length or garage_width or garage_height: - writer.writerow(["Garage", garage_length, garage_width, garage_height]) + writer.writerow([QCoreApplication.translate("main","Garage"), garage_length, garage_width, garage_height]) # loop over table Stuff for row in range(tblStuff.rowCount()): @@ -412,12 +392,13 @@ class MyMainWindow(QMainWindow): self.is_modified = False if is_file_saved: - self.statusBar.showMessage(f"Datei {fileName} gespeichert.", 2000) + msg = QCoreApplication.translate("main", "file") + " '" + fileName + "' " + QCoreApplication.translate("main", "saved") + self.statusBar.showMessage(msg, 2000) def file_open(self): if self.is_modified: - msg = "Es wurden bereits Einträge manuell geändert. Ohne Speichern sind alle Änderungen verloren. Trotzdem fortfahren?" - reply = QMessageBox.question(self, "Fortfahren?", msg, \ + msg = QCoreApplication.translate("main", TXT_UNSAVED_CHANGES) + reply = QMessageBox.question(self, QCoreApplication.translate("main", "Open"), msg, \ QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if reply == QMessageBox.No: return False @@ -427,11 +408,12 @@ class MyMainWindow(QMainWindow): options = QFileDialog.Options() - sTxtFilesAll = "Alle Dateien" - sTxtFiles = "CSV-Datei" + txt_title = QCoreApplication.translate("main", "Open") + txt_file = QCoreApplication.translate("main", "CSV-file") + txt_all_files = QCoreApplication.translate("main", "All files") - fileName, _ = QFileDialog.getOpenFileName(self, "Öffnen", None, - sTxtFiles + " (*.csv);;" + sTxtFilesAll + " (*)", + fileName, _ = QFileDialog.getOpenFileName(self, txt_title, None, + txt_file + " (*.csv);;" + txt_all_files + " (*)", options=options) if fileName: self.init_ui() @@ -487,138 +469,140 @@ class MyMainWindow(QMainWindow): tblGarage = self.ui.tableGarage tblStuff = self.ui.tableStuff options = QFileDialog.Options() - file_name, _ = QFileDialog.getSaveFileName(None, "Exportieren", None, "Excel-Datei (*.xlsx);;Alle Dateien (*)", options=options) + + txt_title = QCoreApplication.translate("main", "Export") + txt_file = QCoreApplication.translate("main", "EXCEL-file") + txt_all_files = QCoreApplication.translate("main", "All files") + + file_name, _ = QFileDialog.getSaveFileName(None, txt_title, None, txt_file + " (*.xlsx);;" + txt_all_files + " (*)", options=options) if file_name: - try: - import xlsxwriter - except ModuleNotFoundError: - print(f"[{__file__}] Module 'xlsxwriter' not found!") - else: - print(f"Exporting into file -> {file_name}") + print(f"Exporting into file -> {file_name}") - workbook = xlsxwriter.Workbook(file_name) - worksheet = workbook.add_worksheet() + workbook = xlsxwriter.Workbook(file_name) + worksheet = workbook.add_worksheet() - # write col header - start_row = 0 - worksheet.write(start_row, 0, "Dimension der Garage") + # write col header + start_row = 0 + worksheet.write(start_row, 0, QCoreApplication.translate("main", "Dimension of the garage")) - start_row = 1 - worksheet.write(start_row, COL_LENGTH, "Länge [m]") - worksheet.write(start_row, COL_WIDTH, "Breite [m]") - worksheet.write(start_row, COL_HEIGHT, "Höhe [m]") - worksheet.set_column(0, 0, 25) - worksheet.set_column(1, 3, 10) + start_row = 1 - start_row = 2 - # loop over table Garage - for row in range(tblGarage.rowCount()): - # get garage length - garage_length = tblGarage.item(0, 0).text() + worksheet.write(start_row, COL_LENGTH, QCoreApplication.translate("main","Length") + " [m]") + worksheet.write(start_row, COL_WIDTH, QCoreApplication.translate("main","Width") + " [m]") + worksheet.write(start_row, COL_HEIGHT, QCoreApplication.translate("main","Height") + " [m]") + worksheet.set_column(0, 0, 25) + worksheet.set_column(1, 3, 10) + + start_row = 2 + # loop over table Garage + for row in range(tblGarage.rowCount()): + # get garage length + garage_length = tblGarage.item(0, 0).text() + try: + garage_length = float(garage_length) + except ValueError: + garage_length = 0.0 + + # get garage width + garage_width = tblGarage.item(0, 1).text() + try: + garage_width = float(garage_width) + except ValueError: + garage_width = 0.0 + + # get garage height + garage_height = tblGarage.item(0, 2).text() + try: + garage_height = float(garage_height) + except ValueError: + garage_height = 0.0 + + worksheet.write(start_row + row, COL_LENGTH, garage_length) + worksheet.write(start_row + row, COL_WIDTH, garage_width) + worksheet.write(start_row + row, COL_HEIGHT, garage_height) + + start_row = 4 + worksheet.write(start_row, 0, QCoreApplication.translate("main", "Dimensions of the objects to be stored")) + + start_row = 5 + worksheet.write(start_row, COL_LENGTH, QCoreApplication.translate("main","Length") + " [m]") + worksheet.write(start_row, COL_WIDTH, QCoreApplication.translate("main","Width") + " [m]") + worksheet.write(start_row, COL_HEIGHT, QCoreApplication.translate("main","Height") + " [m]") + worksheet.write(start_row, COL_WEIGHT, QCoreApplication.translate("main","Weight") + " [kg]") + + start_row = 6 + # loop over table Stuff + row_idx = start_row + for row in range(tblStuff.rowCount()): + item_stuff = tblStuff.item(row, COL_STUFF) + item_length = tblStuff.item(row, COL_LENGTH) + item_width = tblStuff.item(row, COL_WIDTH) + item_height = tblStuff.item(row, COL_HEIGHT) + item_weight = tblStuff.item(row, COL_WEIGHT) + + if item_stuff: + stuff_text = item_stuff.text() + if len(stuff_text)>0: + worksheet.write(start_row + row, COL_STUFF, stuff_text) + + if item_length: try: - garage_length = float(garage_length) + length = float(item_length.text()) + if length: + worksheet.write(start_row + row, COL_LENGTH, length) except ValueError: - garage_length = 0.0 + pass - # get garage width - garage_width = tblGarage.item(0, 1).text() + if item_width: try: - garage_width = float(garage_width) + width = float(item_width.text()) + if width: + worksheet.write(start_row + row, COL_WIDTH, width) except ValueError: - garage_width = 0.0 + pass - # get garage height - garage_height = tblGarage.item(0, 2).text() + if item_height: try: - garage_height = float(garage_height) + height = float(item_height.text()) + if height: + worksheet.write(start_row + row, COL_HEIGHT, height) except ValueError: - garage_height = 0.0 + pass - worksheet.write(start_row + row, COL_LENGTH, garage_length) - worksheet.write(start_row + row, COL_WIDTH, garage_width) - worksheet.write(start_row + row, COL_HEIGHT, garage_height) + if item_weight: + try: + weight = float(item_weight.text()) + if weight: + worksheet.write(start_row + row, COL_WEIGHT, weight) + except ValueError: + pass - start_row = 4 - worksheet.write(start_row, 0, "Dimensionen der zu verstauenden Gegenstände") + if item_stuff or item_length or item_width or item_height or item_weight: + row_idx += 1 - start_row = 5 - worksheet.write(start_row, COL_LENGTH, "Länge [m]") - worksheet.write(start_row, COL_WIDTH, "Breite [m]") - worksheet.write(start_row, COL_HEIGHT, "Höhe [m]") - worksheet.write(start_row, COL_WEIGHT, "Gewicht [kg]") + row_idx += 1 + # loop over Results + worksheet.write(row_idx, 0, QCoreApplication.translate("main","Result")) - start_row = 6 - # loop over table Stuff - row_idx = start_row - for row in range(tblStuff.rowCount()): - item_stuff = tblStuff.item(row, COL_STUFF) - item_length = tblStuff.item(row, COL_LENGTH) - item_width = tblStuff.item(row, COL_WIDTH) - item_height = tblStuff.item(row, COL_HEIGHT) - item_weight = tblStuff.item(row, COL_WEIGHT) + row_idx += 1 + worksheet.write(row_idx, 0, QCoreApplication.translate("main","Volume of the garage") + ":") + worksheet.write(row_idx, 1, float(self.ui.efVol_Garage.text())) - if item_stuff: - stuff_text = item_stuff.text() - if len(stuff_text)>0: - worksheet.write(start_row + row, COL_STUFF, stuff_text) + row_idx += 1 + worksheet.write(row_idx, 0, QCoreApplication.translate("main","Volume of the items") + ":") + worksheet.write(row_idx, 1, float(self.ui.efVol_Stuff.text())) - if item_length: - try: - length = float(item_length.text()) - if length: - worksheet.write(start_row + row, COL_LENGTH, length) - except ValueError: - pass + row_idx += 1 + worksheet.write(row_idx, 0, QCoreApplication.translate("main","Free space in the garage") + ":") + worksheet.write(row_idx, 1, float(self.ui.efVol_Free.text())) - if item_width: - try: - width = float(item_width.text()) - if width: - worksheet.write(start_row + row, COL_WIDTH, width) - except ValueError: - pass + row_idx += 1 + worksheet.write(row_idx, 0, QCoreApplication.translate("main", "Total weight") + ":") + worksheet.write(row_idx, 1, float(self.ui.efWeight.text())) - if item_height: - try: - height = float(item_height.text()) - if height: - worksheet.write(start_row + row, COL_HEIGHT, height) - except ValueError: - pass - - if item_weight: - try: - weight = float(item_weight.text()) - if weight: - worksheet.write(start_row + row, COL_WEIGHT, weight) - except ValueError: - pass - - if item_stuff or item_length or item_width or item_height or item_weight: - row_idx += 1 - - row_idx += 1 - # loop over Results - worksheet.write(row_idx, 0, "Ergebnis") - - row_idx += 1 - worksheet.write(row_idx, 0, "Volumen der Garage:") - worksheet.write(row_idx, 1, float(self.ui.efVol_Garage.text())) - - row_idx += 1 - worksheet.write(row_idx, 0, "Volumen der Gegenstände:") - worksheet.write(row_idx, 1, float(self.ui.efVol_Stuff.text())) - - row_idx += 1 - worksheet.write(row_idx, 0, "Freier Raum") - worksheet.write(row_idx, 1, float(self.ui.efVol_Free.text())) - - row_idx += 1 - worksheet.write(row_idx, 0, "Gesamtgewicht") - worksheet.write(row_idx, 1, float(self.ui.efWeight.text())) - - workbook.close() - self.statusBar.showMessage(f"Erfolgreich nach EXCEL exportiert.", 5000) + workbook.close() + msg = QCoreApplication.translate("main", "Successfully exported to EXCEL") + " :-)" + self.statusBar.showMessage(msg, 5000) def on_garage_changed(self): self.is_modified = True @@ -670,8 +654,8 @@ class MyMainWindow(QMainWindow): if not is_error: garage_vol = round(float(garage_length) * float(garage_width) * float(garage_height), 2) else: - garage_vol = 0.0 - self.statusBar.showMessage("Fehler in der Garagen-Dimension. :-(", 5000) + msg = QCoreApplication.translate("main", "Error in the garage dimension") + " :-(" + self.statusBar.showMessage(msg, 5000) return garage_vol @@ -722,8 +706,9 @@ class MyMainWindow(QMainWindow): stuff_vol = stuff_vol + vol if is_error: - stuff_vol = 0.0 - self.statusBar.showMessage("Fehler in den Dimensionen der zu verstauenden Gegenstände :-(", 5000) + # stuff_vol = 0.0 + msg = QCoreApplication.translate("main", "Error in the dimensions of the objects to be stored") + " :-(" + self.statusBar.showMessage(msg, 5000) return stuff_vol @@ -732,11 +717,9 @@ class MyMainWindow(QMainWindow): # get garage vol garage_vol = self.get_garage_vol() - print("garage_vol", garage_vol) # get stuff vol stuff_vol = self.get_stuff_vol() - print("stuff_vol", stuff_vol) # display results self.ui.efVol_Garage.setText(f"{garage_vol:2.2f}") @@ -768,6 +751,36 @@ class MyMainWindow(QMainWindow): self.ui.efWeight.setText(f"{weight_sum:2.2f}") + def store_selected_language(self, menu): + """ Stores selected menu labels to ini-file. """ + # [print(action.text()) for action in menu.actions() if action.isChecked()] + + print("Current dir:", os.getcwd()) + language = "English" + for action in menu.actions(): + if action.isChecked(): + language = action.text() + + config['DEFAULT']['language'] = language + if not os.path.exists(os.path.join(DIR_APPDATA, APP_DISPNAME)): + os.makedirs(os.path.join(DIR_APPDATA, APP_DISPNAME)) + + with open(os.path.join(DIR_APPDATA, APP_DISPNAME, APP_DISPNAME + '.ini'), 'w') as configfile: # save + config.write(configfile) + + if language == "Deutsch": + print("Loading german language file.") + translator.load(resource_path('./i18n/de_DE')) + elif language == "Magyar": + print("Loading hungarian langauge file.") + translator.load(resource_path('./i18n/hu_HU')) + else: + #qApp.removeTranslator(translator) + translator.load("dummy") + print(f"Unknown language setting '{language}' -> defaulting to english language.") + + self.retranslateUi() + if __name__ == "__main__": qApp = QApplication([]) @@ -781,8 +794,38 @@ if __name__ == "__main__": qApp.setWindowIcon(QIcon(APP_ICON)) qApp.setDesktopFileName(APP_DESKTOPFILENAME) - winMain = MyMainWindow() - winMain.setFixedWidth(600) - winMain.setFixedHeight(620) + config = configparser.ConfigParser() + + language = "Deutsch" + + if os.path.exists(os.path.join(DIR_APPDATA, APP_DISPNAME, APP_DISPNAME + '.ini')): + config.read(os.path.join(DIR_APPDATA, APP_DISPNAME, APP_DISPNAME + '.ini')) + language = config['DEFAULT']['language'] + else: + config['DEFAULT']['language'] = language + if not os.path.exists(os.path.join(DIR_APPDATA, APP_DISPNAME)): + os.makedirs(os.path.join(DIR_APPDATA, APP_DISPNAME)) + + with open(os.path.join(DIR_APPDATA, APP_DISPNAME, APP_DISPNAME + '.ini'), 'w') as configfile: # save + config.write(configfile) + + translator = QTranslator() + + print("Current dir:", os.getcwd()) + if language == "Deutsch": + print("Loading german language file.") + translator.load(resource_path('./i18n/de_DE')) + elif language == "Magyar": + print("Loading hungarian langauge file.") + translator.load(resource_path('./i18n/hu_HU')) + else: + print(f"Unknown language setting '{language}' -> defaulting to english language.") + + qApp.installTranslator(translator) + + winMain = MyMainWindow(language) + # winMain.setWidth(600) + # winMain.setHeight(625) + winMain.resize(610, 640) winMain.show() sys.exit(qApp.exec_()) diff --git a/src/utils.py b/src/utils.py index ec0dcfc..9d008bb 100644 --- a/src/utils.py +++ b/src/utils.py @@ -34,18 +34,21 @@ def show_about(): msg = QMessageBox() msg.setIconPixmap(QPixmap(resource_path(APP_ICON))) + APP_NAME = qApp.translate("main", "Garage Space Calculator") + APP_DESCR = qApp.translate("main", "Calculates available garage space") + text = "

" + qApp.applicationDisplayName() + " " + \ "
" + qApp.applicationVersion() + "

" + \ - "
" + qApp.applicationName() + "
" + \ - "
" + qApp.description + "
" + \ - "
" + "Idee von: Balazs Fabian" + "
" + \ + "
" + APP_NAME + "
" + \ + "
" + APP_DESCR + "
" + \ + "
" + qApp.translate("utils", "Idea") + ": Balazs Fabian" + "
" + \ "
" + qApp.copyright + "
" \ "
" + qApp.website + "

" - text = text + "

Used icons: Theme 'Cute Color' from Icons8

" - text = text + "

Python version: " + f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro} {sys.version_info.releaselevel}" + text = text + "

" + qApp.translate("utils", "Used icons: Theme") + " 'Cute Color' " + qApp.translate("utils", "from") + " Icons8

" + text = text + "

Python " + qApp.translate("utils", "Version") + ": " + f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro} {sys.version_info.releaselevel}" text = text + "
" + f"{sys.executable}" + "
" - text = text + "
Qt version: " + f"{QtCore.__version__}" + text = text + "
Qt " + qApp.translate("utils", "Version") + ": " + f"{QtCore.__version__}" msg.setText(text) msg.setWindowTitle("About") diff --git a/ui/main.ui b/ui/main.ui index c799198..85b7d1d 100644 --- a/ui/main.ui +++ b/ui/main.ui @@ -64,7 +64,7 @@ - Freier Raum i. d. Garage: + Freier Raum in der Garage: @@ -183,7 +183,7 @@ kg - + 10 @@ -212,52 +212,7 @@ true - - - - - - 0 - 0 - - - - true - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - - Gegenstand - - - - - Länge [m] - - - - - Breite [m] - - - - - Höhe [m] - - - - - Gewicht [kg] - - - - - + @@ -326,7 +281,6 @@ tableGarage - tableStuff efVol_Garage efVol_Stuff efVol_Free