Search code examples
pythontkintercanvascheckboxtkinter.checkbutton

How to hide or show a canvas line controlled by sliders using Checkbuttom from Tkinter?


With the code below it is possible to control the amplitude and frequency of a triangular signal.
Just starting out with Python, I fail to hide or show a given signal using a Checkbutton from tkinter. The few examples found on Internet apply to label texts or images, but none to canvas objects controlled via functions...
Thanks for help

import tkinter as tk
from tkinter import ttk
import numpy as np
from scipy import signal as sg

root = tk.Tk()
root.title('Oscilloscope')
root.geometry("1000x600+0+0")

N1 = 2000
X1 = 1000
O1 = 200

# canva
cnv = tk.Canvas(root, width = 800, height = 400, bg = 'white')
cnv.place(relx=0.5, rely=0.3, anchor=tk.CENTER)
cnv.create_line(400, 0, 400, 400, fill='black', width=1)
cnv.create_line(0, 200, 1000, 200, fill='black', width=1)

# sliders labels
amplitude = ttk.Label(root, text="Amplitude", font=("Arial", 12, "bold"), foreground="black")
amplitude.place(relx=0.3, rely=0.7, anchor=tk.CENTER)
frequence = ttk.Label(root, text="Frequency", font=("Arial", 12, "bold"), foreground="black")
frequence.place(relx=0.7, rely=0.7, anchor=tk.CENTER)

# signal
def draw1(cnv, A1, F1, O1, N1):
    cnv.delete("line1")
    xpts1 = X1 / (N1-1)
    line1 = []
    for i in range(N1):
        x = (i * xpts1)
        y = A1*sg.sawtooth(2*np.pi*F1*i/N1, width=0.5) + O1
        line1.extend((x, y))
    cnv.create_line(line1, fill="red2", width=3, tag="line1")

# control sliders
def ctrl_curseurs_1(*args):
    A1 = A1_valeur.get()
    F1 = F1_valeur.get()
    draw1(cnv, A1, F1, O1, N1)

# horizontal slider for amplitude A1 ############################
def curseur_A1():
    sel = "Value = " + str(value.get(A1))
A1_valeur = tk.IntVar()
A1_frm = ttk.Frame(root)
A1_frm.place(relx=0.3, rely=0.8, anchor=tk.CENTER)
A1_scale = tk.Scale(A1_frm, variable=A1_valeur, command=ctrl_curseurs_1,
                           from_ = -200, to = 200, length=200, showvalue=1, tickinterval=100, orient = tk.HORIZONTAL, resolution=1)
A1_scale.pack(anchor=tk.CENTER)

# horizontal slider for frequency F1 ############################
def curseur_F1():
    sel = "Value = " + str(value.get(F1))
F1_valeur = tk.IntVar()
F1_frm = ttk.Frame(root)
F1_frm.place(relx=0.7, rely=0.8, anchor=tk.CENTER)
F1_scale = tk.Scale(F1_frm, variable=F1_valeur, command=ctrl_curseurs_1,
                           from_ = 0, to = 50, length=200, showvalue=1, tickinterval=10, orient = tk.HORIZONTAL, resolution=1)
F1_scale.pack(anchor=tk.CENTER)

root.mainloop()

Solution

  • Taking into account two things:

    • You can set whether or not a canvas item should be shown using the state attribute, which accepts "hidden" and "normal" as values.

    • You can modify that state once the item is created using canvas.itemconfigure.

    With it, you can do something like:

    import tkinter as tk
    from tkinter import ttk
    import numpy as np
    from scipy import signal as sg
    
    root = tk.Tk()
    root.title('Oscilloscope')
    root.geometry("1000x600+0+0")
    
    N1 = 2000
    X1 = 1000
    O1 = 200
    
    # canvas
    cnv = tk.Canvas(root, width = 800, height = 400, bg = 'white')
    cnv.place(relx=0.5, rely=0.3, anchor=tk.CENTER)
    cnv.create_line(400, 0, 400, 400, fill='black', width=1)
    cnv.create_line(0, 200, 1000, 200, fill='black', width=1)
    
    # sliders labels
    amplitude = ttk.Label(root, text="Amplitude", font=("Arial", 12, "bold"), foreground="black")
    amplitude.place(relx=0.3, rely=0.7, anchor=tk.CENTER)
    frequence = ttk.Label(root, text="Frequency", font=("Arial", 12, "bold"), foreground="black")
    frequence.place(relx=0.7, rely=0.7, anchor=tk.CENTER)
    
    # signal
    def draw1(cnv, A1, F1, O1, N1):
        cnv.delete("line1")
        xpts1 = X1 / (N1-1)
        line1 = []
        for i in range(N1):
            x = (i * xpts1)
            y = A1*sg.sawtooth(2*np.pi*F1*i/N1, width=0.5) + O1
            line1.extend((x, y))
        cnv.create_line(
            line1, fill="red2", width=3, tag="line1",
            state='normal' if check_control_1.get() else 'hidden'
            )
    
    # control sliders
    def ctrl_curseurs_1(*args):
        A1 = A1_valeur.get()
        F1 = F1_valeur.get()
        draw1(cnv, A1, F1, O1, N1)
    
    # horizontal slider for amplitude A1 ############################
    def curseur_A1():
        sel = "Value = " + str(value.get(A1))
    
    A1_valeur = tk.IntVar()
    A1_frm = ttk.Frame(root)
    A1_frm.place(relx=0.3, rely=0.8, anchor=tk.CENTER)
    A1_scale = tk.Scale(A1_frm, variable=A1_valeur, command=ctrl_curseurs_1,
                               from_ = -200, to = 200, length=200, showvalue=1, tickinterval=100, orient = tk.HORIZONTAL, resolution=1)
    A1_scale.pack(anchor=tk.CENTER)
    
    # horizontal slider for frequency F1 ############################
    def curseur_F1():
        sel = "Value = " + str(value.get(F1))
    
    F1_valeur = tk.IntVar()
    F1_frm = ttk.Frame(root)
    F1_frm.place(relx=0.7, rely=0.8, anchor=tk.CENTER)
    F1_scale = tk.Scale(F1_frm, variable=F1_valeur, command=ctrl_curseurs_1,
                               from_ = 0, to = 50, length=200, showvalue=1, tickinterval=10, orient = tk.HORIZONTAL, resolution=1)
    F1_scale.pack(anchor=tk.CENTER)
    
    check_control_1 = tk.BooleanVar(root, value=False)
    ttk.Checkbutton(
        root,
        text="Signal 1",
        variable=check_control_1,
        command=lambda: cnv.itemconfigure(
            "line1", state='normal' if check_control_1.get() else 'hidden')
        ).place(
            relx=0.5, rely=0.8, anchor=tk.CENTER
            )
    
    root.mainloop()