PyInstaller
Gestern hat es dann noch funktioniert, musste 2 Daten adden 
Nun erhalte ich ne exe mit 330MB
Kann man diese eventuell noch verkleinern?
Vielleicht nur die notwendigen Bibliotheken hinzufügen? Oder beim Import etwas weglassen?
Ist jetzt aber nur mehr der schönheitshalber

Nun erhalte ich ne exe mit 330MB

Kann man diese eventuell noch verkleinern?
Vielleicht nur die notwendigen Bibliotheken hinzufügen? Oder beim Import etwas weglassen?
Ist jetzt aber nur mehr der schönheitshalber

Dann schau doch mal nach, was da alles in das Archiv wandert.
Während das Programm läuft, wird der Inhalt ja temporär entpackt.
Wenn ich mich richtig erinnere, hat ein Programm mit QT und Python-Interpreter unter 30 MB. Da scheinen bei dir also viel mehr Dinge eingesammelt zu werden. Aber das können wir ohne Glaskugel nicht beantworten.
Während das Programm läuft, wird der Inhalt ja temporär entpackt.
Wenn ich mich richtig erinnere, hat ein Programm mit QT und Python-Interpreter unter 30 MB. Da scheinen bei dir also viel mehr Dinge eingesammelt zu werden. Aber das können wir ohne Glaskugel nicht beantworten.
Okay wenn es 50MB werden wäre es also auch kein Problem.sparrow hat geschrieben: Donnerstag 25. Juli 2019, 08:03 Dann schau doch mal nach, was da alles in das Archiv wandert.
Während das Programm läuft, wird der Inhalt ja temporär entpackt.
Wenn ich mich richtig erinnere, hat ein Programm mit QT und Python-Interpreter unter 30 MB. Da scheinen bei dir also viel mehr Dinge eingesammelt zu werden. Aber das können wir ohne Glaskugel nicht beantworten.
Habe es auch mal als
Code: Alles auswählen
--onedir

Ich bin nicht sehr Bewandert mit Windows, deshalb meine Frage an dich bzw. an alle, da ich --onedir gemacht habe sollten hier ja alle Daten enthalten sein oder?
Diese könnte ich hier Posten?
Nochmal: Wir haben keine Glaskugel. Wir wissen nicht, was du da tust und wie du es tust. Wir wissen nicht, welche Daten da alle verwendet werden.
Was ich dir sagen kann: Python 3, PyQt5 und plotly mit pyinstaller gewrappt ergeben eine .exe von rund 20 MB größe.
Zeig doch mal deine .spec-Datei.
Dort solltest du übrigens auch die Daten eintragen, die du da irgendwie per Parameterübergabe an pyinstaller überigst.
Was ich dir sagen kann: Python 3, PyQt5 und plotly mit pyinstaller gewrappt ergeben eine .exe von rund 20 MB größe.
Zeig doch mal deine .spec-Datei.
Dort solltest du übrigens auch die Daten eintragen, die du da irgendwie per Parameterübergabe an pyinstaller überigst.
Bitteschön, das ist der gesamte Inhalt der .spec Datei
Code: Alles auswählen
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(['ascFileReader_V2.5.py'],
pathex=['C:\\Users\\stf83wi\\Desktop\\Test_asc'],
binaries=[],
datas=[('C:\\Python37\\Lib\\site-packages\\plotly\\package_data\\templates\\plotly.json', 'plotly\\package_data\\templates'), ('C:\\Python37\\Lib\\site-packages\\plotly\\package_data\\plotly.min.js', 'plotly\\package_data')],
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
[],
exclude_binaries=True,
name='ascFileReader_V2.5',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=False , icon='asc.ico')
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='ascFileReader_V2.5')
Mir fällt da zumindest nichts ins Auge, was sofort falsch aussieht.
Dann lass pyinstaller einmal ohne --one* Option laufen und schau mal, was alles in "dist" ankommt. Da liegt dann auch die entsprechend kleine .exe, aber der Rest ist dort nicht mit eingepackt. Mit PyQt5 sind das bei mir 72 MB.
So solltest du sehen können, was da so riesig ist.
Dann lass pyinstaller einmal ohne --one* Option laufen und schau mal, was alles in "dist" ankommt. Da liegt dann auch die entsprechend kleine .exe, aber der Rest ist dort nicht mit eingepackt. Mit PyQt5 sind das bei mir 72 MB.
So solltest du sehen können, was da so riesig ist.
Wenn ich es ohne onedir exportiere, ist der Ordner "dist" ganze 146MB groß, was sehr erfreulich wäre wenn die exe starten würde
Die .spec Datei ergibt das gleiche wie vorhin:
Allzu viele große Dateien gibt es da nicht, eine mit 27MB und dann kommt schon die exe mit 11MB

Die .spec Datei ergibt das gleiche wie vorhin:
Code: Alles auswählen
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(['ascFileReader_V2.5.py'],
pathex=['C:\\Users\\stf83wi\\Desktop\\asc_Test'],
binaries=[],
datas=[('C:\\Python37\\Lib\\site-packages\\plotly\\package_data\\templates\\plotly.json', 'plotly\\package_data\\templates'), ('C:\\Python37\\Lib\\site-packages\\plotly\\package_data\\plotly.min.js', 'plotly\\package_data')],
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
[],
exclude_binaries=True,
name='ascFileReader_V2.5',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=True , icon='asc.ico')
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='ascFileReader_V2.5')
Hier sind:
Das ist jetzt mal ein Auszug.
Oder kann jemand mal den Code selbst mit pyinstaller zu einer exe machen? Vielleicht ergibt sich hier eine Erkenntnis?
Die exe Datei hat legendlich 23MBmkl_core.dll mit 68MB
mkl_avx512.dll mit 59MB
mkl_avx2.dll mit 45MB
mkl_avx.dll mit 42MB
mkl_mc3.dll mit 41MB
mkl_mc.dll mit 40MB
mkl_def.dll mit 34MB
Das ist jetzt mal ein Auszug.
Oder kann jemand mal den Code selbst mit pyinstaller zu einer exe machen? Vielleicht ergibt sich hier eine Erkenntnis?
Offensichtlich verwendest du Anaconda und numpy:
https://stackoverflow.com/questions/492 ... -necessary
https://stackoverflow.com/questions/492 ... -necessary
Hm interessant, dann installiere ich mir Miniconda3 noch zusätzlich, vielleicht schaut es dann hier besser aus.
Alles erledigt, habe es mit
exportieren lassen.
Das stand alles in der Comandline
Nun ist der Ordner 108MB groß!
Nach dem starten in der Comandline erscheint folgende Fehlermeldung
Was ich aber nicht ganz verstehe, weil ich ja mit
diese dll explizit hinzugefügt habe!
Alles erledigt, habe es mit
Code: Alles auswählen
pyinstaller ascFileReader_V2.5.py --add-data "C:\Python37\Lib\site-packages\plotly\package_data\templates\plotly.json;plotly\package_data\templates" --add-data "C:\Python37\Lib\site-packages\plotly\package_data\plotly.min.js;plotly\package_data" --add-data "C:\Program Files\Anaconda3\Lib\site-packages\PyQt5\Qt\bin\Qt5Core.dll;bin\Qt\PyQt5" --icon=asc.ico --onedir
Das stand alles in der Comandline
Code: Alles auswählen
144 INFO: PyInstaller: 3.5
144 INFO: Python: 3.7.4
147 INFO: Platform: Windows-10-10.0.17763-SP0
151 INFO: wrote C:\Users\stf83wi\Desktop\asc_Test\ascFileReader_V2.5.spec
153 INFO: UPX is not available.
159 INFO: Extending PYTHONPATH with paths
['C:\\Users\\stf83wi\\Desktop\\asc_Test',
'C:\\Users\\stf83wi\\Desktop\\asc_Test']
159 INFO: checking Analysis
162 INFO: Building Analysis because Analysis-00.toc is non existent
162 INFO: Initializing module dependency graph...
173 INFO: Initializing module graph hooks...
178 INFO: Analyzing base_library.zip ...
3616 INFO: running Analysis Analysis-00.toc
3620 INFO: Adding Microsoft.Windows.Common-Controls to dependent assemblies of final executable
required by c:\python37\python.exe
6380 INFO: Caching module hooks...
6387 INFO: Analyzing C:\Users\stf83wi\Desktop\asc_Test\ascFileReader_V2.5.py
6505 INFO: Processing pre-find module path hook distutils
7116 INFO: Processing pre-find module path hook site
7117 INFO: site: retargeting to fake-dir 'c:\\python37\\lib\\site-packages\\PyInstaller\\fake-modules'
10231 INFO: Processing pre-safe import module hook six.moves
42808 INFO: Loading module hooks...
42808 INFO: Loading module hook "hook-distutils.py"...
42817 INFO: Loading module hook "hook-encodings.py"...
42982 INFO: Loading module hook "hook-matplotlib.backends.py"...
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "c:\python37\lib\site-packages\matplotlib\__init__.py", line 138, in <module>
from . import cbook, rcsetup
File "c:\python37\lib\site-packages\matplotlib\cbook\__init__.py", line 31, in <module>
import numpy as np
ModuleNotFoundError: No module named 'numpy'
43217 INFO: Loading module hook "hook-matplotlib.py"...
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "c:\python37\lib\site-packages\matplotlib\__init__.py", line 138, in <module>
from . import cbook, rcsetup
File "c:\python37\lib\site-packages\matplotlib\cbook\__init__.py", line 31, in <module>
import numpy as np
ModuleNotFoundError: No module named 'numpy'
43472 INFO: Loading module hook "hook-pandas.py"...
43723 INFO: Loading module hook "hook-pkg_resources.py"...
44224 INFO: Processing pre-safe import module hook win32com
Traceback (most recent call last):
File "<string>", line 2, in <module>
ModuleNotFoundError: No module named 'win32com'
44361 INFO: Processing pre-safe import module hook win32com
Traceback (most recent call last):
File "<string>", line 2, in <module>
ModuleNotFoundError: No module named 'win32com'
44635 INFO: Loading module hook "hook-pydoc.py"...
44637 INFO: Loading module hook "hook-PyQt5.py"...
44923 INFO: Loading module hook "hook-PyQt5.QtCore.py"...
46724 INFO: Loading module hook "hook-PyQt5.QtGui.py"...
49154 INFO: Loading module hook "hook-PyQt5.QtWidgets.py"...
52813 INFO: Loading module hook "hook-pytz.py"...
53024 INFO: Loading module hook "hook-sqlite3.py"...
53198 INFO: Loading module hook "hook-sysconfig.py"...
53200 INFO: Loading module hook "hook-xml.dom.domreg.py"...
53201 INFO: Loading module hook "hook-xml.py"...
53306 INFO: Looking for ctypes DLLs
53338 INFO: Analyzing run-time hooks ...
53352 INFO: Including run-time hook 'pyi_rth_mplconfig.py'
53357 INFO: Including run-time hook 'pyi_rth_mpldata.py'
53358 INFO: Including run-time hook 'pyi_rth_pkgres.py'
53360 INFO: Including run-time hook 'pyi_rth_pyqt5.py'
53398 INFO: Looking for dynamic libraries
104588 INFO: Looking for eggs
104588 INFO: Using Python library c:\python37\python37.dll
104591 INFO: Found binding redirects:
[]
104611 INFO: Warnings written to C:\Users\stf83wi\Desktop\asc_Test\build\ascFileReader_V2.5\warn-ascFileReader_V2.5.txt
104922 INFO: Graph cross-reference written to C:\Users\stf83wi\Desktop\asc_Test\build\ascFileReader_V2.5\xref-ascFileReader_V2.5.html
105025 INFO: Appending 'datas' from .spec
105038 INFO: checking PYZ
105038 INFO: Building PYZ because PYZ-00.toc is non existent
105039 INFO: Building PYZ (ZlibArchive) C:\Users\stf83wi\Desktop\asc_Test\build\ascFileReader_V2.5\PYZ-00.pyz
108859 INFO: Building PYZ (ZlibArchive) C:\Users\stf83wi\Desktop\asc_Test\build\ascFileReader_V2.5\PYZ-00.pyz completed successfully.
108930 INFO: checking PKG
108930 INFO: Building PKG because PKG-00.toc is non existent
108933 INFO: Building PKG (CArchive) PKG-00.pkg
108981 INFO: Building PKG (CArchive) PKG-00.pkg completed successfully.
108984 INFO: Bootloader c:\python37\lib\site-packages\PyInstaller\bootloader\Windows-32bit\run.exe
108984 INFO: checking EXE
108985 INFO: Building EXE because EXE-00.toc is non existent
108985 INFO: Building EXE from EXE-00.toc
108999 INFO: Copying icons from ['asc.ico']
109001 INFO: Writing RT_GROUP_ICON 0 resource with 20 bytes
109003 INFO: Writing RT_ICON 1 resource with 67624 bytes
109010 INFO: Appending archive to EXE C:\Users\stf83wi\Desktop\asc_Test\build\ascFileReader_V2.5\ascFileReader_V2.5.exe
109022 INFO: Building EXE from EXE-00.toc completed successfully.
109028 INFO: checking COLLECT
109029 INFO: Building COLLECT because COLLECT-00.toc is non existent
109034 INFO: Building COLLECT COLLECT-00.toc
113267 INFO: Building COLLECT COLLECT-00.toc completed successfully.
Nach dem starten in der Comandline erscheint folgende Fehlermeldung
Code: Alles auswählen
PS C:\Users\stf83wi\Desktop\asc_Test\dist\ascFileReader_V2.5> .\ascFileReader_V2.5.exe
Traceback (most recent call last):
File "ascFileReader_V2.5.py", line 5, in <module>
File "c:\python37\lib\site-packages\PyInstaller\loader\pyimod03_importers.py", line 627, in exec_module
exec(bytecode, module.__dict__)
File "site-packages\PyQt5\__init__.py", line 41, in <module>
File "site-packages\PyQt5\__init__.py", line 33, in find_qt
ImportError: unable to find Qt5Core.dll on PATH
[19640] Failed to execute script ascFileReader_V2.5
Code: Alles auswählen
--add-data "C:\Program Files\Anaconda3\Lib\site-packages\PyQt5\Qt\bin\Qt5Core.dll;bin\Qt\PyQt5"
Numpy habe ich mitsparrow hat geschrieben: Freitag 26. Juli 2019, 09:11 Offensichtlich verwendest du Anaconda und numpy:
https://stackoverflow.com/questions/492 ... -necessary
Code: Alles auswählen
pip uninstall numpy
- __blackjack__
- User
- Beiträge: 14045
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
@JohannX: Ein paar Anmerkungen zum Quelltext.
`qtpy` macht keinen Sinn. Du importierst ja auch zusätzlich noch Sachen aus `PyQt5` (von denen dann nur ein Name tatsächlich verwendet wird.) Und um eine EXE zu erstellen wo alles drin ist, *musst* Du Dich ja für eine Python-Qt-Anbindung entscheiden.
Faustregel zu Kommentaren: Die sollten nicht sagen *was* gemacht wird, denn das steht da ja bereits als Code, sondern *warum* das so gemacht wird – sofern das nicht offensichtlich ist. Vor Importen zu kommentieren „# importieren“ bringt dem Leser absolut keinen Mehrwert, genau so wie „# Klasse MainWindow samt Konstruktor“ vor der ``class``-Definition von `MainWindow` keinerlei ”Nährwert” hat.
Auch `app` und `window` sollten keine modulglobalen Variablen sein.
Die ganzen Methoden die aufgrund einer `QCheckBox` den Text 'Yes' oder 'No' setzen sind von der Struktur her alle gleich. Da sollte man *eine* Methode für schreiben, welche die beiden Unterschiede als Argumente übergeben bekommt.
`choose_button` ist ein guter Name für den Button selbst, aber nicht für die Methode die darauf hin aufgerufen wird. Funktionen und Methoden werden üblicherweise nach der Tätigkeit benannt, die sie ausführen. Bei Rückrufen ist auch `on_<event_description>()` eine gängige Konvention.
Das `file`-Attribut wird nie an eine Datei gebunden, sondern immer an einen Datei*namen*. Und statt `self.file` und `self.file2` (und `choose_button` und `choose_button_2`) zu haben, sollte man die Namen so wählen das man weiss *was* für Dateinamen das sind (und *was* da ausgewählt wird.)
Ausserhalb der `__init__()` sollten keine neuen Attribute eingeführt werden. Wenn `self.file2` tatsächlich irgendwo verwendet werden würde, dann käme es zu einem `AttributeError` wenn man vorher kein Speicherverzeichnis ausgewählt hat.
Laufwerk C:\ macht das ganze nicht wirklich portabel, obwohl da ansonsten ja nicht wirklich etwas von Windows abhängt.
`self.var` ist schlecht benannt. Namen sollen dem Leser vermitteln was der Wert im Programm bedeutet, nicht das es irgendeine Variable ist (sofern ich die Abkürzung hier richtig geraten habe).
Code der aufgrund einer Bedingung etwas mit einem literalen `True` macht falls die Bedingung zutrifft, und mit einem literalen `False` falls die Bedingung nicht zutrifft, ist unnötig kompliziert, weil man da auch gleich die Bedingung selbst hätte verwenden können. `isChecked()` liefert `True` oder `False`, also kann man das hier:
viel einfacher ausdrücken:
Letztlich ist aber `self.var` auch komplett überflüssig, denn man kann `isChecked()` auch einfach dort aufrufen wo man den Zustand verwendet, statt ihn in einem nichtssagenden Attribut zwischenzuspeichern.
`None` als einziger Ausdruck in einem ``if``-Zweig macht keinen Sinn. Syntaktisch ist dafür das Schlüsselwort ``pass`` vorgesehen, aber auch das macht in einem ``if``-Zweig keinen Sinn. Wenn man einen ``if``- oder ``else``-Zweig nicht braucht, dann lässt man ihn einfach weg.
Beim Export in eine HTML-Datei macht es keinen Sinn das `Figure`-Objekt oder den `DataFrame` an das Hauptfenster zu binden. Das sind einfach nur lokale Namen.
Die Kommentare in der Methode sind teilweise auch wieder sehr überflüssig. Noch schlimmer sind falsche Kommentare, weil der Leser dann nicht weiss wem er glauben soll: dem Kommentar oder dem Code. Bei „# Sortiert die Daten“ wird überhaupt nichts sortiert. Ist da nun der Kommentar falsch oder fehlt da was im Code wo etwas sortiert wird?
Ähm, Nein. Einfach nur Nein. Das ist nicht nur nicht portabel wegen dem \ sondern auch unter Windows selbst können Pfadkomponenten auch mit einem / getrennt werden. Pfade sind keine einfachen Zeichenketten sondern unterliegen besonderen Regeln. Dafür gibt es die Funktionen in `os.path` oder `pathlib.Path`. In diesem Fall beispielsweise:
Beim Hinzufügen der einzelnen Daten zum Diagramm wiederholt sich wieder fast der selbe Code für alle sechs Messgrössen. Man sollte hier die Unterschiede in eine Datenstruktur herausziehen und den Code nur einmal, in einer Schleife, stehen haben.
Der Kommentar „# Sauberes Schließen des Programmes“ ist zu einem harten `sys.exit()` falsch. Sauber wäre es die Qt-Hauptschleife zu verlassen damit das `sys.exit()` im Hauptprogramm den Rückgabewert von `exec_()` verwenden kann. Und das sollte man einfach durch das schliessen des Fensters erreichen können, denn der Normalfall bei Qt-Anwendungen ist, dass die sich beenden wenn das letzte Fenster geschlossen ist.
Ungetestet:
`qtpy` macht keinen Sinn. Du importierst ja auch zusätzlich noch Sachen aus `PyQt5` (von denen dann nur ein Name tatsächlich verwendet wird.) Und um eine EXE zu erstellen wo alles drin ist, *musst* Du Dich ja für eine Python-Qt-Anbindung entscheiden.
Faustregel zu Kommentaren: Die sollten nicht sagen *was* gemacht wird, denn das steht da ja bereits als Code, sondern *warum* das so gemacht wird – sofern das nicht offensichtlich ist. Vor Importen zu kommentieren „# importieren“ bringt dem Leser absolut keinen Mehrwert, genau so wie „# Klasse MainWindow samt Konstruktor“ vor der ``class``-Definition von `MainWindow` keinerlei ”Nährwert” hat.
Auch `app` und `window` sollten keine modulglobalen Variablen sein.
Die ganzen Methoden die aufgrund einer `QCheckBox` den Text 'Yes' oder 'No' setzen sind von der Struktur her alle gleich. Da sollte man *eine* Methode für schreiben, welche die beiden Unterschiede als Argumente übergeben bekommt.
`choose_button` ist ein guter Name für den Button selbst, aber nicht für die Methode die darauf hin aufgerufen wird. Funktionen und Methoden werden üblicherweise nach der Tätigkeit benannt, die sie ausführen. Bei Rückrufen ist auch `on_<event_description>()` eine gängige Konvention.
Das `file`-Attribut wird nie an eine Datei gebunden, sondern immer an einen Datei*namen*. Und statt `self.file` und `self.file2` (und `choose_button` und `choose_button_2`) zu haben, sollte man die Namen so wählen das man weiss *was* für Dateinamen das sind (und *was* da ausgewählt wird.)
Ausserhalb der `__init__()` sollten keine neuen Attribute eingeführt werden. Wenn `self.file2` tatsächlich irgendwo verwendet werden würde, dann käme es zu einem `AttributeError` wenn man vorher kein Speicherverzeichnis ausgewählt hat.
Laufwerk C:\ macht das ganze nicht wirklich portabel, obwohl da ansonsten ja nicht wirklich etwas von Windows abhängt.
`self.var` ist schlecht benannt. Namen sollen dem Leser vermitteln was der Wert im Programm bedeutet, nicht das es irgendeine Variable ist (sofern ich die Abkürzung hier richtig geraten habe).
Code der aufgrund einer Bedingung etwas mit einem literalen `True` macht falls die Bedingung zutrifft, und mit einem literalen `False` falls die Bedingung nicht zutrifft, ist unnötig kompliziert, weil man da auch gleich die Bedingung selbst hätte verwenden können. `isChecked()` liefert `True` oder `False`, also kann man das hier:
Code: Alles auswählen
if self.ui.html_auto_open.isChecked():
self.var = True
else:
self.var = False
Code: Alles auswählen
self.var = self.ui.html_auto_open.isChecked()
`None` als einziger Ausdruck in einem ``if``-Zweig macht keinen Sinn. Syntaktisch ist dafür das Schlüsselwort ``pass`` vorgesehen, aber auch das macht in einem ``if``-Zweig keinen Sinn. Wenn man einen ``if``- oder ``else``-Zweig nicht braucht, dann lässt man ihn einfach weg.
Beim Export in eine HTML-Datei macht es keinen Sinn das `Figure`-Objekt oder den `DataFrame` an das Hauptfenster zu binden. Das sind einfach nur lokale Namen.
Die Kommentare in der Methode sind teilweise auch wieder sehr überflüssig. Noch schlimmer sind falsche Kommentare, weil der Leser dann nicht weiss wem er glauben soll: dem Kommentar oder dem Code. Bei „# Sortiert die Daten“ wird überhaupt nichts sortiert. Ist da nun der Kommentar falsch oder fehlt da was im Code wo etwas sortiert wird?
Code: Alles auswählen
# Schneidet den nur Filenamen raus und speichert diesen in eine neue Variable
file_title = self.asc_filename[self.asc_filename.rfind('\\')+1:len(self.asc_filename)]
Code: Alles auswählen
file_title = os.path.basename(self.asc_filename)
Der Kommentar „# Sauberes Schließen des Programmes“ ist zu einem harten `sys.exit()` falsch. Sauber wäre es die Qt-Hauptschleife zu verlassen damit das `sys.exit()` im Hauptprogramm den Rückgabewert von `exec_()` verwenden kann. Und das sollte man einfach durch das schliessen des Fensters erreichen können, denn der Normalfall bei Qt-Anwendungen ist, dass die sich beenden wenn das letzte Fenster geschlossen ist.
Ungetestet:
Code: Alles auswählen
#!/usr/bin/env python3
from collections import namedtuple
import os
import sys
from functools import partial
import pandas as pd
import plotly
from plotly import graph_objects as go
from PyQt5 import QtWidgets
from ui.mainwindow import Ui_asc_File_Reader
MeasurementCategory = namedtuple(
'MeasurementCategory', 'name description id convert'
)
#
# TODO Die `lambda`\s lassen sich bestimmt auch als Daten ausdrücken.
#
MEASUREMENT_CATEGORIES = [
MeasurementCategory(
'ubatlogger',
'Ubatlogger',
'800200BA',
lambda x: x * (5 / 4095) * 8.655106
),
MeasurementCategory(
'pressure',
'Pressure',
'80083DCE',
lambda x: (x - 2) * 0.00257038 - 0.5
),
MeasurementCategory(
'temperature',
'Temperature',
'80083DA5',
lambda x: (x - 1) * 0.125 - 73.025
),
MeasurementCategory(
'concentration',
'Concentration',
'80083DEF',
lambda x: (x - 1) * 0.05 - 20
),
MeasurementCategory(
'level',
'Level',
'80083DD6',
lambda x: (x - 1) * 0.125
),
MeasurementCategory(
'speedofsound',
'Speed of Sound',
'80083E09',
lambda x: (x - 1) * 0.25 + 1000
),
]
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle('Test')
self.ui = Ui_asc_File_Reader()
self.ui.setupUi(self)
self.ui.choose_button.clicked.connect(self.choose_asc_filename)
#
# TODO Die GUI-Elemente hier erstellen, statt sie hart kodiert in der
# *.ui-Datei anzulegen.
#
for number, category in enumerate(MEASUREMENT_CATEGORIES, 1):
button = getattr(self.ui, category.name)
button.clicked.connect(
partial(self.on_check_clicked, button, number)
)
self.ui.choose_button_2.clicked.connect(self.choose_target_path)
self.ui.export_to_html.clicked.connect(self.export_to_html)
self.ui.exit.clicked.connect(self.close)
self.asc_filename = None
self.target_path = None
def choose_asc_filename(self):
#
# TODO Nicht unnötig von Windows abhängig machen.
#
self.asc_filename, _ = QtWidgets.QFileDialog.getOpenFileName(
self, 'Open file', 'c:\\', 'Image files (*.asc)'
)
self.ui.Info1.setText(self.asc_filename)
def on_check_clicked(self, checkable, number):
getattr(self.ui, f'pass{number}').setText(
'YES' if checkable.isChecked() else 'NO'
)
def choose_target_path(self):
self.target_path = QtWidgets.QFileDialog.getExistingDirectory()
self.ui.Info2.setText(self.target_path)
def export_to_html(self):
if self.asc_filename is not None:
figure = go.Figure()
#
# TODO `skipinitialspace` kann den Zugriff auf die Spalten
# vielleicht etwas weniger fehleranfällig machen. Auf jeden Fall
# braucht man dann nicht mehr Leerzeichen bei den Namen zählen.
#
data = pd.read_csv(
self.asc_filename,
skiprows=5,
delimiter=';',
#
# FIXME Weglassen oder reparieren, denn das hat offensichtlich
# keinen Effekt.
#
converters={' DATA L': partial(int, base=16)},
)
file_title = os.path.basename(self.asc_filename)
font = dict(
family='Courier New, monospace', size=20, color='#7f7f7f'
)
figure.update_layout(
title=go.layout.Title(text=file_title, xref='paper', x=0),
xaxis=go.layout.XAxis(
title=go.layout.xaxis.Title(text='Second [s]', font=font)
),
yaxis=go.layout.YAxis(
title=go.layout.yaxis.Title(text='Values', font=font)
),
)
for category in MEASUREMENT_CATEGORIES:
if getattr(self.ui, category.name).isChecked():
selected_data = data[data[' DATA H'] == category.id]
times = selected_data[' Offset[s]']
values = selected_data[' DATA L'].apply(category.convert)
figure.add_trace(
go.Scatter(
x=times,
y=values,
mode='lines',
name=category.description,
)
)
filename = self.asc_filename
if self.target_path:
filename = os.path.join(
self.target_path, os.path.basename(filename)
)
plotly.offline.plot(
figure,
filename=filename + '.html',
auto_open=self.ui.html_auto_open.isChecked(),
)
def main():
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
@__blackjack__
Vielen Dank für deine großartige Unterstützung!
Den Code habe ich jetzt sinnvoll verkleinert und das eine oder andere von dir Übernommen.
Er ist jetzt Übersichtlicher als zuvor..... und wieder was gelernt.
Für die Schule hat´s gereicht für ne 1, aber jetzt da wo ich ein Praktikum absolviere sind die Forderungen höher gestellt.
Jetzt fehlt nur mehr die exe zu verkleinern und gut is
Vielen Dank für deine großartige Unterstützung!
Den Code habe ich jetzt sinnvoll verkleinert und das eine oder andere von dir Übernommen.
Er ist jetzt Übersichtlicher als zuvor..... und wieder was gelernt.
Für die Schule hat´s gereicht für ne 1, aber jetzt da wo ich ein Praktikum absolviere sind die Forderungen höher gestellt.
Jetzt fehlt nur mehr die exe zu verkleinern und gut is
