From 6380729e08fa8d6de978f4f848d6ab62909593ab Mon Sep 17 00:00:00 2001 From: Paul S Date: Sun, 27 Jun 2021 11:34:01 +0200 Subject: [PATCH] first version sent --- LICENSE => LICENSE.txt | 0 changelog.md | 12 ++ img/icons8-garage-32.ico | Bin 0 -> 4286 bytes img/icons8-garage-32.png | Bin 950 -> 0 bytes requirements.txt | 5 + scripts/pyinstaller.cmd | 97 ++++++++++++++++ main.py => src/main.py | 231 +++++++++++++++++++-------------------- main.ui => src/main.ui | 0 utils.py => src/utils.py | 15 ++- 9 files changed, 240 insertions(+), 120 deletions(-) rename LICENSE => LICENSE.txt (100%) create mode 100644 changelog.md create mode 100644 img/icons8-garage-32.ico delete mode 100644 img/icons8-garage-32.png create mode 100644 requirements.txt create mode 100644 scripts/pyinstaller.cmd rename main.py => src/main.py (76%) rename main.ui => src/main.ui (100%) rename utils.py => src/utils.py (72%) diff --git a/LICENSE b/LICENSE.txt similarity index 100% rename from LICENSE rename to LICENSE.txt diff --git a/changelog.md b/changelog.md new file mode 100644 index 0000000..b1a2513 --- /dev/null +++ b/changelog.md @@ -0,0 +1,12 @@ +# Changelog GarageCalc1 + +## [0.1] - 2021-06-27 +### Added +- first version + +# Changelog GarageCalc1 + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). diff --git a/img/icons8-garage-32.ico b/img/icons8-garage-32.ico new file mode 100644 index 0000000000000000000000000000000000000000..83af31c54fa5bdd31ac795a66a3f638ac72164a4 GIT binary patch literal 4286 zcmeH|TTc^F5XaZ}1$^?EM?Z!i2#7>MVpP1K5|M37X?mgUE^QIE6jUl=sbY%_SfB}! z7^PeiE|C~X;6*D~_KmL`ICwZTomI^XH@pL z=`hlx1CXzn)iJr*nXifbNOGeM{)Hc^5BXAznqx7SgXbkn<9y$7OJg%ba$de`of!PG z1I03c-dis8b%qd(e8kZDZds8J`BIEx2_dww1Ine%t%o1H??V5pxw5R|YN6--qjLzh zIt}y3T`u_Vc%ei;!T%@#soq@FbDBpf$@L}-J@jJubqf9#4=9(>`Ogf>=xy%8^HvvD zrxM7lCb7E`#op=!u)PTEtT4NTy|oFv?Q|m|N*J>Q5OXT{{XLD1=}Cm!yex+ml*=%m z?S(hCZX|s^WH)D3UUoB8vWCn`9G}BMEJQ|--CD}k#_F354rA2hIK-wp^sGPmO-ODG zKeVgN<7(Jjdwz9tLyx6+#Zkn6+7R1^cZZAEOO{BdVDlp9@Y4Vk5}f<1`^P+91Y7Ko>&24qoAw>-VLpR*op{G$czmHX$}f!n z+u#5BrOzMjI-d4DKlivLo}Tlh5rTyp(E9 z2YfEov!(;N*%OxP#C^9p2Ddd)T`?xHX}+>!3hu51qz0Q>gXdldB&z9D#u$s3yw`m_ zDUjr=cGU(RzX0hp$0&D>FJ_;5lPZhXQ5EY+GQODgH8}!gc`vGG#jM=u2=M=(v$9|p Qv15X8{ABm|+Dg^^?@VkA&O zaS@P;f*T>O1pOPjupk7i8w0_G2zKE|vJit5no^-g#Riku7}GRy%p_wnlRNk8+~XoJ zb=uBNGeaj2TsU0b^E|)vo^#KIBbK=I{^VztrA9T#ng)Yvh>6cl%Tl95{ZVO3QOkUM zA9;1o9yK%+!+-qM0bnhySWBx%`pz^#gERr$f`jsXf0w z{+0kRD>Ye|_xSE>3xF5THPGD`smP~dfS$exJ$==8tOQ{E!cNtORRNrM{^^>8Zq58w z92+%B4AGGTJk%i!vU@nI|4_(U=s<_o3d5lBM4U)x5~Dd@J%G2F!})8Ij^RNXJ6aHi zL32!!j7hc&iflmT;R=}z9~Fcy zrl|FsDqQ<;3Ej&S`(1`Z2g@7SE~YUHcS z9@&gfGG)-+BkAc2zd9D?eHIrK+nWK&7LylVGP&}-MYK$PiJR+r#j$5AqK3ull27*= zBiLIz%uLU6<_XEO^hT_Y3Eq5LGBd5{7#^hUxde-oQ$$BhJX@7`mj!U#(jx8T zNwqbxI5`DCs?9_@dCtFHW;re-Q%$UXd-ZO;od@dg^L`+}gY??Vxr0iost*PD_>=lW znN^}tfS_yt7BiNk)D3hU%GkZWTr&Ug00M*(#qpu_kn_dGv57?uXDW!OS^}1iBDX~U Y0oow70VYsSC;$Ke07*qoM6N<$g7!nul +RMDIR /S /Q .\build>nul + +::Use Virtual env +IF %reinstall_venv%=="false" GOTO PYINSTALL +ECHO Installing virtual env as ".\env2"... +RMDIR /S /Q .\env2>NUL +python -m venv env2 +CALL .\env2\Scripts\activate.bat + +ECHO Installing necessary python packages... +python -m pip install --upgrade pip +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 ^ + --icon=.\img\icons8-garage-32.ico ^ + --add-data="LICENSE.txt;." ^ + --add-data="README.md;." ^ + --add-data="changelog.md;." ^ + --add-data="requirements.txt;." ^ + --add-data=".\src\*.ui;." ^ + --add-data="img;.\img" + +::MOVE .\dist\GarageCalc1\*.md .\dist\ +::MOVE .\dist\GarageCalc1\LICENSE .\dist\ + +::clean-up img-folder +::MOVE .\dist\GarageCalc1\img .\dist\img>NUL + +::Remove temp dir and files +DEL /Q *.spec>NUL +RMDIR /S /Q .\build + +::Create self extracting archive +IF %do_zip%=="false" GOTO END +::-mx0 = Don't compress at all - just copy the contents to archive. +::-mx1 = Consumes least time, but compression is low. +::-mx3 = Better than -mx1. +::-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\* + +::Disable Virtual env +CALL .\env2\Scripts\deactivate.bat + +:END +CD %prev_dir% + +ECHO Done. Check %prev_dir%\..\dist +IF %do_zip%=="true" ECHO Done. Check %release_dir% + +PAUSE diff --git a/main.py b/src/main.py similarity index 76% rename from main.py rename to src/main.py index 837a227..1c0c772 100644 --- a/main.py +++ b/src/main.py @@ -20,7 +20,7 @@ from PySide2.QtCore import QFile, QSize, Qt from PySide2.QtUiTools import QUiLoader # Local imports -from utils import show_about +from utils import show_about, resource_path # Local globals UI_FILE = "main.ui" @@ -32,7 +32,7 @@ APP_DESCR = "Berechnet zur Verfügung stehenden Garagenraum" APP_COPYRIGHT = "(c) Paul Salajean 2021" APP_WEBSITE = "https://gitlab.com/ProfP303" APP_DESKTOPFILENAME = "GarageCalc" -APP_ICON = "./img/icons8-garage-32.png" +APP_ICON = "./img/icons8-garage-32.ico" ICON_NEW = "./img/icons8-new-file-32.png" ICON_OPEN = "./img/icons8-opened-folder-32.png" @@ -47,7 +47,11 @@ COL_WIDTH = 2 COL_HEIGHT = 3 COL_WEIGHT = 4 -STUFF_ROW_COUNT = 50 +DEFAULT_GARAGE_LENGTH = "6" +DEFAULT_GARAGE_WIDTH = "2.5" +DEFAULT_GARAGE_HEIGHT = "2.5" + +TBL_STUFF_ROW_COUNT = 50 class MyMainWindow(QMainWindow): def __init__(self): @@ -59,15 +63,15 @@ class MyMainWindow(QMainWindow): self.create_actions() self.create_toolbar() self.create_statusbar() - self.statusBar.showMessage(f"{APP_NAME} {APP_VERSION} by {APP_AUTHOR}", 7000) + self.statusBar.showMessage(f"{APP_NAME} {APP_VERSION} by {APP_AUTHOR}", 5000) self.calc_voluminae() self.ui.efWeight.setText(str("0")) - self.modified = False + self.is_modified = False self.opened_file = None def load_ui(self): loader = QUiLoader() - path = os.path.join(os.path.dirname(__file__), UI_FILE) + path = os.path.join(os.path.dirname(__file__), resource_path( UI_FILE)) ui_file = QFile(path) ui_file.open(QFile.ReadOnly) self.ui = loader.load(ui_file, self) @@ -81,35 +85,35 @@ class MyMainWindow(QMainWindow): def create_actions(self): self.actionNew = QAction() - self.actionNew.setIcon(QIcon(ICON_NEW)) + 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(ICON_OPEN)) + 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(ICON_SAVE)) + 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(ICON_EXPORT)) + 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(ICON_ABOUT)) + 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(ICON_QUIT)) + self.actionQuit.setIcon(QIcon(resource_path(ICON_QUIT))) self.actionQuit.triggered.connect(QApplication.quit) self.actionQuit.setShortcut("Ctrl+Q") self.actionQuit.setToolTip("Programm beenden (Strg+Q)") @@ -132,13 +136,13 @@ class MyMainWindow(QMainWindow): tblGarage = self.ui.tableGarage tblStuff = self.ui.tableStuff - tblGarage.setItem(0, 0, QTableWidgetItem("6")) - tblGarage.setItem(0, 1, QTableWidgetItem("2.5")) - tblGarage.setItem(0, 2, QTableWidgetItem("2.5")) + tblGarage.setItem(0, 0, QTableWidgetItem(DEFAULT_GARAGE_LENGTH)) + tblGarage.setItem(0, 1, QTableWidgetItem(DEFAULT_GARAGE_WIDTH)) + tblGarage.setItem(0, 2, QTableWidgetItem(DEFAULT_GARAGE_HEIGHT)) - tblStuff.setRowCount(STUFF_ROW_COUNT) + tblStuff.setRowCount(TBL_STUFF_ROW_COUNT) - self.modified = False + self.is_modified = False def create_statusbar(self): self.statusBar = QStatusBar() @@ -154,7 +158,6 @@ class MyMainWindow(QMainWindow): def show_rowheader_context_menu(self, position): tblStuff = self.ui.tableStuff row = self.headers.logicalIndexAt(position) - print("row click", row) menu = QMenu() remove_row = menu.addAction("Zeile entfernen") @@ -179,7 +182,7 @@ class MyMainWindow(QMainWindow): # clear stuff # tblStuff.clear() tblStuff.setRowCount(0) - tblStuff.setRowCount(STUFF_ROW_COUNT) + tblStuff.setRowCount(TBL_STUFF_ROW_COUNT) # clear results self.ui.efVol_Garage.clear() @@ -194,124 +197,126 @@ class MyMainWindow(QMainWindow): self.setWindowTitle(f"{APP_NAME} {APP_VERSION} by {APP_AUTHOR}") self.ui.efVol_Free.setStyleSheet("") - self.modified = False + self.is_modified = False def file_new(self): - if self.modified: + 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, \ + reply = QMessageBox.question(self, "Neu", msg, \ QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if reply == QMessageBox.No: return False self.init_ui() def file_save(self): - print("file_save() called.") tblGarage = self.ui.tableGarage tblStuff = self.ui.tableStuff + is_file_saved = False fileName = self.opened_file if not self.opened_file: # if not file already open options = QFileDialog.Options() - sTxtFilesAll = "Alle Dateien" - sTxtFiles = "CSV-Datei" - fileName, _ = QFileDialog.getSaveFileName(None, "Speichern", None, - sTxtFiles + " (*.csv);;" + sTxtFilesAll + " (*)", + "CSV-Datei (*.csv);;Alle Dateien (*)", options=options) - with open(fileName, mode='w', newline='') as garagecalc_file: - writer = csv.writer(garagecalc_file, delimiter=';', quotechar='"', quoting=csv.QUOTE_MINIMAL) + if fileName: # if not file already open + with open(fileName, mode='w', newline='') as garagecalc_file: + writer = csv.writer(garagecalc_file, delimiter=';', quotechar='"', quoting=csv.QUOTE_MINIMAL) - garage_length = 0 - garage_width = 0 - garage_height = 0 + garage_length = 0 + garage_width = 0 + garage_height = 0 - item_length = tblGarage.item(0, 0) - item_width = tblGarage.item(0, 1) - item_height = tblGarage.item(0, 2) + item_length = tblGarage.item(0, 0) + item_width = tblGarage.item(0, 1) + item_height = tblGarage.item(0, 2) - # loop over table Garage - for row in range(tblGarage.rowCount()): - # get garage length - if item_length: - garage_length = item_length.text() - try: - garage_length = float(garage_length) - except ValueError: - garage_length = 0.0 + # loop over table Garage + for row in range(tblGarage.rowCount()): + # get garage length + if item_length: + garage_length = item_length.text() + try: + garage_length = float(garage_length) + except ValueError: + garage_length = 0.0 - # get garage width - if item_width: - garage_width = item_width.text() - try: - garage_width = float(garage_width) - except ValueError: - garage_width = 0.0 + # get garage width + if item_width: + garage_width = item_width.text() + try: + garage_width = float(garage_width) + except ValueError: + garage_width = 0.0 - # get garage height - if item_height: - garage_height = item_height.text() - try: - garage_height = float(garage_height) - except ValueError: - garage_height = 0.0 + # get garage height + if item_height: + garage_height = item_height.text() + try: + garage_height = float(garage_height) + except ValueError: + garage_height = 0.0 - if garage_length or garage_width or garage_height: - writer.writerow(["Garage", garage_length, garage_width, garage_height]) + if garage_length or garage_width or garage_height: + writer.writerow(["Garage", garage_length, garage_width, garage_height]) - # loop over table Stuff - for row in range(tblStuff.rowCount()): - stuff_text = None - length = None - width = None - height = None - weight = None + # loop over table Stuff + for row in range(tblStuff.rowCount()): + stuff_text = None + length = None + width = None + height = None + weight = None - 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) + 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 item_stuff: + stuff_text = item_stuff.text() - if item_length: - try: - length = float(item_length.text()) - except ValueError: - length = None + if item_length: + try: + length = float(item_length.text()) + except ValueError: + length = None - if item_width: - try: - width = float(item_width.text()) - except ValueError: - width = None + if item_width: + try: + width = float(item_width.text()) + except ValueError: + width = None - if item_height: - try: - height = float(item_height.text()) - except ValueError: - height = None + if item_height: + try: + height = float(item_height.text()) + except ValueError: + height = None - if item_weight: - try: - weight = float(item_weight.text()) - except ValueError: - weight = None + if item_weight: + try: + weight = float(item_weight.text()) + except ValueError: + weight = None - if item_stuff or item_length or item_width or item_height or item_weight: - writer.writerow([stuff_text, length, width, height, weight]) + if item_stuff or item_length or item_width or item_height or item_weight: + writer.writerow([stuff_text, length, width, height, weight]) + is_file_saved = True self.opened_file = os.path.basename(fileName) self.setWindowTitle(self.opened_file) - self.modified = False + self.is_modified = False + + if is_file_saved: + self.statusBar.showMessage(f"Datei {fileName} gespeichert.", 2000) def file_open(self): - if self.modified: + 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, \ QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) @@ -332,7 +337,7 @@ class MyMainWindow(QMainWindow): if fileName: self.init_ui() - file = open(fileName, "rU") + file = open(fileName, "r", newline='') reader = csv.reader(file, delimiter=';') row_idx = 0 @@ -342,9 +347,6 @@ class MyMainWindow(QMainWindow): garage_length = row[1] garage_width = row[2] garage_height = row[3] - print("garage_length", garage_length) - print("garage_width", garage_width) - print("garage_height", garage_height) tblGarage.setItem(0, COL_LENGTH-1, QTableWidgetItem(str(garage_length))) tblGarage.setItem(0, COL_WIDTH-1, QTableWidgetItem(str(garage_width))) @@ -352,7 +354,7 @@ class MyMainWindow(QMainWindow): except IndexError as ex: pass - print("") + if row_idx > 0: try: stuff = row[0] @@ -360,11 +362,6 @@ class MyMainWindow(QMainWindow): stuff_width = row[2] stuff_height = row[3] stuff_weight = row[4] - print("stuff", stuff) - print("length", stuff_length) - print("width", stuff_width) - print("height", stuff_height) - print("weight", stuff_weight) tblStuff.setItem(row_idx - 1, COL_STUFF, QTableWidgetItem(stuff)) tblStuff.setItem(row_idx - 1, COL_LENGTH, QTableWidgetItem(str(stuff_length))) @@ -377,9 +374,9 @@ class MyMainWindow(QMainWindow): row_idx += 1 - tblStuff.setRowCount(STUFF_ROW_COUNT) + tblStuff.setRowCount(TBL_STUFF_ROW_COUNT) - self.modified = False + self.is_modified = False self.opened_file = os.path.basename(fileName) if fileName: @@ -443,7 +440,7 @@ class MyMainWindow(QMainWindow): worksheet.write(start_row + row, COL_HEIGHT, garage_height) start_row = 4 - worksheet.write(start_row, 0, "Dimensionen der zu verstaueneden Gegenstände") + worksheet.write(start_row, 0, "Dimensionen der zu verstauenden Gegenstände") start_row = 5 worksheet.write(start_row, COL_LENGTH, "Länge [m]") @@ -522,14 +519,14 @@ class MyMainWindow(QMainWindow): worksheet.write(row_idx, 1, float(self.ui.efWeight.text())) workbook.close() - self.statusBar.showMessage(f"Erfolgreich nach EXCEL exportiert.", 7000) + self.statusBar.showMessage(f"Erfolgreich nach EXCEL exportiert.", 5000) def on_garage_changed(self): - self.modified = True + self.is_modified = True self.calc_voluminae() def on_stuff_changed(self): - self.modified = True + self.is_modified = True self.calc_voluminae() self.calc_weight() @@ -575,7 +572,7 @@ class MyMainWindow(QMainWindow): garage_vol = garage_length * float(garage_width) * float(garage_height) else: garage_vol = 0.0 - self.statusBar.showMessage("Fehler in der Garagen-Dimension. :-(", 7000) + self.statusBar.showMessage("Fehler in der Garagen-Dimension. :-(", 5000) return garage_vol @@ -627,12 +624,11 @@ class MyMainWindow(QMainWindow): if is_error: stuff_vol = 0.0 - self.statusBar.showMessage("Fehler in den Gegenstände-Dimensionen. :-(", 7000) + self.statusBar.showMessage("Fehler in den Dimensionen der zu verstauenden Gegenstände :-(", 5000) return stuff_vol def calc_voluminae(self): - print("calc_voluminae() called.") tblGarage = self.ui.tableGarage # get garage vol @@ -685,7 +681,6 @@ if __name__ == "__main__": qApp.setDesktopFileName(APP_DESKTOPFILENAME) winMain = MyMainWindow() - # winMain.resize(600, 600) winMain.setFixedWidth(600) winMain.setFixedHeight(620) winMain.show() diff --git a/main.ui b/src/main.ui similarity index 100% rename from main.ui rename to src/main.ui diff --git a/utils.py b/src/utils.py similarity index 72% rename from utils.py rename to src/utils.py index f58f05f..ec0dcfc 100644 --- a/utils.py +++ b/src/utils.py @@ -17,11 +17,22 @@ from PySide2.QtCore import QFile, QSize from PySide2.QtUiTools import QUiLoader # local globals -APP_ICON = "icons8-garage-32.png" +APP_ICON = "./img/icons8-garage-32.ico" + +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 + except Exception: + base_path = os.path.abspath(".") + + return os.path.join(base_path, relative_path) def show_about(): + qApp = QApplication.instance() msg = QMessageBox() - msg.setIconPixmap(QPixmap(APP_ICON)) + msg.setIconPixmap(QPixmap(resource_path(APP_ICON))) text = "

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

" + \