Eigenes Widget mit pack positionieren

Fragen zu Tkinter.
Antworten
snowflake
User
Beiträge: 93
Registriert: Mittwoch 9. November 2016, 15:46

Guten Morgen zusammen,

ich möchte mir ein eigenes Widget schreiben und mit pack positionieren. Das Widget funktioniert so wie ich es mir vorstelle, allerdings würde ich gerne die Position nicht wie von mir programmiert übergeben, sondern ganz normal wie bei jedem anderen Widget mit k1.pack(side="top") durchführen. Kann mir jemand helfen?

Code: Alles auswählen

from tkinter import *
import random

class Kreisdiagramm():
    
    """ Master angeben, Anordnung (left, top, right, bottom), Größe, Vordergrundfarbe, Hintergrundfarbe, Hintergrundfarbe1, Maximalwert, Einheit"""
    
    def __init__(self, master, side="top", size=100, bgcolor="white", fgcolor="green", fg1color="gray86", max=100, unit=""):
        
        self.master = master
        self.side = side
        self.size = size
        self.bgcolor = bgcolor
        self.fgcolor = fgcolor
        self.fg1color = fg1color
        self.max = max
        self.unit = unit
        
        self.Diagramm = Canvas(self.master, width=self.size, height=self.size)
        self.Diagramm.pack(side=self.side)
        
        self.Diagramm.create_rectangle(0, 0, self.size, self.size, fill=self.bgcolor)
        
        coord1 = self.size/10, self.size/10, self.size-self.size/10, self.size-self.size/10
        self.Diagramm.create_oval(coord1, fill=fg1color, width=0)

        coord2 = self.size/10, self.size/10, self.size-self.size/10, self.size-self.size/10
        stickness = int(self.size/14)
        self.Kreisbogen = self.Diagramm.create_arc(coord2, fill=self.fgcolor, outline=self.fgcolor, start=90, extent=0, style=ARC, width=stickness)
        
        self.font1 =("Arial", int(self.size/5))
        self.Anzeige = self.Diagramm.create_text(self.size/2, self.size/2, text="", font=self.font1)

        self.font2 =("Arial", int(self.size/10))
        self.Einheit = self.Diagramm.create_text(self.size/2, self.size-self.size/3+self.size/20, text="", font=self.font2)

    def aktualisiereKreisdiagramm(self, value):
        value1 = int(360 * value / k1.max)
        self.Diagramm.itemconfigure(self.Kreisbogen, extent=value1)
        self.Diagramm.itemconfigure(self.Anzeige, text=str(value*-1))
        self.Diagramm.itemconfigure(self.Einheit, text=self.unit)

def zufallswinkel():
    zufallszahl = random.randint(0, k1.max) * -1
    k1.aktualisiereKreisdiagramm(zufallszahl)


if __name__ == "__main__":
    root = Tk()
    
    k1 = Kreisdiagramm(root, "top", 400, "white", "BlueViolet", "gray86", 360, "Grad")

    Aendern = Button(root, text="Neuer Winkel", command=zufallswinkel)
    Aendern.pack(side="top")

    root.mainloop()

Viele Grüße und einen schönen Sonntag.

snowflake

PS: Was ich auch nicht ganz verstehe ist, warum bei genau 360° kein Kreisbogen zu sehen ist?
Benutzeravatar
__blackjack__
User
Beiträge: 14085
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@snowflake: Du hast ja nicht wirklich ein eigenes Widget. Das ist ja nicht vom Typ `Widget` sondern vom Typ `Kreisdiagramm` und `object`. Letzteres weil nicht explizit von einer anderen Klasse abgeleitet wird, und dann implizit `object` die Basisklasse ist. Wenn das ein `Widget` sein soll, dann musst Du die Klasse `Kreisdiagramm` von einer `Widget`-(Unter)Klasse ableiten. Beispielsweise von `Canvas`. Dann ist Deine Klasse vom Typ `Kreisdiagramm` auch vom Typ `Canvas` was wiederum ein `Widget` ist:

Code: Alles auswählen

In [21]: tk.Canvas.__bases__                                                    
Out[21]: (tkinter.Widget, tkinter.XView, tkinter.YView)
Der Sternchen-Import sollte auch verschwinden und es wäre gut sich an die Namenskonventionen zu halten. Englischsprachige Bezeichner sind auch besser als diese Mischung.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Sirius3
User
Beiträge: 18289
Registriert: Sonntag 21. Oktober 2012, 17:20

@snowflake: Du hast hier jetzt 66 Beiträge und da kann es kaum sein, dass Dich niemand auf die Schreibweise von Variablennamen und Methoden hingewiesen hat, das man keine *-Importe oder globale Variablen benutzen sollte.

Man muß nicht alles an Attribute binden. Das verrechnen des -1 ist an der falschen Stelle.
Wenn eine Klasse eine Methode haben soll, dann muß man die implementieren.

Code: Alles auswählen

import tkinter as tk
import random

class Kreisdiagramm():
    def __init__(self, master, size=100, bgcolor="white", fgcolor="green", fg1color="gray86", max=100, unit=""):
        self.max = max
        self.unit = unit
        coordinates = size/10, size/10, size-size/10, size-size/10
        thickness = int(size/14)
        font1 =("Arial", int(size/5))
        font2 =("Arial", int(size/10))
        self.diagramm = tk.Canvas(master, width=size, height=size)        
        self.diagramm.create_rectangle(0, 0, size, size, fill=bgcolor)        
        self.diagramm.create_oval(coordinates, fill=fg1color, width=0)
        self.kreisbogen = self.diagramm.create_arc(coordinates, fill=fgcolor, outline=fgcolor, start=90, extent=0, style=tk.ARC, width=thickness)        
        self.anzeige = self.diagramm.create_text(size/2, size/2, text="", font=font1)
        self.einheit = self.diagramm.create_text(size/2, size-size/3+size/20, text="", font=font2)

    def aktualisiere(self, value):
        value1 = int(360 * value / self.max)
        self.diagramm.itemconfigure(self.kreisbogen, extent=-value1)
        self.diagramm.itemconfigure(self.anzeige, text=str(value))
        self.diagramm.itemconfigure(self.einheit, text=self.unit)

    def pack(self, **kw):
        self.diagramm.pack(**kw)

    def zufallswinkel(self):
        zufallszahl = random.randint(0, self.max)
        self.aktualisiereKreisdiagramm(zufallszahl)

def main():
    root = tk.Tk()
    kreisdiagramm = Kreisdiagramm(root, 400, "white", "BlueViolet", "gray86", 360, "Grad")
    kreisdiagramm.pack(side="top")
    Button(root, text="Neuer Winkel", command=kreisdiagramm.zufallswinkel).pack(side="top")
    root.mainloop()

if __name__ == "__main__":
    main()
Benutzeravatar
__blackjack__
User
Beiträge: 14085
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@snowflake: Der Code im ``if __name__ …``-Zweig gehört in eine Funktion. Dann sieht man auch, das `k1` (sehr schlechter Name) einfach so von `zufallswinkel()` (ein Funktionsname der keine Tätigkeit beschreibt) verwendet wird, ohne das der Wert als Argument übergeben wird. Das ist unübersichtlich und unsauber. Und noch schlimmer ist, das die `aktualisiereKreisdiagramm()` Methode auch auf `k1` zugreift statt `self` zu verwenden.

Man kann auch so einiges *nicht* an das Objekt binden was später nie wieder benötigt wird.

Der erste `create_rectangle()` macht nicht so viel Sinn, weil man da einfach die Hintergrundfarbe vom gesamten Canvas setzen kann.

Man nummeriert keine Namen. `coord1` und `coord2` haben zudem den gleichen Wert — warum wird das zweimal berechnet und an unterschiedliche Namen gebunden? `font1` und `font2` werden auch noch als Attribute gesetzt.

`value1` sollte wohl besser `degrees` oder `angle` heissen.

Code: Alles auswählen

#!/usr/bin/env python3
import random
import tkinter as tk


class CircleDiagram(tk.Canvas):
    """
    Master angeben, Anordnung (left, top, right, bottom), Größe,
    Vordergrundfarbe, Hintergrundfarbe, Hintergrundfarbe1, Maximalwert, Einheit
    """

    def __init__(
        self,
        master,
        size=100,
        background_color="white",
        foreground_color="green",
        inner_circle_color="gray86",
        max_value=100,
        unit="",
    ):
        self.max_value = max_value
        self.unit = unit

        tk.Canvas.__init__(
            self, master, width=size, height=size, background=background_color
        )
        margin = size / 10
        circle_coordinates = (margin, margin, size - margin, size - margin)
        self.create_oval(circle_coordinates, fill=inner_circle_color, width=0)
        self.arc_id = self.create_arc(
            circle_coordinates,
            fill=foreground_color,
            outline=foreground_color,
            start=90,
            extent=0,
            style=tk.ARC,
            width=int(size / 14),
        )
        self.value_text_id = self.create_text(
            size / 2, size / 2, text="", font=("Arial", int(size / 5))
        )
        self.unit_text_id = self.create_text(
            size / 2,
            size - size / 3 + size / 20,
            text="",
            font=("Arial", int(size / 10)),
        )

    def set_value(self, value):
        angle = int(360 * value / self.max_value)
        self.itemconfigure(self.arc_id, extent=-angle)
        self.itemconfigure(self.value_text_id, text=value)
        self.itemconfigure(self.unit_text_id, text=self.unit)


def main():
    root = tk.Tk()

    diagram = CircleDiagram(
        root, 400, "white", "BlueViolet", "gray86", 360, "Grad"
    )
    diagram.pack()
    tk.Button(
        root,
        text="Neuer Winkel",
        command=lambda: diagram.set_value(
            random.randint(0, diagram.max_value)
        ),
    ).pack()

    root.mainloop()


if __name__ == "__main__":
    main()
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
snowflake
User
Beiträge: 93
Registriert: Mittwoch 9. November 2016, 15:46

Hallo zusammen,

vielen Dank für die vielen wertvollen Hinweise, die ich soweit auch nachvollziehen kann. Allerdings verstehe ich nicht den Unterschied zwischen der Klasse mit und ohne die Vererbung von tk.Canvas. Darf ich es nur als Widget bezeichnen, wenn es eine Vererbung (z. B. von tk.Canvas) gibt? Das Skript ohne die Vererbung mit tk.Canvas funktioniert ja auch. Wenn dem so ist, wäre es kein Widget, aber eine Klasse mit der gleichen Funktion.

Viele Grüße
snowflake
Sirius3
User
Beiträge: 18289
Registriert: Sonntag 21. Oktober 2012, 17:20

@snowflake: außer pack haben Widgets ja noch eine ganze Reihe weiterer Methoden, die Du auch nachprogrammieren müßtest.
Die eine Variante hat ihre Vor- und Nachteile, die andere auch.
Im Fall von Vererbung muß man dafür sorgen, dass alle Methoden, die geerbt werden, auch funktionieren und konsistent sind. Im anderen Fall muß man natürlich auch dafür sorgen, dass in allen Anwendungsfällen die Klasse sich so verhält wie ein richtiges Widget.
snowflake
User
Beiträge: 93
Registriert: Mittwoch 9. November 2016, 15:46

@Sirius3: Jetzt habe ich es verstanden. Danke dafür.
Antworten