Three times the same - Python Code

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
grba
User
Beiträge: 7
Registriert: Mittwoch 14. September 2016, 10:23

Hallo zusammen,

ich brauche Eure Hilfe! Im Anhang als Link steht mein Code. Was ich eigentlich brauche ? Ich muss den Code, der für eine SMU geignet so umrscheiben, dass es für mehrer eigentlich 3 SMU nutzbar ist. Am Bild ist Code für ein SMU und das brauche ich jetzt dreimal. Vor allem sollte den Wahl haben ob ich erste, zweite, dritte, alle usw. SMU wähle. Abhängig was ich gewählt habe, fährt mein Code ein. Also der schreibt die Daten in einem File, führt die SCPI code in Abhängigkeit welche SMU läuft. Können Sie mir helfen ...ich habe Idee das mit Class zu schreiben aber weiß nicht genau. Is too much complicate für mich. Danke im Voraus!

Python Code - https://drive.google.com/open?id=0B4KeR ... XNvS2xzTTg

Bild
BlackJack

@grba: Davon abgesehen das der Quelltext auch für dieses eine Fenster schon zu unstrukturiert ist — es ist ja eigentlich nur eine Gott-Klasse die *alles* kennt und macht — ist das etwas komplizierter als nur eine weitere Klasse zu schreiben. So wie es jetzt ist, hat man ja schon das Problem, dass während die Messung läuft, die GUI einfriert und sich nicht mehr aktualisiert und nicht mehr bedienbar ist. Wenn davon drei Fenster existieren, dann kann auf die Weise wie es jetzt gelöst ist, nur von einem aus eine Messung ausgeführt werden, während die anderen beiden Fenster (also eigentlich alle drei) einfrieren.

Man müsste als erstes mal mindestens die Geschäftslogik von der GUI trennen. Aber ich würde da noch weiter gehen und das nicht nur in zwei Klassen aufteilen. Die Geräte (Stromversorgung/Multimeter) kann man beispielsweise auch gut jeweils mit einer eigenen Klasse modellieren.

Dann muss man die eigentliche Messung asynchron lösen. Zum Beispiel mittels `after()`-Methode auf Widgets falls die eigentliche Messung schnell geht und nicht blockiert, oder aber als Threads mit dem `threading`-Modul. Da braucht man dann auch `after()` um von der GUI aus regelmässig zu prüfen ob der Thread mit der Messung fertig ist.

Die Instrumentenauswahl könnte man in das Hauptfenster verlegen, von dem aus man dann durch eine Schaltfläche ein Fenster für die getroffene Auswahl erzeugen kann, das den Rest der momentanen Benutzeroberfläche enthält.


`delChar()` und `delCharIn()` sehen übrigens furchterregend aus. Nämlich so als wnn dort eine Liste mit Tupeln und Unicode-Zeichenketten in eine Zeichenkettendarstellung umgewandelt wird und auf dieser Zeichenkette dann die Klammern und 'u's entfernt werden. Das ist nicht wirklich robust. Statt erst aus strukturierten Daten eine flache Zeichenkette zu erstellen um dann Zeichen zu entfernen die die Struktur widerspiegeln, sollte man die strukturierten Daten explizit gleich in das gewünschte Endergebnis überführen, so das man da hinterher nichts mehr korrigieren muss.

Die beiden Funktionen sehen ausserdem fast gleich aus, da kann man also entweder den gemeinsamen Teil in eine weitere Funktion herausziehen, oder den Code so verteilen das man zwei Funktionen hat, die keine Codewiederholung enthalten.

Die erste Funktion greift ausserdem auf Daten die nicht Konstant sind zu, die nicht als Argument übergeben werden.

Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer `main()`-Funktion.

Namen, ausser Klassennamen und Konstanten, werden in Python laut Style Guide for Python Code übrigens `klein_mit_unterstrichen` geschrieben. Damit es verständlich bleibt sollte man auf kryptische Abkürzungen verzichten. `query_power_supply()` ist wesentlich einfacher zu verstehen als `Q_KPower()`. Funktionen und Methoden werden üblicherweise nach Tätigkeiten benannt die beschreiben was sie tun. Zum einen um diese Information zu transportieren und dann sind sie auch leichter von ”passiven” Werten unterscheidbar.

Nachdem die `__init__()`-Methode durchlaufen wurde, sollten auf dem Objekt alle Attribute existieren. Am besten sollten die alle in der `__init__()` definiert werden. Da etwas in eine weitere `initialize()`-Methode auszulagern macht wenig Sinn. `__init__()` steht ja schon für's initialisieren. Einzige Ausnahme die mir da einfallen würde, wäre wenn die `__init__()` etwas vor und nach der `initialize()` erledigt, in einer Art und Weise dass es Sinn macht die `initialize()` in abgeleiteten Klassen zu überschreiben. Das ist hier aber nicht der Fall.

Es werden aber einige Attribute ausserhalb von `__init__()` und `initialize()` erstellt, was nicht sein sollte. So wird es sehr schwer einen Überblick zu bekommen, welche Attribute auf solchen Objekten existieren oder auch nur vielleicht irgendwann einmal existieren werden. Gerade bei einer solch (zu) grossen Klasse, besteht dann leicht mal die Gefahr das man einen Attributnamen aus versehen für unterschiedliche Dinge verwendet. Oder das man den Überblick verliert wann welches Attribut existiert und wann nicht, und irgendwo versucht auf ein noch nicht existierendes Attribut zuzugreifen.
grba
User
Beiträge: 7
Registriert: Mittwoch 14. September 2016, 10:23

@BlackJack Danke für Deine Bemühungen! ich werde versuchen den Code zu korrigieren und dann melde mich wieder. Eigentlich habe noch nicht viel Ahnung wie eine Klasse funktioniert, deswegen mache ich Fehler.Es wird jetzt schwer eine Klasse für ein Instument zu schreiben. Allerdings werde ich versuchen und dann komme wieder zurück.
grba
User
Beiträge: 7
Registriert: Mittwoch 14. September 2016, 10:23

Nach langem Kampf mit dem Programm komme ich nicht weiter. Ich hab den ganzen Kode verändern lassen aber jetz weiß nicht, wie ich das weiter tue..es ist langsam immer komplizierter. Nochmal zu Pyhon-Kode: Das Programm läuft in 3 getrennte Ablaufe (Instrument #A #B und #C) Es kann sein, dass nur ein, zwei oder drei Geräte eingeschaltet sind (Checkbutton Instrument - #ABC) und eventuell dazu noch Multimeter (Checkbutton Temperatursensor - #ABC) . Außerdem muss mein Programm erkennen welche Gerät (Checkbutton ) eingeschaltet ist und entsprechend SCPI Code an der Gerät senden. Das Programm erkennt die Gärete und dann schaltet ein/aus ...also nochmal erklärt in Schritten:

1. Es muss erkannt sein, welche Geräte sollte verwenden werden ( ob Instrument #ABC oder Temperatursensor #ABC eingeschaltet sind)
2. Dann startet das Programm ein/aus (Es gibt mehrere Zyklusen und jede dauert eine bestimmte Zeit. In Abhängiget Ein/Aus stellst du der unterschiedliche Spannung und das alles in eine definierte Zeit.Also Programm es muss SCPI code an den Geräte geschickt werden, nur die man gewählt hat. Außerdem muss man vor allem die Geräte initializieren)
3. Für jede Messung speichert die gemessene Daten in Buffer und beschriftet es entsprechend der Gerätbezeichnung #AB oder C, der gerade läuft oder misst.
4. Die Daten aus dem Buffer in txt speichern.

Ich bin bereit mit dem Programm verwirrt und brauche DRINGEND IHRE HILFE!

LG

Python Code: https://drive.google.com/file/d/0B4KeR9 ... sp=sharing
BlackJack

@grba: Das ist kompliziert weil es immer noch nicht genug in kleine, übersichtliche, in sich geschlossene Teilprobleme aufgeteilt ist und Programmlogik und GUI nicht ordentlich getrennt sind. Die Instrumentenklasse darf von der GUI nichts wissen, die greift aber an vielen Stellen auf das globale `gui`-Exemplar zu. Der Quelltext macht einen sehr GUI-lastigen Eindruck, als wenn er von der GUI her entwickelt wurde und nicht von unten, von der Programmlogik her. Man müsste ein Instrument, ein einzelnes Intrument ohne GUI, zum Beispiel in einer Python-Shell erstellen und mit Methodenaufrufen steuern können. Also sowohl eine Stromquelle als auch ein Multimeter. Die entsprechenden Klassen haben aber nur eine `__init__()` aber gar kein Verhalten.

`rm`, `inst`, `root`, und `gui` sollten auf Modulebene nicht existieren, das heisst Funktionen oder Methoden sollten auf keines dieser Objekte einfach so zugreifen.

Bei der GUI sind die ganzen Wiederholungen für die drei Geräte(kombinationen) schlecht. Da würde es sich anbieten den Anteil für eine Gerätekombination in eine eigene Klasse auszulagern. Üblicherweise leitet man solche Klassen auch von `Tkinter.Frame` (oder `LabelFrame`) ab, damit man sie als Widgets in andere Containerwidgets einfügen kann. Und wenn man n Objekte hat, mit denen man das gleiche anstellt, dann schreibt man das nicht alles n-mal hin, sondern steckt die Objekte in eine Liste und schreibt eine Schleife.

Dürfen zwei von den durch die `LabelFrame`\s angeboteten Einstellungen die selbe Gerätekombination verwenden wenn man die Messung startet? Ich sehe da nichts in der GUI was das verhindern würde.
grba
User
Beiträge: 7
Registriert: Mittwoch 14. September 2016, 10:23

Vor allem bin ich Python-Einsteiger und das ist wirklich meine erste "selbserstellte" Klasse. Deswegen ist es mir nicht alles klar, was ich noch umprogrammieren sollte. Unter anderem habe ich überhaupt keine Idee wie kann ich eine Klasse schreiben, die mit ausgewählte Geräte komuniziert. Eigentlich ich komme nicht voran, wie kann ich es machen, dass 3 getrennte Prozesse in gleciher Zeit laufen. D.h A,B und C laufen in verschiedene Schleifen mit unterschiedlichem Werten und Geräteeinstellungen. Wenn du magst, ich bitte dir den Kode markieren, was ich ändern sollte. Neue Vorschläge sind auch super, wie du bis jetzt geteilt hast.

Code: Alles auswählen

import Tkinter
import visa, time, datetime
from ttk import *
from tkFileDialog import *
import tkMessageBox, os
import numpy as np

rm = visa.ResourceManager()
inst = rm.list_resources()

class Interface:

    def __init__(self, parent):
        self.parent = parent
        parent.title("Cycling")
        local_directory = os.path.dirname(os.path.realpath(__file__))
        self.dataname = "/does/not/exist"

    # Frame
        self.frame_instruments = Tkinter.Frame(parent, bg='', colormap='new')
        self.frame_settings = Tkinter.Frame(parent, bg='', colormap='new')
        self.frame_image = Tkinter.Frame(parent, bg='', colormap='new')

    # Labelframe
        self.lframe_instrumet_a = Tkinter.LabelFrame(self.frame_instruments, text="Choose an Instruments - #A", padx=8, pady=9)
        self.lframe_settings_a = Tkinter.LabelFrame(self.frame_instruments, text="Settings - #A", padx=7, pady=11)

        self.lframe_instrumet_b = Tkinter.LabelFrame(self.frame_instruments, text="Choose an Instruments - #B", padx=8, pady=9)
        self.lframe_settings_b = Tkinter.LabelFrame(self.frame_instruments, text="Settings - #B", padx=7, pady=11)

        self.lframe_instrumet_c = Tkinter.LabelFrame(self.frame_instruments, text="Choose an Instruments - #C", padx=8, pady=9)
        self.lframe_settings_c = Tkinter.LabelFrame(self.frame_instruments, text="Settings - #C", padx=7, pady=11)

    # Combobox
        #A
        self.choices_power_supply_a_var = Tkinter.StringVar()
        self.choices_power_supply_a_var.set(inst[0])
        self.combo_power_supply_a = Combobox(self.lframe_instrumet_a, values=inst, textvariable=self.choices_power_supply_a_var)

        self.choices_multimeter_a_var = Tkinter.StringVar()
        self.choices_multimeter_a_var.set(inst[0])
        self.combo_multimeter_a = Combobox(self.lframe_instrumet_a, values=inst, textvariable=self.choices_multimeter_a_var)

        #B
        self.choices_power_supply_b_var = Tkinter.StringVar()
        self.choices_power_supply_b_var.set(inst[0])
        self.combo_power_supply_b = Combobox(self.lframe_instrumet_b, values=inst, textvariable=self.choices_power_supply_b_var)

        self.choices_multimeter_b_var = Tkinter.StringVar()
        self.choices_multimeter_b_var.set(inst[0])
        self.combo_multimeter_b = Combobox(self.lframe_instrumet_b, values=inst, textvariable=self.choices_multimeter_b_var)

        #C
        self.choices_power_supply_c_var = Tkinter.StringVar()
        self.choices_power_supply_c_var.set(inst[0])
        self.combo_power_supply_c = Combobox(self.lframe_instrumet_c, values=inst, textvariable=self.choices_power_supply_c_var)

        self.choices_multimeter_c_var = Tkinter.StringVar()
        self.choices_multimeter_c_var.set(inst[0])
        self.combo_multimeter_c = Combobox(self.lframe_instrumet_c, values=inst, textvariable=self.choices_multimeter_c_var)

    # Menu
        menu = Tkinter.Menu(parent, tearoff=0)
        parent.config(menu=menu)
        filemenu = Tkinter.Menu(parent, tearoff=0)
        menu.add_cascade(label='File', menu=filemenu)
        filemenu.add_command(label='Create New File...', command=lambda: self.save_file())
        helpmenu = Tkinter.Menu(menu, tearoff=0)
        menu.add_cascade(label='?', menu=helpmenu)
        helpmenu.add_command(label='About', command=lambda: self.about())

    # Label
        #A
        self.label_power_supply_a = Tkinter.Label(self.lframe_instrumet_a, text="Power Supply 2200: ")
        self.label_multimeter_a = Tkinter.Label(self.lframe_instrumet_a, text="Multimeter 2700: ")
        self.label_voltage_range_a = Tkinter.Label(self.lframe_settings_a, text='Voltage Range [V]')
        self.label_over_voltage_a = Tkinter.Label(self.lframe_settings_a, text='Over Voltage Protection [V]')
        self.label_voltage_set_a = Tkinter.Label(self.lframe_settings_a, text='Set Voltage [V]')
        self.label_heat_set_a = Tkinter.Label(self.lframe_settings_a, text='Heat.Set [A]')
        self.label_meas_set_a = Tkinter.Label(self.lframe_settings_a, text='Meas. Set [A]')
        self.label_set_time_a = Tkinter.Label(self.lframe_settings_a, text='Time Limit [s]')
        self.label_set_delay_a = Tkinter.Label(self.lframe_settings_a, text='Measurement Delay [s]')
        self.label_set_repeat_a = Tkinter.Label(self.lframe_settings_a, text='Repeat')

        #B
        self.label_power_supply_b = Tkinter.Label(self.lframe_instrumet_b, text="Power Supply 2200: ")
        self.label_multimeter_b = Tkinter.Label(self.lframe_instrumet_b, text="Multimeter 2700: ")
        self.label_voltage_range_b = Tkinter.Label(self.lframe_settings_b, text='Voltage Range [V]')
        self.label_over_voltage_b = Tkinter.Label(self.lframe_settings_b, text='Over Voltage Protection [V]')
        self.label_voltage_set_b = Tkinter.Label(self.lframe_settings_b, text='Set Voltage [V]')
        self.label_heat_set_b = Tkinter.Label(self.lframe_settings_b, text='Heat.Set [A]')
        self.label_meas_set_b = Tkinter.Label(self.lframe_settings_b, text='Meas. Set [A]')
        self.label_set_time_b = Tkinter.Label(self.lframe_settings_b, text='Time Limit [s]')
        self.label_set_delay_b = Tkinter.Label(self.lframe_settings_b, text='Measurement Delay [s]')
        self.label_set_repeat_b = Tkinter.Label(self.lframe_settings_b, text='Repeat')

        #C
        self.label_power_supply_c = Tkinter.Label(self.lframe_instrumet_c, text="Power Supply 2200: ")
        self.label_multimeter_c = Tkinter.Label(self.lframe_instrumet_c, text="Multimeter 2700: ")
        self.label_voltage_range_c = Tkinter.Label(self.lframe_settings_c, text='Voltage Range [V]')
        self.label_over_voltage_c = Tkinter.Label(self.lframe_settings_c, text='Over Voltage Protection [V]')
        self.label_voltage_set_c = Tkinter.Label(self.lframe_settings_c, text='Set Voltage [V]')
        self.label_heat_set_c = Tkinter.Label(self.lframe_settings_c, text='Heat.Set [A]')
        self.label_meas_set_c = Tkinter.Label(self.lframe_settings_c, text='Meas. Set [A]')
        self.label_set_time_c = Tkinter.Label(self.lframe_settings_c, text='Time Limit [s]')
        self.label_set_delay_c = Tkinter.Label(self.lframe_settings_c, text='Measurement Delay [s]')
        self.label_set_repeat_c = Tkinter.Label(self.lframe_settings_c, text='Repeat')

    # Checkbutton
        #A
        self.temp_sensor_a_var = Tkinter.IntVar()
        self.check_temp_sensor_a = Checkbutton(self.lframe_instrumet_a, text="Temperatursensor - #A", variable=self.temp_sensor_a_var,
                                               onvalue=1, offvalue=0)
        self.instrument_var_a = Tkinter.IntVar()
        self.check_instrument_a = Checkbutton(self.lframe_settings_a, text="Instrument - #A", variable=self.instrument_var_a,
                                               onvalue=1, offvalue=0)
        #B
        self.temp_sensor_b_var = Tkinter.IntVar()
        self.check_temp_sensor_b = Checkbutton(self.lframe_instrumet_b, text="Temperatursensor - #B", variable=self.temp_sensor_b_var,
                                               onvalue=1, offvalue=0)
        self.instrument_var_b = Tkinter.IntVar()
        self.check_instrument_b = Checkbutton(self.lframe_settings_b, text="Instrument - #B", variable=self.instrument_var_b,
                                               onvalue=1, offvalue=0)
        #C
        self.temp_sensor_c_var = Tkinter.IntVar()
        self.check_temp_sensor_c = Checkbutton(self.lframe_instrumet_c, text="Temperatursensor - #C", variable=self.temp_sensor_c_var,
                                               onvalue=1, offvalue=0)
        self.instrument_var_c = Tkinter.IntVar()
        self.check_instrument_c = Checkbutton(self.lframe_settings_c, text="Instrument - #C", variable=self.instrument_var_c,
                                               onvalue=1, offvalue=0)
    # Entry/Spinbox
        #A
        self.voltage_range_a_var = Tkinter.IntVar()
        self.spin_voltage_range_a = Tkinter.Spinbox(self.lframe_settings_a, textvariable=self.voltage_range_a_var, width=5,
                                                    from_=0, to=30, justify='right')
        self.over_voltage_a_var = Tkinter.IntVar()
        self.spin_over_voltage_a = Tkinter.Spinbox(self.lframe_settings_a, textvariable=self.over_voltage_a_var, width=5,
                                                   from_=0, to=30, justify='right')
        self.heat_set_a_var = Tkinter.IntVar()
        self.entry_heat_set_a = Tkinter.Entry(self.lframe_settings_a, textvariable=self.heat_set_a_var, width=7, justify='right')

        self.meas_set_a_var = Tkinter.IntVar()
        self.entry_meas_set_a = Tkinter.Entry(self.lframe_settings_a, textvariable=self.meas_set_a_var, width=7, justify='right')

        self.voltage_set_a_var = Tkinter.IntVar()
        self.entry_voltage_set_a = Tkinter.Entry(self.lframe_settings_a, textvariable=self.voltage_set_a_var, width=7, justify='right')

        self.time_set_a_var = Tkinter.IntVar()
        self.entry_time_set_a = Tkinter.Entry(self.lframe_settings_a, textvariable=self.time_set_a_var, width=7, justify='right')

        self.delay_set_a_var = Tkinter.IntVar()
        self.entry_delay_set_a = Tkinter.Entry(self.lframe_settings_a, textvariable=self.delay_set_a_var, width=7, justify='right')

        self.repeat_set_a_var = Tkinter.IntVar()
        self.entry_repeat_set_a = Tkinter.Entry(self.lframe_settings_a, textvariable=self.repeat_set_a_var, width=7, justify='right')

        #B
        self.voltage_range_b_var = Tkinter.IntVar()
        self.spin_voltage_range_b = Tkinter.Spinbox(self.lframe_settings_b, textvariable=self.voltage_range_b_var, width=5,
                                                    from_=0, to=30, justify='right')
        self.over_voltage_b_var = Tkinter.IntVar()
        self.spin_over_voltage_b = Tkinter.Spinbox(self.lframe_settings_b, textvariable=self.over_voltage_b_var, width=5,
                                                   from_=0, to=30, justify='right')
        self.heat_set_b_var = Tkinter.IntVar()
        self.entry_heat_set_b = Tkinter.Entry(self.lframe_settings_b, textvariable=self.heat_set_b_var, width=7, justify='right')

        self.meas_set_b_var = Tkinter.IntVar()
        self.entry_meas_set_b = Tkinter.Entry(self.lframe_settings_b, textvariable=self.meas_set_b_var, width=7, justify='right')

        self.voltage_set_b_var = Tkinter.IntVar()
        self.entry_voltage_set_b = Tkinter.Entry(self.lframe_settings_b, textvariable=self.voltage_set_b_var, width=7, justify='right')

        self.time_set_b_var = Tkinter.IntVar()
        self.entry_time_set_b = Tkinter.Entry(self.lframe_settings_b, textvariable=self.time_set_b_var, width=7, justify='right')

        self.delay_set_b_var = Tkinter.IntVar()
        self.entry_delay_set_b = Tkinter.Entry(self.lframe_settings_b, textvariable=self.delay_set_b_var, width=7, justify='right')

        self.repeat_set_b_var = Tkinter.IntVar()
        self.entry_repeat_set_b = Tkinter.Entry(self.lframe_settings_b, textvariable=self.repeat_set_b_var, width=7, justify='right')

        #C
        self.voltage_range_c_var = Tkinter.IntVar()
        self.spin_voltage_range_c = Tkinter.Spinbox(self.lframe_settings_c, textvariable=self.voltage_range_c_var, width=5,
                                                    from_=0, to=30, justify='right')
        self.over_voltage_c_var = Tkinter.IntVar()
        self.spin_over_voltage_c = Tkinter.Spinbox(self.lframe_settings_c, textvariable=self.over_voltage_c_var, width=5,
                                                   from_=0, to=30, justify='right')
        self.heat_set_c_var = Tkinter.IntVar()
        self.entry_heat_set_c = Tkinter.Entry(self.lframe_settings_c, textvariable=self.heat_set_c_var, width=7, justify='right')

        self.meas_set_c_var = Tkinter.IntVar()
        self.entry_meas_set_c = Tkinter.Entry(self.lframe_settings_c, textvariable=self.meas_set_c_var, width=7, justify='right')

        self.voltage_set_c_var = Tkinter.IntVar()
        self.entry_voltage_set_c = Tkinter.Entry(self.lframe_settings_c, textvariable=self.voltage_set_c_var, width=7, justify='right')

        self.time_set_c_var = Tkinter.IntVar()
        self.entry_time_set_c = Tkinter.Entry(self.lframe_settings_c, textvariable=self.time_set_c_var, width=7, justify='right')

        self.delay_set_c_var = Tkinter.IntVar()
        self.entry_delay_set_c = Tkinter.Entry(self.lframe_settings_c, textvariable=self.delay_set_c_var, width=7, justify='right')

        self.repeat_set_c_var = Tkinter.IntVar()
        self.entry_repeat_set_c = Tkinter.Entry(self.lframe_settings_c, textvariable=self.repeat_set_c_var, width=7, justify='right')

    # Button
        self.button_run = Tkinter.Button(self.frame_settings, text="Run", command=lambda: self.on_run(), width=25)
        self.button_stop = Tkinter.Button(self.frame_settings, text="Stop", command=lambda: self.on_stop(), width=25)
        self.button_quit = Tkinter.Button(self.frame_settings, text="Quit", command=lambda: self.on_quit(), width=25)

    # Grid
        parent.resizable(False, False)
        parent.grid_columnconfigure(0, weight=0)

        # Instrument #A
        self.frame_instruments.grid(row=0, column=0)

        self.lframe_instrumet_a.grid(row=0, column=0)
        self.label_power_supply_a.grid(row=0, column=0, sticky='W')
        self.label_multimeter_a.grid(row=1, column=0, sticky='W')
        self.combo_power_supply_a.grid(row=0, column=1)
        self.combo_multimeter_a.grid(row=1, column=1)
        self.check_temp_sensor_a.grid(row=2, column=1)

        self.lframe_settings_a.grid(row=0, column=2)
        self.label_voltage_range_a.grid(row=0, column=2, sticky='W')
        self.spin_voltage_range_a.grid(row=0, column=3)
        self.label_over_voltage_a.grid(row=1, column=2, sticky='W')
        self.label_heat_set_a.grid(row=2, column=2, sticky='W')
        self.spin_over_voltage_a.grid(row=1, column=3)
        self.entry_heat_set_a.grid(row=2, column=3)
        self.label_voltage_set_a.grid(row=0, column=4, sticky='W')
        self.label_meas_set_a.grid(row=1, column=4, sticky='W')
        self.label_set_time_a.grid(row=2, column=4, sticky='W')
        self.entry_voltage_set_a.grid(row=0, column=5)
        self.entry_meas_set_a.grid(row=1, column=5)
        self.entry_time_set_a.grid(row=2, column=5)
        self.label_set_delay_a.grid(row=0, column=6, sticky='W')
        self.label_set_repeat_a.grid(row=1, column=6, sticky='W')
        self.entry_delay_set_a.grid(row=0, column=7)
        self.entry_repeat_set_a.grid(row=1, column=7)
        self.check_instrument_a.grid(row=2, column=6, sticky='W')

        # Instrument #B
        self.lframe_instrumet_b.grid(row=3)
        self.label_power_supply_b.grid(row=3, column=0)
        self.label_multimeter_b.grid(row=4, column=0)
        self.combo_power_supply_b.grid(row=3, column=1)
        self.combo_multimeter_b.grid(row=4, column=1)
        self.check_temp_sensor_b.grid(row=5, column=1)

        self.lframe_settings_b.grid(row=3, column=2)
        self.label_voltage_range_b.grid(row=3, column=2, sticky='W')
        self.spin_voltage_range_b.grid(row=3, column=3)
        self.label_over_voltage_b.grid(row=4, column=2, sticky='W')
        self.label_heat_set_b.grid(row=5, column=2, sticky='W')
        self.spin_over_voltage_b.grid(row=4, column=3)
        self.entry_heat_set_b.grid(row=5, column=3)
        self.label_voltage_set_b.grid(row=3, column=4, sticky='W')
        self.label_meas_set_b.grid(row=4, column=4, sticky='W')
        self.label_set_time_b.grid(row=5, column=4, sticky='W')
        self.entry_voltage_set_b.grid(row=3, column=5)
        self.entry_meas_set_b.grid(row=4, column=5)
        self.entry_time_set_b.grid(row=5, column=5)
        self.label_set_delay_b.grid(row=3, column=6, sticky='W')
        self.label_set_repeat_b.grid(row=4, column=6, sticky='W')
        self.entry_delay_set_b.grid(row=3, column=7)
        self.entry_repeat_set_b.grid(row=4, column=7)
        self.check_instrument_b.grid(row=5, column=6, sticky='W')

         # Instrument #C
        self.lframe_instrumet_c.grid(row=6)
        self.label_power_supply_c.grid(row=6, column=0)
        self.label_multimeter_c.grid(row=7, column=0)
        self.combo_power_supply_c.grid(row=6, column=1)
        self.combo_multimeter_c.grid(row=7, column=1)
        self.check_temp_sensor_c.grid(row=8, column=1)

        self.lframe_settings_c.grid(row=6, column=2)
        self.label_voltage_range_c.grid(row=6, column=2, sticky='W')
        self.spin_voltage_range_c.grid(row=6, column=3)
        self.label_over_voltage_c.grid(row=7, column=2, sticky='W')
        self.label_heat_set_c.grid(row=8, column=2, sticky='W')
        self.spin_over_voltage_c.grid(row=7, column=3)
        self.entry_heat_set_c.grid(row=8, column=3)
        self.label_voltage_set_c.grid(row=6, column=4, sticky='W')
        self.label_meas_set_c.grid(row=7, column=4, sticky='W')
        self.label_set_time_c.grid(row=8, column=4, sticky='W')
        self.entry_voltage_set_c.grid(row=6, column=5)
        self.entry_meas_set_c.grid(row=7, column=5)
        self.entry_time_set_c.grid(row=8, column=5)
        self.label_set_delay_c.grid(row=6, column=6, sticky='W')
        self.label_set_repeat_c.grid(row=7, column=6, sticky='W')
        self.entry_delay_set_c.grid(row=6, column=7)
        self.entry_repeat_set_c.grid(row=7, column=7)
        self.check_instrument_c.grid(row=8, column=6, sticky='W')

        # Button
        self.frame_settings.grid(row=8)
        self.button_run.grid(row=8, column=0)
        self.button_stop.grid(row=8,column=1)
        self.button_quit.grid(row=8, column=2)


    # Function
    def about(self):
        tkMessageBox.showinfo("About", "About" %chr(64))

    def on_quit(self):
        self.parent.quit()
        self.parent.destroy()
        self.fout.close()

    def save_file(self):
        self.file_opt = self.options = {}
        self.options['filetypes'] = [('Text Files', '.txt')]
        self.dataname = asksaveasfilename(**self.file_opt)
        self.datamode = 'a'

        try:
            self.fout = open(self.dataname, self.datamode)
        except Exception, e:
            strError = "Output file open error: "+ str(e)
            tkMessageBox.showerror("Error", strError)
    def on_stop(self):
        self.button_quit.configure(state='active')

    def on_run(self):
        self.button_quit.configure(state='disable')
        create_header = Header(gui.fout, self.selected_instruments_query(),self.dataname, self.voltage_range_a_var.get(),
                               self.voltage_set_a_var.get(), self.delay_set_a_var.get(), self.over_voltage_a_var.get(),
                               self.meas_set_a_var.get(), self.repeat_set_a_var.get(), self.heat_set_a_var.get(),
                               self.temp_sensor_a_var.get(), self.voltage_range_b_var.get(),
                               self.voltage_set_b_var.get(), self.delay_set_b_var.get(), self.over_voltage_b_var.get(),
                               self.meas_set_b_var.get(), self.repeat_set_b_var.get(), self.heat_set_b_var.get(),
                               self.temp_sensor_b_var.get(),self.voltage_range_c_var.get(),
                               self.voltage_set_c_var.get(), self.delay_set_c_var.get(), self.over_voltage_c_var.get(),
                               self.meas_set_c_var.get(), self.repeat_set_c_var.get(), self.heat_set_c_var.get(),
                               self.temp_sensor_c_var.get())

    def selected_instruments_query(self):
        selected_power_query = [self.instrument_var_a.get(), self.instrument_var_b.get(), self.instrument_var_c.get()]
        selected_multimeter_query = [self.temp_sensor_a_var.get(), self.temp_sensor_b_var.get(), self.temp_sensor_c_var.get()]
        selected_instruments_query = [selected_power_query,selected_multimeter_query]
        return selected_instruments_query


class Header:
    WRITE = 0
    def __init__(self,fout,selected_instruments,dataname,voltage_range_a_var,voltage_set_a_var, delay_set_a_var,
                 over_voltage_a_var,meas_set_a_var, repeat_set_a_var, heat_set_a_var, time_set_a_var, voltage_range_b_var,
                 voltage_set_b_var, delay_set_b_var,over_voltage_b_var,meas_set_b_var, repeat_set_b_var,
                 heat_set_b_var, time_set_b_var, voltage_range_c_var,voltage_set_c_var, delay_set_c_var,
                 over_voltage_c_var,meas_set_c_var, repeat_set_c_var, heat_set_c_var, time_set_c_var):

        self.fout = fout
        self.selected_instruments = selected_instruments
        self.dataname = dataname
        self.voltage_range_a_var = voltage_range_a_var
        self.voltage_set_a_var = voltage_set_a_var
        self.delay_set_a_var = delay_set_a_var
        self.over_voltage_a_var = over_voltage_a_var
        self.meas_set_a_var = meas_set_a_var
        self.repeat_set_a_var = repeat_set_a_var
        self.heat_set_a_var = heat_set_a_var
        self.time_set_a_var = time_set_a_var
        self.voltage_range_b_var = voltage_range_b_var
        self.voltage_set_b_var = voltage_set_b_var
        self.delay_set_b_var = delay_set_b_var
        self.over_voltage_b_var = over_voltage_b_var
        self.meas_set_b_var = meas_set_b_var
        self.repeat_set_b_var = repeat_set_b_var
        self.heat_set_b_var = heat_set_b_var
        self.time_set_b_var = time_set_b_var
        self.voltage_range_c_var = voltage_range_c_var
        self.voltage_set_c_var = voltage_set_c_var
        self.delay_set_c_var = delay_set_c_var
        self.over_voltage_c_var = over_voltage_c_var
        self.meas_set_c_var = meas_set_c_var
        self.repeat_set_c_var = repeat_set_c_var
        self.heat_set_c_var = heat_set_c_var
        self.time_set_c_var = time_set_c_var
        print self.selected_instruments
        # Header involve data only once
        if Header.WRITE == 0:
            self.header_file()
            Header.WRITE = 1

    def header_file(self):
        t = datetime.datetime.now()
        curr_datetime = t.timetuple()
        yr = str(curr_datetime[0])
        curr_date = "%02d."%int(yr[2:]) + "%02d."%curr_datetime[1] + "%02d."%curr_datetime[2]
        curr_time = "%02d:"%curr_datetime[3] + "%02d:"%curr_datetime[4] + "%02d"%curr_datetime[5]
        time_data = curr_date + " " + curr_time
        separate_line = "*"*120

        str_out_time = "Filename: \t%s \nTime/Date: \t%s \n\nInstrument - #A\n\n" % (self.dataname, time_data)
        self.fout.write(str_out_time)
        str_out_a = "Voltage Range: \t%d\tSet Voltage: \t%d\tMeasurement Delay: \t%d\n" \
                    "OVP: \t\t%d\tMeas.Set: \t%d\tRepeat: \t\t%d\n" \
                    "Heat.Set: \t%d\tTime Limit: \t%d\n\n\n" % (self.voltage_range_a_var,
                    self.voltage_set_a_var, self.delay_set_a_var, self.over_voltage_a_var,
                    self.meas_set_a_var, self.repeat_set_a_var, self.heat_set_a_var,
                    self.time_set_a_var)

        str_out_b = "Instrument - #B\n\nVoltage Range: \t%d\tSet Voltage: \t%d\tMeasurement Delay: \t%d\n" \
                    "OVP: \t\t%d\tMeas.Set: \t%d\tRepeat: \t\t%d\n" \
                    "Heat.Set: \t%d\tTime Limit: \t%d\n\n\n"  % (self.voltage_range_b_var,
                    self.voltage_set_b_var, self.delay_set_b_var, self.over_voltage_b_var,
                    self.meas_set_b_var, self.repeat_set_b_var, self.heat_set_b_var,
                    self.time_set_b_var)
        str_out_c = "Instrument - #C\n\nVoltage Range: \t%d\tSet Voltage: \t%d\tMeasurement Delay: \t%d\n" \
                    "OVP: \t\t%d\tMeas.Set: \t%d\tRepeat: \t\t%d\n" \
                    "Heat.Set: \t%d\tTime Limit: \t%d\n\n\n%s\n" % (self.voltage_range_c_var,
                    self.voltage_set_c_var, self.delay_set_c_var, self.over_voltage_c_var,
                    self.meas_set_c_var, self.repeat_set_c_var, self.heat_set_c_var,
                    self.time_set_c_var, separate_line)

        # Print header file depending of the instruments
        for condition, string in zip(self.selected_instruments[0], [str_out_a, str_out_b, str_out_c]):
            if condition:
                self.fout.write(string)


if __name__ == '__main__':
    root = Tkinter.Tk()
    gui = Interface(root)
    root.mainloop()
Zuletzt geändert von Anonymous am Mittwoch 21. September 2016, 11:06, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@grba: wenn man was im Code markieren wollte, müßte man den ganzen Code markieren. Als erstes müßte man Importe aufräumen, ungenutzte Importe löschen und die *-Importe loswerden, denn solange man nicht weiß, woher was importiert wird, ist ein Refaktorieren viel zu gefährlich, dass man etwas kaputt macht. Dann schiebt man alle Attribute, die mit _a enden in eine eigene Klasse "Instrument". Dann schaut man, wo sich die Zeilen mit _b unterscheiden, führt Parameter ein, und ersetzt alles mit _b durch ein zweites Exemplar von Instrument. Das selbe mit _c.
An diesem Punkt, wenn man als die 400 Zeilen Code auf 100 reduziert hat, kann man anfangen, die Abhängigkeiten aufzulösen: globale Variablen loswerden, Verarbeitungslogik von GUI trennen, ...
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Hat dir die Antwort zu http://stackoverflow.com/questions/3963 ... -same-time denn jetzt geholfen?
BlackJack

Eine Idee wie man zum Beispiel die Stromversorgung in einen eigenen Datentyp verpacken kann:

Code: Alles auswählen

class PowerSupply(object):

    def __init__(self, ressource, over_voltage, voltage_range):
        self.ressource = ressource
        self.ressource.write('*rst, *cls')
        self.ressource.write('sour:func:mode:fix')
        self.ressource.write('trig:sour:bus')
        self.ressource.write('sour:volt:prot {0}'.format(over_voltage))
        self.ressource.write('sour:volt:range {0}'.format(voltage_range))

    @property
    def voltage(self):
        return self.ressource.query(':meas:volt?')

    @voltage.setter
    def voltage(self, value):
        self.ressource.write('sour:volt:lev {0}'.format(value))

    def set_output(self, value):
        self.ressource.write(':sour:outp ' + ('on' if value else 'off'))
 
    def set_current(self, value):
        self.ressource.write(':sour:curr:lev {0}'.format(value))

    def get_voltage(self, currents):
        self.ressource.write('trig:imm')
        for value, duration in currents:
            self.set_current(value)
            time.sleep(duration)
        return self.voltage

    def iter_voltages(self, voltage, currents):
        self.set_output(True)
        self.voltage = voltage
        while True:
            yield self.get_voltage(currents)

    def turn_off(self):
        self.set_output(False)
        self.ressource.write(':syst:loc')

    def close(self):
        self.ressource.close()
Dabei weiss ich jetzt nicht ob die Kommandos wirklich alle sinnvoll/richtig auf die Methoden/Properties verteilt sind, da ich weder das Protokoll noch das Gerät kenne. Ist also nur ein Ansatz, der vielleicht in die richtige Richtung geht.
grba
User
Beiträge: 7
Registriert: Mittwoch 14. September 2016, 10:23

denken sie das es der richtige Weg ist ?. Also beim Code funktioniert irgendwas überhaupt nicht.

Code: Alles auswählen

import Tkinter,visa
from ttk import *

rm = visa.ResourceManager()
inst = rm.list_resources()

class Interface:

    def __init__(self, parent, choose_istrument, start_row, start_column):
        self.parent = parent
        self.choose_istrument = choose_istrument
        self.start_row = start_row
        self.start_column = start_column


    # Frame
        self.frame_instruments = Tkinter.Frame(parent, bg='', colormap='new')
        self.frame_settings = Tkinter.Frame(parent, bg='', colormap='new')
        self.frame_image = Tkinter.Frame(parent, bg='', colormap='new')

    # Labelframe
        self.lframe_instrumet = Tkinter.LabelFrame(self.frame_instruments, text="Choose an Instruments - #%s"%self.choose_istrument, padx=8, pady=9)
        self.lframe_settings = Tkinter.LabelFrame(self.frame_instruments, text="Settings - #A", padx=7, pady=11)


    # Combobox
        #A
        self.choose_power_var = Tkinter.StringVar()
        self.choose_power_var.set(inst[0])
        self.combo_power = Combobox(self.lframe_instrumet, values=inst, textvariable=self.choose_power_var)

    # Label
        self.label_power_supply = Tkinter.Label(self.lframe_instrumet, text="Power Supply 2200: ")

    # Grid
        parent.resizable(False, False)
        parent.grid_columnconfigure(0, weight=0)

        self.frame_instruments.grid(row=0+self.start_row, column=0+self.start_column)
        self.lframe_instrumet.grid(row=0+self.start_row, column=0+self.start_column)
        self.label_power_supply.grid(row=0+self.start_row, column=0+self.start_column, sticky='W')
        self.combo_power.grid(row=0+self.start_row, column=1+self.start_column)
        self.lframe_settings.grid(row=0+self.start_row, column=2+self.start_column)



if __name__ == '__main__':
    root = Tkinter.Tk()
    inst_a = Interface(root, "A", 0, 0)
    inst_b = Interface(root,"B", 0, 0) #!!!!???????
    root.mainloop()
Zuletzt geändert von Anonymous am Donnerstag 22. September 2016, 09:42, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
grba
User
Beiträge: 7
Registriert: Mittwoch 14. September 2016, 10:23

Hallo zusammen, ich hoffe jetzt, dass die Verarbeitungslogik von deer GUI getrennt ist.

CODE:
https://docs.google.com/document/d/1_aa ... sp=sharing
BlackJack

@grba: Also bei der Vererbung und den Zugriffen auf Elternwidgets ist noch einiges komisch bis verkehrt. `MainApplication` wird drei mal erstellt, also kann das `Main` in dem Namen nicht wirklich stimmen. Ausserdem macht die `__init__()` Sachen mit dem übergebenen `parent` die es nicht machen sollte. Titel, Menü, und andere Eigenschaften setzen beispielsweise. Das geht ein Widget was irgendwo von einem `parent` eingebunden wird, nichts an. Ausserdem passiert das alles dreimal während des Programmablaufs. Und es funktioniert dann nicht mehr wenn `parent` gar kein Fenster ist, sondern irgendein anderes Containerwidget.

Das `MainApplication` ein `Frame` *ist* wird auch nirgends verwendet. `AppNew` ist eine `MainApplication`, also im Code, aber doch nicht wirklich.

Wie so oft beim Programmieren versucht man auch bei GUIs ein Problem in kleinere Teilprobleme zu zerlegen. Ein so ein Teilproblem wäre halt die Anzeigen/Auswahlen für *ein* (Kombi)gerät in einem von `Frame` abgeleiteten Widget zu modellieren, das nichts mit seinem `parent` anstellt. Denn nur dann ist es ein in sich geschlossenes Objekt das man beliebig in eine übergeordnete GUI integrieren kann. Ob die dann die drei Anzeigen für die Geräte übereinander, nebeneinander, oder vielleicht auch in drei Feldern eines 2×2 Gitters irgendwie platziert, geht das Widget für *ein* Gerät nichts an.

Was ich noch sehr unübersichtlich finde ist die Gruppierung der Zeilen in der `__init__()`. Ganz zum Schluss bei dem Block mit den ganzen `grid()`-Aufrufen, weiss ich doch nicht mehr in welchem Container welches der Objekte steckt, da muss man also immer wieder weiter oben suchen. Es wäre besser wenn der Codeaufbau mehr dem GUI-Aufbau entspricht und nicht die Zeilen für ein GUI-Element über die ganze Methode verstreut sind.
grba
User
Beiträge: 7
Registriert: Mittwoch 14. September 2016, 10:23

@BlackJack jetzt bin ich total verwirrt und habe keine Idee, was ich weiter ändern könnte, damit das wirklich funktioniert.Außerdem bin ich Anfänger und verstehe Klasse nicht ganzvollständig. Trotzdem brauche ich die in diesem Fall. Ich würde dankbar, wenn du mir anhand eines Beispels zeigen kannst, was du damit alles geimeint hast.
BlackJack

Ui, schon wieder eine ganze Woche vergangen. Der Tag müsste mehr Stunden haben. Und ich müsste weniger Schlaf benötigen. :-)

Mal als Ansatz wie ein unabhängig in einer Klasse gekapselter UI-Teil für eine Gerätekombination aussehen könnte und drei Exemplare davon die in ein Hauptfenster eingebaut werden:

Code: Alles auswählen

import Tkinter as tk
from ttk import Combobox, LabelFrame, Checkbutton


class InstrumentUI(tk.Frame):

    def __init__(self, parent, instrument_label, instrument_choices):
        tk.Frame.__init__(self, parent)

        frame = LabelFrame(
            self, text='Choose Instruments - ' + instrument_label
        )
        tk.Label(
            frame, text='Power Supply 2200'
        ).grid(column=0, row=0, sticky=tk.E)

        self.power_supply_var = tk.StringVar()
        Combobox(
            frame, values=instrument_choices, textvariable=self.power_supply_var
        ).grid(column=1, row=0, sticky=tk.W)
        
        tk.Label(frame, text='Multimeter').grid(column=0, row=1, sticky=tk.E)
        self.multimeter_var = tk.StringVar()
        Combobox(
            frame, values=instrument_choices, textvariable=self.multimeter_var
        ).grid(column=1, row=1, sticky=tk.W)

        self.temperature_sensor_var = tk.IntVar()
        Checkbutton(
            frame,
            text='Temperature sensor',
            variable=self.temperature_sensor_var,
            onvalue=1,
            offvalue=0,
        ).grid(column=1, row=3, sticky=tk.W)

        frame.pack(side=tk.LEFT, anchor=tk.N)

        frame = LabelFrame(self, text='Settings - ' + instrument_label)

        tk.Label(
            frame, text='Voltage Range [V]'
        ).grid(column=0, row=0, sticky=tk.E)
        self.voltage_range_var = tk.IntVar()
        tk.Spinbox(
            frame,
            textvariable=self.voltage_range_var,
            from_=0,
            to=30,
            justify=tk.RIGHT,
        ).grid(column=1, row=0, sticky=tk.W)

        frame.pack(side=tk.LEFT, anchor=tk.N)


def main():
    instrument_choices = ['X', 'Y', 'Z']
    root = tk.Tk()
    instrument_uis = list()
    for text in ['#A', '#B', '#C']:
        instrument_ui = InstrumentUI(root, text, instrument_choices)
        instrument_ui.pack()
        instrument_uis.append(instrument_ui) 
    root.mainloop()


if __name__ == '__main__':
    main()
Im rechten Teil ist nur der Wert für 'Voltage Range [V]' vorhanden, weil ich zu faul war da jetzt alle anderen Eingaben auch rein zu schreiben. Das ist ja aber nur reine Fleissarbeit.
Antworten