Matplotlib in Tkinter Gui einbetten

Fragen zu Tkinter.
stefanxfg
User
Beiträge: 85
Registriert: Sonntag 2. April 2017, 14:11

Hallo an Alle,

ich weiß leider nicht so richtig was ich machen soll. Ich möchte zunächst aus einer Excel x- und y- Werte einlesen und in ein Matplotlib zeichnen. Ich will das Element zunächst initialisieren, so dass es erst einmal sichtbar ist. Somit habe ich erst einmal folgendes:

Code: Alles auswählen

class App:
	def __init__(self, top=None):
Könnte ich hier einfach einen Container (sagen wir ein Frame) verwenden und darin einen Canvas für das Diagramm initialisieren?

Später möchte ich die Werte in eine Entry-Tabelle schreiben, so dass das Diagramm durch eine Event oder Change-Anweisung sich anpasst. Dies soll über eine def passieren. Des Weiteren möchte ich weitere defs schreiben, welche ein Steuerkreuz zeigen.

Aber mein Problem ist erst einmal die Initialisierung des Ganzen. Aber ich weiß oder bekommen das einfach nicht im Kopf hin. Bitte helft mir mal auf die Sprünge. Die im Netz dargestellten Beispiel zeigen immer nur den kompletten Code entweder ohne Class oder in der Class Init.

Grüße von Stefan
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

stefanxfg
User
Beiträge: 85
Registriert: Sonntag 2. April 2017, 14:11

Hallo noch einmal. Ich möchte das Thema noch einmal aufgreifen und besser erklären, was ich meine. Auf die folgende Weise bette ich das Matplotlib in Tkinter ein.

Code: Alles auswählen

class GUI: 
	def __init__(self, top=None):
		......
		self.ya = np.array(ddd)
		self.yb = np.array(ddd)
		self.z = np.array(ddd)
		
		self.canvasframe11 = Frame(self.TPanedwindow4_f2)
		self.canvasframe11.place(x=0, y=0, relwidth=1.0, relheight=0.95, bordermode=INSIDE)
		self.canvasframe12 = Frame(self.TPanedwindow4_f2)
		self.canvasframe12.place(x=0, rely=0.95, relwidth=1.0, bordermode=INSIDE)
		
		global ax1
		self.fig1 = Figure (figsize=(5,4), dpi=100)

		self.ax1 = self.fig1.add_subplot(111) #für 2d-Plot
		self.ax1.set_title('Definition der LRUGL')
		self.ax1.set_xlabel('Breite y [mm]')
		self.ax1.set_ylabel('Hoehe z [mm]')
		
		self.ax1.axis([-2000,2000,0, 5000])
		self.ax1.grid(True)
		self.ax1.plot(self.ya,self.z, color='red', linestyle='--', marker='')
		self.ax1.plot(self.yb,self.z, color='red', linestyle='--', marker='')
		
		self.canvas1 = FigureCanvasTkAgg(self.fig1,self.canvasframe11)
		toolbar1 = NavigationToolbar2TkAgg(self.canvas1, self.canvasframe12)

		self.canvas1.get_tk_widget().pack(fill=BOTH, expand=TRUE, pady=2, padx=2, anchor="n")	


In vielen Beispielen werden Graphen vom Root aus gezeichnet. Daher funktioniert der folgende Code.

[code]
def onMouseMove(event):
	ax1.lines = [ax1.lines[0]]
	ax1.axhline(y=event.ydata, color="k")
	ax1.axvline(x=event.xdata, color="k")

def updateData():
	global level1, val1
	clamp = lambda n, minn, maxn: max(min(maxn, n), minn)
	yield 1     # FuncAnimation expects an iterator

def visualize(i):
	lineVal1.set_ydata(val1)
	return lineVal1	

def vp_start_gui():
	'''Starting point when module is the main routine.'''
	global val, w, root
	root = Tk()
	GUI_support.set_Tk_var()
	top = GUI (root)
	GUI_support.init(root, top)
	f.canvas.mpl_connect('motion_notify_event', onMouseMove)
	ani = animation.FuncAnimation(f, visualize, updateData, interval=100)
	root.mainloop() 	
Meine Frage ist, ob meine Methodik der EInbindung und des Zeichnens richtig sind. Oder soll ich die Diagrammfunkitonalitäten nicht in die "class" schreiben außer der EInbindung des Canvas?

Beste Grüße von Stefan
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

stefanxfg hat geschrieben: Meine Frage ist, ob meine Methodik der EInbindung und des Zeichnens richtig sind. Oder soll ich die Diagrammfunkitonalitäten nicht in die "class" schreiben außer der EInbindung des Canvas?
Beste Grüße von Stefan
Zuerst solltest Du einmal Deine Gui in Klassen strukturieren. So ist deine GUI Klasse keine GUI Klasse, sondern nur ein Objekt, das Du angelegt hast, um darin Deine globalen Variablen hineinzustecken.

Mein Vorschlag wäre, es so zu strukturieren:

Code: Alles auswählen

import tkinter as tk

class GUI(tk.Tk):
    def __init__(self):
        Tk.__init__(self)

        self.fig1 = Figure (figsize=(5,4), dpi=100)
        self.tpanedwindow4_f2T=TPanedwindow4_f2(self,self.fig1)
        self.canvas1 = self.tpanedwindow4_f2T.canvas1

class TPanedwindow4_f2(tk.Panedwindow):

    def __init__(self,master,fig1,**kwargs):
        Tk.Panedwindow.__init__(self,master,**kwargs)

        self.canvasframe11 = CanvasFrame11(self,fig1)
        self.canvasframe11.place(x=0, y=0, relwidth=1.0, relheight=0.95, bordermode=INSIDE)
        self,canvas1 = self.canvasframe11.canvas1

class CanvasFrame11(tk.Frame):

    def __init__(self,master,fig1,**kwargs):
        Tk.Frame.__init__(self,master,**kwargs)

        self.canvas1 = FigureCanvasTkAgg(fig1,self)


def vp_start_gui():
root = GUI()
# ...
root.mainloop()
Dann kann man erkennen, was GUI ist, und was eine Verbindung nach außen darstellt und hat kein Sammelsurium von wer weiß was.

Ob Du Die Zeichenfunktionen im oder außerhalb des Canvas haben solltet, hängt von deinen Rahmenbedingungen ab. Vor Allem, was ist f: f.canvas.mpl_connect('motion_notify_event', onMouseMove)

Anscheinend noch ein globales Objekt. Wichtig wäre auch GUI Aufbau und Funktionalität nicht zu vermengen, sondern erkennen zu lassen, was was ist.

Ich würde sagen, wenn Du eine Exceltabelle einliest und dann das zeichnen willst, sollte dort wo Du einliest und zeichnest, der Zugriff auf fig1 stattfinden. Und wahrscheinlich würde man es auch dort definieren. Also fig1 an GUI übergeben mit root = GUI(fig1), also nicht so wie in meinem Code Beispiel.

def onMouseMove(event) gehört jedenfalls in die GUI
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Sorry, das mit onMouseMove(event) habe ich falsch geschrieben, es ist ja der MouseMove von matplotlib, also stimmt schon, wo Du ihn hast.

Und auf den canvas in den man zeichnet, sollte man auch auch von außen zugreifen können, dann diesen wohl außen definieren und ihm dabei den master übergeben, den man ja aus der Gui bekommt. Aber GUI in weitere Klassen für Panedwindow und Frames splitten, sollte man tun.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Ich tendiere dazu, den canvas doch in dem frame zu definieren in dem er drin ist, damit man das aus der GUI Definition erkennt. Und wenn man doch von außen zugreifen will, kann man dies ja tun, wenn man self.canvas1 bis in die root zurückliefert und dann noch außen da einträgt, wo man es braucht - etwa ein Interface.
stefanxfg
User
Beiträge: 85
Registriert: Sonntag 2. April 2017, 14:11

Hallo Alfons,

gut ich nehme mir deinen Rat an. Macht man für jedes Kind eine eigene Klasse? Ich habe natürlich noch mehrere Widgets. Die habe ich bereits zu anderen Klassen eingeteilt:
- MenuGUI
-ToolbarGUI
-ContentGUI

Die rufe ich zu geeigneter Zeit auf. Aber du hast schon recht. Im Prinzip dienen die zur Zeit als Container her.

Nach deinem Beitrag zufolge müsste ich demnach die GUI-Klasse in die 3 von mir angesprochenen Ebenen unterteilen und dann in die unteren Ebenen als Frames weiter herunterbrechen.

Zu dem onMouseMove: Ich habe meine Variable mit self.ax und self.fig benannt. Damit kann doch von außen nicht einfach mit fig.canvas.mpl_connect('motion_notify_event', onMouseMove) anteuern.

Naja. Dann schreib ich erst einmal mein Klassen so wie du vorgeschlagen hast um. Danke für deine umfassenden Ratschläge.

Beste Grüße

Stefan
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

stefanxfg hat geschrieben:Hallo Alfons,
gut ich nehme mir deinen Rat an. Macht man für jedes Kind eine eigene Klasse? Ich habe natürlich noch mehrere Widgets. Die habe ich bereits zu anderen Klassen eingeteilt:
- MenuGUI
-ToolbarGUI
-ContentGUI
Nein, nicht für jedes Widget, aber für Widgets die Widgets etnhalten, etwa das Mainwindow, Toplevel, Frame, Labelframe,Panedwindow,Menu, Labelframe, Panedwindow, aber auch Canvas, weil dieser canvas items enthält.

So hast Du dann eine gute Übersicht, was ein Containerwidget enthält. Für widgets, auf die Du später nicht mehr zugreifen willst, weder von außen, noch durch einen Callback, erübrigt sich auch das self, etwa bei einem Label, bei dem nachher nichts mehr geändert wird. So bekommst Du auch einen Überblick, was statisch ist, und was Veränderungen unterliegt.
stefanxfg hat geschrieben:Zu dem onMouseMove: Ich habe meine Variable mit self.ax und self.fig benannt. Damit kann doch von außen nicht einfach mit fig.canvas.mpl_connect('motion_notify_event', onMouseMove) ansteuern.
fig.canvas ist falsch, richtig ist canvas. Und natürlich kannst Du auf canvas von außen zugreifen, etwa:

Code: Alles auswählen

import tkinter as tk

class GUI(tk.Tk):
    def __init__(self,fig):
        Tk.__init__(self)
        self.tpanedwindow4_f2T=TPanedwindow4_f2(self,fig)
        self.canvas1 = self.tpanedwindow4_f2T.canvas1
 
class TPanedwindow4_f2(tk.Panedwindow):
 
    def __init__(self,master,fig,**kwargs):
        Tk.Panedwindow.__init__(self,master,**kwargs)
 
        self.canvasframe11 = CanvasFrame11(self,fig)
        self.canvasframe11.place(x=0, y=0, relwidth=1.0, relheight=0.95, bordermode=INSIDE)
        self,canvas1 = self.canvasframe11.canvas1
 
class CanvasFrame11(tk.Frame):
 
    def __init__(self,master,fig,**kwargs):
        Tk.Frame.__init__(self,master,**kwargs)
 
        self.canvas1 = FigureCanvasTkAgg(fig,self)
 

fig = Figure(figsize=(5, 4), dpi=100)

 
def vp_start_gui():
    root = GUI(fig)
    canvas = root.canvas1
    canvas.mpl_connect('motion_notify_event', onMouseMove)
    root.mainloop()
stefanxfg
User
Beiträge: 85
Registriert: Sonntag 2. April 2017, 14:11

Hallo,

leider bekomme ich das nicht hin. Ich habe wie folgt begonnen den Code umzustellen.

Code: Alles auswählen

def vp_start_gui():
	root = tk.Tk()
	GUI(root).pack(side='top', fill='both', expand=True)
	root.geometry("1024x618+363+230")
	root.mainloop()


class GUI(tk.Frame):
	def __init__(self, parent, *args, **kwargs):
		tk.Frame.__init__(self, parent, *args, **kwargs)

		#Setzen der Main-Frames
		#self.Frameset = framesetting(self)
		
		#Setzen des Hauptmenüs für das Programm
		self.MainMenu = MenuGUI(self)
		self.MainMenu.pack(side='top', fill='both', expand=True)

		
class MenuGUI(tk.Frame):
	def __init__(self, parent, *args, **kwargs):
		tk.Frame.__init__(self, parent, *args, **kwargs)
		
		self.menubar = Menu(self)
		datei = Menu(self.menubar, tearoff=0, activebackground = _bgcolor)
		self.menubar.add_cascade(menu=datei, label=languagefile[0])
		datei.add_command(command=self.action, label=languagefile[1])
		datei.add_command(command=self.action, label=languagefile[2])
		datei.add_command(command=self.action, label=languagefile[3])
		datei.add_command(command=self.action, label=languagefile[4])
		datei.add_command(command=self.action, label=languagefile[5])
		datei.add_separator()
		datei.add_command(command=self.action, label=languagefile[6])
		datei.add_separator()
		datei.add_command(command=self.action, label=languagefile[7])		

	def action(self):
		print 'hallo'
		

if __name__ == '__main__':
    vp_start_gui()
Wie gesagt, ich möchte als oberste Class die GUI, darunter die Classes MenuGUI, ToolbarGUI, ContentGUI. Im Prinzip so:

Code: Alles auswählen

class MenuGUI(tk.Frame): ...
class ToolbarGUI(tk.Frame): ...
class ContentGUI(tk.Frame): ...
Aber zur Zeit wird zwar der Code ausgeführt, aber das Menü nicht angezeigt.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Meinst Du vielleicht so etwas?

Code: Alles auswählen

# -*- coding: utf-8 -*-
import Tkinter as tk

languagefile = ('Sprachen','english','russisch','polnisch','italienisch','spanisch','französisch','dänisch')

def vp_start_gui():
    GUI().mainloop()


class GUI(tk.Tk):
   def __init__(self,**kwargs):
      tk.Tk.__init__(self,**kwargs)
      self.geometry("1024x618+363+230")

      #Setzen der Main-Frames
      #self.Frameset = framesetting(self)
      
      #Setzen des Hauptmenues fuer das Programm
      self.MainMenu = MenuGUI(self)
      self['menu'] = self.MainMenu

      
class MenuGUI(tk.Menu):
   def __init__(self, parent, **kwargs):
      tk.Menu.__init__(self, parent, **kwargs)

      datei = tk.Menu(self, tearoff=0)
      self.add_cascade(menu=datei, label=languagefile[0])
      datei.add_command(command=self.action, label=languagefile[1])
      datei.add_command(command=self.action, label=languagefile[2])
      datei.add_command(command=self.action, label=languagefile[3])
      datei.add_command(command=self.action, label=languagefile[4])
      datei.add_command(command=self.action, label=languagefile[5])
      datei.add_separator()
      datei.add_command(command=self.action, label=languagefile[6])
      datei.add_separator()
      datei.add_command(command=self.action, label=languagefile[7])      

   def action(self):
      print 'hallo'
      

if __name__ == '__main__':
    vp_start_gui()
stefanxfg
User
Beiträge: 85
Registriert: Sonntag 2. April 2017, 14:11

Hallo Alfons,

ich habe nun meine GUI nach deinen Vorschlägen umgearbeitet und kann nun den Plot in den Canvas zeichnen. Wenn ich die Eventfunktionen außen definiere und die fig bis in den root zurückgebe, ist ax für die Eventfunktionen nicht definiert. Wenn ich die Eventfunktionen in die entsprechende Class zuordne, passiert nichts.
Was ist nun besser? Im Prinzip soll etwas in einer anderen Class berechnet werden und in einen Canvas gezeichnet werden.
Hier der Code bisher:

Code: Alles auswählen


def vp_start_gui():
	root = BOStrab_Fahrzeugeinschraenkung(fig1)
	#canvas = root.canvas1
	#canvas.mpl_connect('motion_notify_event', onMouseMove)
	root.mainloop()

class GUI(tk.Tk):

	def __init__(self, fig1, **kwargs):
		tk.Tk.__init__(self, **kwargs)


		#Erzeugung Event Broker Objekt und Speicherung in der GUI 
		self.eventbroker = eventbroker.EventBroker()
		
		#Initialisierung des Hauptmenüs für das Programm
		self.MainMenu = mainmenu.MenuGUI(self, self.eventbroker)
		self['menu'] = self.MainMenu
		
		self.eventbroker.subscribe('NEW_FILE', self.newfile)
			
		
		self.DatenEingabe = DatenEingabeGUI(self, fig1)
		#wird woanders sichtbar gemacht
		
		self.canvas1 = self.DatenEingabe.canvas1

class DatenEingabeGUI(tk.Frame):

	def __init__(self, parent, fig1, **kwargs):
		tk.Frame.__init__(self, parent, **kwargs)
		
		#Content Eingabe	
		self.TNotebook1 = ttk.Notebook(self)
		self.TNotebook1.place(relx=0.0, rely=0.0, relheight=0.93, relwidth=1.0)
		#Sub-Eingabe
		self.TNotebook1_pg0 = EingabeSeite1(self)
		self.TNotebook1.add(self.TNotebook1_pg0, padding=3)
		self.TNotebook1.tab(0, text=languagefile[37],underline="-1",) 
		self.TNotebook1_pg1 = EingabeSeite2(self)
		self.TNotebook1.add(self.TNotebook1_pg1, padding=3)
		self.TNotebook1.tab(1, text=languagefile[43],underline="-1",) 
		self.TNotebook1_pg2 = EingabeSeite3(self)
		self.TNotebook1.add(self.TNotebook1_pg2, padding=3)
		self.TNotebook1.tab(2, text=languagefile[64],underline="-1",) 
		self.TNotebook1_pg3 = EingabeSeite4(self, fig1)
		self.TNotebook1.add(self.TNotebook1_pg3, padding=3)
		self.TNotebook1.tab(3, text=languagefile[75],underline="-1",) 
		
		self.entr1 = self.TNotebook1_pg0.entry1
		self.entr2 = self.TNotebook1_pg0.entry2
		self.entr3 = self.TNotebook1_pg0.entry3
		self.entr4 = self.TNotebook1_pg0.entry4
		
		self.entr1_1 = self.TNotebook1_pg1.entry1_1
		self.entr2_1 = self.TNotebook1_pg1.entry2_1
		self.entr3_1 = self.TNotebook1_pg1.entry3_1		
		self.entr4_1 = self.TNotebook1_pg1.entry4_1	
		self.entr5_1 = self.TNotebook1_pg1.entry5_1
		self.entr6_1a = self.TNotebook1_pg1.entry6_1a
		self.entr6_1b = self.TNotebook1_pg1.entry6_1b
		self.entr7_1a = self.TNotebook1_pg1.entry7_1a
		self.entr7_1b = self.TNotebook1_pg1.entry7_1b
		self.entr8_1a = self.TNotebook1_pg1.entry8_1a
		self.entr8_1b = self.TNotebook1_pg1.entry8_1b
		self.entr9_1a = self.TNotebook1_pg1.entry9_1a
		self.entr9_1b = self.TNotebook1_pg1.entry9_1b
		
		self.entr12 = self.TNotebook1_pg2.entry12
		self.entr13 = self.TNotebook1_pg2.entry13
		self.entr14 = self.TNotebook1_pg2.entry14
		self.entr15 = self.TNotebook1_pg2.entry15
		
		self.entr16 = self.TNotebook1_pg3.entry16
		self.entr20 = self.TNotebook1_pg3.entry20
		self.entr21 = self.TNotebook1_pg3.entry21
		self.entr22 = self.TNotebook1_pg3.entry22
		self.entr23 = self.TNotebook1_pg3.entry23
		
		self.canvas1 = self.TNotebook1_pg3.canvas1

class EingabeSeite4(ttk.Frame):

	def __init__(self, parent, fig1, **kwargs):
		ttk.Frame.__init__(self, parent, **kwargs)		

		self._varF=IntVar()	
				
		self.TPanedwindow4 = ttk.Panedwindow(self, orient="horizontal")
		self.TPanedwindow4.place(relx=0.0, rely=0.0, relheight=1.0, relwidth=1.0)
		self.TPanedwindow4_f1 = EingabeSeite4_1(self, width=75, text='')
		self.TPanedwindow4.add(self.TPanedwindow4_f1)
		self.TPanedwindow4_f2 = EingabeSeite4_2(self, fig1, text='')
		self.TPanedwindow4.add(self.TPanedwindow4_f2)
		#self.__funcid3 = self.TPanedwindow4.bind('<Map>', self.__adjust_sash3)

		self.entry16 = self.TPanedwindow4_f1.Entry16_1
		self.entry20 = self.TPanedwindow4_f1.entrys20
		self.entry21 = self.TPanedwindow4_f1.entrys21
		self.entry22 = self.TPanedwindow4_f1.entrys22
		self.entry23 = self.TPanedwindow4_f1.entrys23
		
		self.canvas1 = self.TPanedwindow4_f2.canvas1

class EingabeSeite4_2(ttk.Labelframe):

	def __init__(self, parent, fig1, **kwargs):
		ttk.Labelframe.__init__(self, parent, **kwargs)

		self.canvas1 = FigureCanvasTkAgg(fig1,self)		
		self.canvas1.get_tk_widget().pack(fill=BOTH, expand=TRUE, pady=2, padx=2, anchor="n")		
		self.canvasframe = MenuCanvas(self, fig1)
		self.canvasframe.pack(side=tk.BOTTOM, fill=tk.X)
		
		global val1, level1, ax1
		self.val1 = np.zeros(100)         
		self.level1 = 0.2
		self.ax1 = fig1.add_subplot(111) #für 2d-Plot
		self.ax1.set_title('Definition der LRUGL')
		self.ax1.set_xlabel('Breite y [mm]')
		self.ax1.set_ylabel('Hoehe z [mm]')
		self.ax1.axis([-2000,2000,0, 5000])
		self.ax1.grid(True)
		self.ax1.plot(parent.TPanedwindow4_f1.ya,parent.TPanedwindow4_f1.z, color='red', linestyle='--', marker='')
		self.ax1.plot(parent.TPanedwindow4_f1.yb,parent.TPanedwindow4_f1.z, color='red', linestyle='--', marker='')
		self.lineVal1, = self.ax1.plot(np.zeros(100))
		self.canvas1.mpl_connect('motion_notify_event', self.onMouseMove)

	def onMouseMove(self, event):
		self.ax1.lines = [self.ax1.lines[0]]
		self.ax1.axhline(y=event.ydata, color="k") #(y=event.ydata, color="k")
		self.ax1.axvline(x=event.xdata, color="k") #(x=event.xdata, color="k")	

	def updateData(self):
		global level1, val1
		clamp = lambda n, minn, maxn: max(min(maxn, n), minn)
		yield 1     # FuncAnimation expects an iterator
	
	def visualize(self, i):
		self.lineVal1.set_ydata(self.val1)
		return self.lineVal1	
	
class MenuCanvas(tk.Frame):

	def __init__(self, parent, fig1,**kwargs):
		tk.Frame.__init__(self, parent, **kwargs)
		
		self.toolbar1 = NavigationToolbar2TkAgg(parent.canvas1, self)

Sicherlich sind hier auch noch überflüssige Anteile. Bitte ignoriere diese.

Grüße Stefan
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

stefanxfg hat geschrieben: Sicherlich sind hier auch noch überflüssige Anteile. Bitte ignoriere diese.
Grüße Stefan
Hallo Stefan, das Programm war nicht lauffähig. Ich habe dann einmal auskommentiert, was nicht definiert war und es zum Laufen gebracht.
Auf der einen Seite sind wohl überflüssige Anteile da, Auf der anderen Seite fehlen aber die wesentliche Anteile.

Das ist übrig, wenn man es laufen lassen will:

Code: Alles auswählen

import tkinter as tk
from tkinter import ttk
#from mainmenu import MenuGUI
import numpy as np
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg

from communication import subscribe, publish

class BOStrab_Fahrzeugeinschraenkung(tk.Tk):

   def __init__(self, fig1, **kwargs):
      tk.Tk.__init__(self, **kwargs)


      #Initialisierung des Hauptmenüs für das Programm
      
      # es fehlt funcSymbolleiste
      #self['menu'] = MenuGUI(self,funcSymbolleiste)

      
      self.DatenEingabe = DatenEingabeGUI(self, fig1)
      #wird woanders sichtbar gemacht
      
      self.canvas1 = self.DatenEingabe.canvas1

class DatenEingabeGUI(tk.Frame):


   def __init__(self, parent, fig1, **kwargs):
      tk.Frame.__init__(self, parent, **kwargs)


      
      #Content Eingabe   
      self.TNotebook1 = ttk.Notebook(self)
      self.TNotebook1.place(relx=0.0, rely=0.0, relheight=0.93, relwidth=1.0)


      #Sub-Eingabe
      '''
      self.TNotebook1_pg0 = EingabeSeite1(self)
      self.TNotebook1.add(self.TNotebook1_pg0, padding=3)
      self.TNotebook1.tab(0, text=languagefile[37],underline="-1",)
      self.TNotebook1_pg1 = EingabeSeite2(self)
      self.TNotebook1.add(self.TNotebook1_pg1, padding=3)
      self.TNotebook1.tab(1, text=languagefile[43],underline="-1",)
      self.TNotebook1_pg2 = EingabeSeite3(self)
      self.TNotebook1.add(self.TNotebook1_pg2, padding=3)
      self.TNotebook1.tab(2, text=languagefile[64],underline="-1",)
      '''

      self.TNotebook1_pg3 = EingabeSeite4(self, fig1)
      self.TNotebook1.add(self.TNotebook1_pg3, padding=3)

      '''
      self.TNotebook1.tab(3, text=languagefile[75],underline="-1",)
      
      self.entr1 = self.TNotebook1_pg0.entry1
      self.entr2 = self.TNotebook1_pg0.entry2
      self.entr3 = self.TNotebook1_pg0.entry3
      self.entr4 = self.TNotebook1_pg0.entry4
      
      self.entr1_1 = self.TNotebook1_pg1.entry1_1
      self.entr2_1 = self.TNotebook1_pg1.entry2_1
      self.entr3_1 = self.TNotebook1_pg1.entry3_1      
      self.entr4_1 = self.TNotebook1_pg1.entry4_1   
      self.entr5_1 = self.TNotebook1_pg1.entry5_1
      self.entr6_1a = self.TNotebook1_pg1.entry6_1a
      self.entr6_1b = self.TNotebook1_pg1.entry6_1b
      self.entr7_1a = self.TNotebook1_pg1.entry7_1a
      self.entr7_1b = self.TNotebook1_pg1.entry7_1b
      self.entr8_1a = self.TNotebook1_pg1.entry8_1a
      self.entr8_1b = self.TNotebook1_pg1.entry8_1b
      self.entr9_1a = self.TNotebook1_pg1.entry9_1a
      self.entr9_1b = self.TNotebook1_pg1.entry9_1b
      
      self.entr12 = self.TNotebook1_pg2.entry12
      self.entr13 = self.TNotebook1_pg2.entry13
      self.entr14 = self.TNotebook1_pg2.entry14
      self.entr15 = self.TNotebook1_pg2.entry15

      self.entr16 = self.TNotebook1_pg3.entry16
      self.entr20 = self.TNotebook1_pg3.entry20
      self.entr21 = self.TNotebook1_pg3.entry21
      self.entr22 = self.TNotebook1_pg3.entry22
      self.entr23 = self.TNotebook1_pg3.entry23
      '''
      
      self.canvas1 = self.TNotebook1_pg3.canvas1

class EingabeSeite4(ttk.Frame):

   def __init__(self, parent, fig1, **kwargs):
      ttk.Frame.__init__(self, parent, **kwargs)      

      self._varF=tk.IntVar()   
            
      self.TPanedwindow4 = ttk.Panedwindow(self, orient="horizontal")
      self.TPanedwindow4.place(relx=0.0, rely=0.0, relheight=1.0, relwidth=1.0)
      #self.TPanedwindow4_f1 = EingabeSeite4_1(self, width=75, text='')
      #self.TPanedwindow4.add(self.TPanedwindow4_f1)
      self.TPanedwindow4_f2 = EingabeSeite4_2(self, fig1, text='')
      self.TPanedwindow4.add(self.TPanedwindow4_f2)
      #self.__funcid3 = self.TPanedwindow4.bind('<Map>', self.__adjust_sash3)

      '''
      self.entry16 = self.TPanedwindow4_f1.Entry16_1
      self.entry20 = self.TPanedwindow4_f1.entrys20
      self.entry21 = self.TPanedwindow4_f1.entrys21
      self.entry22 = self.TPanedwindow4_f1.entrys22
      self.entry23 = self.TPanedwindow4_f1.entrys23
      '''
      self.canvas1 = self.TPanedwindow4_f2.canvas1

class EingabeSeite4_2(ttk.Labelframe):

   def __init__(self, parent, fig1, **kwargs):
      ttk.Labelframe.__init__(self, parent, **kwargs)

      self.canvas1 = FigureCanvasTkAgg(fig1,self)      
      self.canvas1.get_tk_widget().pack(fill=tk.BOTH, expand=True, pady=2, padx=2, anchor="n")      
      self.canvasframe = MenuCanvas(self, fig1)
      self.canvasframe.pack(side=tk.BOTTOM, fill=tk.X)
      
      self.val1 = np.zeros(100)         
      self.level1 = 0.2
      self.ax1 = fig1.add_subplot(111) #für 2d-Plot
      self.ax1.set_title('Definition der LRUGL')
      self.ax1.set_xlabel('Breite y [mm]')
      self.ax1.set_ylabel('Hoehe z [mm]')
      self.ax1.axis([-2000,2000,0, 5000])
      self.ax1.grid(True)
      #self.ax1.plot(parent.TPanedwindow4_f1.ya,parent.TPanedwindow4_f1.z, color='red', linestyle='--', marker='')
      #self.ax1.plot(parent.TPanedwindow4_f1.yb,parent.TPanedwindow4_f1.z, color='red', linestyle='--', marker='')
      self.lineVal1, = self.ax1.plot(np.zeros(100))
      self.canvas1.mpl_connect('motion_notify_event', self.onMouseMove)

   def onMouseMove(self, event):
      self.ax1.lines = [self.ax1.lines[0]]
      self.ax1.axhline(y=event.ydata, color="k") #(y=event.ydata, color="k")
      self.ax1.axvline(x=event.xdata, color="k") #(x=event.xdata, color="k")   

   def updateData(self):
      global level1, val1
      clamp = lambda n, minn, maxn: max(min(maxn, n), minn)
      yield 1     # FuncAnimation expects an iterator
   
   def visualize(self, i):
      self.lineVal1.set_ydata(self.val1)
      return self.lineVal1   
   
class MenuCanvas(tk.Frame):

   def __init__(self, parent, fig1,**kwargs):
      tk.Frame.__init__(self, parent, **kwargs)
      
      self.toolbar1 = NavigationToolbar2TkAgg(parent.canvas1, self)

def vp_start_gui():
   fig = Figure(figsize=(5, 4), dpi=100)
   root = BOStrab_Fahrzeugeinschraenkung(fig)
   #canvas = root.canvas1
   #canvas.mpl_connect('motion_notify_event', onMouseMove)
   root.mainloop()


vp_start_gui()
Wenn man dann Deine GUI anschaut, was sie zeigt, hätte man dieses auch einfacher so machen können:

Code: Alles auswählen

import tkinter as tk
tk.Tk().mainloop()
Es wäre gut, wenn Du etwas Lauffähiges postest, das auch etwas zeigt!

Ich würde Folgendes vorschlagen. Ich baue diese Woche in meinen GuiDesigner noch das Notebook ein. Dann entwirfst Du damit eine sichtbare GUI und fügst dann Deine sonstigen Methoden und Callbacks ein. Wäre das ein Vorschlag?
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@stefanxfg:

Ich schlage Dir eine andere Vorgehensweise vor, mit der Du sicher ans Ziel kommst.
Nimm meinen GuiDesigner, entwerfe das Menü und speichere es ab unter File->Save (sehr wichtig, das nicht wegwerfen)
Außerdem machst Du ein File->Export tkinter->tkinter (names).
Und wenn Du das hast, postest Du es hier und ich zeige Dir dann, wie Du Deine Methoden, Callbacks etc. da einbaust.

Was meinst Du dazu?
stefanxfg
User
Beiträge: 85
Registriert: Sonntag 2. April 2017, 14:11

Hallo Alfons,

entschuldige vielmals für meinen Blödsinn, den ich hier gepostet habe. Ich hatte den Code klein machen wollen. Ich habe nun eigentlich schon viel herumgearbeitet, aber ich werde trotzdem nochmal deinen Rat annehmen und deinen Designer verwenden.
Vielleicht machen wir dann dazu einen neuen Post auf oder per PN.

Danke dir erst einmal für deine Mühen.

Viele Grüße von Stefan
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Das habe ich mal völlig dynamisch gemacht:

Code: Alles auswählen

import tkinter as tk
from DynTkImports import *

# Application definition ============================

class BOStrab_Fahrzeugeinschraenkung(tk.Tk):

    def __init__(self,**kwargs):
        tk.Tk.__init__(self,**kwargs)
        self.myclass = 'BOStrab_Fahrzeugeinschraenkung'
        # widget definitions ===================================
        self.MainMenu = MenuGUI(self)
        self['menu'] = self.MainMenu

class MenuGUI(tk.Menu):

    def __init__(self,master,**kwargs):
        tk.Menu.__init__(self,master,**kwargs)
        self.myclass = 'MenuGUI'
        # widget definitions ===================================
        self.language_submenu = LanguageSubmenu(self,name='#0_language_submenu',tearoff=0)
        self.add_cascade(menu=self.language_submenu,label='Sprachen')
        # indexes for entryconfig later
        self.languages_index = 1

class LanguageSubmenu(tk.Menu):

    def __init__(self,master,**kwargs):
        tk.Menu.__init__(self,master,**kwargs)
        self.myclass = 'LanguageSubmenu'
        # widget definitions ===================================
        self.add_command(label='deutsch')
        # indexes for entryconfig later
        self.deutsch_index = 0
    # this was GuiDefinition by GuiDesigner Export (tkinter names)

        self.container = self # for compatibility with the following code

    # after GUI definition: the Code ===========================
    # this code may also be inserted in GuiDesigner Scripts
    
        self.create_menu()

    def create_menu(self):    

        # for calling more times, delete the menu, which existed before,
        # except the first command

        # for marking the end
        self.container.add_checkbutton()

        # now we delete the menu entries after deutsch
        after_deutsch = self.deutsch_index + 1
        while True:
            itemtype = self.container.type(after_deutsch)
            self.container.delete(after_deutsch)
            if itemtype == 'checkbutton':
                break
 
        # now we make a dynamic creation
        # first we get the style of the first command
        # this style should also be used for the other commands

        command_config = get_entryconfig(self.container,self.deutsch_index)
        
        # we dont't use some now not defined languagefile[i]
        # we can think later of this

        # now we create dynamic commands

        languages = ('deutsch','english','russisch','polnisch','italienisch',None,'spanisch','französisch',None,'dänisch')

        for language in languages:

            if not language:
                self.container.add_separator()
                continue
                
            command_config['label'] = language
            command_config['command'] = partial(self.do_action,language)
            self.container.add_command(**command_config)
                 
    def do_action(self,language):
        publish("SELECT_LANGUAGE",language)
            


BOStrab_Fahrzeugeinschraenkung().mainloop()
Damit es läuft, braucht es dazu:

DynTkImports.py

Code: Alles auswählen

from functools import partial
from Imports.communication import publish,subscribe
import tkinter as tk


def short_dictionary(dictionary):

    # reduce tuple to last entry
    for n,e in dictionary.items():
        dictionary[n] = e[-1]

    # erase doubles
    if "bd" in dictionary:	
        dictionary['bd'] = dictionary['borderwidth']
        dictionary.pop('borderwidth',None)
    if "bg" in dictionary:	
        dictionary['bg'] = dictionary['background']
        dictionary.pop('background',None)
    if "fg" in dictionary:	
        dictionary['fg'] = dictionary['foreground']
        dictionary.pop('foreground',None)
    if "validatecommand" in dictionary:
        dictionary['vcmd'] = dictionary['validatecommand']
        dictionary.pop('validatecommand',None)
    if "invalidcommand" in dictionary:
        dictionary['invcmd'] = dictionary['invalidcommand']
        dictionary.pop('invalidcommand',None)

    # changing not allowed after widget definition - maybe I should save it?. But then I would need the default value.
    dictionary.pop('colormap',None)
    dictionary.pop('screen',None)
    dictionary.pop('visual',None)
    dictionary.pop('class',None)
    dictionary.pop('use',None)
    dictionary.pop('container',None)
    return dictionary


def get_entryconfig(menu,index):

    index = 0
    dictionary = {}
    for entry in (
'activebackground',
'activeforeground',
'accelerator',
'background',
'bitmap',
'columnbreak',
'command',
'font',
'foreground',
'hidemargin',
'image',
'indicatoron',
'label',
'menu',
'offvalue',
'onvalue',
'selectcolor',
'selectimage',
'state',
'underline',
'value',
'variable'
):

        try:
            dictionary[entry] = (menu.entrycget(index,entry),)
        except tk.TclError: pass

    return short_dictionary(dictionary)
Sowie im Unterverzeichnis Imports, die beiden Dateien:

communication.py

Code: Alles auswählen

from Imports.eventbroker import EventBroker

_eventbroker = EventBroker()
publish = _eventbroker.publish
subscribe = _eventbroker.subscribe
eventbroker.py

Code: Alles auswählen

class EventBroker():
    def __init__(self):
        self._dictionary_ = {}
 
    def subscribe(self,message_id,callback):
        if message_id in self._dictionary_:
            print('EventBroker: callback already defined for message id: {}\nThe callback before will be overwritten'.format(message_id))

        self._dictionary_[message_id] = callback
 
    def publish(self,message_id,*args,**kwargs):
        if message_id not in self._dictionary_:
            print('EventBroker: no callback defined for message id: {}'.format(message_id))
        else:
            self._dictionary_[message_id](*args,**kwargs)
So etwas könnte ein schönes GuiDesigner Projekt werden, denn in DynTkInter Scripten entwickelt man alles unabhängig voneinander und kann beliebig umstellen. So sieht es als ein derartiges Script aus:

Code: Alles auswählen

config(myclass='BOStrab_Fahrzeugeinschraenkung')

Menu('MainMenu',myclass='MenuGUI')
goIn()

MenuItem('languages','cascade',label='Sprachen')
goIn()

Menu('language_submenu',myclass='LanguageSubmenu', tearoff=0)
goIn()

MenuItem('deutsch','command',label='deutsch')

widget('deutsch').layout(index=1)

### CODE ===================================================

# this shall later be exported in another form from widgets in tkinter style
# later the GuiDesigner should be able to create such a code

# this name doesn't matter. Only for better recognition
class LanguageSubmenu:
    def __init__(self):

        self._start()
        self._dont_save_dynamically_created()

    def _start(self,container = container()):
        self.container = container
        self.deutsch_index = self.container['tearoff']
        
        # here should follow own code in tkinter style
        # later the GuiDesigner should be able to export this code

# EXPORT =============================

    # after GUI definition: the Code ===========================
    # this code may also be inserted in GuiDesigner Scripts
    
        self.create_menu()

    def create_menu(self):    

        # for calling more times, delete the menu, which existed before,
        # except the first command

        # for marking the end
        self.container.add_checkbutton()

        # now we delete the menu entries after deutsch
        after_deutsch = self.deutsch_index + 1
        while True:
            itemtype = self.container.type(after_deutsch)
            self.container.delete(after_deutsch)
            if itemtype == 'checkbutton':
                break
 
        # now we make a dynamic creation
        # first we get the style of the first command
        # this style should also be used for the other commands

        command_config = get_entryconfig(self.container,self.deutsch_index)
        
        # we dont't use some now not defined languagefile[i]
        # we can think later of this

        # now we create dynamic commands

        languages = ('deutsch','english','russisch','polnisch','italienisch',None,'spanisch','französisch',None,'dänisch')

        for language in languages:

            if not language:
                self.container.add_separator()
                continue
                
            command_config['label'] = language
            command_config['command'] = partial(self.do_action,language)
            self.container.add_command(**command_config)
                 
    def do_action(self,language):
        publish("SELECT_LANGUAGE",language)
            
# /EXPORT =============================

    # we don't want to save dynamically created widgets after self.deutsch_index
    # by the GuiDesigner
    def _dont_save_dynamically_created(self):
        start_index = self.deutsch_index - self.container['tearoff']
        for element in self.container.PackList[start_index+1:]:
            element.dontSave()
        
        
LanguageSubmenu()

### ========================================================

goOut()
select_menu()

goOut()


widget('languages').layout(index=1)

goOut()
select_menu()
Du willst es sicherlich mehrsprachig machen. Und dabei soll wohl auch das Menü in anderen Sprachen erscheinen. Welche Sprachen, sollen das sein und wo kommt Deine Liste languagefile her?
stefanxfg
User
Beiträge: 85
Registriert: Sonntag 2. April 2017, 14:11

Hallo Alfonos,

bitte warte mal. Ich habe alles per Hand geschrieben. Ich denke, du hast recht. Ich werde deinen Designer verwenden. Ich poste dir dann den Code. Ich muss aber alles verstehen.
In meinem ursprünglichen Code habe ich nun für Matplotlib die Funktionen zeichnen lassen und mit Steuerkreuz einen mpl_connect verbunden. Es läuft, aber noch nicht perfekt.

Ich melde mich dann, wenn ich erst einmal etwas habe.

Vielen Dank für deine Hilfe.

Grüße von Stefan
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Alfons Mittelmeyer: ich fang mal mit meinen Bemerkungen von oben an. Sternchenimporte sind, Du weißt es schon, böse. Diese Kommentare mit den vielen = stören nur. Wenn die Struktur nicht aus dem Code an sich klar wird, dann hast Du ein Problem. Was Du da in create_menu löschen mußt, ist mir ein Rätsel. In einem neu erzeugten Widget sollte man eigentlich nichts löschen können/müssen. Zeile 77: das continue durch ein else ersetzen. Die variablen Teile von add_command übergibt man als Keywords und ändert nicht das Wörterbuch in jedem Schleifendurchgang.

In DynTkImports: es heißt shorten_dictionary, weil man Funktionen Tätigkeiten beschreiben. Einen Wörterbucheintrag erst zu lesen und dann zu poppen ist irgendwie umständlich. Da shorten_dictionary das übergebene Wörterbuch ändert, sollte es nicht auch noch Rückgabewert sein:

Code: Alles auswählen

def shorten_dictionary(dictionary):
    # reduce tuple to last entry
    for key, value in dictionary.items():
        dictionary[key] = value[-1]
 
    # erase doubles
    for short, long in [("bd", "borderwidth"), ("bg", "background"), ...]:
        if long in dictionary:
            dictionary[short] = dictionary.pop(long)

    for to_be_removed in ["colormap", ...]:
        if  to_be_removed in dictionary:
            del dictionary[to_be_removed]
get_entryconfig bekommt einen index, der aber gar nicht benutzt wird. communication.py enthält nur irgendwelche seltsamen globalen Variablen, das muß also weg. Was soll der hintere Unterstrich in `_dictionary_`? Und die letzte Datei möchte keiner haben. Gemisch aus globalen Anweisungen und Klassendefinitionen.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Sirius3 hat geschrieben:@Alfons Mittelmeyer: ich fang mal mit meinen Bemerkungen von oben an. Sternchenimporte sind, Du weißt es schon, böse.
Das sind Deine vorgefaßten Meinungen. Sternchenimporte sind in manchen Fällen notwendig.

Oder kann man so etwas anbieten?

Code: Alles auswählen

import DynTkInter as ttk

...
    self.mybutton = ttk.ttk.ttk.Button()
Das käme ohne Sternchenimporte heraus. Meinst Du nicht, das so etwas unsinnig ist?

Es ließe sich vielleicht vermeiden durch:

from DynTkInter import ttk.ttk as ttk

Ich weiß nicht, ob das so geht. und wenn es ginge, dann wäre es doch sehr unschön.
BlackJack

@Alfons Mittelmeyer: Du hast da eine vorgefasste Meinung, Sirius3 hat nur die allgemein anerkannte Vorgehensweise vorgetragen, das man keine Sternchen-Importe verwendet. Du bist der beratungsresistente Geisterfahrer hier. Wieder mal. Deine Importe zeigen das Du anscheinend auch Probleme hast den offensichtlichen Weg das passende `ttk` zu importieren zu verwenden. Deshalb zählt Deine vorgefasste Meinung auch weniger als die von jemanden der Python kann. Sie basiert halt auf Unwissenheit. Ist nicht weiter schlimm, dagegen kann man ja etwas machen.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Sirius3 hat geschrieben:Was Du da in create_menu löschen mußt, ist mir ein Rätsel. In einem neu erzeugten Widget sollte man eigentlich nichts löschen können/müssen.
Man kann natürlich etwas für eine Bestimmte Anzahl von Sprachen machen, etwa einige aus Europa. Es könnten aber auch mehr werden, eventuell kommt Asien auch dazu mit einer unterschiedlichen Sprachenanzahl.

So ist das Menü voll flexibel. Es wird eine Liste übergeben, egal wie kurz oder lang und das Menü baut sich entsprechend der Liste neu auf. Und wenn man etwa activebackground oder sonst etwas an den commands ändern will, braucht man das nur beim ersten tun. Der erste bleibt fix, wobei sich auch der Text ändert, das Aussehen er anderen, die dynamisch erzeugt werden, richtet sich danach!
Sirius3 hat geschrieben: Zeile 77: das continue durch ein else ersetzen. Die variablen Teile von add_command übergibt man als Keywords und ändert nicht das Wörterbuch in jedem Schleifendurchgang
Wie willst Du das Wörterbuch durch Keywords von etwas ändern, das von etwas ändern, das gar nicht noch nicht existiert? Das Wörtebuch stamt vom ersten command und darin wird der label und der command geändert!
Sirius3 hat geschrieben:Einen Wörterbucheintrag erst zu lesen und dann zu poppen ist irgendwie umständlich.
Ja finde iich auch, bei entryconfig bekommt man dumme border objects, etwa so:

'background': ('background', '', '', '', <border object: 'white'>)
Wenn man das liest mit entryconfig, bekommt man 'white'.
Das shorten dagegen hate ich bei config und layouts gemacht, ist aber hier in diesem Fallö überflüssig, darauf hatte ich nicht geachtet, dass ich das hier gar nicht brauche.

Nö, ich brauche es dooch, weil ich dann so eine Fehlermeldung bekomme: bitmap "{}" not defined
Sirius3 hat geschrieben:get_entryconfig bekommt einen index, der aber gar nicht benutzt wird.
Das ist Unsinn, der ist ganz wichtig und wird benutzt bei: menu.entrycget(index,entry)
Sirius3 hat geschrieben:communication.py enthält nur irgendwelche seltsamen globalen Variablen, das muß also weg.
Nein das sind zwei Funktionen, und nur über die ist das Zusammenspiel von Teilen möglich alled andere muss sweg, außer den Widget und Namenlosen Klassen, für die es kein Objekt gibt, und auf die sonst nicht zugreifen kann.

Der Code sieht dann so aus:

def noname1:
def __init__(self,ref_to_widget,ref_to_widget..) # aber nur im selben container (Frame, oder Menu, oder App oder Toplevel, etc.)
...

noname1()

Und nach der Definition werden die sofort aufgerufen und das war es dann ohne die Funktionen subcribe und publish läuft fast gar nichts, höchstens dass eine Betätigung eines Buttons etwas anderes im selben container verändern könnte aber ohne jeglichen Bezug zu außen. Und genau das ist der Sinn, unabhängige Teile, die ganz egal irgenwo stehen könnte, die man einfach jederzeit umverlagern kann und bei denen man nicht verschiedenen Code zu einem Wirrwarr vermischen kann.
Sirius3 hat geschrieben:Und die letzte Datei möchte keiner haben. Gemisch aus globalen Anweisungen und Klassendefinitionen.
Nein es geht nicht um Klassendefinitionen, sondern nur um einen Aufruf, die Klassen sind innerhalb von Funktionen völlig unbekannt und dienen nur dazu, bei der Initialisiereung auch einen Code aufzurufen. Das ist mit einer Klasse einfacher, weil man bei einer Funktion nur auf rückwärts Bezug nehmen kann, bei einer Klasse aber auch vorwärts. Ansonsten wäre eine Klasse unnötig.

Na als Enresultat möchte wahrscheinlich niemand solchen Code haben. Aber als Zwischensfufe ist er ideal. Man ,kann den Code beliebig umverlagern und zerteilen, etwa das:

Code: Alles auswählen

config(myclass='BOStrab_Fahrzeugeinschraenkung')

Menu('MainMenu',myclass='MenuGUI')
goIn()

MenuItem('languages','cascade',label='Sprachen')
goIn()

Menu('language_submenu',myclass='LanguageSubmenu', tearoff=0)
goIn()

MenuItem('deutsch','command',label='deutsch')

widget('deutsch').layout(index=1)

### CODE ===================================================

class LanguageSubmenu:
    def __init__(self):

        self._start()
        self._dont_save_dynamically_created()

    def _start(self,container = container()):
        self.container = container
        self.deutsch_index = self.container['tearoff']
        self._create_menu()
               

    def _create_menu(self):
        current_selection = Selection()
        self.create_menu()
        setSelection(current_selection)

        # here should follow own code in tkinter style
        # later the GuiDesigner should be able to export this code

# EXPORT =============================

    def create_menu(self):    
        
        self.container.add_checkbutton()

        # now we delete the menu entries after deutsch
        after_deutsch = self.deutsch_index + 1
        while True:
            itemtype = self.container.type(after_deutsch)
            self.container.delete(after_deutsch)
            if itemtype == 'checkbutton':
                break
 
        command_config = get_entryconfig(self.container,self.deutsch_index)
        languages = ('deutsch','english','russisch','polnisch','italienisch',None,'spanisch','französisch',None,'dänisch')

        for index,language in enumerate(languages):

            if not language:
                self.container.add_separator()
                continue

            command_config['label'] = language
            command_config['command'] = partial(self.do_action,language)
                
            try:
                self.container.entryconfig(index+self.deutsch_index,**command_config)
            except IndexError:
                self.container.add_command(**command_config)
                 
    def do_action(self,language):
        publish("SELECT_LANGUAGE",language)
            
# /EXPORT =============================

    # we don't want to save dynamically created widgets after self.deutsch_index by the GuiDesigner
    def _dont_save_dynamically_created(self):
        start_index = self.deutsch_index - self.container['tearoff']
        for element in self.container.PackList[start_index+1:]:
            element.dontSave()
        
        
LanguageSubmenu()

### ========================================================

goOut()
select_menu()

goOut()


widget('languages').layout(index=1)

goOut()
select_menu()
Zerteilt man in das:

Code: Alles auswählen

config(myclass='BOStrab_Fahrzeugeinschraenkung')

Menu('MainMenu',myclass='MenuGUI')
goIn()

MenuItem('languages','cascade',label='Sprachen')
goIn()

Menu('language_submenu',link='Scripts/languages.py').select_menu()

goOut()


widget('languages').layout(index=1)

goOut()
select_menu()
Das interessiert aber nicht weiter, denn das sind nur die Widgets und Widget directories für den gui designer.

Und in das:

Code: Alles auswählen

config(myclass='LanguageSubmenu', tearoff=0)

MenuItem('deutsch','command',label='deutsch')

widget('deutsch').layout(index=1)

### CODE ===================================================

class LanguageSubmenu:
    def __init__(self):

        self._start()
        self._dont_save_dynamically_created()

    def _start(self,container = container()):
        self.container = container
        self.deutsch_index = self.container['tearoff']
        self._create_menu()
              

    def _create_menu(self):
        current_selection = Selection()
        self.create_menu()
        setSelection(current_selection)

# EXPORT =============================

    def create_menu(self):    


        # now we delete the menu entries after deutsch
        self.container.add_checkbutton()
        after_deutsch = self.deutsch_index + 1
        while True:
            itemtype = self.container.type(after_deutsch)
            self.container.delete(after_deutsch)
            if itemtype == 'checkbutton':
                break

        command_config = get_entryconfig(self.container,self.deutsch_index)
        languages = ('deutsch','english','russisch','polnisch','italienisch',None,'spanisch','französisch',None,'dänisch')

        for index,language in enumerate(languages):

            if not language:
                self.container.add_separator()
                continue

            command_config['label'] = language
            command_config['command'] = partial(self.do_action,language)
                
            try:
                self.container.entryconfig(index+self.deutsch_index,**command_config)
            except IndexError:
                self.container.add_command(**command_config)
                 
    def do_action(self,language):
        publish("SELECT_LANGUAGE",language)
            
# /EXPORT =============================

    # we don't want to save dynamically created widgets after self.deutsch_index by the GuiDesigner
    def _dont_save_dynamically_created(self):
        start_index = self.deutsch_index - self.container['tearoff']
        for element in self.container.PackList[start_index+1:]:
            element.dontSave()
        
        
LanguageSubmenu()

### ========================================================
Und auf diesen Code kann man sich, dann ohne alles Beiwerk voll konzentrieren, kann nichts anderes hineinmischen, und dann fällt einem ein, dass ja die language Liste empfangen werden muss und macht dann eine entsprechene Methode unter Benutzung von subscribe hinein. Wenn später ein Sender hierfür implementiert ist, dann tut sich auch etwas. Dem Programm fehlt nichts, es ist immer lauffähig, nur ohne Sender, tut es nicht viel.

Und genau darum geht es, immer ein lauffähiges Programm haben und die Funktionalität ohne Nebenwirkungen auf Anderes erweitern.

PS: Und dann muss die Sprachliste jeweils ein Tuple haben nämlich die Sprache, die angezeigt werden soll und die Sprachidentität, eventuell da bei Englisch bleiben.
Antworten