From 2054de9ec35f854548dd259460d5470007842fe1 Mon Sep 17 00:00:00 2001 From: Paul S Date: Sun, 27 Jun 2021 01:09:56 +0200 Subject: [PATCH] first version --- img/icons8-export-xls-32.png | Bin 0 -> 703 bytes img/icons8-garage-32.png | Bin 0 -> 950 bytes img/icons8-information-32.png | Bin 0 -> 699 bytes img/icons8-new-file-32.png | Bin 0 -> 994 bytes img/icons8-opened-folder-32.png | Bin 0 -> 774 bytes img/icons8-save-32.png | Bin 0 -> 839 bytes img/system-shutdown.png | Bin 0 -> 883 bytes main.py | 692 ++++++++++++++++++++++++++++++++ main.ui | 334 +++++++++++++++ utils.py | 43 ++ 10 files changed, 1069 insertions(+) create mode 100644 img/icons8-export-xls-32.png create mode 100644 img/icons8-garage-32.png create mode 100644 img/icons8-information-32.png create mode 100644 img/icons8-new-file-32.png create mode 100644 img/icons8-opened-folder-32.png create mode 100644 img/icons8-save-32.png create mode 100644 img/system-shutdown.png create mode 100644 main.py create mode 100644 main.ui create mode 100644 utils.py diff --git a/img/icons8-export-xls-32.png b/img/icons8-export-xls-32.png new file mode 100644 index 0000000000000000000000000000000000000000..76aba0fc4657ffa7745b6d7c5102dfbdf39c3f3a GIT binary patch literal 703 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtmSN`?>!lvVtU&J%W50 z7^+km7#f-x7=Hc-(k~epN(~qoUL`OvSj}Ky5YL|!f7A`Awl2UY#P$FG{|ybRCKoU2 z&6(eyx1c|7-ttMCf4#r==hL(Q-#`5R_~`GK7oT6;o0&Fq*S(+r|Nh=|=llO(-!`v1 z4%FCs`p5IH|E6C0`Tobh{ZD@1djI#xvp*eYe!Ttuucv51PyYO(Q|sH#{Fq(0^81^+ z?OjWXkF6^{vA$;c%K43J&h9+%{q3uOW3w`VE@3PQ@(X5gcy=QV#7XjYcVXyYmGuB} zI14-?iy0WWg+Z8+Vb&Z8pdfpRr>`sf6GnChbCK-qX(d2CGM+AuAr`0CUVfX`tRTUf zAj;-yzC`rn^<$xpMc4qPjm+^ez9LjowI-`evceuPLa~f&IvsG zop#rI3$Xgl7kcoOZHaq6^W|)ZPiy@{feho0!m@ks&Yzbv*uC$~XU~bd58STz`oeU- zajX5K<^r7q&Ofy^S~V<}&govY!iC|AZ}8`Bq2jvvnG7YhS0xXen$_x761t!9+^rx^ z&I8pIETPI-rT+~W*iD>5=7_njH@n%*5U|sVUy|WZ`h(OI@nw!YkJNuu*;`2-6Q1P3 ztoc-};dZh~{TZnhlfN{+6OEX5+u+W$RQB>s>z*~Z?^!22XS$>7*Fu2@f|(n?3;x-1 z&3xvUDSP|h8GNuk>n{H<{OS9@6WIk^)dG#5lsf=}PPN1}q9i4;B-JXpC>2OC7#SE^ z>KYpA8k&R{8Cn?|SQ%Sr8yHv_80c0BSDcptAuDUu+TL$4FRe%GO;o-foLe0 SJl7DYfx*+&&t;ucLK6Tv@hrRm literal 0 HcmV?d00001 diff --git a/img/icons8-garage-32.png b/img/icons8-garage-32.png new file mode 100644 index 0000000000000000000000000000000000000000..6673d8c25ad5fb51749d043385e85af61ff93280 GIT binary patch literal 950 zcmV;n14;aeP)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!!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10;&6aZh^u>n&wqyhJ%#hT zi{|&_&F?Ci-<>zVvt(g=-69~nH+ODV>4LtT`MufmnACTyFRv2tF*j=4aiN{*~6J-n`R^Xk%l zYl}~;uU)jFt8!t&zWIf-=Czg2Puw>5|NsB{Rv7C8y`xhSo}Ugv8v5NUY0Xa&bRU5kh=ji3Ws&J(`Z zYpV*WG+rxwTYqtO-Am7ChJBr@LS9DAJ=DyWEW^93^KQDOuv12nu1rRGLeTbh)>`e% z2G%zh>fYQ|e#2of&q6)jO8HLlIqpAA1-?n%j7vFJ2Pw=+yp>v@$lZ zGPck*Ft9Q((5(`#K+%w!pOTqY3DIC+u4`l%Vqk7%YGGvp)nHrPdKajH!PC{xWt~$( F699seCo2E| literal 0 HcmV?d00001 diff --git a/img/icons8-new-file-32.png b/img/icons8-new-file-32.png new file mode 100644 index 0000000000000000000000000000000000000000..943f73ccfb728cfb6c622a486aa175960485a2bd GIT binary patch literal 994 zcmV<810DQ{P)004R>004l5008;`004mK004C`008P>0026e000+ooVrmw0003; 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 RohASP002ovPDHLkV1hFrqjdlP literal 0 HcmV?d00001 diff --git a/img/system-shutdown.png b/img/system-shutdown.png new file mode 100644 index 0000000000000000000000000000000000000000..4e38373a88dafd11bc34622a922c106e596fdbe2 GIT binary patch literal 883 zcmV-(1C0EMP)004R>004l5008;`004mK004C`008P>0026e000+ooVrmw0003F zP)t-sM{rD#a-@!IpN?&yk#VDrY@m*Ap_yixlXIh!bfbWPs*`h~tdg;qmpx>s*kXfbEA@Nl9Y6#m2{+&a-*w{uat74lXIezbfS)JpN?&zmVc^` zZJ?EatBq`*zQ^Li&F93<=*-yc$kXc1+3m*8=EKkE&DZS9*6YK~=f=?J*4^#M(&*&r z@$2vN*Wd2b+U(@&^3d7s(%S9H*6hO0=k@B_`0U;H>)euaq|m*_`tRP$*6fmXq|v>` z&DH9|&gkIg@YCGw#n0&C=L|K}$^cVmD00(qQO+^Rf0S5p#6l-gibN~PWGf6~2 zR5;76lUG~9Fc^ias6nOfy)mXWw(gBpPytavP*D8;zr=hQHKTWWE^>3;oNwfb=lz2Q z2C=2wkO2(u5UC&x1F}o$-iQHUd%MfM(}z6Mud| z0K&hsUZh-+8JO3_e0Qqu0HZBO7TwzVFiYE2` zi3IqV{5}H=zgm?E1%D06Md|sqdc6jKht~7ROE*dBlge*2c!Kd*;6J~p`oskw-te;k z!*_sJ+H8?G(r&B60y^&>-EQx*mmXgxu+jgv)Za9Kt)l()irats%a(os#71o#ag>m{ z0000bbVXQnWMOn=I%9HWVRU5xGB7bYEio}IF*H;%F*-9aIx{#cFfckWFfDo$cmMzZ zC3HntbYx+4WjbwdWNBu305UK!IV~_ZEipAzF*rIgF*-6dD=;uRFfdJ|ohASP002ov JPDHLkV1iHIv~vIe literal 0 HcmV?d00001 diff --git a/main.py b/main.py new file mode 100644 index 0000000..837a227 --- /dev/null +++ b/main.py @@ -0,0 +1,692 @@ +#!/usr/bin/env python3 +""" +project: GarageCalc1 +file: main.py +summary: Calculates available size of a garage space +license: GPL +author: Paul Salajean (p.salajean[at]gmx.de) +""" + +# Standard library imports +import sys +import os +import csv + +# Third party imports +from PySide2.QtWidgets import QApplication, QMainWindow, QTableWidgetItem, QStatusBar, QAction, QFileDialog, \ + QAbstractItemView, QMenu, QMessageBox +from PySide2.QtGui import QIcon +from PySide2.QtCore import QFile, QSize, Qt +from PySide2.QtUiTools import QUiLoader + +# Local imports +from utils import show_about + +# Local globals +UI_FILE = "main.ui" +APP_NAME = "Garagenraum-Rechner" +APP_DISPNAME = "GarageCalc" +APP_VERSION = "v0.1" +APP_AUTHOR = "Paul Salajean" +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" + +ICON_NEW = "./img/icons8-new-file-32.png" +ICON_OPEN = "./img/icons8-opened-folder-32.png" +ICON_SAVE = "./img/icons8-save-32.png" +ICON_EXPORT = "./img/icons8-export-xls-32.png" +ICON_ABOUT = "./img/icons8-information-32.png" +ICON_QUIT = "./img/system-shutdown.png" + +COL_STUFF = 0 +COL_LENGTH = 1 +COL_WIDTH = 2 +COL_HEIGHT = 3 +COL_WEIGHT = 4 + +STUFF_ROW_COUNT = 50 + +class MyMainWindow(QMainWindow): + def __init__(self): + super().__init__() + self.load_ui() + self.init_ui() + self.set_defaults() + self.connect_signals() + self.create_actions() + self.create_toolbar() + self.create_statusbar() + self.statusBar.showMessage(f"{APP_NAME} {APP_VERSION} by {APP_AUTHOR}", 7000) + self.calc_voluminae() + self.ui.efWeight.setText(str("0")) + self.modified = False + self.opened_file = None + + def load_ui(self): + loader = QUiLoader() + path = os.path.join(os.path.dirname(__file__), UI_FILE) + ui_file = QFile(path) + ui_file.open(QFile.ReadOnly) + 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) + + def create_actions(self): + self.actionNew = QAction() + self.actionNew.setIcon(QIcon(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.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.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.triggered.connect(self.file_export) + self.actionExport.setToolTip("Export nach EXCEL...") + + self.actionAbout = QAction() + self.actionAbout.setIcon(QIcon(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.triggered.connect(QApplication.quit) + self.actionQuit.setShortcut("Ctrl+Q") + self.actionQuit.setToolTip("Programm beenden (Strg+Q)") + + def create_toolbar(self): + # Main Toolbar (for all pages/views) + self.toolbar = self.addToolBar('Main Toolbar') + self.toolbar.setIconSize(QSize(32, 32)) + self.toolbar.addAction(self.actionNew) + self.toolbar.addAction(self.actionOpen) + self.toolbar.addAction(self.actionSave) + self.toolbar.addSeparator() + self.toolbar.addAction(self.actionExport) + self.toolbar.addSeparator() + self.toolbar.addAction(self.actionAbout) + self.toolbar.addSeparator() + self.toolbar.addAction(self.actionQuit) + + def set_defaults(self): + 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")) + + tblStuff.setRowCount(STUFF_ROW_COUNT) + + self.modified = False + + def create_statusbar(self): + self.statusBar = QStatusBar() + self.setStatusBar(self.statusBar) + + def connect_signals(self): + tblGarage = self.ui.tableGarage + tblStuff = self.ui.tableStuff + + tblGarage.itemChanged.connect(self.on_garage_changed) + tblStuff.itemChanged.connect(self.on_stuff_changed) + + 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") + insert_row = menu.addAction("Zeile einfügen") + ac = menu.exec_(tblStuff.mapToGlobal(position)) + if ac == remove_row: + tblStuff.removeRow(row) + self.calc_voluminae() + elif ac == insert_row: + tblStuff.insertRow(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() + tblStuff.setRowCount(0) + tblStuff.setRowCount(STUFF_ROW_COUNT) + + # clear results + self.ui.efVol_Garage.clear() + self.ui.efVol_Stuff.clear() + self.ui.efVol_Free.clear() + self.ui.efWeight.clear() + + self.opened_file = None + if self.opened_file: + self.setWindowTitle(self.opened_file) + else: + self.setWindowTitle(f"{APP_NAME} {APP_VERSION} by {APP_AUTHOR}") + + self.ui.efVol_Free.setStyleSheet("") + self.modified = False + + def file_new(self): + if self.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) + 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 + + 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 + " (*)", + options=options) + + 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 + + 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 + + # 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 + + 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 + + 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_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_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_stuff or item_length or item_width or item_height or item_weight: + writer.writerow([stuff_text, length, width, height, weight]) + + self.opened_file = os.path.basename(fileName) + self.setWindowTitle(self.opened_file) + self.modified = False + + def file_open(self): + if self.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) + if reply == QMessageBox.No: + return False + + tblGarage = self.ui.tableGarage + tblStuff = self.ui.tableStuff + + options = QFileDialog.Options() + + sTxtFilesAll = "Alle Dateien" + sTxtFiles = "CSV-Datei" + + fileName, _ = QFileDialog.getOpenFileName(self, "Öffnen", None, + sTxtFiles + " (*.csv);;" + sTxtFilesAll + " (*)", + options=options) + if fileName: + self.init_ui() + + file = open(fileName, "rU") + reader = csv.reader(file, delimiter=';') + + row_idx = 0 + for row in reader: + if row_idx == 0: # first row (index=0) ist always garage dimension + try: + 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))) + tblGarage.setItem(0, COL_HEIGHT-1, QTableWidgetItem(str(garage_height))) + + except IndexError as ex: + pass + print("") + if row_idx > 0: + try: + stuff = row[0] + stuff_length = row[1] + 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))) + tblStuff.setItem(row_idx - 1, COL_WIDTH, QTableWidgetItem(str(stuff_width))) + tblStuff.setItem(row_idx - 1, COL_HEIGHT, QTableWidgetItem(str(stuff_height))) + tblStuff.setItem(row_idx - 1, COL_WEIGHT, QTableWidgetItem(str(stuff_weight))) + + except IndexError as ex: + pass + + row_idx += 1 + + tblStuff.setRowCount(STUFF_ROW_COUNT) + + self.modified = False + self.opened_file = os.path.basename(fileName) + + if fileName: + self.setWindowTitle(self.opened_file) + else: + self.setWindowTitle(f"{APP_NAME} {APP_VERSION} by {APP_AUTHOR}") + + def file_export(self): + 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) + if file_name: + try: + import xlsxwriter + except ModuleNotFoundError: + print(f"[{__file__}] Module 'xlsxwriter' not found!") + else: + print(f"Exporting into file -> {file_name}") + + workbook = xlsxwriter.Workbook(file_name) + worksheet = workbook.add_worksheet() + + # write col header + start_row = 0 + worksheet.write(start_row, 0, "Dimension der 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 = 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, "Dimensionen der zu verstaueneden Gegenstände") + + 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]") + + 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: + length = float(item_length.text()) + if length: + worksheet.write(start_row + row, COL_LENGTH, length) + except ValueError: + pass + + if item_width: + try: + width = float(item_width.text()) + if width: + worksheet.write(start_row + row, COL_WIDTH, width) + except ValueError: + pass + + 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.", 7000) + + def on_garage_changed(self): + self.modified = True + self.calc_voluminae() + + def on_stuff_changed(self): + self.modified = True + self.calc_voluminae() + self.calc_weight() + + def get_garage_vol(self): + tblGarage = self.ui.tableGarage + is_error = False + + garage_length = 0 + garage_width = 0 + garage_height = 0 + + # get garage length + if tblGarage.item(0, 0): + garage_length = tblGarage.item(0, 0).text() + if garage_length: + try: + garage_length = float(garage_length) + except ValueError: + garage_length = 0.0 + is_error = True + + # get garage width + if tblGarage.item(0, 1): + garage_width = tblGarage.item(0, 1).text() + if garage_width: + try: + garage_width = float(garage_width) + except ValueError: + garage_width = 0.0 + is_error = True + + # get garage height + if tblGarage.item(0, 2): + garage_height = tblGarage.item(0, 2).text() + if garage_height: + try: + garage_height = float(garage_height) + except ValueError: + garage_height = 0.0 + is_error = True + + if not is_error: + garage_vol = garage_length * float(garage_width) * float(garage_height) + else: + garage_vol = 0.0 + self.statusBar.showMessage("Fehler in der Garagen-Dimension. :-(", 7000) + + return garage_vol + + def get_stuff_vol(self): + tblStuff = self.ui.tableStuff + stuff_vol = 0 + length = 0 + width = 0 + height = 0 + # get number of rows + row_count = tblStuff.rowCount() + + is_error = False + + # get stuff voluminae + for row in range(row_count): + vol = 0 + length = 0 + width = 0 + height = 0 + item_length = tblStuff.item(row, COL_LENGTH) + item_width = tblStuff.item(row, COL_WIDTH) + item_height = tblStuff.item(row, COL_HEIGHT) + + if item_length: + try: + length = float(item_length.text()) + except ValueError: + length = 0 + is_error = True + + if item_width: + try: + width = float(item_width.text()) + except ValueError: + width = 0 + is_error = True + + if item_height: + try: + height = float(item_height.text()) + except ValueError: + height = 0 + is_error = True + + vol = length * width * height + + stuff_vol = stuff_vol + vol + + if is_error: + stuff_vol = 0.0 + self.statusBar.showMessage("Fehler in den Gegenstände-Dimensionen. :-(", 7000) + + return stuff_vol + + def calc_voluminae(self): + print("calc_voluminae() called.") + tblGarage = self.ui.tableGarage + + # get garage vol + garage_vol = self.get_garage_vol() + + # get stuff vol + stuff_vol = self.get_stuff_vol() + + # display results + self.ui.efVol_Garage.setText(str(garage_vol)) + self.ui.efVol_Stuff.setText(str(stuff_vol)) + self.ui.efVol_Free.setText(str(garage_vol - stuff_vol)) + + if ( garage_vol - stuff_vol ) < 0: + self.ui.efVol_Free.setStyleSheet("border: 1px solid red;") + else: + self.ui.efVol_Free.setStyleSheet("") + + def calc_weight(self): + tblStuff = self.ui.tableStuff + weight_sum = 0 + + # get number of rows + row_count = tblStuff.rowCount() + + for row in range(row_count): + weight = 0.0 + item = tblStuff.item(row, COL_WEIGHT) + if item: + try: + weight = float(item.text()) + except ValueError: + weight = 0.0 + + weight_sum = weight_sum + weight + + self.ui.efWeight.setText(str(weight_sum)) + + +if __name__ == "__main__": + qApp = QApplication([]) + + qApp.setApplicationName(APP_NAME) + qApp.setApplicationDisplayName(APP_DISPNAME) + qApp.setApplicationVersion(APP_VERSION) + qApp.description = APP_DESCR + qApp.copyright = APP_COPYRIGHT + qApp.website = APP_WEBSITE + qApp.setWindowIcon(QIcon(APP_ICON)) + qApp.setDesktopFileName(APP_DESKTOPFILENAME) + + winMain = MyMainWindow() + # winMain.resize(600, 600) + winMain.setFixedWidth(600) + winMain.setFixedHeight(620) + winMain.show() + sys.exit(qApp.exec_()) diff --git a/main.ui b/main.ui new file mode 100644 index 0000000..c8bdd36 --- /dev/null +++ b/main.ui @@ -0,0 +1,334 @@ + + + Form + + + + 0 + 0 + 604 + 596 + + + + Form + + + + + 20 + 440 + 541 + 121 + + + + Ergebnis + + + true + + + + + 10 + 20 + 140 + 16 + + + + Volumen der Garage: + + + + + + 10 + 40 + 140 + 16 + + + + Volumen der Gegenstände: + + + + + + 10 + 60 + 140 + 16 + + + + Freier Raum i. d. Garage: + + + + + + 174 + 20 + 113 + 20 + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + true + + + + + + 174 + 40 + 113 + 20 + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + true + + + + + + 174 + 60 + 113 + 20 + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + true + + + + + + 294 + 20 + 47 + 13 + + + + + + + + + + 294 + 40 + 47 + 13 + + + + + + + + + + 294 + 60 + 47 + 13 + + + + + + + + + + 174 + 80 + 113 + 20 + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + true + + + + + + 294 + 80 + 47 + 13 + + + + kg + + + + + + 10 + 80 + 140 + 16 + + + + Gesamtgewicht: + + + + + + + 20 + 120 + 561 + 301 + + + + Dimensionen der zu verstauenden Gegenstände + + + true + + + + + + + 0 + 0 + + + + true + + + QAbstractItemView::NoSelection + + + + Gegenstand + + + + + Länge [m] + + + + + Breite [m] + + + + + Höhe [m] + + + + + Gewicht [kg] + + + + + + + + + + 20 + 20 + 561 + 91 + + + + + 0 + 91 + + + + Dimension der Garage + + + true + + + + + 10 + 23 + 351 + 58 + + + + + 0 + 0 + + + + + 0 + 58 + + + + + Garage + + + + + Länge [m] + + + + + Breite [m] + + + + + Höhe [m] + + + + + + + tableGarage + tableStuff + efVol_Garage + efVol_Stuff + efVol_Free + efWeight + + + + diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..f58f05f --- /dev/null +++ b/utils.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +""" +project: GarageCalc1 +file: utils.py +summary: helper functions +""" + +# Standard library imports +import sys +import os + +# Third party imports +from PySide2.QtWidgets import QMessageBox, QApplication, QMainWindow, QTableWidgetItem, QStatusBar, QAction +from PySide2 import QtCore +from PySide2.QtGui import QIcon, QPixmap +from PySide2.QtCore import QFile, QSize +from PySide2.QtUiTools import QUiLoader + +# local globals +APP_ICON = "icons8-garage-32.png" + +def show_about(): + msg = QMessageBox() + msg.setIconPixmap(QPixmap(APP_ICON)) + + text = "

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

" + \ + "
" + qApp.applicationName() + "
" + \ + "
" + qApp.description + "
" + \ + "
" + "Idee von: 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 + "
" + f"{sys.executable}" + "
" + text = text + "
Qt version: " + f"{QtCore.__version__}" + + msg.setText(text) + msg.setWindowTitle("About") + msg.setStandardButtons(QMessageBox.Ok) + + msg.exec_()