punkte und linien HILFE

Fragen zu Tkinter.
Antworten
cindyH1
User
Beiträge: 5
Registriert: Dienstag 1. Mai 2018, 09:16

Hallo!
Ich muss eine Aufgabe lösen und bin (meiner Meinung nach) schon sehr weit gekommen, aber ich komm bei ein paar Punkten nicht weiter...
Meine Aufgabe ist: Ein Tkinter Programm zum Zeichnen programmieren, es gibt 2 Buttons, von denen immer einer geklickt ist:
-"O" : Wenn dieser Button geklickt ist, werden bei Mausklick auf dem Zeichenfeld Kreise gezeichnet
-"|" : Wenn dieser Button geklickt ist, werden bei Mausklick Linien gezeichnet (Es sollen eigentlich Verbindungslinien zwischen den Kreisen sein, aber ich habe noch nicht raus gefunden wie das geht...)
Ich habe mein Fenster schon so designt wie es aussehen soll (Menü, Button, Größe, Farbwahl...), aber ich verstehe nicht wie ich das mit den Buttons und den commands mache, also dass ein Button geklickt ist, dann werden Kreise gezeichnet, und wenn man auf den anderen klickt werden nur noch Linien gezeichnet... Ich hatte einen sehr schlechten Tutor auf der Uni und es wurden uns keine hilfreichen Unterlagen zur Verfügung gestellt... ich bin schon sehr stolz dass ich es mit dem Programm überhaupt soweit geschafft habe :-) (nach stundenlang im Internet nach vergleichbaren Funktionen zu suchen), vielleicht erkennt jemand von euch mein Problem....

Code: Alles auswählen

from tkinter import *

class Zeichenflaeche(Frame):
    def __init__(self,parent):
        Frame.__init__(self,parent)
        self.parent = parent
        self.color='blue'
    
        # Canvas
        self.canvas = Canvas(parent,width=800,height=600,bg='white')
        
        #Funktionen anbinden um Kreise zu zeichnen
        self.canvas.bind('<Button-1>', self.klicken)
        self.canvas.bind('<ButtonRelease-1>', self.klicken)
               
        self.canvas.bind('<Button-2>', self.linie)
        self.canvas.bind('<ButtonRelease-2>', self.linie)
                
        self.button1=ttk.Button(parent, text='O', command=self.button_clicked)
        self.button1.bind('<1>')
        self.button1.pack()
        
        self.button2=ttk.Button(parent, text='|', command=self.button_clicked)
        self.button2.bind('<2>')
        self.button2.pack()
        

        # Menu
        menu = Menu(parent)
        parent.config(menu=menu)
        filemenu = Menu(menu,tearoff=0)
        menu.add_cascade(label="Datei",menu=filemenu,underline=0)
        filemenu.add_command(label="Neu", command=self.initCanvas,underline=0,accelerator='Strg+N')
        filemenu.add_separator()
        filemenu.add_command(label="Beenden", command=root.quit,underline=0,accelerator='Strg+Q')
        viewmenu = Menu(menu,tearoff=0)
        menu.add_cascade(label="Ansicht", menu=viewmenu,underline=0)
        viewmenu.add_command(label="Farbe wählen...", command=self.chooseColor,underline=0,accelerator='Strg+F')
        helpmenu = Menu(menu,tearoff=0)
        menu.add_cascade(label="Hilfe", menu=helpmenu,underline=0)
        helpmenu.add_command(label="Über...", command=self.aboutApp,underline=0)

        # Accelerator-Bindungen
        root.bind_all('<Control-n>',self.accNew)
        root.bind_all('<Control-q>',self.accQuit)
        root.bind_all('<Control-f>',self.accChooseColor)
        
        self.canvas.pack()
        
    def klicken(self, event):
        self.canvas.create_oval(event.x, event.y, event.x+50, event.y+50, fill=self.color)
       
    def linie(self, event):
        if str(event.type)=='ButtonPress':
            self.canvas.old_coords=event.x, event.y
        elif str(event.type)=='ButtonRelease':
            x, y= event.y, event.y
            x1, y1= self.canvas.old_coords
            self.canvas.create_line(x,y,x1,y1)
        
    def button_clicked(self):
        if str(self.button1['state']) == 'normal':
            self.klicken()
        elif str(self.button2['state']) == 'normal':
            self.linie()
            
                
    def loslassen(self, event):
        self.geklickt=False 
        
    def initCanvas(self):
        self.canvas.delete(ALL)
        
    def chooseColor(self):
        (triple,hexcode) = colorchooser.askcolor()
        if hexcode:
            self.color = hexcode
        self.klicken()

    def aboutApp(self):
        messagebox.showinfo("Zeichenblatt","Das ist ein Zeichenblatt zum Zeichnen mit Kreisen und Linien")

    def accNew(self,event):
        self.initCanvas()

    def accQuit(self,event):
        root.quit()

    def accChooseColor(self,event):
        self.chooseColor()


root = Tk()
root.title("Zeichenblatt")
app = Zeichenflaeche(root)

root.mainloop()
Benutzeravatar
ThomasL
User
Beiträge: 1366
Registriert: Montag 14. Mai 2018, 14:44
Wohnort: Kreis Unna NRW

also ich kriege hier nen Error "NameError: name 'tkk' is not defined"

Code: Alles auswählen

self.button1=tkk.Button(parent, text='O', command=self.button_clicked)
Ich bin Pazifist und greife niemanden an, auch nicht mit Worten.
Für alle meine Code Beispiele gilt: "There is always a better way."
https://projecteuler.net/profile/Brotherluii.png
cindyH1
User
Beiträge: 5
Registriert: Dienstag 1. Mai 2018, 09:16

vielleicht noch oben hinzufügen: from tkinter import ttk
bei mir funktioniert es aber auch so....
__deets__
User
Beiträge: 14533
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du musst dir bei deinen Buttons merken, welcher der letzte war, der ausgewählt wurde. In deinen Event-Handlern reagierst du dann darauf, statt das über die verschiedenen Buttons zu machen.
cindyH1
User
Beiträge: 5
Registriert: Dienstag 1. Mai 2018, 09:16

Ich habe es jetzt ein wenig umgeschrieben, aber jetzt werden nur Kreise gezeichnet.... ich weiß einfach nicht wie ich das mit den Buttons mache, dass es sich merkt welcher gedrückt ist...

Code: Alles auswählen

from tkinter import *
from tkinter import ttk

class Zeichenflaeche(Frame):
    def __init__(self,parent):
        Frame.__init__(self,parent)
        self.parent = parent
        self.color='blue'
    
        # Canvas
        self.canvas = Canvas(parent,width=800,height=600,bg='white')
        
        #Funktionen anbinden um Kreise zu zeichnen
        self.canvas.bind('<Button-1>', self.button_clicked)
        self.canvas.bind('<ButtonRelease-1>', self.loslassen)   
        
        self.button1=ttk.Button(parent, text='O', command=self.button_clicked)
        self.button1.bind('<1>')
        self.button1.pack()
        
        self.button2=ttk.Button(parent, text='|', command=self.button_clicked)
        self.button2.bind('<1>')
        self.button2.pack()

        self.canvas.pack()

   def button_clicked(self, event):
        if str(self.button1['state']) == 'normal':
            self.canvas.create_oval(event.x, event.y, event.x+50, event.y+50, fill=self.color)
        elif str(self.button2['state']) == 'normal':
            self.button1.conifg(state=DISABLED)
            if str(event.type)=='ButtonPress':
                self.canvas.old_coords=event.x, event.y
            elif str(event.type)=='ButtonRelease':
                x, y= event.y, event.y
                x1, y1= self.canvas.old_coords
                self.canvas.create_line(x,y,x1,y1)

Benutzeravatar
__blackjack__
User
Beiträge: 13100
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@cindyH1: Du reagierst völlig falsch auf die Schaltflächen. Wenn der Benutzer beispielsweise auf die Schaltfläche mit dem O klickt, dann will er doch zu dem Zeitpunkt wo er da drauf klickt gar keinen Kreis zeichnen, sondern nur auswählen, dass er von jetzt an Kreise zeichen will wenn er im `Canvas` klickt. Und da kannst Du nicht an beide Schaltflächen den gleichen Code binden, sondern jeweils welchen der sich im Objekt merkt was der Benutzer ab sofort gerne Zeichnen möchte, Kreise/Punkte oder Linien.

An der Stelle kann man sich den Code vereinfachen wenn man anstelle von zwei unabhängigen `Button` besser `Radiobutton` erstellt bei denen man ohne viel selbst zu programmieren, leicht sehen kann welche Option gerade ausgewählt ist. Mit der `indicatoron`-Option kann man auch entscheiden ob das wie ein Radiobutton oder wie ein normaler Button aussehen soll. Im letzteren Fall wird die ausgewählte Variante dauerhaft gedrückt dargestellt. Die Schaltfläche rastet dann sozusagen ein.

Tatsächlich zeichnen muss man wenn die Maustaste losgelassen wird. Erst an der Stelle musst Du prüfen *was* gezeichnet werden soll, Linie oder Kreis. (Also eigentlich würde ich das da nicht prüfen sondern einfach die Methode aufrufen die mit den Schaltflächen ausgesucht wurde, aber das muss man als Anfänger noch nicht können.)

Kein einziger der `str()`-Aufrufe in dem Code macht Sinn. Weg damit.

Den *-Import solltest Du auch los werden. Das kippt Dir ca. 190 Namen in das Modul von denen Du kaum etwas verwendest. Bei `tkinter` ist es üblich ``import tkinter as tk`` zu schreiben und dann ``tk.Button`` statt nur ``Button``.

`button1` und `button2` sind schlechte Namen. Ein Name soll beschreiben wofür der Wert steht. Und der Leser will ja nicht wissen der wie vielte Button das ist, sondern welche Bedeutung der in dem Programm hat. Also beispielsweise `select_line_drawing_button`. Falls man denen überhaupt explizite Namen geben und sie an das Objekt binden möchte.

Entscheide Dich mal für Deutsch oder Englisch. Also am besten Englisch. Das das Gegenstück zu `button_clicked` `losgelassen` heisst, ist beispielsweise wenig intuitiv. Zudem ist das `clicked` falsch, denn darunter versteht man die komplette Abfolge von Taste gedrückt *und* dann wieder losgelassen. Die beiden Einzelereignisse heissen `pressed` und `released`, das zusammen macht ein `clicked` aus.

Abkz snd ebenflls unschön. Das `acc*` ist nicht selbsterklärend und auch keine gängige Abkürzung. Zumal man alle drei Methoden entweder mit einem optionalen Argument auf den Methoden die die dann aufrufen und/oder durch einfache ``lamdba``-Funktionen ersetzen kann.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten