Hallo,
ich habe folgendes Anliegen und hoffe mir kann jemand weiter helfen. Ich habe folgende Skripte:
1.) Hauptskript (Importiert df von Skript 2. startet die Plotter Funktion von Skript 3 und übergibt df)
2.) Skript für BLE Kommunikation (erzeugt Pandas Dataframe, df und übergibt df an 1. indem die Variable von diesem importiert wird )
3.) Skript für Plotter Fenster (plottet die Daten von df indem eine Plotter Funktion integriert ist an die das Hauptskript die Daten des df übergibt wenn er sie startet)
Das klappt auch alles soweit. Das Plotter Fenster wurde über pyqtgraph realisiert. Zuerst wurde ein GUI Fenster im QT Designer gezeichnet und dann wurde das Widget hochgestuft und die entsprechende Klasse zugewiesen. In dem Skript wurde dann die UI importiert via:
"uiclass, baseclass = pg.Qt.loadUiType("plotter_window.ui")" Hier wurde ein Mainwindow von der uiclass erstellt und in dieser die Funktion für das Plotten. Soweit so gut. Das klappt auch und meine Daten werden geplottet, wobei ich seltsamerweise immer nur alle Daten oder die ersten Daten plotten kann, bei den letzten bekomme ich einen Error, dass der Index nicht gefunden würde. Der Code dazu:
Klappt:"spo2_list = df['Red'].astype(float) "
Klappt nicht: " spo2_list = df['Red'].tail(100).astype(float)"
Eventuell kann mir da wer helfen?
Mein Hauptproblem ist aber, dass ich nicht weiß wie ich den Plotter aktualisiere, wenn ich auf Datei > Aktualisieren im Fenster oben klicke (via qt Designer wurde ein Menü angelegt). Kann mir da jemand helfen? Ich habe verschiedene Wege versucht, nichts hat aber geklappt.
Menü in Plotter Fenster (als Modul gestartet) mit Funktion (plotten) verbinden [pyqtgraph]
Das ist jetzt schon der dritte Thread, indem du von verschiedenen Skripten schreibst. Hast du inzwischen __blackjacks__ Vorschläge umgesetzt? Ein GUI-Programm ist in Klassen gegliedert. Ein Programm kann mehrere Module haben, aber nicht aus mehreren Skripten bestehen.
Aus Modulen importiert man Funktionen oder Klassen, aber keine Variablen. Denn das würde ja bedeuten dass man globale Varianten hat ordentlichen Programm nicht vorkommen sollten.
Statt Fehlermeldung zu beschreiben, poste hier bitte den kompletten Traceback mit dem dazugehörigen Code.
Aus Modulen importiert man Funktionen oder Klassen, aber keine Variablen. Denn das würde ja bedeuten dass man globale Varianten hat ordentlichen Programm nicht vorkommen sollten.
Statt Fehlermeldung zu beschreiben, poste hier bitte den kompletten Traceback mit dem dazugehörigen Code.
Hallo,
ich versuche mal alles aus dem Forum so gut umzusetzen wie ich es verstanden habe.
Zunächst zu den Modulen/ Skripten, vielleicht verstehe ich da was falsch. Soweit ich in meinen Python Buch gelesen habe, sind Module (auch) Skripte die beim Einbinden einmal gestartet werden, oder ist das falsch?
z.B.:
Bzgl der globalen Variable. Das könnte ich so ändern, dass ich die Variable über eine Return Funktion aus dem Modul zurückgebe, dann kann ich diese lokal machen.
Ad 1: Aktualisieren des Plotter Fensters
Hier ist einmal mein Code aus dem Modul "plotter.py".
Das starte ich nun in meinem Hauptprogramm über eine Funktion, wenn ein Button geklickt wird:
Das Fenster wird mir korrekt geöffnet und die Daten von df übergeben, wobei ich nun immer das Plotter Fenster neu öffnen muss, ich möchte es aber über den Menüpunkt Datei >> Aktualisieren aktualisieren. Wie bereits gesagt hat mein Plotter Fenster eine .ui im qt Designer erhalten, hier wurde ein Widget hochgestuft. Ich muss jetzt also den Aktualisieren Menüpunkt mit dem aktualisieren des Plotters verknüpfen. Fehler habe ich hier nie erhalten, aber einfach keine Reaktion. Ich hätte zuerst einmal versucht eine einfache Funktion mit print "Aktualisieren Butten gedrückt" zu verbinden, aber irgendwie klappt das nicht. Bei meinem anderen Fenster hatte ich dieses Problem nicht. Ich kann den Code dann nochmal reinschreiben. (Hab den Code gestern gelöscht um es nochmal neu zu versuchen)
Ad 2 Error beim Umstellen der Plotter Daten
Wenn ich alle Daten des df plotten will, oder die ersten, dann klappt es, nur wenn ich die letzten plotten will bekomme ich:
Wenn ich das auf alle Daten oder nur die ersten umändere (im Beispiel oben plotte ich alle, siehe Kommentare) klappt es.
Ich hoffe ich habe nun alles nachvollziehbar beschrieben, sonst kann ich auch kurz ein Video machen und auf YT hochladen um die GUI zu erklären.
MFG
ich versuche mal alles aus dem Forum so gut umzusetzen wie ich es verstanden habe.

Zunächst zu den Modulen/ Skripten, vielleicht verstehe ich da was falsch. Soweit ich in meinen Python Buch gelesen habe, sind Module (auch) Skripte die beim Einbinden einmal gestartet werden, oder ist das falsch?
z.B.:
__blackjack__ hat hier geschrieben:Jedes Mal, wenn ein Python-Skript ausgeführt wird, wird ein Bytecode erstellt. Wenn ein Python-Skript als Modul importiert wird, wird der Bytecode in der entsprechenden .pyc-Datei gespeichert
Jetzt bin ich mir da unsicher ob es zulässig ist, dass man in einem "Modul" auch einen Multithread startet, indem die Funktion aufgerufen wird. Weil man könnte das auch so interpretieren, dass die Threads nur im Hauptskript geöffnet werden sollen und die Funktionen und Klassen in den Modulen? Ich binde die .py Module jedenfalls als Modul ein bzw importiere from modul_xyz import xyz.Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.
Bzgl der globalen Variable. Das könnte ich so ändern, dass ich die Variable über eine Return Funktion aus dem Modul zurückgebe, dann kann ich diese lokal machen.
Ad 1: Aktualisieren des Plotter Fensters
Hier ist einmal mein Code aus dem Modul "plotter.py".
Code: Alles auswählen
import pyqtgraph as pg
from PyQt6.QtWidgets import QMainWindow
import numpy as np
uiclass, baseclass = pg.Qt.loadUiType("plotter_window.ui")
class PlotterWindow(QMainWindow, uiclass):
def __init__(self, df):
super().__init__()
self.setupUi(self)
def plot(self, df):
print("Daten des DF in Plotter Modul")
print(df)
spo2_list = df['Red'].astype(float) # Gibt alle Werte aus, funktioniert
# spo2_list = df['Red'][:100].astype(float) #gibt die ersten 100 Werte aus, funktioniert
# spo2_list = df['Red'].tail(100).astype(float) #soll die letzten 100 Werte ausgeben, funktioniert nicht
print("spo2_list = ")
print(spo2_list)
print("letzte 100 Werte")
letzte_werte = df['Red'].tail(100).astype(float)
print(letzte_werte) #Gibt die letzten 100 Werte an der Konsole aus, funktioniert
spo2_list = spo2_list[np.isfinite(spo2_list)] # Filtert ungültige Werte
self.graphWidget.plot(spo2_list)
def update_button_plotter(self):"
print("Daten des DF in Plotter Modul")
Code: Alles auswählen
plotter_window = PlotterWindow(df)
# Überprüfen, ob das Plotter-Fenster bereits erstellt wurde
if plotter_window is None:
# Erstellen einer Instanz des Plotter-Fensters und übergeben von df
plotter_window = PlotterWindow(df)
plotter_window_ui = plotter_window.ui # Referenz auf die UI des Plotter-Fensters
uic.loadUi("plotter_window.ui", self)
plotter_window.plot(df) # Aufruf der plot-Funktion, um den Graphen anzuzeigen
plotter_window.show() # Annzeigen des Plotter Fensters
Ad 2 Error beim Umstellen der Plotter Daten
Wenn ich alle Daten des df plotten will, oder die ersten, dann klappt es, nur wenn ich die letzten plotten will bekomme ich:
Code: Alles auswählen
Start Button wurde gedrückt
30-05-2023_11-26-59.xlsx
30-05-2023_11-26-59.xlsx
Datei angelegt
Red IR
0 1014 4900
1 1013 4900
2 1012 4897
3 1021 4895
4 1019 4888
.. ... ...
295 1021 4887
296 1020 4886
297 1011 4882
298 1024 4885
299 1029 4890
[300 rows x 2 columns]
Daten des DF in Plotter Modul
Red IR
0 1014 4900
1 1013 4900
2 1012 4897
3 1021 4895
4 1019 4888
.. ... ...
295 1021 4887
296 1020 4886
297 1011 4882
298 1024 4885
299 1029 4890
[300 rows x 2 columns]
spo2_list =
200 1023.0
201 1020.0
202 1019.0
203 1018.0
204 1015.0
...
295 1021.0
296 1020.0
297 1011.0
298 1024.0
299 1029.0
Name: Red, Length: 100, dtype: float64
letzte 100 Werte
200 1023.0
201 1020.0
202 1019.0
203 1018.0
204 1015.0
...
295 1021.0
296 1020.0
297 1011.0
298 1024.0
299 1029.0
Name: Red, Length: 100, dtype: float64
Traceback (most recent call last):
File "C:\Users\emanu\miniconda34\lib\site-packages\pandas\core\indexes\base.py", line 3652, in get_loc
return self._engine.get_loc(casted_key)
File "pandas\_libs\index.pyx", line 147, in pandas._libs.index.IndexEngine.get_loc
File "pandas\_libs\index.pyx", line 176, in pandas._libs.index.IndexEngine.get_loc
File "pandas\_libs\hashtable_class_helper.pxi", line 2606, in pandas._libs.hashtable.Int64HashTable.get_item
File "pandas\_libs\hashtable_class_helper.pxi", line 2630, in pandas._libs.hashtable.Int64HashTable.get_item
KeyError: 0
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "C:\Users\emanu\source\repos\PythonApplication3\PythonApplication3\PythonApplication3.py", line 369, in Plotter_clicked
plotter_window.plot(df) # Aufruf der plot-Funktion, um den Graphen anzuzeigen
File "C:\Users\emanu\source\repos\PythonApplication3\PythonApplication3\plotter.py", line 27, in plot
self.graphWidget.plot(spo2_list)
File "C:\Users\emanu\miniconda34\lib\site-packages\pyqtgraph\graphicsItems\PlotItem\PlotItem.py", line 630, in plot
item = PlotDataItem(*args, **kargs)
File "C:\Users\emanu\miniconda34\lib\site-packages\pyqtgraph\graphicsItems\PlotDataItem.py", line 366, in __init__
self.setData(*args, **kargs)
File "C:\Users\emanu\miniconda34\lib\site-packages\pyqtgraph\graphicsItems\PlotDataItem.py", line 697, in setData
dt = dataType(data)
File "C:\Users\emanu\miniconda34\lib\site-packages\pyqtgraph\graphicsItems\PlotDataItem.py", line 1207, in dataType
first = obj[0]
File "C:\Users\emanu\miniconda34\lib\site-packages\pandas\core\series.py", line 1007, in __getitem__
return self._get_value(key)
File "C:\Users\emanu\miniconda34\lib\site-packages\pandas\core\series.py", line 1116, in _get_value
loc = self.index.get_loc(label)
File "C:\Users\emanu\miniconda34\lib\site-packages\pandas\core\indexes\base.py", line 3654, in get_loc
raise KeyError(key) from err
KeyError: 0
Ich hoffe ich habe nun alles nachvollziehbar beschrieben, sonst kann ich auch kurz ein Video machen und auf YT hochladen um die GUI zu erklären.
MFG
- __blackjack__
- User
- Beiträge: 13931
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
@Nietzsche Module werden beim ersten importieren einmal ausgeführt, aber das macht sie nicht zu Skripten. Ein Skript ist ein Modul das dafür *gedacht* ist, *direkt* ausgeführt zu werden.
`pg.Qt.loadUiType()` ist komisch, ich würde es sogar als falsch ansehen. Das ist die `PyQt6.uic.loadUiType()`-Funktion. Die von woanders zu importieren ist verwirrend, weil der Leser natürlich denkt `pg.Qt.loadUiType()` ist etwas anderes. Das `baseclass` nicht verwendet wird ist auch falsch. Das wird doch da extra zurückgegeben, damit man die Basisklasse nicht hart in den Quelltext schreiben muss, sondern darantiert die verwendet, die auch im Designer verwendet wurde.
Wenn `PlotterWindow.__init__()` nicht unsinnigerweise ein Argument entgegen nehmen würde mit dem dann überhaupt nichts gemacht wird, könnte man sich die `__init__()` komplett sparen und die der generierten Basisklasse verwenden.
Das die Plot-Methode die Daten erst in `float` umwandeln muss ist komisch. Die sollte die Daten schon in der passenden Form bekommen. Auch die Auswahl und das filtern würde ich da nicht rein schreiben. Das erwartet man bei einem generischen `plot()` nicht.
`spo2` ist eine kryptische Abkürzung und `_list` ist ein bisschen irreführend für etwas das keine Liste ist.
Das mein Erstellen des `PlotterWindow`-Exemplars dann noch mal `loadUi()` auf ein Attribut von dem Objekt mit der gleichen *.ui-Datei angewendet wird, ist sehr wirr und falsch.
Bei dem Fehler mit dem `tail()` ist das Problem anscheinend das `pyqtgraph` eigentlich eine Sequenz erwartet und auf die Werte per Index von 0 an aufwärts zugreift. Das funktioniert bei einem `Series`-Objekt dass einen solchen Index hat. Bei `tail()` fängt der Index vom `Series`-Objekt dann aber nicht mehr bei 0 an. Und das ausfiltern von nicht-endlichen Werten lässt auch Indexwerte verschwinden, was beim Plotten zu Problemen führen kann. Entweder Du nimmst da nur die Werte aus dem `Series`-Objekt, oder Du sorgst dafür dass das vor dem Plotten einen Index mit aufsteigenden Werten bekommt, die bei 0 anfangen.
`pg.Qt.loadUiType()` ist komisch, ich würde es sogar als falsch ansehen. Das ist die `PyQt6.uic.loadUiType()`-Funktion. Die von woanders zu importieren ist verwirrend, weil der Leser natürlich denkt `pg.Qt.loadUiType()` ist etwas anderes. Das `baseclass` nicht verwendet wird ist auch falsch. Das wird doch da extra zurückgegeben, damit man die Basisklasse nicht hart in den Quelltext schreiben muss, sondern darantiert die verwendet, die auch im Designer verwendet wurde.
Wenn `PlotterWindow.__init__()` nicht unsinnigerweise ein Argument entgegen nehmen würde mit dem dann überhaupt nichts gemacht wird, könnte man sich die `__init__()` komplett sparen und die der generierten Basisklasse verwenden.
Das die Plot-Methode die Daten erst in `float` umwandeln muss ist komisch. Die sollte die Daten schon in der passenden Form bekommen. Auch die Auswahl und das filtern würde ich da nicht rein schreiben. Das erwartet man bei einem generischen `plot()` nicht.
`spo2` ist eine kryptische Abkürzung und `_list` ist ein bisschen irreführend für etwas das keine Liste ist.
Das mein Erstellen des `PlotterWindow`-Exemplars dann noch mal `loadUi()` auf ein Attribut von dem Objekt mit der gleichen *.ui-Datei angewendet wird, ist sehr wirr und falsch.
Bei dem Fehler mit dem `tail()` ist das Problem anscheinend das `pyqtgraph` eigentlich eine Sequenz erwartet und auf die Werte per Index von 0 an aufwärts zugreift. Das funktioniert bei einem `Series`-Objekt dass einen solchen Index hat. Bei `tail()` fängt der Index vom `Series`-Objekt dann aber nicht mehr bei 0 an. Und das ausfiltern von nicht-endlichen Werten lässt auch Indexwerte verschwinden, was beim Plotten zu Problemen führen kann. Entweder Du nimmst da nur die Werte aus dem `Series`-Objekt, oder Du sorgst dafür dass das vor dem Plotten einen Index mit aufsteigenden Werten bekommt, die bei 0 anfangen.
“Java is a DSL to transform big Xml documents into long exception stack traces.”
— Scott Bellware
— Scott Bellware
Hallo _blackjack__,
da hab ich nun einiges zu verbessern, danke. Ja, ich hab die Funktion 2,3 mal umgeschrieben: "Das mein Erstellen des `PlotterWindow`-Exemplars dann noch mal `loadUi()` auf ein Attribut von dem Objekt mit der gleichen *.ui-Datei angewendet wird, ist sehr wirr und falsch." - das ist mir in der Nacht "liegen" geblieben, habs gleich ausgebessert. Ich denke, dass ich hier "Bei dem Fehler mit dem `tail()`" mal grob verstanden habe, wo das Problem liegt, zur Lösung muss ich mir dann einige Gedanken machen.
"spo2` ist eine kryptische Abkürzung" Das sehe ich nicht so, ist aber eine andere Disziplin:
"Was ist SpO2? Die Sauerstoffsättigung Ihres Blutes (SpO2) bezeichnet den Prozentsatz des Blutes, der mit Sauerstoff gesättigt ist oder Sauerstoff enthält. "
Die restlichen Fehler bessere ich auch gleich mal aus.
Bzgl des Menüs, Datei > Aktualisieren > hat da jemand Tipps bzgl Signale / Solts? Ich versuche das gleich nochmal zu verbinden. Das Signal muss ich ja in meinem Modul in der Klasse des Fensters mit der Funktion des Fensters verbinden, oder? Oder in meinem Hauptprogramm?
da hab ich nun einiges zu verbessern, danke. Ja, ich hab die Funktion 2,3 mal umgeschrieben: "Das mein Erstellen des `PlotterWindow`-Exemplars dann noch mal `loadUi()` auf ein Attribut von dem Objekt mit der gleichen *.ui-Datei angewendet wird, ist sehr wirr und falsch." - das ist mir in der Nacht "liegen" geblieben, habs gleich ausgebessert. Ich denke, dass ich hier "Bei dem Fehler mit dem `tail()`" mal grob verstanden habe, wo das Problem liegt, zur Lösung muss ich mir dann einige Gedanken machen.
"spo2` ist eine kryptische Abkürzung" Das sehe ich nicht so, ist aber eine andere Disziplin:
"Was ist SpO2? Die Sauerstoffsättigung Ihres Blutes (SpO2) bezeichnet den Prozentsatz des Blutes, der mit Sauerstoff gesättigt ist oder Sauerstoff enthält. "
Die restlichen Fehler bessere ich auch gleich mal aus.
Bzgl des Menüs, Datei > Aktualisieren > hat da jemand Tipps bzgl Signale / Solts? Ich versuche das gleich nochmal zu verbinden. Das Signal muss ich ja in meinem Modul in der Klasse des Fensters mit der Funktion des Fensters verbinden, oder? Oder in meinem Hauptprogramm?
sO2 ist die Sauerstoffsättigung allgemein, spO2 ist die pulsoxymetrisch gemessene Sauerstoffsättigung, das ist ein allgemein üblicher Begriff in der Medizin/ Medizintechnik.
Das Verbinden mit dem Menü klappt jetzt, ich kann auch die letzten 100 Werte plotten mit;
Jetzt möchte ich die 100 Datenpunkte aber einzeln ausgeben und danach die neuen 100 Werte laden. Das laden kann ich über einen Timer machen, das klappt also schon, aber wie kann ich die 100 Werte einzeln plotten, kann mir da mal jemand weiterhelfen? Ich glaub ich brauche einen Timer der dann einen Datenpunkt von den 100 plottet und dann den Index um einen erhöht und nach einer kurzen Zeit von wenigen ms einen neuen Datenpunkt zu plottet (den alten aber nicht löscht) und den Index erhöht, solange bis alle 100 durch sind und dann den neuen Datensatz von 100 Daten ladet und hier den Index neu setzt, sowie die alten Daten löscht oder?
Code: Alles auswählen
spo2_list = df['Red'].tail(100).astype(float) # Gibt die letzten 100 Werte aus
spo2_list.reset_index(drop=True, inplace=True) # Setzt den Index zurück
self.plot_item.setData(y=np.array(spo2_list)) # Aktualisiere die Daten des Graphen
print("Daten des DF in Plotter Modul")
Hab das gefunden, aber ganz hinbekommen hab ichs noch nicht: https://pypi.org/project/pglive/
Ich hab das nun anders gelöst und hier die Erklärung. Zuerst wird ein Datensatz geplottet. Danach werden 100 Datenpunkte geladen (die letzten 100) und der Index wird auf 0 gesetzt. Dann wird ein Datenpunkt geplottet mit dem Index 0 und der Index der ausgelesen wird wird um eins erhöht. Jetzt folgt eine Pause und es wird der nächste Datenpunkt geprintet usw usf. Bis die 100 Daten ausgelesen wurden und dann werden neue 100 Datenpunkte geladen. Damit das dann flüssig läuft muss man sich nun etwas bei den ms der Pause spielen, dann klappt es einwandfrei.
- __blackjack__
- User
- Beiträge: 13931
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
@Nietzsche Wäre es nicht besser ein Fenster mit 100 Datenpunkten über den Datensatz laufen zu lassen, statt alle 100 Datenpunkte alle vorherigen Datenpunkte komplett aus der Anzeige zu werfen‽
“Java is a DSL to transform big Xml documents into long exception stack traces.”
— Scott Bellware
— Scott Bellware
Das kann man natürlich auch machen, ich habe es aktuell so gelöst, dass ich 2 Plotter Fenster erzeugt habe. Einmal einen live Plotter der mir jeweils die letzten 100 Datenpunkte gibt und einmal ein Fenster bei dem ich mit Aktualisieren bzw F5 jeweils die letzten 100, 1000, 10 000 oder 100 000 Daten lade. Wobei ich das später auf Zeitstempel umstellen will. Am Ende kann man das dann je nach eigenem Belieben anpassen.__blackjack__ hat geschrieben: ↑Donnerstag 1. Juni 2023, 12:22 @Nietzsche Wäre es nicht besser ein Fenster mit 100 Datenpunkten über den Datensatz laufen zu lassen, statt alle 100 Datenpunkte alle vorherigen Datenpunkte komplett aus der Anzeige zu werfen‽
