i18n and ini-file

added internationalization, clsTableWidget and INI-file settings and pyinstaller-scripts
This commit is contained in:
Paul S 2021-06-30 18:25:01 +02:00
parent 90afe07406
commit 48f8dc221a
20 changed files with 1230 additions and 333 deletions

BIN
i18n/de_DE.qm Normal file

Binary file not shown.

308
i18n/de_DE.ts Normal file
View file

@ -0,0 +1,308 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="de_DE">
<context>
<name>TableWidget</name>
<message>
<location filename="clsTableWidget.py" line="81"/>
<source>Cut</source>
<translation>Ausschneiden</translation>
</message>
<message>
<location filename="clsTableWidget.py" line="82"/>
<source>Copy</source>
<translation>Kopieren</translation>
</message>
<message>
<location filename="clsTableWidget.py" line="83"/>
<source>Paste</source>
<translation>Einfügen</translation>
</message>
<message>
<location filename="clsTableWidget.py" line="256"/>
<source>Delete</source>
<translation>Löschen</translation>
</message>
<message>
<location filename="clsTableWidget.py" line="106"/>
<source>Cut row</source>
<translation>Zeile ausschneiden</translation>
</message>
<message>
<location filename="clsTableWidget.py" line="107"/>
<source>Copy row</source>
<translation>Zeile kopieren</translation>
</message>
<message>
<location filename="clsTableWidget.py" line="108"/>
<source>Paste row</source>
<translation>Zeile einfügen</translation>
</message>
<message>
<location filename="clsTableWidget.py" line="110"/>
<source>Insert row before</source>
<translation>Zeile oberhalb einfügen</translation>
</message>
<message>
<location filename="clsTableWidget.py" line="111"/>
<source>Insert row after</source>
<translation>Zeile unterhalb einfügen</translation>
</message>
<message>
<location filename="clsTableWidget.py" line="113"/>
<source>Remove row</source>
<translation>Zeile entfernen</translation>
</message>
<message>
<location filename="clsTableWidget.py" line="114"/>
<source>Delete items</source>
<translation>Zellinhalte löschen</translation>
</message>
<message>
<location filename="clsTableWidget.py" line="149"/>
<source>Row Nr.</source>
<translation>Zeile Nr.</translation>
</message>
<message>
<location filename="clsTableWidget.py" line="150"/>
<source>to be removed?</source>
<translation>entfernen?</translation>
</message>
<message>
<location filename="clsTableWidget.py" line="150"/>
<source>Remove</source>
<translation>Entfernen</translation>
</message>
<message>
<location filename="clsTableWidget.py" line="256"/>
<source>Delete cell content?</source>
<translation>Zellinhalt löschen?</translation>
</message>
</context>
<context>
<name>main</name>
<message>
<location filename="clsTableWidget.py" line="231"/>
<source>HELLO</source>
<translation type="obsolete">Hallo Welt</translation>
</message>
<message>
<location filename="main.py" line="114"/>
<source>&amp;Settings</source>
<translation>&amp;Einstellungen</translation>
</message>
<message>
<location filename="main.py" line="115"/>
<source>Language</source>
<translation>Sprache</translation>
</message>
<message>
<location filename="main.py" line="125"/>
<source>Stuff</source>
<translation>Gegenstand</translation>
</message>
<message>
<location filename="main.py" line="516"/>
<source>Length</source>
<translation>Länge</translation>
</message>
<message>
<location filename="main.py" line="517"/>
<source>Width</source>
<translation>Breite</translation>
</message>
<message>
<location filename="main.py" line="518"/>
<source>Height</source>
<translation>Höhe</translation>
</message>
<message>
<location filename="main.py" line="519"/>
<source>Weight</source>
<translation>Gewicht</translation>
</message>
<message>
<location filename="main.py" line="474"/>
<source>Dimension of the garage</source>
<translation>Dimension der Garage</translation>
</message>
<message>
<location filename="main.py" line="513"/>
<source>Dimensions of the objects to be stored</source>
<translation>Dimensionen der zu verstauenden Gegenstände</translation>
</message>
<message>
<location filename="main.py" line="573"/>
<source>Result</source>
<translation>Ergebnis</translation>
</message>
<message>
<location filename="main.py" line="576"/>
<source>Volume of the garage</source>
<translation>Volumen der Garage</translation>
</message>
<message>
<location filename="main.py" line="580"/>
<source>Volume of the items</source>
<translation>Volumen der Gegenstände</translation>
</message>
<message>
<location filename="main.py" line="584"/>
<source>Free space in the garage</source>
<translation>Freier Raum in der Garage</translation>
</message>
<message>
<location filename="main.py" line="588"/>
<source>Total weight</source>
<translation>Gesamtgewicht</translation>
</message>
<message>
<location filename="main.py" line="142"/>
<source>New (Ctrl+N)</source>
<translation>Neu (Strg+N)</translation>
</message>
<message>
<location filename="main.py" line="143"/>
<source>Open... (Ctrl+O)</source>
<translation>Öffnen... (Strg+O)</translation>
</message>
<message>
<location filename="main.py" line="144"/>
<source>Save (Ctrl+S)</source>
<translation>Speichern (Strg+S)</translation>
</message>
<message>
<location filename="main.py" line="145"/>
<source>Export to EXCEL...</source>
<translation>Export nach EXCEL...</translation>
</message>
<message>
<location filename="main.py" line="146"/>
<source>Information about the application</source>
<translation>Informationen über das Programm</translation>
</message>
<message>
<location filename="main.py" line="147"/>
<source>Quit the application (Strg+Q)</source>
<translation>Programm beenden (Strg+Q)</translation>
</message>
<message>
<location filename="main.py" line="331"/>
<source>Garage</source>
<translation>Garage</translation>
</message>
<message>
<location filename="main.py" line="259"/>
<source>Quit</source>
<translation>Beenden</translation>
</message>
<message>
<location filename="main.py" line="268"/>
<source>New</source>
<translation>Neu</translation>
</message>
<message>
<location filename="main.py" line="285"/>
<source>Save</source>
<translation>Speichern</translation>
</message>
<message>
<location filename="main.py" line="401"/>
<source> CSV-file</source>
<translation type="obsolete">CSV-Datei</translation>
</message>
<message>
<location filename="main.py" line="465"/>
<source>All files</source>
<translation>Alle Dateien</translation>
</message>
<message>
<location filename="main.py" line="383"/>
<source>file</source>
<translation>Datei</translation>
</message>
<message>
<location filename="main.py" line="384"/>
<source>saved</source>
<translation>gespeichert</translation>
</message>
<message>
<location filename="main.py" line="400"/>
<source>Open</source>
<translation>Öffnen</translation>
</message>
<message>
<location filename="main.py" line="462"/>
<source>Export</source>
<translation>Export</translation>
</message>
<message>
<location filename="main.py" line="463"/>
<source> EXCEL-file</source>
<translation type="obsolete">EXCEL-Datei</translation>
</message>
<message>
<location filename="main.py" line="645"/>
<source>Error in the garage dimension</source>
<translation>Fehler in der Garagen-Dimension</translation>
</message>
<message>
<location filename="main.py" line="698"/>
<source>Error in the dimensions of the objects to be stored</source>
<translation>Fehler in den Dimensionen der zu verstauenden Gegenstände</translation>
</message>
<message>
<location filename="main.py" line="592"/>
<source>Successfully exported to EXCEL</source>
<translation>Erfolgreich nach EXCEL exportiert</translation>
</message>
<message>
<location filename="main.py" line="401"/>
<source>CSV-file</source>
<translation>CSV-Datei</translation>
</message>
<message>
<location filename="main.py" line="463"/>
<source>EXCEL-file</source>
<translation>EXCEL-Datei</translation>
</message>
<message>
<location filename="main.py" line="69"/>
<source>There are unsaved entries. Without saving, all changes are lost. Continue anyway?</source>
<translation>Es gibt ungespeicherte Einträge. Ohne zu speichern, gehen alle Änderungen verloren. Trotzdem fortfahren?</translation>
</message>
<message>
<location filename="main.py" line="36"/>
<source>Garage Space Calculator</source>
<translation>Garagenraum-Rechner</translation>
</message>
<message>
<location filename="main.py" line="39"/>
<source>Calculates available garage space</source>
<translation>Berechnet zur Verfügung stehenden Garagenraum</translation>
</message>
</context>
<context>
<name>utils</name>
<message>
<location filename="utils.py" line="41"/>
<source>Idea</source>
<translation>Idee</translation>
</message>
<message>
<location filename="utils.py" line="45"/>
<source>Used icons: Theme</source>
<translation>Verwendete Icons: Thema</translation>
</message>
<message>
<location filename="utils.py" line="45"/>
<source>from</source>
<translation>von</translation>
</message>
<message>
<location filename="utils.py" line="48"/>
<source>Version</source>
<translation>Version</translation>
</message>
</context>
</TS>

BIN
i18n/hu_HU.qm Normal file

Binary file not shown.

308
i18n/hu_HU.ts Normal file
View file

@ -0,0 +1,308 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="de_DE">
<context>
<name>TableWidget</name>
<message>
<location filename="clsTableWidget.py" line="81"/>
<source>Cut</source>
<translation>Vágd ki</translation>
</message>
<message>
<location filename="clsTableWidget.py" line="82"/>
<source>Copy</source>
<translation>Vettem</translation>
</message>
<message>
<location filename="clsTableWidget.py" line="83"/>
<source>Paste</source>
<translation>Beillesztés</translation>
</message>
<message>
<location filename="clsTableWidget.py" line="256"/>
<source>Delete</source>
<translation>Törölje</translation>
</message>
<message>
<location filename="clsTableWidget.py" line="106"/>
<source>Cut row</source>
<translation>Vágott vonal</translation>
</message>
<message>
<location filename="clsTableWidget.py" line="107"/>
<source>Copy row</source>
<translation>Vettem a szöveget</translation>
</message>
<message>
<location filename="clsTableWidget.py" line="108"/>
<source>Paste row</source>
<translation>Beilleszteni a sort</translation>
</message>
<message>
<location filename="clsTableWidget.py" line="110"/>
<source>Insert row before</source>
<translation>A fenti sort beilleszteni</translation>
</message>
<message>
<location filename="clsTableWidget.py" line="111"/>
<source>Insert row after</source>
<translation>Az alábbi sor beillesztése</translation>
</message>
<message>
<location filename="clsTableWidget.py" line="113"/>
<source>Remove row</source>
<translation>Távolítsa el a sort</translation>
</message>
<message>
<location filename="clsTableWidget.py" line="114"/>
<source>Delete items</source>
<translation>Tartalom törlése</translation>
</message>
<message>
<location filename="clsTableWidget.py" line="149"/>
<source>Row Nr.</source>
<translation>Vonalszám.</translation>
</message>
<message>
<location filename="clsTableWidget.py" line="150"/>
<source>to be removed?</source>
<translation>eltávolítani?</translation>
</message>
<message>
<location filename="clsTableWidget.py" line="150"/>
<source>Remove</source>
<translation>A eltávolítása</translation>
</message>
<message>
<location filename="clsTableWidget.py" line="256"/>
<source>Delete cell content?</source>
<translation>Cellatartalom törlése?</translation>
</message>
</context>
<context>
<name>main</name>
<message>
<location filename="clsTableWidget.py" line="231"/>
<source>HELLO</source>
<translation type="obsolete">Hallo Welt</translation>
</message>
<message>
<location filename="main.py" line="114"/>
<source>&amp;Settings</source>
<translation>&amp;Beállítások</translation>
</message>
<message>
<location filename="main.py" line="115"/>
<source>Language</source>
<translation>Nyelv</translation>
</message>
<message>
<location filename="main.py" line="125"/>
<source>Stuff</source>
<translation>Tárgy</translation>
</message>
<message>
<location filename="main.py" line="516"/>
<source>Length</source>
<translation>Hosszúság</translation>
</message>
<message>
<location filename="main.py" line="517"/>
<source>Width</source>
<translation>Szélesség</translation>
</message>
<message>
<location filename="main.py" line="518"/>
<source>Height</source>
<translation>Magasság</translation>
</message>
<message>
<location filename="main.py" line="519"/>
<source>Weight</source>
<translation>Súly</translation>
</message>
<message>
<location filename="main.py" line="474"/>
<source>Dimension of the garage</source>
<translation>A garázs mérete</translation>
</message>
<message>
<location filename="main.py" line="513"/>
<source>Dimensions of the objects to be stored</source>
<translation>A tárolandó objektumok méretei</translation>
</message>
<message>
<location filename="main.py" line="573"/>
<source>Result</source>
<translation>Eredmény</translation>
</message>
<message>
<location filename="main.py" line="576"/>
<source>Volume of the garage</source>
<translation>A garázs térfogata</translation>
</message>
<message>
<location filename="main.py" line="580"/>
<source>Volume of the items</source>
<translation>A tételek mennyisége</translation>
</message>
<message>
<location filename="main.py" line="584"/>
<source>Free space in the garage</source>
<translation>Szabad hely a garázsban</translation>
</message>
<message>
<location filename="main.py" line="588"/>
<source>Total weight</source>
<translation>Teljes súly</translation>
</message>
<message>
<location filename="main.py" line="142"/>
<source>New (Ctrl+N)</source>
<translation>Új (Ctrl+N)</translation>
</message>
<message>
<location filename="main.py" line="143"/>
<source>Open... (Ctrl+O)</source>
<translation>Nyissa ki (Ctrl+O)</translation>
</message>
<message>
<location filename="main.py" line="144"/>
<source>Save (Ctrl+S)</source>
<translation>Mentés (Ctrl+S)</translation>
</message>
<message>
<location filename="main.py" line="145"/>
<source>Export to EXCEL...</source>
<translation>Exportálás EXCEL-be...</translation>
</message>
<message>
<location filename="main.py" line="146"/>
<source>Information about the application</source>
<translation>Az alkalmazással kapcsolatos információk</translation>
</message>
<message>
<location filename="main.py" line="147"/>
<source>Quit the application (Strg+Q)</source>
<translation>Az alkalmazás kilépése (Ctrl+Q)</translation>
</message>
<message>
<location filename="main.py" line="331"/>
<source>Garage</source>
<translation>Garázs</translation>
</message>
<message>
<location filename="main.py" line="259"/>
<source>Quit</source>
<translation>Kilépés</translation>
</message>
<message>
<location filename="main.py" line="268"/>
<source>New</source>
<translation>Új</translation>
</message>
<message>
<location filename="main.py" line="285"/>
<source>Save</source>
<translation>Mentés</translation>
</message>
<message>
<location filename="main.py" line="401"/>
<source> CSV-file</source>
<translation type="obsolete">CSV-fájl</translation>
</message>
<message>
<location filename="main.py" line="465"/>
<source>All files</source>
<translation>Minden fájl</translation>
</message>
<message>
<location filename="main.py" line="383"/>
<source>file</source>
<translation>fájl</translation>
</message>
<message>
<location filename="main.py" line="384"/>
<source>saved</source>
<translation>mentett</translation>
</message>
<message>
<location filename="main.py" line="400"/>
<source>Open</source>
<translation>Megnyitott</translation>
</message>
<message>
<location filename="main.py" line="462"/>
<source>Export</source>
<translation>Exportálás</translation>
</message>
<message>
<location filename="main.py" line="463"/>
<source> EXCEL-file</source>
<translation type="obsolete">EXCEL-fájl</translation>
</message>
<message>
<location filename="main.py" line="645"/>
<source>Error in the garage dimension</source>
<translation>Hiba a garázs dimenziójában</translation>
</message>
<message>
<location filename="main.py" line="698"/>
<source>Error in the dimensions of the objects to be stored</source>
<translation>Hiba a tárolandó objektumok méreteiben</translation>
</message>
<message>
<location filename="main.py" line="592"/>
<source>Successfully exported to EXCEL</source>
<translation>Sikeresen exportált EXCEL-be</translation>
</message>
<message>
<location filename="main.py" line="401"/>
<source>CSV-file</source>
<translation>CSV-fájl</translation>
</message>
<message>
<location filename="main.py" line="463"/>
<source>EXCEL-file</source>
<translation>EXCEL-fájl</translation>
</message>
<message>
<location filename="main.py" line="69"/>
<source>There are unsaved entries. Without saving, all changes are lost. Continue anyway?</source>
<translation>Vannak mentetlen bejegyzések. Mentés nélkül minden módosítás elveszik. Folytassa mégis?</translation>
</message>
<message>
<location filename="main.py" line="36"/>
<source>Garage Space Calculator</source>
<translation>Garázs hely kalkulátor</translation>
</message>
<message>
<location filename="main.py" line="39"/>
<source>Calculates available garage space</source>
<translation>Kiszámítja a rendelkezésre álló garázshelyet</translation>
</message>
</context>
<context>
<name>utils</name>
<message>
<location filename="utils.py" line="41"/>
<source>Idea</source>
<translation>Ötlet</translation>
</message>
<message>
<location filename="utils.py" line="45"/>
<source>Used icons: Theme</source>
<translation>Használt ikonok: Téma</translation>
</message>
<message>
<location filename="utils.py" line="45"/>
<source>from</source>
<translation>a weboldalról</translation>
</message>
<message>
<location filename="utils.py" line="48"/>
<source>Version</source>
<translation>Verzió</translation>
</message>
</context>
</TS>

View file

@ -1,5 +1,4 @@
# project: GarageCalc1 # project: GarageCalc1
# file: requirements.txt # file: requirements.txt
wheel pyside2
pyside2==5.15.2 xlsxwriter
XlsxWriter==1.3.8

View file

@ -1,36 +1,36 @@
@ECHO OFF @ECHO OFF
:: -------------------------------------------------------------------------------------------------------------------------------------- :: --------------------------------------------------------------------------------------------------------------------------------------
:: project: GarageCalc1 Window-Exe Generator :: project: GarageCalc Window-Exe Generator
:: summary: create a GarageCalc1-executable for Windows (version with no-console-windows as well as version with console windows for traces) :: summary: create a GarageCalc-executable for Windows (version with no-console-windows as well as version with console windows for traces)
:: file: pyinstaller.cmd :: file: pyinstaller.cmd
:: date: version author :: date: version author
:: 2021-06-27 1 paul salajean :: 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 release_dir="D:\Temp\Prog\ownCloud\profp@uberspace\transfer"
SET prev_dir=%cd% SET prev_dir=%cd%
SET reinstall_venv="true" SET reinstall_venv="false"
SET do_zip="false" SET do_zip="false"
CD .. CD ..
ECHO Creating GarageCalc1 Windows.exe-version in folder %cd%\dist ECHO Creating GarageCalc Windows.exe-version in folder %cd%\dist
PAUSE PAUSE
REM pyinstaller --noconfirm --log-level=ERROR ^ REM pyinstaller --noconfirm --log-level=ERROR ^
REM --onedir --nowindow ^ REM --onedir --nowindow ^
REM --add-data="README;." ^ REM --add-data="README;." ^
REM --add-data="image1.png;img" ^ REM --add-data="image1.png;img" ^
REM --add-data="img;doc;ui" ^ 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 --add-binary="libfoo.so;lib" ^
REM --hidden-import=secret1 ^ REM --hidden-import=secret1 ^
REM --hidden-import=secret2 ^ REM --hidden-import=secret2 ^
REM --icon=.\img\GarageCalc1.ico ^ REM --icon=.\img\GarageCalc.ico ^
REM --debug=imports ^ REM --debug=imports ^
REM --key=N0T1me40pp0ssum5 REM --key=N0T1me40pp0ssum5
REM --paths=.\src ^ 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 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 This feature uses the "tinyaes" module internally for the encryption.
REM REM
@ -55,19 +55,21 @@ python -m pip install -r requirements.txt
:PYINSTALL :PYINSTALL
IF %reinstall_venv%=="false" CALL .\env2\Scripts\activate.bat IF %reinstall_venv%=="false" CALL .\env2\Scripts\activate.bat
ECHO Running "pyinstaller"... 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" 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"
pyinstaller .\src\main.py --name=GarageCalc1 --noconfirm --windowed --clean --onefile --log-level=ERROR --hidden-import=PySide2.QtXml ^ 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 ^ --icon=.\img\icons8-garage-32.ico ^
--add-data="*.txt;." ^ --add-data="*.txt;." ^
--add-data="*.md;." ^ --add-data="*.md;." ^
--add-data="ui;.\ui" ^ --add-data="ui;.\ui" ^
--add-data="i18n;.\i18n" ^
--add-data="img;.\img" --add-data="img;.\img"
::MOVE .\dist\GarageCalc1\*.md .\dist\ ::MOVE .\dist\GarageCalc\*.md .\dist\
::MOVE .\dist\GarageCalc1\LICENSE .\dist\ ::MOVE .\dist\GarageCalc\LICENSE .\dist\
::clean-up img-folder ::clean-up img-folder
::MOVE .\dist\GarageCalc1\img .\dist\img>NUL ::MOVE .\dist\GarageCalc\img .\dist\img>NUL
::Remove temp dir and files ::Remove temp dir and files
DEL /Q *.spec>NUL DEL /Q *.spec>NUL
@ -81,7 +83,7 @@ IF %do_zip%=="false" GOTO END
::-mx5 = This is default (compression is normal). ::-mx5 = This is default (compression is normal).
::-mx7 = Maximum compression. ::-mx7 = Maximum compression.
::-mx9 = Ultra 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 ::Disable Virtual env
CALL .\env2\Scripts\deactivate.bat CALL .\env2\Scripts\deactivate.bat

View file

@ -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

258
src/clsTableWidget.py Normal file
View file

@ -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)

Binary file not shown.

After

Width:  |  Height:  |  Size: 447 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 416 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 506 B

BIN
src/img/icons8-copy-16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 663 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 751 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 672 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 469 B

BIN
src/img/icons8-paste-16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 612 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 777 B

View file

@ -11,27 +11,34 @@ author: Paul Salajean (p.salajean[at]gmx.de)
import sys import sys
import os import os
import csv import csv
import configparser
# Third party imports # Third party imports
from PySide2.QtWidgets import QApplication, QMainWindow, QTableWidgetItem, QStatusBar, QAction, QFileDialog, \ from PySide2.QtWidgets import QApplication, QMainWindow, QTableWidgetItem, QStatusBar, QFileDialog, \
QAbstractItemView, QMenu, QMessageBox QAbstractItemView, QMenu, QMessageBox, QHBoxLayout, QVBoxLayout, QAction, QActionGroup
from PySide2.QtGui import QIcon 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 from PySide2.QtUiTools import QUiLoader
import xlsxwriter
# Local imports # Local imports
from utils import show_about, resource_path from utils import show_about, resource_path
# Local globals # my own classes imports
APP_VERSION = "v0.3.1" 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_DISPNAME = "GarageCalc"
APP_AUTHOR = "Paul Salajean" 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_COPYRIGHT = "(c) Paul Salajean 2021"
APP_WEBSITE = "https://gitlab.com/ProfP303" APP_WEBSITE = "https://gitlab.com/ProfP303"
APP_DESKTOPFILENAME = "GarageCalc" APP_DESKTOPFILENAME = APP_DISPNAME
APP_ICON = "./img/icons8-garage-32.ico" APP_ICON = "./img/icons8-garage-32.ico"
@ -54,24 +61,93 @@ DEFAULT_GARAGE_LENGTH = "6"
DEFAULT_GARAGE_WIDTH = "2.5" DEFAULT_GARAGE_WIDTH = "2.5"
DEFAULT_GARAGE_HEIGHT = "2.5" DEFAULT_GARAGE_HEIGHT = "2.5"
TBL_STUFF_COL_COUNT = 5
TBL_STUFF_ROW_COUNT = 50 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): class MyMainWindow(QMainWindow):
def __init__(self): def __init__(self, language):
super().__init__() super().__init__()
self.language = language
self.is_modified = False
self.opened_file = None
self.remembered_row = None
self.load_ui() self.load_ui()
self.init_ui() self.init_ui()
self.set_defaults() self.set_defaults()
self.create_menu(self.language)
self.connect_signals() self.connect_signals()
self.create_actions() self.create_actions()
self.create_toolbar() self.create_toolbar()
self.create_statusbar() 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.calc_voluminae()
self.ui.efWeight.setText(str("0")) self.ui.efWeight.setText(str("0"))
self.is_modified = False self.retranslateUi()
self.opened_file = None self.ui.tableStuff.setFocus()
self.remembered_row = None
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): def load_ui(self):
loader = QUiLoader() loader = QUiLoader()
@ -81,46 +157,49 @@ class MyMainWindow(QMainWindow):
self.ui = loader.load(ui_file, self) self.ui = loader.load(ui_file, self)
ui_file.close() ui_file.close()
# self.headers = self.ui.tableStuff.horizontalHeader() #self.ui.tableStuff = TableWidget(self.ui.gbStuff)
self.headers = self.ui.tableStuff.verticalHeader() self.ui.tableStuff = TableWidget()
self.headers.setContextMenuPolicy(Qt.CustomContextMenu) self.ui.tableStuff.setColumnCount(TBL_STUFF_COL_COUNT)
self.headers.customContextMenuRequested.connect(self.show_rowheader_context_menu) self.ui.tableStuff.setRowCount(TBL_STUFF_ROW_COUNT)
self.headers.setSelectionMode(QAbstractItemView.SingleSelection) 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): def create_actions(self):
self.actionNew = QAction() self.actionNew = QAction()
self.actionNew.setIcon(QIcon(resource_path(ICON_NEW))) self.actionNew.setIcon(QIcon(resource_path(ICON_NEW)))
self.actionNew.triggered.connect(self.file_new) self.actionNew.triggered.connect(self.file_new)
self.actionNew.setShortcut("Ctrl+N") self.actionNew.setShortcut("Ctrl+N")
self.actionNew.setToolTip("Neu (Strg+N)")
self.actionOpen = QAction() self.actionOpen = QAction()
self.actionOpen.setIcon(QIcon(resource_path(ICON_OPEN))) self.actionOpen.setIcon(QIcon(resource_path(ICON_OPEN)))
self.actionOpen.triggered.connect(self.file_open) self.actionOpen.triggered.connect(self.file_open)
self.actionOpen.setShortcut("Ctrl+O") self.actionOpen.setShortcut("Ctrl+O")
self.actionOpen.setToolTip("Öffnen... (Strg+O)")
self.actionSave = QAction() self.actionSave = QAction()
self.actionSave.setIcon(QIcon(resource_path(ICON_SAVE))) self.actionSave.setIcon(QIcon(resource_path(ICON_SAVE)))
self.actionSave.triggered.connect(self.file_save) self.actionSave.triggered.connect(self.file_save)
self.actionSave.setShortcut("Ctrl+S") self.actionSave.setShortcut("Ctrl+S")
self.actionSave.setToolTip("Speichern (Strg+S)")
self.actionExport = QAction() self.actionExport = QAction()
self.actionExport.setIcon(QIcon(resource_path(ICON_EXPORT))) self.actionExport.setIcon(QIcon(resource_path(ICON_EXPORT)))
self.actionExport.triggered.connect(self.file_export) self.actionExport.triggered.connect(self.file_export)
self.actionExport.setToolTip("Export nach EXCEL...")
self.actionAbout = QAction() self.actionAbout = QAction()
self.actionAbout.setIcon(QIcon(resource_path(ICON_ABOUT))) self.actionAbout.setIcon(QIcon(resource_path(ICON_ABOUT)))
self.actionAbout.triggered.connect(show_about) self.actionAbout.triggered.connect(show_about)
self.actionAbout.setToolTip("Informationen über das Programm")
self.actionQuit = QAction() self.actionQuit = QAction()
self.actionQuit.setIcon(QIcon(resource_path(ICON_QUIT))) self.actionQuit.setIcon(QIcon(resource_path(ICON_QUIT)))
self.actionQuit.triggered.connect(self.app_quit) self.actionQuit.triggered.connect(self.app_quit)
self.actionQuit.setShortcut("Ctrl+Q") self.actionQuit.setShortcut("Ctrl+Q")
self.actionQuit.setToolTip("Programm beenden (Strg+Q)")
def create_toolbar(self): def create_toolbar(self):
# Main Toolbar (for all pages/views) # Main Toolbar (for all pages/views)
@ -159,115 +238,12 @@ class MyMainWindow(QMainWindow):
tblGarage.itemChanged.connect(self.on_garage_changed) tblGarage.itemChanged.connect(self.on_garage_changed)
tblStuff.itemChanged.connect(self.on_stuff_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): def init_ui(self):
tblGarage = self.ui.tableGarage tblGarage = self.ui.tableGarage
tblStuff = self.ui.tableStuff tblStuff = self.ui.tableStuff
# clear garage # clear garage
# tblGarage.clear()
tblGarage.setRowCount(0)
tblGarage.setRowCount(1) tblGarage.setRowCount(1)
tblGarage.setVerticalHeaderLabels(["Garage"])
# clear stuff # clear stuff
# tblStuff.clear() # tblStuff.clear()
@ -291,8 +267,8 @@ class MyMainWindow(QMainWindow):
def app_quit(self): def app_quit(self):
if self.is_modified: if self.is_modified:
msg = "Es existieen ungespeicherte Einträge. Ohne Speichern sind alle Änderungen verloren. Trotzdem fortfahren?" msg = QCoreApplication.translate("main", TXT_UNSAVED_CHANGES)
reply = QMessageBox.question(self, "Beenden", msg, \ reply = QMessageBox.question(self, QCoreApplication.translate("main", "Quit"), msg, \
QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
if reply == QMessageBox.No: if reply == QMessageBox.No:
return False return False
@ -300,8 +276,8 @@ class MyMainWindow(QMainWindow):
def file_new(self): def file_new(self):
if self.is_modified: if self.is_modified:
msg = "Es wurden bereits Einträge manuell geändert. Ohne Speichern sind alle Änderungen verloren. Trotzdem fortfahren?" msg = QCoreApplication.translate("main", TXT_UNSAVED_CHANGES)
reply = QMessageBox.question(self, "Neu", msg, \ reply = QMessageBox.question(self, QCoreApplication.translate("main", "New"), msg, \
QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
if reply == QMessageBox.No: if reply == QMessageBox.No:
return False return False
@ -317,8 +293,12 @@ class MyMainWindow(QMainWindow):
if not self.opened_file: # if not file already open if not self.opened_file: # if not file already open
options = QFileDialog.Options() options = QFileDialog.Options()
fileName, _ = QFileDialog.getSaveFileName(None, "Speichern", None, txt_title = QCoreApplication.translate("main", "Save")
"CSV-Datei (*.csv);;Alle Dateien (*)", 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) options=options)
if fileName: # if not file already open if fileName: # if not file already open
@ -360,7 +340,7 @@ class MyMainWindow(QMainWindow):
garage_height = 0.0 garage_height = 0.0
if garage_length or garage_width or garage_height: 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 # loop over table Stuff
for row in range(tblStuff.rowCount()): for row in range(tblStuff.rowCount()):
@ -412,12 +392,13 @@ class MyMainWindow(QMainWindow):
self.is_modified = False self.is_modified = False
if is_file_saved: 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): def file_open(self):
if self.is_modified: if self.is_modified:
msg = "Es wurden bereits Einträge manuell geändert. Ohne Speichern sind alle Änderungen verloren. Trotzdem fortfahren?" msg = QCoreApplication.translate("main", TXT_UNSAVED_CHANGES)
reply = QMessageBox.question(self, "Fortfahren?", msg, \ reply = QMessageBox.question(self, QCoreApplication.translate("main", "Open"), msg, \
QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
if reply == QMessageBox.No: if reply == QMessageBox.No:
return False return False
@ -427,11 +408,12 @@ class MyMainWindow(QMainWindow):
options = QFileDialog.Options() options = QFileDialog.Options()
sTxtFilesAll = "Alle Dateien" txt_title = QCoreApplication.translate("main", "Open")
sTxtFiles = "CSV-Datei" txt_file = QCoreApplication.translate("main", "CSV-file")
txt_all_files = QCoreApplication.translate("main", "All files")
fileName, _ = QFileDialog.getOpenFileName(self, "Öffnen", None, fileName, _ = QFileDialog.getOpenFileName(self, txt_title, None,
sTxtFiles + " (*.csv);;" + sTxtFilesAll + " (*)", txt_file + " (*.csv);;" + txt_all_files + " (*)",
options=options) options=options)
if fileName: if fileName:
self.init_ui() self.init_ui()
@ -487,13 +469,13 @@ class MyMainWindow(QMainWindow):
tblGarage = self.ui.tableGarage tblGarage = self.ui.tableGarage
tblStuff = self.ui.tableStuff tblStuff = self.ui.tableStuff
options = QFileDialog.Options() 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: 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) workbook = xlsxwriter.Workbook(file_name)
@ -501,12 +483,13 @@ class MyMainWindow(QMainWindow):
# write col header # write col header
start_row = 0 start_row = 0
worksheet.write(start_row, 0, "Dimension der Garage") worksheet.write(start_row, 0, QCoreApplication.translate("main", "Dimension of the garage"))
start_row = 1 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_LENGTH, QCoreApplication.translate("main","Length") + " [m]")
worksheet.write(start_row, COL_HEIGHT, "Höhe [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(0, 0, 25)
worksheet.set_column(1, 3, 10) worksheet.set_column(1, 3, 10)
@ -539,13 +522,13 @@ class MyMainWindow(QMainWindow):
worksheet.write(start_row + row, COL_HEIGHT, garage_height) worksheet.write(start_row + row, COL_HEIGHT, garage_height)
start_row = 4 start_row = 4
worksheet.write(start_row, 0, "Dimensionen der zu verstauenden Gegenstände") worksheet.write(start_row, 0, QCoreApplication.translate("main", "Dimensions of the objects to be stored"))
start_row = 5 start_row = 5
worksheet.write(start_row, COL_LENGTH, "Länge [m]") worksheet.write(start_row, COL_LENGTH, QCoreApplication.translate("main","Length") + " [m]")
worksheet.write(start_row, COL_WIDTH, "Breite [m]") worksheet.write(start_row, COL_WIDTH, QCoreApplication.translate("main","Width") + " [m]")
worksheet.write(start_row, COL_HEIGHT, "Höhe [m]") worksheet.write(start_row, COL_HEIGHT, QCoreApplication.translate("main","Height") + " [m]")
worksheet.write(start_row, COL_WEIGHT, "Gewicht [kg]") worksheet.write(start_row, COL_WEIGHT, QCoreApplication.translate("main","Weight") + " [kg]")
start_row = 6 start_row = 6
# loop over table Stuff # loop over table Stuff
@ -599,26 +582,27 @@ class MyMainWindow(QMainWindow):
row_idx += 1 row_idx += 1
# loop over Results # loop over Results
worksheet.write(row_idx, 0, "Ergebnis") worksheet.write(row_idx, 0, QCoreApplication.translate("main","Result"))
row_idx += 1 row_idx += 1
worksheet.write(row_idx, 0, "Volumen der Garage:") worksheet.write(row_idx, 0, QCoreApplication.translate("main","Volume of the garage") + ":")
worksheet.write(row_idx, 1, float(self.ui.efVol_Garage.text())) worksheet.write(row_idx, 1, float(self.ui.efVol_Garage.text()))
row_idx += 1 row_idx += 1
worksheet.write(row_idx, 0, "Volumen der Gegenstände:") worksheet.write(row_idx, 0, QCoreApplication.translate("main","Volume of the items") + ":")
worksheet.write(row_idx, 1, float(self.ui.efVol_Stuff.text())) worksheet.write(row_idx, 1, float(self.ui.efVol_Stuff.text()))
row_idx += 1 row_idx += 1
worksheet.write(row_idx, 0, "Freier Raum") worksheet.write(row_idx, 0, QCoreApplication.translate("main","Free space in the garage") + ":")
worksheet.write(row_idx, 1, float(self.ui.efVol_Free.text())) worksheet.write(row_idx, 1, float(self.ui.efVol_Free.text()))
row_idx += 1 row_idx += 1
worksheet.write(row_idx, 0, "Gesamtgewicht") worksheet.write(row_idx, 0, QCoreApplication.translate("main", "Total weight") + ":")
worksheet.write(row_idx, 1, float(self.ui.efWeight.text())) worksheet.write(row_idx, 1, float(self.ui.efWeight.text()))
workbook.close() workbook.close()
self.statusBar.showMessage(f"Erfolgreich nach EXCEL exportiert.", 5000) msg = QCoreApplication.translate("main", "Successfully exported to EXCEL") + " :-)"
self.statusBar.showMessage(msg, 5000)
def on_garage_changed(self): def on_garage_changed(self):
self.is_modified = True self.is_modified = True
@ -670,8 +654,8 @@ class MyMainWindow(QMainWindow):
if not is_error: if not is_error:
garage_vol = round(float(garage_length) * float(garage_width) * float(garage_height), 2) garage_vol = round(float(garage_length) * float(garage_width) * float(garage_height), 2)
else: else:
garage_vol = 0.0 msg = QCoreApplication.translate("main", "Error in the garage dimension") + " :-("
self.statusBar.showMessage("Fehler in der Garagen-Dimension. :-(", 5000) self.statusBar.showMessage(msg, 5000)
return garage_vol return garage_vol
@ -722,8 +706,9 @@ class MyMainWindow(QMainWindow):
stuff_vol = stuff_vol + vol stuff_vol = stuff_vol + vol
if is_error: if is_error:
stuff_vol = 0.0 # stuff_vol = 0.0
self.statusBar.showMessage("Fehler in den Dimensionen der zu verstauenden Gegenstände :-(", 5000) msg = QCoreApplication.translate("main", "Error in the dimensions of the objects to be stored") + " :-("
self.statusBar.showMessage(msg, 5000)
return stuff_vol return stuff_vol
@ -732,11 +717,9 @@ class MyMainWindow(QMainWindow):
# get garage vol # get garage vol
garage_vol = self.get_garage_vol() garage_vol = self.get_garage_vol()
print("garage_vol", garage_vol)
# get stuff vol # get stuff vol
stuff_vol = self.get_stuff_vol() stuff_vol = self.get_stuff_vol()
print("stuff_vol", stuff_vol)
# display results # display results
self.ui.efVol_Garage.setText(f"{garage_vol:2.2f}") 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}") 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__": if __name__ == "__main__":
qApp = QApplication([]) qApp = QApplication([])
@ -781,8 +794,38 @@ if __name__ == "__main__":
qApp.setWindowIcon(QIcon(APP_ICON)) qApp.setWindowIcon(QIcon(APP_ICON))
qApp.setDesktopFileName(APP_DESKTOPFILENAME) qApp.setDesktopFileName(APP_DESKTOPFILENAME)
winMain = MyMainWindow() config = configparser.ConfigParser()
winMain.setFixedWidth(600)
winMain.setFixedHeight(620) 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() winMain.show()
sys.exit(qApp.exec_()) sys.exit(qApp.exec_())

View file

@ -34,18 +34,21 @@ def show_about():
msg = QMessageBox() msg = QMessageBox()
msg.setIconPixmap(QPixmap(resource_path(APP_ICON))) 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 = "<p align='center'><h1>" + qApp.applicationDisplayName() + " " + \ text = "<p align='center'><h1>" + qApp.applicationDisplayName() + " " + \
"<br>" + qApp.applicationVersion() + "</h1>" + \ "<br>" + qApp.applicationVersion() + "</h1>" + \
"<br>" + qApp.applicationName() + "<br>" + \ "<br>" + APP_NAME + "<br>" + \
"<br>" + qApp.description + "<br>" + \ "<br>" + APP_DESCR + "<br>" + \
"<br>" + "Idee von: Balazs Fabian" + "<br>" + \ "<br>" + qApp.translate("utils", "Idea") + ": Balazs Fabian" + "<br>" + \
"<br>" + qApp.copyright + "<br>" \ "<br>" + qApp.copyright + "<br>" \
"<br> <a href='" + qApp.website + "'>" + qApp.website + "</a></p>" "<br> <a href='" + qApp.website + "'>" + qApp.website + "</a></p>"
text = text + "<p align='center'>Used icons: Theme 'Cute Color' from <a href='https://icons8.com/'>Icons8</a></p>" text = text + "<p align='center'>" + qApp.translate("utils", "Used icons: Theme") + " 'Cute Color' " + qApp.translate("utils", "from") + " <a href='https://icons8.com/'>Icons8</a></p>"
text = text + "<p align='center'>Python version: " + f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro} {sys.version_info.releaselevel}" text = text + "<p align='center'>Python " + qApp.translate("utils", "Version") + ": " + f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro} {sys.version_info.releaselevel}"
text = text + "<br>" + f"{sys.executable}" + "<br>" text = text + "<br>" + f"{sys.executable}" + "<br>"
text = text + "<br>Qt version: " + f"{QtCore.__version__}" text = text + "<br>Qt " + qApp.translate("utils", "Version") + ": " + f"{QtCore.__version__}"
msg.setText(text) msg.setText(text)
msg.setWindowTitle("About") msg.setWindowTitle("About")

View file

@ -64,7 +64,7 @@
</rect> </rect>
</property> </property>
<property name="text"> <property name="text">
<string>Freier Raum i. d. Garage:</string> <string>Freier Raum in der Garage:</string>
</property> </property>
</widget> </widget>
<widget class="QLineEdit" name="efVol_Garage"> <widget class="QLineEdit" name="efVol_Garage">
@ -183,7 +183,7 @@
<string>kg</string> <string>kg</string>
</property> </property>
</widget> </widget>
<widget class="QLabel" name="lblVol_Free_2"> <widget class="QLabel" name="lblWeight">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>10</x> <x>10</x>
@ -212,52 +212,7 @@
<property name="flat"> <property name="flat">
<bool>true</bool> <bool>true</bool>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout"/>
<item>
<widget class="QTableWidget" name="tableStuff">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<column>
<property name="text">
<string>Gegenstand</string>
</property>
</column>
<column>
<property name="text">
<string>Länge [m]</string>
</property>
</column>
<column>
<property name="text">
<string>Breite [m]</string>
</property>
</column>
<column>
<property name="text">
<string>Höhe [m]</string>
</property>
</column>
<column>
<property name="text">
<string>Gewicht [kg]</string>
</property>
</column>
</widget>
</item>
</layout>
</widget> </widget>
<widget class="QGroupBox" name="gbGarage"> <widget class="QGroupBox" name="gbGarage">
<property name="geometry"> <property name="geometry">
@ -326,7 +281,6 @@
</widget> </widget>
<tabstops> <tabstops>
<tabstop>tableGarage</tabstop> <tabstop>tableGarage</tabstop>
<tabstop>tableStuff</tabstop>
<tabstop>efVol_Garage</tabstop> <tabstop>efVol_Garage</tabstop>
<tabstop>efVol_Stuff</tabstop> <tabstop>efVol_Stuff</tabstop>
<tabstop>efVol_Free</tabstop> <tabstop>efVol_Free</tabstop>