How to draw diagonal arrows in tkinter? - python

I need to draw two arrows from the blue and green box pointing to the yellow box. I tried drawing diagonal lines using create_line but it didn't work. can anyone please suggest any ways I could draw these arrows.
the error message when using create_line is: AttributeError: '_tkinter.tkapp' object has no attribute 'create_line'
from tkinter import *
import tkinter as tk
window = Tk()
window.geometry("900x500")
window.configure(background='red')
window.title("Theoretical")
label1 = Label(window, text="Hess' cycle of combustion", fg="black", bg="red", font=("Comic Sans MS", 20))
label1.pack()
text1 = Text(window, width=20, height = 1, bg= "blue")
text1.place(x=200, y=100)
window.create_line(0, 0, 200, 100)
window.create_line(0, 100, 200, 0, fill="white")
text2 = Text(window, width=20, height = 1, bg= "green")
text2.place(x=520, y=100)
text3 = Text(window, width=20, height = 1, bg= "yellow")
text3.place(x=370, y=250)
## arrow = Label(window, width=13,height = 1, text = "-------------->", bg= "lawn green", font=("Helvetica", 20))
## arrow.place(x= 330, y=90)
global textbox
textbox = Text(window, width=400, height=10)
textbox.place(x=0, y= 365)

tkinter lines have an arrow option; however, as pointed out in te comments,create_line is a Canvas method: you must therefore use a tk.Canvas object to draw lines:
This minimum example shows you how:
import tkinter as tk
window = tk.Tk()
canvas = tk.Canvas(window)
canvas.pack()
canvas.create_line(0, 0, 200, 100, arrow=tk.LAST)
window.mainloop()
Please note that to avoid "hard to fix" problems and unexpected behavior, it is usually recommended not to import modules in the main namespace (i/e do not from tkinter import *), and not to mix geometry managers ((i/e do not use .place and .pack in the same app)
Edit:
In order to place widgets on a canvas, you must use the Canvas.create_window() method:
import tkinter as tk
window = tk.Tk()
window.geometry("600x600")
canvas = tk.Canvas(window, width=600, height=600)
label_1 = tk.Label(window, text = "from here", anchor = tk.W)
label_1.configure(width = 10, activebackground = "#33B5E5", relief = tk.FLAT)
label_1_window = canvas.create_window(280, 0, anchor=tk.NW, window=label_1)
label_2 = tk.Label(window, text = "to there", anchor = tk.W)
label_2.configure(width = 10, activebackground = "#33B5E5", relief = tk.FLAT)
label_2_window = canvas.create_window(280, 310, anchor=tk.NW, window=label_2)
canvas.pack()
canvas.create_line(300, 40, 300, 300, arrow=tk.LAST)
window.mainloop()

Related

I need to make a label expand based on the window size

I wanted to make a title label that spans the top of the screen with the text in the middle. this works when the window opens but if I go into fullscreen, the label only spans half the length. if I make the label bigger, the text is not in the middle. it's a win-lose situation. any way to make both of them work?
import tkinter as tk
from tkinter import *
Title_Font = ("Hallo Sans", 20, "bold")
MyBlue = '#30D5C8'
Home = tk.Tk()
Home.title("Guitar Bud")
Home.geometry('1000x700')
Home.configure(bg='grey')
ExersisesLbl = tk.Label(Home, width=60, height=1, bg='black', fg=MyBlue, text='Exersises', font=Title_Font, anchor=CENTER)
ExersisesLbl.grid(row=0, column=0, columnspan=5, sticky='ew')
like #JacksonPro said, use grid_columnconfigure().
import tkinter as tk
from tkinter import *
Title_Font = ("Hallo Sans", 20, "bold")
MyBlue = '#30D5C8'
Home = tk.Tk()
Home.title("Guitar Bud")
Home.geometry('1000x700')
Home.configure(bg='grey')
Home.grid_columnconfigure(0, weight=1)
ExersisesLbl = tk.Label(Home, width=60, height=1, bg='black', fg=MyBlue, text='Exersises', font=Title_Font, anchor=CENTER)
ExersisesLbl.grid(row=0, column=0, columnspan=5, sticky='ew')
mainloop()

How to make button using canvas in toplevel() Tkinter

I'm trying to make a button using canvas.create_window in toplevel() in Tkinter. Button is used to go back to main window. First "Start" button is displayed but second "Back" button is not. Code below.
from tkinter import *
win = Tk()
def play_button():
win.withdraw()
top = Toplevel()
top.geometry("300x300")
button_back = Button(top, text="Back", command=back_button)
canvas_two = Canvas(top, width = 300, height = 300)
canvas_two.pack(fill="both", expand=True)
Button_reverse = canvas_two.create_window(0, 0, anchor="nw", window=button_back)
top.resizable(False, False)
def back_button():
win.deiconify()
win.geometry("300x300")
canvas = Canvas(win, width = 300, height = 300)
canvas.pack(fill="both", expand=True)
button_play = Button(win, text="Play", command=play_button)
Play_button = canvas.create_window(0, 0, anchor="nw", window=button_play )
win.mainloop()
The problem is the ordering of creation of canvas_two and button_back. You need to create the Canvas first and then put the Button on top of it as shown below.
def play_button():
win.withdraw()
top = Toplevel()
top.geometry("300x300")
canvas_two = Canvas(top, width=300, height=300)
canvas_two.pack(fill="both", expand=True)
button_back = Button(top, text="Back", command=back_button)
Button_reverse = canvas_two.create_window(0, 0, anchor="nw", window=button_back)
top.resizable(False, False)

Tkinter building windows similar to Root

I am currently working on Tkinter codes. I happen to need to create new windows exactly similar to root but my codes do not perfectly work well. The title does not appear on the new windows. This is an excerpt:
from tkinter import *
#New Window
def New_page():
window()
#Main Window
def window():
window = Tk()
window.resizable(0,0)
window.configure(background='grey')
window.state('zoomed')
window.geometry("2000x4000")
#Title Frame
TITLE_FRAME = Frame(window, relief = GROOVE, bg = "black", width=2000, height=160).grid(row=0, column=0, columnspan = 150, rowspan = 30, sticky=E+W)
Label(TITLE_FRAME, text= 'THIS IS THE TITLE PART', fg='sky blue', bg='black', font='none 40 bold',
borderwidth=5).grid(row=0,column=10)
#New Window Button
ENTRY_FRAME = Frame(window, bg='sky blue', relief = SUNKEN)
ENTRY_FRAME.grid(row=40, column=0, columnspan=20, padx=15, pady=15)
Label(ENTRY_FRAME, text= 'SELECT THE APPROPRIATE DETAILS:',
bg = 'sky blue', fg='black', font='none 10 bold', borderwidth=5).grid(row=0, column=0, columnspan=20)
NEW_WINDOW = Button(ENTRY_FRAME, text="NEW WINDOW", font='None 8 bold', width=30, command=New_page, fg= 'black', bg='white')
NEW_WINDOW.grid(row = 3, column = 0, columnspan = 3, padx = 10, pady = 10)
window.mainloop()
#Calling the Tkinter function
window()
Like in the comments, Toplevel is the way to go for this one. What I changed:
Moved making the window object globally
Renamed the function to make it makeWindow(master)
makeWindow(master) now takes in a master widget. This will make
all of the widgets made there be part of the master window.
New_page was modified to make a new Toplevel() widget
TITLE_FRAME is now made first and then grid is called on it
(EDIT) These edits fix the problems with the original window closing the program.
We want to remap the closing behaviour to act how we want. This is done with window.protocol("WM_DELETE_WINDOW",callback). We must define the callback function, in this case, deleteWindow(win).
What delete window does is take a window, and if it is the root window it hides it. Otherwise, it deletes the window. I used window.withdraw() in my code, but there's probably a better way to do it.
The way it knows if it should close the program is by keeping track of the number of active windows in the activeWindows variable. When a window is created, the number increases, when delete it decreases. If the number of active windows is 0, we can delete the main window to close the program cleanly.
The way we bind the deleteWindow(win) callback is through an anonymous function. Normally, the protocol mentioned above does not give any arguments, but we want to know which window called the function. To do this, whenever we bind the destruction of the window, we define a anonymous function using lambda that calls deleteWindow.
.
from tkinter import *
window = Tk()
window.resizable(0,0)
window.configure(background='grey')
window.state('zoomed')
window.geometry("2000x4000")
activeWindows = 1
def deleteWindow(win):
if win == window:
window.withdraw()
else:
win.destroy()
global activeWindows
activeWindows-=1
if activeWindows <= 0:
window.destroy()
#New Window
def New_page():
global activeWindows
activeWindows+=1
NEW_WINDOW=Toplevel(background='grey')
NEW_WINDOW.geometry("2000x4000")
NEW_WINDOW.protocol("WM_DELETE_WINDOW",lambda:deleteWindow(NEW_WINDOW))
makeWindow(NEW_WINDOW)
#Main Window
def makeWindow(master):
#Title Frame
TITLE_FRAME = Frame(master, relief = GROOVE, bg = "black", width=2000, height=160)
TITLE_FRAME.grid(row=0, column=0, columnspan = 150, rowspan = 30, sticky=E+W)
Label(TITLE_FRAME, text= 'THIS IS THE TITLE PART', fg='sky blue', bg='black', font='none 40 bold',
borderwidth=5).grid(row=0,column=10)
#New Window Button
ENTRY_FRAME = Frame(master, bg='sky blue', relief = SUNKEN)
ENTRY_FRAME.grid(row=40, column=0, columnspan=20, padx=15, pady=15)
Label(ENTRY_FRAME, text= 'SELECT THE APPROPRIATE DETAILS:',
bg = 'sky blue', fg='black', font='none 10 bold', borderwidth=5).grid(row=0, column=0, columnspan=20)
NEW_WINDOW = Button(ENTRY_FRAME, text="NEW WINDOW", font='None 8 bold', width=30, command=New_page, fg= 'black', bg='white')
NEW_WINDOW.grid(row = 3, column = 0, columnspan = 3, padx = 10, pady = 10)
window.protocol("WM_DELETE_WINDOW",lambda: deleteWindow(window))
#Calling the Tkinter function
makeWindow(window)
window.mainloop()

How to create back button on tkinter/or delete all widgets on canvas?

from Tkinter import *
import Tkinter as ttk
from ttk import *
master = Tk()
master.title("Learn Spanish")
canvas = Canvas(master, width = 600, height = 600, bg = 'orange')
canvas.grid(row=0, column=0, rowspan=2, columnspan=2)
title = canvas.create_text(300, 100, text = 'Learn To Speak Spanish', font = ('Freestyle Script', 60), fill = 'firebrick')
text = canvas.create_text(300, 250, text = 'Welcome! What are you ready \n to learn Spanish today?', font = ('Freestyle Script', 35), fill = 'firebrick')
def nextScreen():
canvas.delete("all")
canvas.create_text(300, 125, text = 'Select one of the activities below to get started.', font = ('Freestyle Script', 30), fill = 'firebrick')
btn4 = ttk.Button(master, bg='white', command=wordStudy, text='Word Study', font=('Freestyle Script', 35))
canvas.create_window(300, 300, width=300, height=90, window=btn4)
btn5 = ttk.Button(master, bg='white', command=matchGame, text='Matching Game', font=('Freestyle Script', 35))
canvas.create_window(300, 400, width=300, height=90, window=btn5)
btn6 = ttk.Button(master, bg='white', command=quiz, text='Quiz Yourself', font=('Freestyle Script', 35))
canvas.create_window(300, 500, width=300, height=90, window=btn6)
def quiz():
canvas.delete("all") # haven't worked on this one yet
def matchGame():
canvas.delete("all") # haven't worked on this one yet either
def wordStudy(): #i'm right here
canvas.delete("all")
Canvas(master, width = 600, height = 600, bg = 'orange')
canvas.create_text(300, 50, text = 'Study the terms below.', font = ('Freestyle Script', 30), fill = 'firebrick')
btn = ttk.Button(master, bg='white', command=back, text='Back', font=('Freestyle Script', 15))
canvas.create_window(100, 575, width=40, height=20, window=btn)
def textvariable(text): # translation of english pop up when buttons pressed
entry.delete(0,END)
entry.insert(0,text)
return
b1=Button(canvas, text="Good morning.", command=lambda:textvariable("Buenos dias.")).grid(row=1, column=1, pady=(100,50), padx=100)
b2=Button(canvas, text="Good night.", command=lambda:textvariable("Buenas noches.")).grid(row=1, column=2, pady=(100,50), padx=100)
b3=Button(canvas, text="Goodbye.", command=lambda:textvariable("Adiós.")).grid(row=2, column=1, pady=50, padx=100)
b4=Button(canvas, text="Hello.", command=lambda:textvariable("Hola.")).grid(row=2, column=2, pady=50, padx=100)
b5=Button(canvas, text="What is your name?", command=lambda:textvariable("¿Cómo te llamas?")).grid(row=3, column=1, pady=50, padx=100)
b6=Button(canvas, text="How are you?", command=lambda:textvariable("¿Cómo estás?")).grid(row=3, column=2, pady=50, padx=100)
b7=Button(canvas, text="Please.", command=lambda:textvariable("Por favor.")).grid(row=4, column=1, pady=50, padx=100)
b8=Button(canvas, text="Thank you.", command=lambda:textvariable("Gracias.")).grid(row=4, column=2, pady=50, padx=100)
entry = Entry(canvas, textvariable=textvariable, width=30)
entry.grid(column=2, row=8, padx=10, pady=(10, 50))
canvas.create_text(295, 570, text = 'Translation:', font = ('Freestyle Script', 25), fill = 'firebrick')
btnStart = ttk.Button(master, bg='white', command=nextScreen, text='Start', font=('Freestyle Script', 35))
canvas.create_window(295, 400, width=400, height=90, window=btnStart)
def back(): # i'm not sure how to make back button or make this work!
canvas.delete("all")
mainloop()
I am working on a Spanish app. I want to know how I can effectively create a back button or delete everything on my canvas, including widgets. I saw some posts that had created a class, and use self. and so on, but I am not familiar with all that stuff. I'm a beginner.
In the first pages you are placing widgets on canvas with:
canvas.create_window(..., window=btn)
In wordStudy you are using grid:
b1=Button(canvas, text=... )).grid(row=1,...
To delete widgets placed with grid use either of:
b1.grid_remove()
b1.grid_forget()
I can't remember the exact difference so you have to google it.
Response to comment
When you delete all windows from canvas in the function nextScreen(),
it works because canvas is defined in the main script. By rules for
name scope a function will use variables from main if they are not
defined inside the function. As canvas is mutable you can change it
without declaring it global.
The buttons b1-b8 is created inside the function wordStudy() and
the references (names: b1, b2 etc.) are garbage collected when the
function exits. For declaring them global to work they must be
created in the main script outside the function; all the global
statement does is include variables from the main script into the
function scope.
I recommend that you use the same method of putting widgets on the
canvas as in the function nextScreen() throughout the script, as it
works just fine. The code is also easier to read and maintain if you
solve the same task in the same way all the time.
If you want to experiment with grid_forget() here is some example
code to play with. When you do it, be sure to pay attention to the
scope.
from tkinter import *
root = Tk()
root.geometry('200x100')
field = Canvas(root, bg='white')
field.pack(expand='yes', fill='both')
def start_function(event=None):
print('Start button pressed.')
start_button = Button(field, text='start', command=start_function)
start_button.grid(row=0, column=0) # Place the button on the canvas
def remove_button(event=None): # Forget the button
start_button.grid_forget()
def show_button(event=None): # Remember the button
start_button.grid()
root.bind('a', remove_button) # Press "a" on the keyboard to forget
root.bind('s', show_button) # Press "s" on the keyboard to remember

Why is my code printing ".!canvas.!entry"?

I don't understand what is going on. I have defined 2 functions. 1 function displays text, entry boxes, and a submit button. I have it so that when the submit button is pressed, it prints the information the user put inside of the entry boxes. I've shortened my code for easier readability.
from tkinter import *
from PIL import Image, ImageTk
canvas_width = 360
canvas_height = 525
file = r"C:\Users\kraak\Desktop\PyCharm Community Edition 2017.1.2\borderedpaper.GIF"
master = Tk()
canvas = Canvas(master, width=canvas_width, height=canvas_height)
old_img = PhotoImage(file=file)
new_img = old_img.subsample(3, 3)
canvas.create_image(-11, -10, anchor=NW, image=new_img)
canvas.create_window(0, 0, height=1, width=1, anchor=NW)
canvas.create_text(0, 0, text="Test")
e1 = Entry(canvas)
def callback():
print(e1)
def answer():
e1 = Entry(canvas)
canvas.create_window(250, 100, window=e1, height=15, width=100)
label = Label(text="Enter a word.")
label.place(x=40, y=90)
e2 = Entry(canvas)
canvas.create_window(250, 125, window=e2, height=15, width=100)
label = Label(text="Enter a word.")
label.place(x=40, y=115)
button = Button(text="Submit.", command=callback)
button.place(x=150, y=460)
answer()
canvas.pack()
mainloop()

Categories