From a6e6370fd6af0c23166a589a2182e32738f041fe Mon Sep 17 00:00:00 2001 From: Paul S Date: Sun, 11 Jul 2021 10:41:42 +0200 Subject: [PATCH] unit conversion per cell (and ini settings) --- changelog.md | 9 + i18n/de_DE.qm | Bin 4748 -> 5843 bytes i18n/de_DE.ts | 169 +++++++---- i18n/hu_HU.qm | Bin 4676 -> 5743 bytes i18n/hu_HU.ts | 171 +++++++---- img/qt_logo_small.png | Bin 0 -> 565 bytes src/clsTableWidget.py | 180 +++++++++--- src/main.py | 648 ++++++++++++++++++++++++++++++++---------- src/utils.py | 35 ++- ui/dlg_uom.ui | 92 ++++++ 10 files changed, 1004 insertions(+), 300 deletions(-) create mode 100644 img/qt_logo_small.png create mode 100644 ui/dlg_uom.ui diff --git a/changelog.md b/changelog.md index 3ae34b3..af87779 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,14 @@ # Changelog GarageCalc1 +## [0.7] - 2021-07-09 +## Added +- Einheiten können pro Zelle mit F3 eingegben/editiert/umgerechnet werden +- Menü erweitert +- Ini-Datei erweitert um Standard-Einheiten + +## Changed +- Ini-Datei wird nun im Ordner der Applikation gespeichert/geladen, nicht mehr aus %LOCALAPPDATA% + ## [0.6] - 2021-07-06 ## Added - Dezimalzahlen werden nun mit Dezimaltrennzeichen Komma (",") eingegeben statt Punkt (".") diff --git a/i18n/de_DE.qm b/i18n/de_DE.qm index a3d31e65a3fb827057ca459910889efcc491d17c..eea8d85a883b5fe62a67f180f59cec0f0c0c66db 100644 GIT binary patch delta 1643 zcmb_bYiJy06g``rnQnGBo7YDBuyOh$O(Tuk8bP79ifL?09-v(fk)X@&x5=2<*|0O4 zbPYPzBDP_Fh+DB0ty+9SEHs-`Fe0{^N<~Oe0#!@=QK4Yt14$|vDxTTKB`Fp2Lx$mg zk9+Sq-?{f@YQCv|VZB{c2JHSEz+Pa_7l3~O(mQGH0crMjz&8Zx&l5o9AmrF5KuIU$ zj6^#H`S1h~coE7}4N&nJlyAF$poLqUy;OmRhu#K?UPI^j8KBgMu7&sLcLeFoDD6-9 z?&pKBQL^9~YitITS6uVB8E|baigyNqvftee$IcSql-qt?FyTe_`2!btpOGFf9s|77 z((q;i;4f*i{}A_&NavoPCgHoJOIPM-Bl6}p0{T1T$U4TCpOWnZmE33hsL8p0<&B zCam)ee#nHCZ+fy{k&rZ6tc9xS_j2*8V=wW%y7-T12=L5$quYYaGw40~*c~MFjQ8E! z?2DA>Yw!CLla%PB_e`1q$a&8lqN5^vFKsvotUB%Mt=vz76+UCUor$M>$1`hKg8lxF zel-cW%|G6Ip7Hni&%O~L@tr01@Gn&0MBv`hqa=DEpnu5+NlykQv)ifEv!y$B94CP- zr5)?49ZB|;eth>diEJ;OdzzA$tk;<3aBkLhgH+?kMl>P}3j&&l?4a3)k^mXRToCL)#G?uJwi3afSNKi%ejt= zu$fAVxL~qPq4w6gf+Q|C#W}e$MFK06h_p-Sec2}8jJscOO7-*UAWP6f>6YOki+ zVVL~HzXW?*9m>}URnP{JtMhM>>`mCY6quQuP7W<=sFc&11xf)HKHo#i5MsgdLnQJNQb#>f|`_Ri#ays1^A2mM>m#JNh@FG8C>UCzb>i$$R z$%fR^YNAgVBC5s~XH`yFnr>!C%Ih~2Mw-JGqiRv6cjhrk+syP@cArvJx4I<}30r(q jeiYdI&4yOLJX^lc)$B~U8Z5lZ8#0Niv4zZRZH4$7xvPz6 delta 633 zcmX|7T}V@57=E^$bK1_fv&ugz%3WkXq(nA_tRyTnywEZg5*8h{(=@j!^QRjW(6BXU~P2Mg+!P)N@PFu*hLo~@AtjW_dL)0yzk2NlFRGk zmX|>N3jn8q<|#n-LAW=-Hy|uM1Uyp^PdsDafT#G`c>u z5v!4{c{$2nad&SZQ8p%#Eu#SA?q^Q@X z09%o$kNK#3UCb{3^Av~+cgm^zNjh_uLE)2z>I>q4I4C_|+)P5FeeDPhq)~grqeTkm z?8X}!3qy{MmJP(OIKBs4hSUbb`WgY>4ESE4gVfqvKYZH23>b;rlPae$z*Z{j8s zhm`OuZbUewq>XAOmpsaj8rE4)au+vYt@bRO;|Ar{4wA2#K5L<1_DaXB=0@i!>%mQ^ zgNhhW2pXdQZheTL3{89o(9WKQn4ObD;wh?AV`@nA9_dyCns>h%4)lf9-maK&$z5T5 zac9h(o=U;|k>|GCuATjzCwmr^?&hu0=E>TB!X6 DjO?ZA diff --git a/i18n/de_DE.ts b/i18n/de_DE.ts index ed1c39c..81a9eca 100644 --- a/i18n/de_DE.ts +++ b/i18n/de_DE.ts @@ -4,77 +4,77 @@ TableWidget - + Cut Ausschneiden - + Copy Kopieren - + Paste Einfügen - + Delete Löschen - + Cut row Zeile ausschneiden - + Copy row Zeile kopieren - + Paste row Zeile einfügen - + Insert row before Zeile oberhalb einfügen - + Insert row after Zeile unterhalb einfügen - + Remove row Zeile entfernen - + Delete items Zellinhalte löschen - + Row Nr. Zeile Nr. - + to be removed? entfernen? - + Remove Entfernen - + Delete cell content? Zellinhalt löschen? @@ -87,122 +87,122 @@ Hallo Welt - + &Settings &Einstellungen - + Language Sprache - + Stuff Gegenstand - + Length Länge - + Width Breite - + Height Höhe - + Weight Gewicht - + Dimension of the garage Dimension der Garage - + Dimensions of the objects to be stored Dimensionen der zu verstauenden Gegenstände - + Result Ergebnis - + Volume of the garage Volumen der Garage - + Volume of the items Volumen der Gegenstände - + Free space in the garage Freier Raum in der Garage - + Total weight Gesamtgewicht - + New (Ctrl+N) Neu (Strg+N) - + Open... (Ctrl+O) Öffnen... (Strg+O) - + Save (Ctrl+S) Speichern (Strg+S) - + Export to EXCEL... Export nach EXCEL... - + Information about the application Informationen über das Programm - + Quit the application (Strg+Q) Programm beenden (Strg+Q) - + Garage Garage - + Quit Beenden - + New Neu - + Save Speichern @@ -212,27 +212,27 @@ CSV-Datei - + All files Alle Dateien - + file Datei - + saved gespeichert - + Open Öffnen - + Export Export @@ -242,72 +242,127 @@ EXCEL-Datei - + Error in the garage dimension Fehler in der Garagen-Dimension - + Error in the dimensions of the objects to be stored Fehler in den Dimensionen der zu verstauenden Gegenstände - + Successfully exported to EXCEL Erfolgreich nach EXCEL exportiert - + CSV-file CSV-Datei - + EXCEL-file EXCEL-Datei - + There are unsaved entries. Without saving, all changes are lost. Continue anyway? Es gibt ungespeicherte Einträge. Ohne zu speichern, gehen alle Änderungen verloren. Trotzdem fortfahren? Garage Space Calculator - Garagenraum-Rechner + Garagenraum-Rechner - + Calculates available garage space Berechnet zur Verfügung stehenden Garagenraum - + Comment Kommentar + + + Unit of Measurements + Maßeinheit + + + + Length units + Längeneinheit + + + + Mass units + Masseeinheit + + + + &File + &Datei + + + + Help + Hilfe + + + + Do you want to convert all existing stuff of type length to the new unit of measurement (UOM) + Möchten Sie alle vorhandenen Gegenstände vom Typ Länge auf die neue Maßeinheit umrechnen + + + + UOM conversion + Einheiten Umrechnung + + + + Do you want to convert all existing stuff of type mass to the new unit of measurement (UOM) + Möchten Sie alle vorhandenen Gegenstände vom Typ Gewicht auf die neue Maßeinheit umrechnen + utils - + Idea Idee Used icons: Theme - Verwendete Icons: Thema + Verwendete Icons: Thema - + from von - + Version Version + + + Used icons + Verwendete Icons + + + + Currently opened file + Aktuell geöffnete Datei + + + + Location of the Ini-file + Dateipfad der Ini-Datei + diff --git a/i18n/hu_HU.qm b/i18n/hu_HU.qm index 40edbedd382be8a74b53d18927471bae9de39915..3dacaf97078ceaaac24f92dcc0ac50117ebab565 100644 GIT binary patch delta 1670 zcmb_bU1%I-6g`{FY&Nr-O}0r<8@m0h8h=P^jG@$V#Y7GMh{@I}>(h z6Svh4D70x=(XtVIkSM`_W5p&>Fd|r+N=qcZ6k1!5A}Fzmc}Xm$20gQh+qAV0iYznV z-udo5=iYPgJ#pu3^($+gf-<1#1c2v&UEctK^N`+2(*~sJTEM>r(!wV|c?fd+OQ2*U z9Xf`gJDJfmA10dK}Lk9B}2T#$%X0A&M(^@FDv_+g>*hDyN2!V7&r@_xVcOz|k7 z9F&HtNq}i-vZo&?UX;!rJj3rlNtYJqn0QF8-a*2^dbzEV@gY@q`pRh!$eD(5ps-TT z4Ana%Xpzsm0jm^w?yWi!oc8WXkf{76@4n}$2L9=k`jVh;537#cqXCd zD{0+oinN&$y)nZUwkg_7qeo5JBBtv?eO9w<&FB!8or=eW85efCTNS!$bl9D)v)!o* zL+uqQL$ig$`-B>^Qb{$T66~JH&gOgb5_t+K*5LoHkWB@bR1qVUoG_96g#S96Kqs_5)2ihHLD^DI|-IA$6a)h z$u6oR6j&Le{lNM?cGIGZaPuc&5eQb^NRU{j7i|=l(d$hY=R4J!0q~6gd^wnV3AzK!(lbDef<5(`>PfIEhTh9yZ)O4WTX63-QQraXLldyN z6eqd|0sS>JW^#aK7n*)Q0E7i3QbROXi;v&05Ux^J#0Eg;&_^0ekc4&C;OQTuM!}Hm zlSy^bFx~Z;20EE$(+j}3nMu2d81qd2Ob@WCkD0nWN%bz~+j0RgiLA>@LZ%;Vpp5Xs zMmBXQ3mD4SRN6zlx7bfBbHWH)7;qEsICuUc8HJncuD2%vwwHTTtRTQiRIW} z@qemzlJgtpu + TableWidget - + Cut kivágni - + Copy másolás - + Paste Beillesztés - + Delete Törlés - + Cut row Vágott vonal - + Copy row Vettem a szöveget - + Paste row Beilleszteni a sort - + Insert row before A fenti sort beilleszteni - + Insert row after Az alábbi sor beillesztése - + Remove row Távolítsa el a sort - + Delete items Tartalom törlése - + Row Nr. Vonalszám. - + to be removed? eltávolítani? - + Remove A eltávolítása - + Delete cell content? Cellatartalom törlése? @@ -87,122 +87,122 @@ Hallo Welt - + &Settings &Beállítások - + Language Nyelv - + Stuff Tárgy - + Length Hosszúság - + Width Szélesség - + Height Magasság - + Weight Súly - + Dimension of the garage A garázs mérete - + Dimensions of the objects to be stored A tárolandó objektumok méretei - + Result Eredmény - + Volume of the garage A garázs térfogata - + Volume of the items A tételek mennyisége - + Free space in the garage Szabad hely a garázsban - + Total weight Teljes súly - + New (Ctrl+N) Új (Ctrl+N) - + Open... (Ctrl+O) Nyissa ki (Ctrl+O) - + Save (Ctrl+S) Mentés (Ctrl+S) - + Export to EXCEL... Exportálás EXCEL-be... - + Information about the application Az alkalmazással kapcsolatos információk - + Quit the application (Strg+Q) Az alkalmazás kilépése (Ctrl+Q) - + Garage Garázs - + Quit Kilépés - + New Új - + Save Mentés @@ -212,27 +212,27 @@ CSV-fájl - + All files Minden fájl - + file fájl - + saved mentett - + Open Megnyitott - + Export Exportálás @@ -242,72 +242,127 @@ EXCEL-fájl - + Error in the garage dimension Hiba a garázs dimenziójában - + Error in the dimensions of the objects to be stored Hiba a tárolandó objektumok méreteiben - + Successfully exported to EXCEL Sikeresen exportált EXCEL-be - + CSV-file CSV-fájl - + EXCEL-file EXCEL-fájl - + There are unsaved entries. Without saving, all changes are lost. Continue anyway? Vannak mentetlen bejegyzések. Mentés nélkül minden módosítás elveszik. Folytassa mégis? Garage Space Calculator - Garázs hely kalkulátor + Garázs hely kalkulátor - + Calculates available garage space Kiszámítja a rendelkezésre álló garázshelyet - + Comment Megjegyzés + + + Unit of Measurements + Mérési egység + + + + Length units + Hosszúsági egységek + + + + Mass units + Tömegegységek + + + + &File + &Fájl + + + + Help + Segítség + + + + Do you want to convert all existing stuff of type length to the new unit of measurement (UOM) + Át akarja konvertálni az összes meglévő hosszúság típusú anyagot az új mértékegységre + + + + UOM conversion + Egység átváltás + + + + Do you want to convert all existing stuff of type mass to the new unit of measurement (UOM) + Át akarja konvertálni az összes meglévő tömeg típusú anyagot az új mértékegységre + utils - + Idea Ötlet Used icons: Theme - Használt ikonok: Téma + Használt ikonok: Téma - + from a weboldalról - + Version Verzió + + + Used icons + Használt ikonok + + + + Currently opened file + Jelenleg megnyitott fájl + + + + Location of the Ini-file + Az Ini-fájl helye + diff --git a/img/qt_logo_small.png b/img/qt_logo_small.png new file mode 100644 index 0000000000000000000000000000000000000000..94dfbf2d264445ba8cbabc24ba804a70515a000a GIT binary patch literal 565 zcmeAS@N?(olHy`uVBq!ia0vp^0zk~e!3-p0EYEHRQY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fI`6HdX33Nr_Lu6LvJK3c)9HLuQ&PkD$5?!fBN%j^0Qe_e?F^u z*n0ch-D%I~p89-l!`p2@Lr#A_SO2Kv-uDMv-t9c{@x-q8`-|?^{Qmdn;+HEozTNuq z_h-`WobB)T1l@?WzU+~7CpY765kmw|EzlW^B|(0{3=Yq3q=7g|-tI089jvk*Kn`bt zM`SSr1K(i~W;~w1A_XYOUgGKN%Kn6roxxlrJ9}CQP^i$;#W95AdhEH2d`$*CtQR(| zUa;svgJr@A2FXwV<1>v@$lZGPck*Ft9Q((5(`# zK+%w!pOTqY3DIC+rfX;%VrXP#YH4L;2GMX_(%>;rg9ONi;QX|b^2DN4hJwV*yb`^< c)Di^~JwrXSzkZy+_+?=5boFyt=akR{0AaY`qyPW_ literal 0 HcmV?d00001 diff --git a/src/clsTableWidget.py b/src/clsTableWidget.py index 8af1e30..2058bcc 100644 --- a/src/clsTableWidget.py +++ b/src/clsTableWidget.py @@ -14,13 +14,22 @@ 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 + QMainWindow, QMessageBox, QComboBox, QDialog +from PySide2.QtCore import Qt, QFile, QItemSelectionModel, QCoreApplication, Slot +from PySide2.QtGui import QIcon, QColor, QPalette +from PySide2.QtUiTools import QUiLoader + +# local imports +from utils import resource_path, convert_uom_to_length, convert_uom_to_mass # local globals SCRIPT_PATH = os.path.abspath(os.path.dirname(__file__)) +DEFAULT_UOM_LENGTH = None +DEFAULT_UOM_MASS = None + +UI_DLG_UOM = "./ui/dlg_uom.ui" + 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" @@ -43,11 +52,14 @@ ICON_DEL = SCRIPT_PATH + "/img/icons8-delete-16.png" # return os.path.join(base_path, relative_path) class TableWidget(QTableWidget): - def __init__(self, parent=None): + def __init__(self, parent=None, uom_length=DEFAULT_UOM_LENGTH, uom_mass=DEFAULT_UOM_MASS): super().__init__() self.parent = parent + self.default_uom_length = uom_length + self.default_uom_mass = uom_mass + # self.setSelectionMode(QAbstractItemView.ContiguousSelection) self.setSelectionMode(QAbstractItemView.SingleSelection) @@ -70,8 +82,6 @@ class TableWidget(QTableWidget): self.vertHeader.setSelectionMode(QAbstractItemView.SingleSelection) self.vertHeader.sectionClicked.connect(self.select_row) self.vertHeader.setSectionsMovable(True) - # optimize row height - self.resizeRowsToContents() self.row_selected = False @@ -87,8 +97,6 @@ class TableWidget(QTableWidget): 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)) @@ -183,41 +191,144 @@ class TableWidget(QTableWidget): if key == Qt.Key_Delete: self.item_del() + elif key == Qt.Key_F3: + item = self.item(self.currentRow(), self.currentColumn()) + try: + self.parent.cols_with_uom_length + self.parent.cols_with_uom_mass + except: + return False + else: + if self.currentColumn() in self.parent.cols_with_uom_length: + self.f3_pressed(item, "UOM_TYPE_LENGTH") + elif self.currentColumn() in self.parent.cols_with_uom_mass: + self.f3_pressed(item, "UOM_TYPE_MASS") + elif key == Qt.Key_Escape: self.clearSelection() - def item_paste(self): - cur_row = self.currentRow() - cur_col = self.currentColumn() - # ask_confirmation = True + def f3_pressed(self, item, uom_type) -> bool: + if item: + try: + float(item.text().replace(",", ".")) + except ValueError: + return False # we've got an existing text here - if self.row_selected: - cur_col = 0 + loader = QUiLoader() + path = os.path.join(os.path.dirname(__file__), resource_path(UI_DLG_UOM)) + ui_file = QFile(path) + ui_file.open(QFile.ReadOnly) + self.dlg = loader.load(ui_file, self) + ui_file.close() - col = 0 - if len(self.clipboard_data) == 1: - data = self.clipboard_data[0] - item = QTableWidgetItem(data) + self.dlg.uom_type = uom_type + if uom_type == "UOM_TYPE_MASS": + self.dlg.cmbUOM.addItems(["kg", "g"]) + else: # anticipate uom type length + self.dlg.cmbUOM.addItems(["m", "cm", "mm"]) - self.setItem(cur_row, cur_col, item) - item.setSelected(True) + if item: + self.dlg.efValue.setText(item.text()) + print("UOM of the cell:", item.data(Qt.UserRole)) + self.dlg.cmbUOM.setCurrentText(item.data(Qt.UserRole)) + if item.data(Qt.UserRole): + self.old_uom = item.data(Qt.UserRole) + else: + if uom_type == "UOM_TYPE_MASS": + self.old_uom = self.default_uom_mass + else: + self.old_uom = self.default_uom_length + + self.dlg.cmbUOM.currentIndexChanged.connect(self.on_cmbUOM_itemChanged) + + if self.dlg.exec() == QDialog.Accepted: + if not item: + item = QTableWidgetItem("Dummy") + self.setItem(self.currentRow(), self.currentColumn(), item) + item.setText(self.dlg.efValue.text()) + #item.setStyleSheet("border: 1px solid yellow;") + item.setData(Qt.UserRole, self.dlg.cmbUOM.currentText()) ## store UOM-Text at UserRole + + item.setData(Qt.UserRole+1, self.dlg.cmbUOM.currentText()) ## store UOM-Type at UserRole+1 (1==UOM_LENGTH, 2==UOM_MASS) + + if self.dlg.cmbUOM.currentText() not in (self.default_uom_length, self.default_uom_mass): + item.setData(Qt.BackgroundRole, QColor(Qt.yellow)) + item.setToolTip("[" + item.data(Qt.UserRole) + "]") + else: + item.setData(Qt.BackgroundRole, None) + item.setToolTip(None) + + return True + + @Slot(int) + def on_cmbUOM_itemChanged(self, index): + try: + old_val = float(self.dlg.efValue.text().replace(",", ".")) + except ValueError: + # special case: edit field is emppty because user changes UOM first. + self.old_uom = self.dlg.cmbUOM.currentText() + pass else: - for data in self.clipboard_data: + new_uom = self.dlg.cmbUOM.currentText() + if self.dlg.uom_type == "UOM_TYPE_LENGTH": + new_val = convert_uom_to_length(old_val, self.old_uom, new_uom) + elif self.dlg.uom_type == "UOM_TYPE_MASS": + new_val = convert_uom_to_mass(old_val, self.old_uom, new_uom) + self.dlg.efValue.setText(str(new_val).replace(".", ",")) + self.old_uom = new_uom + + # def setUOM(self, item, uom): + # if uom not in (DEFAULT_UOM_LENGTH, DEFAULT_UOM_MASS): + # item.setData(Qt.UserRole, uom) + # item.setData(Qt.BackgroundRole, QColor(Qt.yellow)) + # item.setToolTip(f"[{uom}]") + # else: + # item.setData(Qt.BackgroundRole, None) + # item.setToolTip(None) + + def setUOM(self, item, uom): + item.setData(Qt.UserRole, uom) + + if uom not in (self.default_uom_length, self.default_uom_mass): + item.setData(Qt.BackgroundRole, QColor(Qt.yellow)) + item.setToolTip(f"[{uom}]") + else: + item.setData(Qt.BackgroundRole, None) + item.setToolTip(None) + + def item_paste(self): + if self.clipboard_data: + 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) - # 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) + 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 - col += 1 + # ask_confirmation = False + self.setItem(cur_row, col, item) + item.setSelected(True) + + col += 1 def item_cut(self): self.item_copy() @@ -259,7 +370,10 @@ class TableWidget(QTableWidget): return False ask_cofirmation = False - item.setData(Qt.DisplayRole, None) + item.setData(Qt.DisplayRole, None) # remove text + item.setData(Qt.BackgroundRole, None) # remove cell background color + item.setData(Qt.UserRole, None) # remove UOM + item.setToolTip(None) # remove tooltip if len(sel_idx) == self.columnCount() * self.rowCount(): try: diff --git a/src/main.py b/src/main.py index 4b5d027..5e9d4f4 100644 --- a/src/main.py +++ b/src/main.py @@ -16,21 +16,22 @@ import configparser # Third party imports from PySide2.QtWidgets import QApplication, QMainWindow, QTableWidgetItem, QStatusBar, QFileDialog, \ QAbstractItemView, QMenu, QMessageBox, QHBoxLayout, QVBoxLayout, QAction, QActionGroup, QSizePolicy -from PySide2.QtGui import QIcon +from PySide2.QtGui import QIcon, QColor from PySide2.QtCore import QFile, QSize, Qt, QCoreApplication, QTranslator from PySide2.QtUiTools import QUiLoader import xlsxwriter # Local imports -from utils import show_about, resource_path, str_iff, fit_col_widths +from utils import show_about, resource_path, str_iff, fit_col_widths, convert_uom_to_length, convert_uom_to_mass, \ + optimizeTableLayout, table_has_items # my own classes imports from clsTableWidget import TableWidget # Local globals -APP_VERSION = "v0.6" +APP_VERSION = "v0.7" -DIR_APPDATA = os.getenv('LOCALAPPDATA') +DIR_CURRENT = os.getcwd() APP_NAME = "Garage Space Calculator" APP_DISPNAME = "GarageCalc" @@ -39,6 +40,7 @@ APP_DESCR = "Calculates available garage space" APP_COPYRIGHT = "(c) Paul Salajean 2021" APP_WEBSITE = "https://gitlab.com/ProfP303" APP_DESKTOPFILENAME = APP_DISPNAME +APP_INI_FILE = os.path.join(DIR_CURRENT, APP_DISPNAME + '.ini') APP_ICON = "./img/icons8-garage-32.ico" @@ -48,6 +50,7 @@ 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" +ICON_QT_LOGO = "./img/qt_logo_small.png" UI_MAIN = "./ui/main.ui" @@ -55,10 +58,18 @@ DEFAULT_GARAGE_LENGTH = "6" DEFAULT_GARAGE_WIDTH = "2,5" DEFAULT_GARAGE_HEIGHT = "2,5" -COL_GARAGE_LENGTH = 1 -COL_GARAGE_WIDTH = 2 -COL_GARAGE_HEIGHT = 3 -COL_GARAGE_COMMENT = 4 +DEFAULT_UOM_LENGTH = "m" +DEFAULT_UOM_MASS = "kg" + +COL_GARAGE_LENGTH = 0 +COL_GARAGE_WIDTH = 1 +COL_GARAGE_HEIGHT = 2 +COL_GARAGE_COMMENT = 3 + +CSV_COL_GARAGE_LENGTH = 1 +CSV_COL_GARAGE_WIDTH = 2 +CSV_COL_GARAGE_HEIGHT = 3 +CSV_COL_GARAGE_COMMENT = 4 COL_STUFF_NAME = 0 COL_STUFF_LENGTH = 1 @@ -67,6 +78,17 @@ COL_STUFF_HEIGHT = 3 COL_STUFF_WEIGHT = 4 COL_STUFF_COMMENT = 5 +CSV_COL_STUFF_NAME = 0 +CSV_COL_STUFF_LENGTH = 1 +CSV_COL_STUFF_LENGTH_UOM = 2 +CSV_COL_STUFF_WIDTH = 3 +CSV_COL_STUFF_WIDTH_UOM = 4 +CSV_COL_STUFF_HEIGHT = 5 +CSV_COL_STUFF_HEIGHT_UOM = 6 +CSV_COL_STUFF_WEIGHT = 7 +CSV_COL_STUFF_WEIGHT_UOM = 8 +CSV_COL_STUFF_COMMENT = 9 + TBL_STUFF_COL_COUNT = 6 TBL_STUFF_ROW_COUNT = 50 @@ -78,11 +100,14 @@ TXT_UNSAVED_CHANGES = QCoreApplication.translate("main", "There are unsaved entr CONFIG = configparser.ConfigParser() TRANSLATOR = QTranslator() +def my_func(): + pass + #################################################################### def main(): qApp = QApplication(sys.argv) - global APP_NAME, CONFIG, TRANSLATOR + global APP_NAME, CONFIG, TRANSLATOR, DIR_CURRENT, APP_INI_FILE, DEFAULT_UOM_LENGTH, DEFAULT_UOM_MASS qApp.setApplicationName(APP_NAME) qApp.setApplicationDisplayName(APP_DISPNAME) @@ -90,23 +115,34 @@ def main(): qApp.description = APP_DESCR qApp.copyright = APP_COPYRIGHT qApp.website = APP_WEBSITE - qApp.setWindowIcon(QIcon(APP_ICON)) + qApp.setWindowIcon(QIcon(resource_path(APP_ICON))) qApp.setDesktopFileName(APP_DESKTOPFILENAME) + print("Current dir:", DIR_CURRENT) + + # define ini-file defaults 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)) + # read from existing ini-file + if os.path.exists(APP_INI_FILE): + print("Reading from existing ini-file", APP_INI_FILE) + CONFIG.read(APP_INI_FILE) + try: + language = CONFIG['DEFAULT']['language'] + DEFAULT_UOM_LENGTH = CONFIG['DEFAULT']['UOM_length'] + DEFAULT_UOM_MASS = CONFIG['DEFAULT']['UOM_mass'] + except KeyError: + pass # key will be created during saving anyway - with open(os.path.join(DIR_APPDATA, APP_DISPNAME, APP_DISPNAME + '.ini'), 'w') as configfile: # save + else: # create new ini-file + print("Creating new ini-file", APP_INI_FILE) + CONFIG['DEFAULT']['language'] = language + CONFIG['DEFAULT']['UOM_length'] = DEFAULT_UOM_LENGTH + CONFIG['DEFAULT']['UOM_mass'] = DEFAULT_UOM_MASS + + with open(APP_INI_FILE, 'w') as configfile: # save CONFIG.write(configfile) - print("Current dir:", os.getcwd()) if language == "Deutsch": print("Loading german language file.") TRANSLATOR.load(resource_path('./i18n/de_DE')) @@ -121,7 +157,7 @@ def main(): app_name = qApp.translate("main", APP_NAME) qApp.setApplicationName(app_name) - winMain = MainWindow(language) + winMain = MainWindow(language, DEFAULT_UOM_LENGTH, DEFAULT_UOM_MASS) if qApp.primaryScreen().size().width() <= 800: winMain.showMaximized() @@ -144,19 +180,18 @@ def create_examples(table): generated_string1 = "".join(choice(string_format) for x in range(randint(min, max))) generated_string2 = "".join(choice(string_format) for x in range(randint(min, max))) - table.setItem(row, 0, QTableWidgetItem(generated_string1)) - table.setItem(row, 1, QTableWidgetItem(str(round(uniform(1.1, 10.2), 2)).replace('.', ','))) - table.setItem(row, 2, QTableWidgetItem(str(round(uniform(1.5, 10.2), 2)).replace('.', ','))) - table.setItem(row, 3, QTableWidgetItem(str(round(uniform(1.125, 8.75), 2)).replace('.', ','))) - table.setItem(row, 4, QTableWidgetItem(str(round(uniform(20, 100), 2)).replace('.', ','))) - table.setItem(row, 5, QTableWidgetItem(generated_string2)) + table.setItem(row, COL_STUFF_NAME, QTableWidgetItem(generated_string1)) + table.setItem(row, COL_STUFF_LENGTH, QTableWidgetItem(str(round(uniform(1.1, 10.2), 2)).replace('.', ','))) + table.setItem(row, COL_STUFF_WIDTH, QTableWidgetItem(str(round(uniform(1.5, 10.2), 2)).replace('.', ','))) + table.setItem(row, COL_STUFF_HEIGHT, QTableWidgetItem(str(round(uniform(1.125, 8.75), 2)).replace('.', ','))) + table.setItem(row, COL_STUFF_WEIGHT, QTableWidgetItem(str(round(uniform(20, 100), 2)).replace('.', ','))) + table.setItem(row, COL_STUFF_COMMENT, QTableWidgetItem(generated_string2)) #################################################################### class MainWindow(QMainWindow): - def __init__(self, language): + def __init__(self, language="Deutsch", uom_length=DEFAULT_UOM_LENGTH, uom_mass=DEFAULT_UOM_MASS): super().__init__() - self.language = language self.is_modified = False self.opened_file = None self.remembered_row = None @@ -164,51 +199,33 @@ class MainWindow(QMainWindow): self.load_ui() self.init_ui() self.set_defaults() - self.create_menu(self.language) - self.connect_signals() self.create_actions() + self.create_menu(language, uom_length, uom_mass) + self.connect_signals() self.create_toolbar() self.create_statusbar() self.statusBar.showMessage(f"{APP_DISPNAME} {APP_VERSION} - {APP_AUTHOR}", 5000) - self.calc_voluminae() - self.ui.efWeight.setText(str("0")) - self.retranslateUi() # TODO: disable for PROD! # create_examples(self.ui.tableStuff) # self.ui.tableGarage.setItem(0, 3, QTableWidgetItem("Garázs az udvaron")) + # self.is_modified = False + # END TODO - for col in range(self.ui.tableStuff.columnCount()): # optimize column width - self.ui.tableStuff.resizeColumnToContents(col) - self.ui.tableStuff.resizeRowsToContents() # optimize row height + self.retranslateUi() + optimizeTableLayout(self.ui.tableStuff) self.ui.tableStuff.setFocus() - def create_menu(self, language=None): - menuMain = self.menuBar() - self.menuSettings = menuMain.addMenu(QCoreApplication.translate("main", "&Settings")) - self.menuLanguage = self.menuSettings.addMenu(QCoreApplication.translate("main", "Language")) - - ag = QActionGroup(self, exclusive=True) - - a = ag.addAction(QAction('English', self.menuSettings, checkable=True)) - self.menuLanguage.addAction(a) - - a = ag.addAction(QAction('Deutsch', self.menuSettings, checkable=True)) - self.menuLanguage.addAction(a) - - a = ag.addAction(QAction('Magyar', self.menuSettings, checkable=True)) - self.menuLanguage.addAction(a) - - menuMain.triggered.connect(lambda: self.store_selected_language(self.menuLanguage)) - - if language: - [action.setChecked(True) for action in self.menuLanguage.actions() if action.text()==language] - def retranslateUi(self): + global DEFAULT_UOM_LENGTH, DEFAULT_UOM_MASS # menus self.menuSettings.setTitle(QCoreApplication.translate("main", "&Settings")) - self.menuLanguage.setTitle(QCoreApplication.translate("main", "Language")) + self.menuSettings_Language.setTitle(QCoreApplication.translate("main", "Language")) + + self.menuSettings_UOMs.setTitle(QCoreApplication.translate("main", "Unit of Measurements")) + self.menuSettings_UOMs_Length.setTitle(QCoreApplication.translate("main", "Length units")) + self.menuSettings_UOMs_Mass.setTitle(QCoreApplication.translate("main", "Mass units")) # tables self.ui.tableGarage.setVerticalHeaderLabels([ @@ -222,11 +239,11 @@ class MainWindow(QMainWindow): ]) 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") + " [kg]", + QCoreApplication.translate("main","Stuff"), + QCoreApplication.translate("main","Length") + " [" + DEFAULT_UOM_LENGTH + "]", + QCoreApplication.translate("main","Width") + " [" + DEFAULT_UOM_LENGTH + "]", + QCoreApplication.translate("main","Height") + " [" + DEFAULT_UOM_LENGTH + "]", + QCoreApplication.translate("main","Weight") + " [" + DEFAULT_UOM_MASS + "]", QCoreApplication.translate("main","Comment") ]) @@ -249,6 +266,7 @@ class MainWindow(QMainWindow): self.actionQuit.setToolTip(QCoreApplication.translate("main","Quit the application (Strg+Q)")) def load_ui(self): + global TableWidget loader = QUiLoader() path = os.path.join(os.path.dirname(__file__), resource_path(UI_MAIN)) ui_file = QFile(path) @@ -256,9 +274,13 @@ class MainWindow(QMainWindow): self.ui = loader.load(ui_file, self) ui_file.close() + # define columns which contain UOM length and mass + self.cols_with_uom_length = [COL_STUFF_LENGTH, COL_STUFF_WIDTH, COL_STUFF_HEIGHT] + self.cols_with_uom_mass = [COL_STUFF_WEIGHT] + # implement custom class 'TableWidget' layoutGb = self.ui.gbStuff.layout() - self.ui.tableStuff = TableWidget(self) + self.ui.tableStuff = TableWidget(self, DEFAULT_UOM_LENGTH, DEFAULT_UOM_MASS) self.ui.tableStuff.setColumnCount(TBL_STUFF_COL_COUNT) self.ui.tableStuff.setRowCount(TBL_STUFF_ROW_COUNT) self.ui.tableStuff.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) @@ -268,34 +290,103 @@ class MainWindow(QMainWindow): layoutGb.addWidget(self.ui.tableStuff) def create_actions(self): - self.actionNew = QAction() - self.actionNew.setIcon(QIcon(resource_path(ICON_NEW))) + self.actionNew = QAction(QIcon(resource_path(ICON_NEW)), "&New") self.actionNew.triggered.connect(self.file_new) self.actionNew.setShortcut("Ctrl+N") - self.actionOpen = QAction() - self.actionOpen.setIcon(QIcon(resource_path(ICON_OPEN))) + self.actionOpen = QAction(QIcon(resource_path(ICON_OPEN)), "&Open...") self.actionOpen.triggered.connect(self.file_open) self.actionOpen.setShortcut("Ctrl+O") - self.actionSave = QAction() - self.actionSave.setIcon(QIcon(resource_path(ICON_SAVE))) + self.actionSave = QAction(QIcon(resource_path(ICON_SAVE)), "&Save") self.actionSave.triggered.connect(self.file_save) self.actionSave.setShortcut("Ctrl+S") - self.actionExport = QAction() - self.actionExport.setIcon(QIcon(resource_path(ICON_EXPORT))) + self.actionExport = QAction(QIcon(resource_path(ICON_EXPORT)), "Export to EXCEL...") self.actionExport.triggered.connect(self.file_export) - self.actionAbout = QAction() - self.actionAbout.setIcon(QIcon(resource_path(ICON_ABOUT))) - self.actionAbout.triggered.connect(show_about) + self.actionAbout_Qt = QAction(QIcon(resource_path(ICON_QT_LOGO)), "About Qt") + self.actionAbout_Qt.triggered.connect(QApplication.aboutQt) - self.actionQuit = QAction() - self.actionQuit.setIcon(QIcon(resource_path(ICON_QUIT))) - self.actionQuit.triggered.connect(self.app_quit) + self.actionAbout = QAction(QIcon(resource_path(ICON_ABOUT)), "About " + qApp.applicationDisplayName()) + global APP_INI_FILE + self.actionAbout.triggered.connect(lambda: show_about(APP_INI_FILE, self.opened_file)) + + self.actionQuit = QAction(QIcon(resource_path(ICON_QUIT)), "Quit") + self.actionQuit.triggered.connect(self.quit_app) self.actionQuit.setShortcut("Ctrl+Q") + def create_menu(self, language=None, uom_length=None, uom_mass=None): + menuMain = self.menuBar() + menuFile = menuMain.addMenu(QCoreApplication.translate("main", "&File")) + menuFile_New = menuFile.addAction(self.actionNew) + menuFile_Open = menuFile.addAction(self.actionOpen) + menuFile_Save = menuFile.addAction(self.actionSave) + menuFile.addSeparator() + menuFile.addAction(self.actionExport) + menuFile.addSeparator() + menuFile.addAction(self.actionQuit) + + self.menuSettings = menuMain.addMenu(QCoreApplication.translate("main", "&Settings")) + + self.menuSettings_Language = self.menuSettings.addMenu(QCoreApplication.translate("main", "Language")) + + self.menuSettings_UOMs = self.menuSettings.addMenu(QCoreApplication.translate("main", "Unit of Measurements")) + self.menuSettings_UOMs_Length = self.menuSettings_UOMs.addMenu(QCoreApplication.translate("main", "Length units")) + self.menuSettings_UOMs_Mass = self.menuSettings_UOMs.addMenu(QCoreApplication.translate("main", "Mass units")) + + ag = QActionGroup(self, exclusive=True) + + a = ag.addAction(QAction('English', self.menuSettings, checkable=True)) + self.menuSettings_Language.addAction(a) + + a = ag.addAction(QAction('Deutsch', self.menuSettings, checkable=True)) + self.menuSettings_Language.addAction(a) + + a = ag.addAction(QAction('Magyar', self.menuSettings, checkable=True)) + self.menuSettings_Language.addAction(a) + + self.menuSettings_Language.triggered.connect(lambda: self.store_selected_language(self.menuSettings_Language)) + + if language: + [action.setChecked(True) for action in self.menuSettings_Language.actions() if action.text()==language] + + ag = QActionGroup(self, exclusive=True) + + a = ag.addAction(QAction("m", self.menuSettings_UOMs_Length, checkable=True)) + self.menuSettings_UOMs_Length.addAction(a) + + a = ag.addAction(QAction("cm", self.menuSettings_UOMs_Length, checkable=True)) + self.menuSettings_UOMs_Length.addAction(a) + + a = ag.addAction(QAction("mm", self.menuSettings_UOMs_Length, checkable=True)) + self.menuSettings_UOMs_Length.addAction(a) + + if uom_length: + [action.setChecked(True) for action in self.menuSettings_UOMs_Length.actions() if action.text()==uom_length] + + ag = QActionGroup(self, exclusive=True) + + a = ag.addAction(QAction("kg", self.menuSettings_UOMs_Mass, checkable=True)) + self.menuSettings_UOMs_Mass.addAction(a) + + a = ag.addAction(QAction("g", self.menuSettings_UOMs_Mass, checkable=True)) + self.menuSettings_UOMs_Mass.addAction(a) + + self.menuSettings_UOMs_Length.triggered.connect(lambda: self.store_selected_uom_length(self.menuSettings_UOMs_Length)) + self.menuSettings_UOMs_Mass.triggered.connect(lambda: self.store_selected_uom_mass(self.menuSettings_UOMs_Mass)) + + if uom_mass: + [action.setChecked(True) for action in self.menuSettings_UOMs_Mass.actions() if action.text()==uom_mass] + + self.menuHelp = menuMain.addMenu(QCoreApplication.translate("main", "Help")) + self.menuHelp_About_Qt = self.menuHelp.addAction(self.actionAbout_Qt) + self.menuHelp_About = self.menuHelp.addAction(self.actionAbout) + + def create_statusbar(self): + self.statusBar = QStatusBar() + self.setStatusBar(self.statusBar) + def create_toolbar(self): # Main Toolbar (for all pages/views) self.toolbar = self.addToolBar('Main Toolbar') @@ -320,16 +411,18 @@ class MainWindow(QMainWindow): tblStuff.setRowCount(TBL_STUFF_ROW_COUNT) - self.is_modified = False + # display results + self.ui.efVol_Garage.setText("0") + self.ui.efVol_Stuff.setText("0") + self.ui.efVol_Free.setText("0") + self.ui.efWeight.setText("0") - def create_statusbar(self): - self.statusBar = QStatusBar() - self.setStatusBar(self.statusBar) + self.is_modified = False 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) @@ -338,7 +431,7 @@ class MainWindow(QMainWindow): tblStuff = self.ui.tableStuff # clear garage - tblGarage.setRowCount(1) + # tblGarage.setRowCount(1) # clear stuff ## tblStuff.clear() @@ -346,6 +439,8 @@ class MainWindow(QMainWindow): # tblStuff.setRowCount(TBL_STUFF_ROW_COUNT) tblStuff.clearContents() + tblStuff.sortByColumn(-1, Qt.AscendingOrder) # reset sorting + # clear results self.ui.efVol_Garage.clear() self.ui.efVol_Stuff.clear() @@ -353,15 +448,12 @@ class MainWindow(QMainWindow): self.ui.efWeight.clear() self.opened_file = None - if self.opened_file: - self.setWindowTitle(self.opened_file) - else: - self.setWindowTitle(f"{qApp.applicationName()} {APP_VERSION} - {APP_AUTHOR}") + self.setWindowTitle(f"{qApp.applicationName()} {APP_VERSION} - {APP_AUTHOR}") self.ui.efVol_Free.setStyleSheet("") self.is_modified = False - def app_quit(self): + def quit_app(self): if self.is_modified: msg = QCoreApplication.translate("main", TXT_UNSAVED_CHANGES) reply = QMessageBox.question(self, QCoreApplication.translate("main", "Quit"), msg, \ @@ -377,16 +469,16 @@ class MainWindow(QMainWindow): QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if reply == QMessageBox.No: return False + self.init_ui() self.ui.tableStuff.sortByColumn(-1, Qt.AscendingOrder) # reset sorting - for col in range(self.ui.tableStuff.columnCount()): # optimize column width - self.ui.tableStuff.resizeColumnToContents(col) - self.ui.tableStuff.resizeRowsToContents() # optimize row height + optimizeTableLayout(self.ui.tableStuff) self.ui.tableStuff.setFocus() def file_save(self): + global DEFAULT_UOM_LENGTH, DEFAULT_UOM_MASS tblGarage = self.ui.tableGarage tblStuff = self.ui.tableStuff is_file_saved = False @@ -413,10 +505,10 @@ class MainWindow(QMainWindow): garage_height = 0 garage_comment = "" - item_length = tblGarage.item(0, 0) - item_width = tblGarage.item(0, 1) - item_height = tblGarage.item(0, 2) - item_comment = tblGarage.item(0, 3) + item_length = tblGarage.item(0, COL_GARAGE_LENGTH) + item_width = tblGarage.item(0, COL_GARAGE_WIDTH) + item_height = tblGarage.item(0, COL_GARAGE_HEIGHT) + item_comment = tblGarage.item(0, COL_GARAGE_COMMENT) # loop over table Garage for row in range(tblGarage.rowCount()): @@ -443,9 +535,13 @@ class MainWindow(QMainWindow): for row in range(tblStuff.rowCount()): stuff_text = None length = None + length_uom = None width = None + width_uom = None height = None + height_uom = None weight = None + weight_uom = None comment = None item_stuff = tblStuff.item(row, COL_STUFF_NAME) @@ -460,25 +556,41 @@ class MainWindow(QMainWindow): if item_length: length = item_length.text() + if length: + length_uom = item_length.data(Qt.UserRole) + if not length_uom: + length_uom = DEFAULT_UOM_LENGTH if item_width: width = item_width.text() + if width: + width_uom = item_width.data(Qt.UserRole) + if not width_uom: + width_uom = DEFAULT_UOM_LENGTH if item_height: height = item_height.text() + if height: + height_uom = item_height.data(Qt.UserRole) + if not height_uom: + height_uom = DEFAULT_UOM_LENGTH if item_weight: weight = item_weight.text() + if weight: + weight_uom = item_weight.data(Qt.UserRole) + if not weight_uom: + weight_uom = DEFAULT_UOM_MASS if item_comment: comment = item_comment.text() if stuff_text or length or width or height or weight: - writer.writerow([stuff_text, length, width, height, weight, comment]) + writer.writerow([stuff_text, length, length_uom, width, width_uom, height, height_uom, weight, weight_uom, comment]) is_file_saved = True - self.opened_file = os.path.basename(fileName) - self.setWindowTitle(self.opened_file) + self.opened_file = fileName + self.setWindowTitle(os.path.basename(fileName)) self.is_modified = False if is_file_saved: @@ -507,7 +619,9 @@ class MainWindow(QMainWindow): options=options) if fileName: self.init_ui() - self.ui.tableStuff.sortByColumn(-1, Qt.AscendingOrder) # reset sorting + # disable calc trigger for any cell! + tblGarage.itemChanged.disconnect() + tblStuff.itemChanged.disconnect() file = open(fileName, "r", newline='') reader = csv.reader(file, delimiter=';') @@ -516,15 +630,15 @@ class MainWindow(QMainWindow): for row in reader: if row_idx == 0: # first row (index=0) ist always garage dimension try: - garage_length = str(row[COL_GARAGE_LENGTH].replace(".", ",")) - garage_width = str(row[COL_GARAGE_WIDTH].replace(".", ",")) - garage_height = str(row[COL_GARAGE_HEIGHT].replace(".", ",")) - garage_comment = row[COL_GARAGE_COMMENT] + garage_length = str(row[CSV_COL_GARAGE_LENGTH].replace(".", ",")) + garage_width = str(row[CSV_COL_GARAGE_WIDTH].replace(".", ",")) + garage_height = str(row[CSV_COL_GARAGE_HEIGHT].replace(".", ",")) + garage_comment = row[CSV_COL_GARAGE_COMMENT] - tblGarage.setItem(0, 0, QTableWidgetItem(garage_length)) - tblGarage.setItem(0, 1, QTableWidgetItem(garage_width)) - tblGarage.setItem(0, 2, QTableWidgetItem(garage_height)) - tblGarage.setItem(0, 3, QTableWidgetItem(garage_comment)) + tblGarage.setItem(0, COL_GARAGE_LENGTH, QTableWidgetItem(garage_length)) + tblGarage.setItem(0, COL_GARAGE_WIDTH, QTableWidgetItem(garage_width)) + tblGarage.setItem(0, COL_GARAGE_HEIGHT, QTableWidgetItem(garage_height)) + tblGarage.setItem(0, COL_GARAGE_COMMENT, QTableWidgetItem(garage_comment)) except IndexError as ex: pass @@ -532,32 +646,44 @@ class MainWindow(QMainWindow): if row_idx > 0: try: stuff = row[COL_STUFF_NAME] - stuff_length = str(row[COL_STUFF_LENGTH].replace(".", ",")) - stuff_width = str(row[COL_STUFF_WIDTH].replace(".", ",")) - stuff_height = str(row[COL_STUFF_HEIGHT].replace(".", ",")) - stuff_weight = str(row[COL_STUFF_WEIGHT].replace(".", ",")) - stuff_comment = row[COL_STUFF_COMMENT] + stuff_length = str(row[CSV_COL_STUFF_LENGTH].replace(".", ",")) + stuff_length_uom = row[CSV_COL_STUFF_LENGTH_UOM] + + stuff_width = str(row[CSV_COL_STUFF_WIDTH].replace(".", ",")) + stuff_width_uom = row[CSV_COL_STUFF_WIDTH_UOM] + + stuff_height = str(row[CSV_COL_STUFF_HEIGHT].replace(".", ",")) + stuff_height_uom = row[CSV_COL_STUFF_HEIGHT_UOM] + + stuff_weight = str(row[CSV_COL_STUFF_WEIGHT].replace(".", ",")) + stuff_weight_uom = str(row[CSV_COL_STUFF_WEIGHT_UOM].replace(".", ",")) + + stuff_comment = row[CSV_COL_STUFF_COMMENT] tblStuff.setItem(row_idx - 1, COL_STUFF_NAME, QTableWidgetItem(stuff)) item = QTableWidgetItem() item.setData(Qt.DisplayRole, stuff_length) tblStuff.setItem(row_idx - 1, COL_STUFF_LENGTH, item) + tblStuff.setUOM(item, stuff_length_uom) # tblStuff.setItem(row_idx - 1, COL_STUFF_WIDTH, QTableWidgetItem(float(stuff_width))) item = QTableWidgetItem() item.setData(Qt.DisplayRole, stuff_width) tblStuff.setItem(row_idx - 1, COL_STUFF_WIDTH, item) + tblStuff.setUOM(item, stuff_width_uom) #tblStuff.setItem(row_idx - 1, COL_STUFF_HEIGHT, QTableWidgetItem(float(stuff_height))) item = QTableWidgetItem(stuff_height) item.setData(Qt.DisplayRole, stuff_height) tblStuff.setItem(row_idx - 1, COL_STUFF_HEIGHT, item) + tblStuff.setUOM(item, stuff_height_uom) # tblStuff.setItem(row_idx - 1, COL_STUFF_WEIGHT, QTableWidgetItem(float(stuff_weight))) item = QTableWidgetItem(stuff_weight) item.setData(Qt.DisplayRole, stuff_weight) tblStuff.setItem(row_idx - 1, COL_STUFF_WEIGHT, item) + tblStuff.setUOM(item, stuff_weight_uom) tblStuff.setItem(row_idx - 1, COL_STUFF_COMMENT, QTableWidgetItem(stuff_comment)) @@ -566,20 +692,19 @@ class MainWindow(QMainWindow): row_idx += 1 - tblStuff.setRowCount(TBL_STUFF_ROW_COUNT) + # enable calc trigger + tblGarage.itemChanged.connect(self.on_garage_changed) + tblStuff.itemChanged.connect(self.on_stuff_changed) + # fire trigger once + self.on_garage_changed() + self.on_stuff_changed() - for col in range(self.ui.tableStuff.columnCount()): # optimize column width - self.ui.tableStuff.resizeColumnToContents(col) - self.ui.tableStuff.resizeRowsToContents() # optimize row height + optimizeTableLayout(self.ui.tableStuff) self.ui.tableStuff.setFocus() + self.opened_file = fileName + self.setWindowTitle(os.path.basename(fileName)) self.is_modified = False - self.opened_file = os.path.basename(fileName) - - if fileName: - self.setWindowTitle(self.opened_file) - else: - self.setWindowTitle(f"{qApp.applicationName()} {APP_VERSION} - {APP_AUTHOR}") def file_export(self): tblGarage = self.ui.tableGarage @@ -601,18 +726,20 @@ class MainWindow(QMainWindow): start_row = 0 worksheet.write_string(start_row, 0, QCoreApplication.translate("main", "Dimension of the garage")) + # table Garage headerts start_row = 1 - worksheet.write_string(start_row, COL_STUFF_LENGTH, QCoreApplication.translate("main","Length") + " [m]") worksheet.write_string(start_row, COL_STUFF_WIDTH, QCoreApplication.translate("main","Width") + " [m]") worksheet.write_string(start_row, COL_STUFF_HEIGHT, QCoreApplication.translate("main","Height") + " [m]") worksheet.write_string(start_row, COL_STUFF_COMMENT, QCoreApplication.translate("main","Comment")) - worksheet.set_column(0, 0, 25) - worksheet.set_column(1, 3, 10) - worksheet.set_column(4, 5, 20) - start_row = 2 + # TODO: check if this is not done later automatically + # worksheet.set_column(0, 0, 25) + # worksheet.set_column(1, 3, 10) + # worksheet.set_column(4, 5, 20) + # loop over table Garage + start_row = 2 for row in range(tblGarage.rowCount()): garage_length = tblGarage.item(0, 0).text() garage_width = tblGarage.item(0, 1).text() @@ -627,15 +754,16 @@ class MainWindow(QMainWindow): start_row = 4 worksheet.write_string(start_row, 0, QCoreApplication.translate("main", "Dimensions of the objects to be stored")) + # table Stuff headers start_row = 5 worksheet.write_string(start_row, COL_STUFF_LENGTH, QCoreApplication.translate("main","Length") + " [m]") worksheet.write_string(start_row, COL_STUFF_WIDTH, QCoreApplication.translate("main","Width") + " [m]") worksheet.write_string(start_row, COL_STUFF_HEIGHT, QCoreApplication.translate("main","Height") + " [m]") worksheet.write_string(start_row, COL_STUFF_WEIGHT, QCoreApplication.translate("main","Weight") + " [kg]") - worksheet.write_string(start_row, COL_STUFF_COMMENT, QCoreApplication.translate("main","Comment") ) + worksheet.write_string(start_row, COL_STUFF_COMMENT, QCoreApplication.translate("main","Comment")) - start_row = 6 # loop over table Stuff + start_row = 6 formula = "" row_idx = start_row for row in range(tblStuff.rowCount()): @@ -655,7 +783,12 @@ class MainWindow(QMainWindow): try: length = item_length.text() if length: - worksheet.write_number(start_row + row, COL_STUFF_LENGTH, float(length.replace(',', '.'))) + value = float(length.replace(',', '.')) + uom = item_length.data(Qt.UserRole) + # always convert to "m" for the export + if uom != "m": + value = convert_uom_to_length(value, uom, "m") + worksheet.write_number(start_row + row, COL_STUFF_LENGTH, value) # value except ValueError: pass @@ -663,7 +796,12 @@ class MainWindow(QMainWindow): try: width = item_width.text() if width: - worksheet.write_number(start_row + row, COL_STUFF_WIDTH, float(width.replace(',', '.'))) + value = float(width.replace(',', '.')) + uom = item_width.data(Qt.UserRole) + # always convert to "m" for the export + if uom != "m": + value = convert_uom_to_length(value, uom, "m") + worksheet.write_number(start_row + row, COL_STUFF_WIDTH, value) except ValueError: pass @@ -671,7 +809,12 @@ class MainWindow(QMainWindow): try: height = item_height.text() if height: - worksheet.write_number(start_row + row, COL_STUFF_HEIGHT, float(height.replace(',', '.'))) + value = float(height.replace(',', '.')) + uom = item_height.data(Qt.UserRole) + # always convert to "m" for the export + if uom != "m": + value = convert_uom_to_length(value, uom, "m") + worksheet.write_number(start_row + row, COL_STUFF_HEIGHT, value) except ValueError: pass @@ -679,7 +822,12 @@ class MainWindow(QMainWindow): try: weight = item_weight.text() if weight: - worksheet.write_number(start_row + row, COL_STUFF_WEIGHT, float(weight.replace(',', '.'))) + value = float(weight.replace(',', '.')) + uom = item_weight.data(Qt.UserRole) + # always convert to "kg" for the export + if uom != "kg": + value = convert_uom_to_mass(value, uom, "kg") + worksheet.write_number(start_row + row, COL_STUFF_WEIGHT, value) except ValueError: pass @@ -725,12 +873,17 @@ class MainWindow(QMainWindow): def on_garage_changed(self): self.is_modified = True + # print("on_garage_changed() calling calc_voluminae()") self.calc_voluminae() + # print("--") def on_stuff_changed(self): self.is_modified = True + # print("on_stuff_changed() calling calc_voluminae()") self.calc_voluminae() + # print("on_stuff_changed() calling calc_weight()") self.calc_weight() + # print("--") def get_garage_vol(self): tblGarage = self.ui.tableGarage @@ -779,7 +932,9 @@ class MainWindow(QMainWindow): return garage_vol def get_stuff_vol(self): + global DEFAULT_UOM_LENGTH tblStuff = self.ui.tableStuff + stuff_vol = 0 length = 0 width = 0 @@ -802,6 +957,8 @@ class MainWindow(QMainWindow): if item_length: try: length = float(item_length.text().replace(',', '.')) + if item_length.data(Qt.UserRole) and item_length.data(Qt.UserRole) != DEFAULT_UOM_LENGTH: + length = convert_uom_to_length(length, item_length.data(Qt.UserRole), DEFAULT_UOM_LENGTH) # convert to std unit except ValueError: length = 0 is_error = True @@ -809,6 +966,8 @@ class MainWindow(QMainWindow): if item_width: try: width = float(item_width.text().replace(',', '.')) + if item_width.data(Qt.UserRole) and item_width.data(Qt.UserRole) != DEFAULT_UOM_LENGTH: + width = convert_uom_to_length(width, item_width.data(Qt.UserRole), DEFAULT_UOM_LENGTH) # convert to std unit except ValueError: width = 0 is_error = True @@ -816,11 +975,13 @@ class MainWindow(QMainWindow): if item_height: try: height = float(item_height.text().replace(',', '.')) + if item_height.data(Qt.UserRole) and item_height.data(Qt.UserRole) != DEFAULT_UOM_LENGTH: + height = convert_uom_to_length(height, item_height.data(Qt.UserRole), DEFAULT_UOM_LENGTH) # convert to std unit except ValueError: height = 0 is_error = True - vol = length * width * height + vol = convert_uom_to_length(length, DEFAULT_UOM_LENGTH, "m") * convert_uom_to_length(width, DEFAULT_UOM_LENGTH, "m") * convert_uom_to_length(height, DEFAULT_UOM_LENGTH, "m") stuff_vol = round(stuff_vol + vol, 2) @@ -832,19 +993,21 @@ class MainWindow(QMainWindow): return stuff_vol def calc_voluminae(self): + # print("calc_voluminae()") + global DEFAULT_UOM_LENGTH tblGarage = self.ui.tableGarage # get garage vol + # print(" calc_voluminae() calling get_garage_vol()") garage_vol = self.get_garage_vol() # print(f"{garage_vol=}") - + # get stuff vol + # print(" calc_voluminae() calling get_stuff_vol()") stuff_vol = self.get_stuff_vol() - # print(f"{stuff_vol=}") # get free space free_vol = garage_vol - stuff_vol - # print(f"{free_vol=}") # display results self.ui.efVol_Garage.setText(f"{str(garage_vol).replace('.', ',')}") @@ -857,6 +1020,8 @@ class MainWindow(QMainWindow): self.ui.efVol_Free.setStyleSheet("") def calc_weight(self): + # print("calc_weight()") + global DEFAULT_UOM_MASS tblStuff = self.ui.tableStuff weight_sum = 0 @@ -865,33 +1030,32 @@ class MainWindow(QMainWindow): for row in range(row_count): weight = 0.0 - item = tblStuff.item(row, COL_STUFF_WEIGHT) - if item: + item_weight = tblStuff.item(row, COL_STUFF_WEIGHT) + if item_weight: try: - weight = float(str(item.text()).replace(',','.')) + weight = float(item_weight.text().replace(',', '.')) + if item_weight.data(Qt.UserRole) and item_weight.data(Qt.UserRole) != DEFAULT_UOM_MASS: + weight = convert_uom_to_mass(weight, item_weight.data(Qt.UserRole), DEFAULT_UOM_MASS) # convert to std unit except ValueError: - weight = 0.0 + weight = 0 - weight_sum = round(weight_sum + weight, 2) + weight_sum = round(weight_sum + convert_uom_to_mass(weight, DEFAULT_UOM_MASS, "kg"), 2) self.ui.efWeight.setText(f"{str(weight_sum).replace('.', ',')}") def store_selected_language(self, menu): """ Stores selected menu labels to ini-file. """ # [print(action.text()) for action in menu.actions() if action.isChecked()] - global CONFIG + global CONFIG, DIR_CURRENT, APP_INI_FILE - 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 + with open(APP_INI_FILE, 'w') as configfile: # save CONFIG.write(configfile) if language == "Deutsch": @@ -906,7 +1070,195 @@ class MainWindow(QMainWindow): TRANSLATOR.load("dummy") self.retranslateUi() + optimizeTableLayout(self.ui.tableStuff) + self.ui.tableStuff.setFocus() + def store_selected_uom_length(self, menu): + """ Stores selected uom to ini-file. """ + # [print(action.text()) for action in menu.actions() if action.isChecked()] + global CONFIG, APP_INI_FILE, DEFAULT_UOM_LENGTH + + selected_uom = "" + for action in menu.actions(): + if action.isChecked(): + selected_uom = action.text() + + CONFIG['DEFAULT']['UOM_length'] = selected_uom + + with open(APP_INI_FILE, 'w') as configfile: # save + CONFIG.write(configfile) + + DEFAULT_UOM_LENGTH = selected_uom + self.ui.tableStuff.default_uom_length = DEFAULT_UOM_LENGTH + + if table_has_items(self.ui.tableStuff): + msg = QCoreApplication.translate("main", "Do you want to convert all existing stuff of type length to the new unit of measurement (UOM)") + " " + selected_uom + "?" + reply = QMessageBox.question(self, QCoreApplication.translate("main", "UOM conversion"), msg, \ + QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) + if reply == QMessageBox.No: + self.flag_deviating_uoms() + self.retranslateUi() + optimizeTableLayout(self.ui.tableStuff) + return False + + self.convert_length_cells_to_std_uom() + + self.retranslateUi() + optimizeTableLayout(self.ui.tableStuff) + return True + + def store_selected_uom_mass(self, menu): + """ Stores selected uom to ini-file. """ + # [print(action.text()) for action in menu.actions() if action.isChecked()] + global CONFIG, APP_INI_FILE, DEFAULT_UOM_MASS + + selected_uom = "" + for action in menu.actions(): + if action.isChecked(): + selected_uom = action.text() + + CONFIG['DEFAULT']['UOM_mass'] = selected_uom + + with open(APP_INI_FILE, 'w') as configfile: # save + CONFIG.write(configfile) + + DEFAULT_UOM_MASS = selected_uom + self.ui.tableStuff.default_uom_mass = DEFAULT_UOM_MASS + + if table_has_items(self.ui.tableStuff): + msg = QCoreApplication.translate("main", "Do you want to convert all existing stuff of type mass to the new unit of measurement (UOM)") + " " + selected_uom + "?" + reply = QMessageBox.question(self, QCoreApplication.translate("main", "UOM conversion"), msg, \ + QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) + if reply == QMessageBox.No: + self.flag_deviating_uoms() + self.retranslateUi() + optimizeTableLayout(self.ui.tableStuff) + return False + + self.convert_mass_cells_to_std_uom() + self.retranslateUi() + optimizeTableLayout(self.ui.tableStuff) + return True + + def convert_length_cells_to_std_uom(self): + print("convert_length_cells_to_std_uom() called") + global DEFAULT_UOM_LENGTH + + tblGarage = self.ui.tableGarage + tblStuff = self.ui.tableStuff + + # loop over all items + # disable calc trigger for any cell! + tblGarage.itemChanged.disconnect() + tblStuff.itemChanged.disconnect() + + for row in range(tblStuff.rowCount()): + for col in range(tblStuff.columnCount()): + item = tblStuff.item(row, col) + if item: + if col in self.cols_with_uom_length: + # read current value + value = float(item.text().replace(",", ".")) + + # read current UOM + uom = item.data(Qt.UserRole) + + # convert to new value + if uom != DEFAULT_UOM_LENGTH: + new_val = convert_uom_to_length(value, uom, DEFAULT_UOM_LENGTH) + # set new value + item.setText(str(new_val).replace(".", ",")) + + # set new std UOM + tblStuff.setUOM(item, DEFAULT_UOM_LENGTH) + + # enable calc trigger + tblGarage.itemChanged.connect(self.on_garage_changed) + tblStuff.itemChanged.connect(self.on_stuff_changed) + # fire trigger once + self.on_garage_changed() + self.on_stuff_changed() + + def convert_mass_cells_to_std_uom(self): + print("convert_mass_cells_to_std_uom() called") + global DEFAULT_UOM_MASS + + tblGarage = self.ui.tableGarage + tblStuff = self.ui.tableStuff + + # loop over all items + # disable calc trigger for any cell! + tblGarage.itemChanged.disconnect() + tblStuff.itemChanged.disconnect() + + for row in range(tblStuff.rowCount()): + for col in range(tblStuff.columnCount()): + item = tblStuff.item(row, col) + if item: + if col in self.cols_with_uom_mass: + # read current value + value = float(item.text().replace(",", ".")) + + # read current UOM + uom = item.data(Qt.UserRole) + + if uom != DEFAULT_UOM_MASS: + new_val = convert_uom_to_mass(value, uom, DEFAULT_UOM_MASS) + # set new value + item.setText(str(new_val).replace(".", ",")) + + # set new std UOM + tblStuff.setUOM(item, DEFAULT_UOM_MASS) + + # enable calc trigger + tblGarage.itemChanged.connect(self.on_garage_changed) + tblStuff.itemChanged.connect(self.on_stuff_changed) + # fire trigger once + self.on_garage_changed() + self.on_stuff_changed() + + def flag_deviating_uoms(self): + print("flag_deviating_uoms() called") + global DEFAULT_UOM_LENGTH, DEFAULT_UOM_MASS + + tblGarage = self.ui.tableGarage + tblStuff = self.ui.tableStuff + + # loop over all items + # disable calc trigger for any cell! + tblGarage.itemChanged.disconnect() + tblStuff.itemChanged.disconnect() + + for row in range(tblStuff.rowCount()): + for col in range(tblStuff.columnCount()): + item = tblStuff.item(row, col) + if item: + uom = item.data(Qt.UserRole) + if col in self.cols_with_uom_length: + # read current UOM + if uom != DEFAULT_UOM_LENGTH: + # color the cell + item.setData(Qt.BackgroundRole, QColor(Qt.yellow)) + item.setToolTip(f"[{uom}]") + else: + # uncolor the cell + item.setData(Qt.BackgroundRole, None) + item.setToolTip(None) + + elif col in self.cols_with_uom_mass: + # read current UOM + if uom != DEFAULT_UOM_MASS: + # color the cell + item.setData(Qt.BackgroundRole, QColor(Qt.yellow)) + item.setToolTip(f"[{uom}]") + else: + # uncolor the cell + item.setData(Qt.BackgroundRole, None) + item.setToolTip(None) + + # enable calc trigger + tblGarage.itemChanged.connect(self.on_garage_changed) + tblStuff.itemChanged.connect(self.on_stuff_changed) #################################################################### diff --git a/src/utils.py b/src/utils.py index c4f9ae8..2e91950 100644 --- a/src/utils.py +++ b/src/utils.py @@ -31,7 +31,10 @@ def resource_path(relative_path): return os.path.join(base_path, relative_path) -def show_about(): +def show_about(ini_file="", opened_file=""): + if not opened_file: + opened_file = "" + qApp = QApplication.instance() msg = QMessageBox() msg.setIconPixmap(QPixmap(resource_path(APP_ICON))) @@ -44,7 +47,9 @@ def show_about(): "
" + qApp.copyright + "
" \ "
" + qApp.website + "

" - text = text + "

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

" + text = text + "

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

" + text = text + "

" + qApp.translate("utils", "Currently opened file") + ":
" + opened_file + "

" + text = text + "

" + qApp.translate("utils", "Location of the Ini-file") + ":
" + ini_file + "

" text = text + "

Python " + qApp.translate("utils", "Version") + ": " + f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro} {sys.version_info.releaselevel}" text = text + "
" + f"{sys.executable}" + "
" text = text + "
Qt " + qApp.translate("utils", "Version") + ": " + f"{QtCore.__version__}" @@ -84,7 +89,7 @@ def fit_col_widths(table, worksheet): sel_rows = [] for sel_index in sel_rows_idx: sel_rows.append(sel_index.row()) - + for col_ind in range(model.columnCount()): cur_item_length = 0 lengths = [] @@ -95,7 +100,7 @@ def fit_col_widths(table, worksheet): if not table.isColumnHidden(col_ind): # export only visible column headers cur_item_length = len(col_header_text) lengths.append(cur_item_length) - + # get columns data lengths row = 1 # iterate over all rows @@ -129,3 +134,25 @@ def fit_col_widths(table, worksheet): if col_width > STD_COL_WIDTH: # anticipated default size worksheet.set_column(col_ind, col_ind, col_width*1.25) col_ind += 1 + +def convert_uom_to_length(val, unit_in, unit_out): + SI = {'mm':0.001, 'cm':0.01, 'm':1.0} + return val*SI[unit_in]/SI[unit_out] + +def convert_uom_to_mass(val, unit_in, unit_out): + SI = {'g':0.001, 'kg':1.0} + return val*SI[unit_in]/SI[unit_out] + +def optimizeTableLayout(table): + for col in range(table.columnCount()): # optimize column width + table.resizeColumnToContents(col) + table.resizeRowsToContents() # optimize row height + +def table_has_items(tablewidget): + for row in range(tablewidget.rowCount()): + for col in range(tablewidget.columnCount()): + item = tablewidget.item(row, col) + if item: + return True + + return False \ No newline at end of file diff --git a/ui/dlg_uom.ui b/ui/dlg_uom.ui new file mode 100644 index 0000000..711d806 --- /dev/null +++ b/ui/dlg_uom.ui @@ -0,0 +1,92 @@ + + + Dialog + + + + 0 + 0 + 222 + 100 + + + + Dialog + + + + + + + + Value: + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + UOM: + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + Dialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Dialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +