Home > Blockchain >  self.canvas.create_text dissappearing
self.canvas.create_text dissappearing

Time:11-06

from tkinter import *
 
class wigdet:
    def __init__(self):
        window = Tk()
        window.title("Buttons and Radio Buttons")

        frame1 = Frame(window)
        frame1.pack()
        self.v1 = IntVar()

        rb_red = Radiobutton(frame1, text="Red", fg="black", variable= self.v1, value = 1, command=self.process_radio_button)
        rb_yellow = Radiobutton(frame1, text="Yellow", fg="black", variable= self.v1, value = 2, command=self.process_radio_button)
        rb_white = Radiobutton(frame1, text="White", fg="black", variable= self.v1, value = 3, command=self.process_radio_button)
        rb_gray = Radiobutton(frame1, text="Gray", fg="black", variable= self.v1, value = 4, command=self.process_radio_button)
        rb_green = Radiobutton(frame1, text="Green", fg="black", variable= self.v1, value = 5, command=self.process_radio_button)

        rb_red.grid(row=0, column=0)
        rb_yellow.grid(row=0, column=1)
        rb_white.grid(row=0, column=2)
        rb_gray.grid(row=0, column=3)
        rb_green.grid(row=0, column=4)

        frame2 = Frame(window)
        frame2.pack()

        self.canvas = Canvas(frame2, width=400, height=100, bg="white")
        self.x1, self.y1 = (200, 50)
        self.canvas.create_text(self.x1,self.y1, fill="black",text="Welcome",font=("Arial"),tags="text")
        self.canvas.pack()
        
        frame3 = Frame(window)
        frame3.pack()

        bt_left = Button(frame3, text = "<=", fg = "black", command = self.process_left)
        bt_right = Button(frame3, text = "=>", fg = "black", command = self.process_right)
        
        bt_left.grid(row=2, column=0)
        bt_right.grid(row=2, column=1)

        window.mainloop()

    def process_radio_button(self):
        if (self.v1.get() == 1):
            self.canvas.create_rectangle(0,0,400, 100, fill="red")
            self.canvas.create_text(self.x1,self.y1,fill="black",text="Welcome",font=("Arial"),tags="text")
        
        elif (self.v1.get() == 2):
            self.canvas.create_rectangle(0,0,400, 100, fill="yellow")
            self.canvas.create_text(self.x1,self.y1,fill="black",text="Welcome",font=("Arial"),tags="text")

        elif (self.v1.get() == 3):
            self.canvas.create_rectangle(0,0,400, 100, fill="white")
            self.canvas.create_text(self.x1,self.y1,fill="black",text="Welcome",font=("Arial"),tags="text")
        
        elif (self.v1.get() == 4):
            self.canvas.create_rectangle(0,0,400, 100, fill="gray")
            self.canvas.create_text(self.x1,self.y1,fill="black",text="Welcome",font=("Arial"),tags="text")
        
        else:
            self.canvas.create_rectangle(0,0,400, 100, fill="green")
            self.canvas.create_text(self.x1,self.y1,fill="black",text="Welcome",font=("Arial"),tags="text")
        
    def process_left(self):
        self.canvas.delete("text")
        if self.x1>10:
            self.x1-=10
        if self.x1>60:
            self.x1-=10
            self.canvas.create_text(self.x1, self.y1, fill="black", tags="text")
        

    def process_right(self):
        self.canvas.delete("text")
        if self.x1 < 350:
            self.x1 =10
        if self.x1 < 400:
            self.x1 =10
            self.canvas.create_text(self.x1, self.y1, fill="black", tags="text")
    

wigdet()

I have two buttons where I am to move the text left and right. I also have 5 Radiobuttons that decide the background color. when i chose for example red, and then move the text left, the text disappears, but reappears slightly to the left when i hit the Radiobutton for red.

any simple solution?

CodePudding user response:

Here is an improved version of the code (explanation in code comments):

import tkinter as tk
from tkinter import font


# inherit from frame for easier use
class MyWidget(tk.Frame):
    def __init__(self, master, **kwargs):
        super().__init__(master, **kwargs)

        # create the frame for radio buttons
        self.rb_frame = tk.Frame(self)
        # variable for radio buttons and set value to something already
        self.rb_var = tk.StringVar(value='white')
        # iterate over colors and place the radio buttons with the color
        # text and set `value=color`, that way `self.rb_var.get()`
        # will return the color
        for color in ('red', 'yellow', 'white', 'gray', 'green'):
            tk.Radiobutton(
                self.rb_frame, text=color.title(), value=color,
                variable=self.rb_var, command=self.color_select
            ).pack(side='left')

        # configure canvas stuff, set width and height
        self.c_w, self.c_h = 600, 500
        # get initial x position for text
        self.x = self.c_w // 2
        self.canvas = tk.Canvas(self, width=self.c_w, height=self.c_h)
        # create rectangle on canvas and save its id
        self.rect = self.canvas.create_rectangle(
            0, 0, self.c_w, self.c_h, fill=self.rb_var.get(),
            outline=self.rb_var.get())
        # define the text to display
        self.text = 'Welcome!'
        # use a `font` so that later can measure the text width
        self.font = font.Font(family='Arial')
        # measure text width to allow for better collision detection with sides
        self.text_width = self.font.measure(self.text)
        # create the text and save the id
        self.canvas_text = self.canvas.create_text(
            self.x, self.c_h // 2, text=self.text, font=self.font)

        # create the frame where the left/right buttons will be
        self.btn_frame = tk.Frame(self)
        # create the buttons
        tk.Button(
            self.btn_frame, text='>>>', command=lambda: self.move('right')
        ).pack(side='right', fill='x', expand=True)
        tk.Button(
            self.btn_frame, text='<<<', command=lambda: self.move('left')
        ).pack(side='left', fill='x', expand=True)

        # place all the created frames
        self.rb_frame.grid(row=0, column=0)
        self.canvas.grid(row=1, column=0)
        self.btn_frame.grid(row=2, column=0, sticky='we')

    # the color selector method which is called from radio buttons
    def color_select(self):
        value = self.rb_var.get()
        # as you can see then using `value=color` earlier now this is possible
        # to easily set the color
        self.canvas.itemconfig(self.rect, fill=value, outline=value)
    
    # method for moving
    def move(self, side):
        # get half width of font because the center of the created text is middle
        half_width = self.text_width // 2
        if side == 'left' and self.x - 10 - half_width > 0:
            self.x -= 10
        elif side == 'right' and self.x   10   half_width < self.c_w:
            self.x  = 10
        # move the created text
        self.canvas.coords(self.canvas_text, self.x, self.c_h // 2)


# in general good practice to use this if statement
if __name__ == '__main__':
    # create the root
    root = tk.Tk()
    
    # put the widget on the window
    frame = MyWidget(root)
    frame.pack()

    root.mainloop()

As you see, there is no need to delete the created text or rectangles, just change their coordinates and options (which can be done with tags too, I just used IDs)

Also:
I strongly advise against using wildcard (*) when importing something, You should either import what You need, e.g. from module import Class1, func_1, var_2 and so on or import the whole module: import module then You can also use an alias: import module as md or sth like that, the point is that don't import everything unless You actually know what You are doing; name clashes are the issue.

I strongly suggest following PEP 8 - Style Guide for Python Code. Function and variable names should be in snake_case, class names in CapitalCase. Don't have space around = if it is used as a part of keyword argument (func(arg='value')) but have space around = if it is used for assigning a value (variable = 'some value'). Have space around operators ( -/ etc.: value = x y(except here value = x y)). Have two blank lines around function and class declarations. Object method definitions have one blank line around them.

CodePudding user response:

It is because you delete existing text and create a new text object without assigning text to it. So you just create a empty text object.

Also it is better to create those canvas items once and using coords() to move them and itemconfig() to change their attributes.

class widget:
    def __init__(self):
        ...
        # create the background rectangle first
        self.canvas.create_rectangle(0,0,400, 100, fill="white", tags="rect")
        self.canvas.create_text(self.x1,self.y1, fill="black",text="Welcome",font=("Arial"),tags="text")
        ...

    def process_radio_button(self):
        if (self.v1.get() == 1):
            fill="red"
        elif (self.v1.get() == 2):
            fill="yellow"
        elif (self.v1.get() == 3):
            fill="white"
        elif (self.v1.get() == 4):
            fill="gray"
        else:
            fill="green"

        # change the fill color
        self.canvas.itemconfig("rect", fill=fill)

    def process_left(self):
        # why two if ???
        if self.x1>10:
            self.x1-=10
        if self.x1>60:
            self.x1-=10
            # move the text
            self.canvas.coords("text", self.x1, self.y1)

    def process_right(self):
        # why two if ???
        if self.x1 < 350:
            self.x1 =10
        if self.x1 < 400:
            self.x1 =10
            # move the text
            self.canvas.coords("text", self.x1, self.y1)
  • Related