I know this is a bit of a broad question. I'm just starting out with Python and Tkinter and I'm building my first app. I have a few widgets done and a few more on the way but I can't seem to place them the way I want at all. Here's my code:
import tkinter
from tkinter import font
import tkinter as tk
import time
from threading import Thread
from PIL import Image, ImageTk
class Window(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.master = master
def update_timeText():
current = time.strftime("%H:%M")
seconds = time.strftime(":%S")
currentDate=time.strftime("%a %e %B, %Y")
timeText1.configure(text=current, fg='white', background='black')
timeText1.grid(row=0,column=0, sticky='NW', padx=15, pady=15)
timeText2.configure(text=seconds, fg='white', background='black')
timeText2.grid(row=0, column=1, pady=17, sticky='NW')
Date.configure(text=currentDate, fg='white', background='black')
Date.grid(row=0, column=0, columnspan=3, sticky='NW', padx=20, pady=124, rowspan=2)
root.after(1000, update_timeText)
def update_Weather():
temperature=int(13)
picture = ImageTk.PhotoImage(picturePNG)
weatherIcon.configure(image=picture, background='black')
weatherIcon.grid(column=5, sticky='ne')
weatherTemperature.configure(text=temperature, fg='white', background='black')
weatherTemperature.grid(column=6, sticky='ne')
root.after(100000, update_Weather)
root = tk.Tk()
root.configure(background='black')
root.title('Smart Mirror')
timeText1 = tk.Label(root, text="", font=("Opinio", 90, "bold"))
timeText2 = tk.Label(root, text="", font=("Opinio", 45, "bold"))
weatherTemperature=tk.Label(root, text="", font=("Roboto Condensed", 80))
weatherIcon=tk.Label(root, image="")
Thread(target=update_Weather).start()
Thread(target=update_timeText).start()
app = Window(root)
root.mainloop()
What I managed to get after hours of messing with the grid and Googling:
Screenshot
What I'm trying to get:
Screenshot2
I just can't seem to make any space horizontally inbetween the widgets. I know I'm asking for a lot but if someone could explain grid a bit instead of just posting an answer I'd be really grateful, since I've read a lot of information about it online and can't seem to get the gist of it at all.
You create widgets as slaves to root instead of as slaves to the class Window(). This is confusing and makes the class dependant on that the main program creates some of the widgets that the Window() object will later configure.
Assign row when you grid widgets, as #Henry Yik comments.
Here is an example of how I would place the widgets:
import tkinter as tk
class Window(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.master = master
self.configure(bg='thistle') # To see where self is
self.pack(expand=True, fill='both', padx=10, pady=10)
self.columnconfigure(0, weight=1) # Which column should change
# when window size changes
Date = tk.Label(self, text='19:38:25', fg='white', bg='black',
font='-size 70')
Date.grid(row=0, column=0, columnspan=3, sticky='NW', padx=20, pady=70, rowspan=2)
picture = tk.PhotoImage(file='sun.png')
weatherIcon = tk.Label(self, image=picture, bg='black')
weatherIcon.grid(row=0, column=5, sticky='NE')
weatherIcon.image = picture # Save a reference to the picture
weatherTemperature = tk.Label(self, text='4°C', fg='white', bg='black',
font='-size 70')
weatherTemperature.grid(row=0, column=6, sticky='NE')
root = tk.Tk()
root.configure(background='tan') # To see where root is
root.geometry('900x300')
app = Window(root)
root.mainloop()
Does this explain any better?
Related
I am creating a login system with Tkinter and the grid() method but I have no idea where I can put the image. As i did not use classes and functions, it was pretty easy to embed the path for the image (img = PhotoImage(file = r"C:\\Users\\admin\\Desktop\\Foto\\Haken.png") img1 = img.subsample(10,10), but, since I am new in Python, I don´t really know where to put the path in this code when the code is more organized. Here is what I tried:
from tkinter import *
from tkinter.ttk import *
class Login_system(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.initUI()
def initUI(self):
self.parent.title("Login System Prova")
Label(text = "Surname").grid(row=0, column=0, sticky=W, pady=2)
Label(text = "Your Password").grid(row=1, column=0, sticky=W, pady=2)
Label(text = "Your E-Mail").grid(row=2, column=0,sticky=W, pady=2)
Entry().grid(row = 0, column=1, pady=2)
Entry().grid(row = 1, column=1, pady=2)
Entry().grid(row = 2, column=1, pady=2)
Entry().grid(row = 3, column=1, pady=2)
Checkbutton(text = "Keep me on-line").grid(row = 4, sticky = W, columnspan= 1)
def main():
root = Tk()
root.geometry("200x150+400+300")
root.resizable(True, True)
global image
image = Frame.PhotoImage(file = r"C:\\Users\\admin\\Desktop\\Foto\\Haken.png")
app = Login_system(root)
root.mainloop()
if __name__ == "__main__":
main()
But I get this error:
Traceback (most recent call last):
File "C:\Users\admin\Desktop\Python\GUI APP\login_system_new.py", line 40, in <module>
main()
File "C:\Users\admin\Desktop\Python\GUI APP\login_system_new.py", line 34, in main
image = Frame.PhotoImage(file = r"C:\\Users\\admin\\Desktop\\Foto_Marco\\Haken.png")
AttributeError: type object 'Frame' has no attribute 'PhotoImage'
[Finished in 0.5s]
Have you got any suggestion? I would like to put the image on the far right column.
If the image is part of the Login_system, then it is better put it inside the class. Also you forget to specify the parent of widgets inside the class, so the widgets will be children of root instead.
Also avoid importing modules like below:
from tkinter import *
from tkinter.ttk import *
In this case, you cannot use some of the widgets from tkinter because they are override by those from ttk.
Below is a modified sample based on your code:
import tkinter as tk
from tkinter import ttk
class Login_system(ttk.Frame):
def __init__(self, parent):
ttk.Frame.__init__(self, parent)
self.initUI()
def initUI(self):
self.master.title("Login System Prova")
ttk.Label(self, text="Surname").grid(row=0, column=0, sticky=tk.W, pady=2)
ttk.Label(self, text="Your Password").grid(row=1, column=0, sticky=tk.W, pady=2)
ttk.Label(self, text="Your E-Mail").grid(row=2, column=0, sticky=tk.W, pady=2)
ttk.Entry(self).grid(row=0, column=1, pady=2)
ttk.Entry(self).grid(row=1, column=1, pady=2)
ttk.Entry(self).grid(row=2, column=1, pady=2)
ttk.Entry(self).grid(row=3, column=1, pady=2)
ttk.Checkbutton(self, text="Keep me on-line").grid(row=4, sticky=tk.W, columnspan=2)
self.image = tk.PhotoImage(file=r"C:\\Users\\admin\\Desktop\\Foto\\Haken.png").subsample(10,10)
ttk.Label(self, image=self.image).grid(row=0, column=2, rowspan=5, padx=(20,0))
def main():
root = tk.Tk()
#root.geometry("200x150+400+300")
#root.resizable(True, True)
app = Login_system(root)
app.pack(fill='both', expand=1, padx=10, pady=10)
root.mainloop()
if __name__ == "__main__":
main()
I am having an issue getting text to show on Tkinter buttons. I don't get any errors so im not sure why this is the case, I will post the full working code below if anyone can see any obvius errors.I have never had any problems with button labels before but I am trying a new layout with dropdown tabs so this could be a reason for the issue.
import tkinter as tk
from tkinter import ttk
class ToggledFrame(tk.Frame):
def __init__(self, parent, text="", *args, **options):
tk.Frame.__init__(self, parent, *args, **options)
root.state('zoomed')
root.configure(background='black')
root.title("Stylibleue dashboard")
self.show = tk.IntVar()
self.show.set(0)
self.title_frame = ttk.Frame(self)
self.title_frame.pack(fill="x", expand=1)
ttk.Label(self.title_frame, text=text).pack(side="left", fill="x")
self.toggle_button = ttk.Checkbutton(self.title_frame, width=1, text='+',
command=self.toggle,
variable=self.show, style='Toolbutton')
self.toggle_button.pack(side="left", fill="x", expand=1)
self.sub_frame = tk.Frame(self, relief="sunken", borderwidth=1)
def toggle(self):
if bool(self.show.get()):
self.sub_frame.pack(fill="x", expand=1)
self.toggle_button.configure(text='-')
else:
self.sub_frame.forget()
self.toggle_button.configure(text='+')
def helloCallBack (self):
print ("hello")
if __name__ == "__main__":
root = tk.Tk()
t = ToggledFrame(root, text='Bassin 1', relief="raised")
t.pack(fill="x", anchor="s")
B = ttk.Button(t.sub_frame, text ='Feeder 1',command = quit)
ttk.Button(t.sub_frame).pack( expand=0, pady=2, padx=2, anchor="w")
c = ttk.Button(t.sub_frame, text ="Feeder 2")
ttk.Button(t.sub_frame).pack(expand=0, pady=2, padx=2, anchor="w")
t2 = ToggledFrame(root, text='Bassin 2', relief="raised")
t2.pack(fill="x")
d = ttk.Button(t2.sub_frame, text ='Feeder 1',command = quit)
ttk.Button(t2.sub_frame).pack( expand=0, pady=2, padx=2, anchor="w")
e = ttk.Button(t2.sub_frame, text ="Feeder 2")
ttk.Button(t2.sub_frame).pack(expand=0, pady=2, padx=2, anchor="w")
root.mainloop()
You are creating 8 buttons but only giving a label to four of them, and it’s the ones without a label that you are calling .pack on. The ones with the label are never added to the window.
I have been looking at my code for a while and new to tkinter. The purpose of my code is to display text within the Canvas widget not overlay a label. But unsure how to do this:
My code is as follows:
from tkinter import *
class Example(Frame):
def printLabel(self):
self.hello = []
self.hello.append('Hello')
self.hello.append('World!')
return(self.hello)
def updatePanel(self):
self.panelA.config(text="{}".format(self.printLabel()))
def __init__(self, root):
Frame.__init__(self, root)
self.buttonA()
self.viewingPanel()
def buttonA(self):
self.firstPage = Button(self, text="Print Text", bd=1, anchor=CENTER, height = 11, width = 13, command=lambda: self.updatePanel())
self.firstPage.place(x=0, y=0)
def viewingPanel(self):
self.panelA = Label(self, bg='white', width=65, height=13, padx=3, pady=3, anchor=NW, text="")
self.panelA.place(x=100, y=0)
self.cl= Canvas(self.panelA,bg='WHITE',width=165,height=113,relief=SUNKEN)
canvas_id = self.cl.create_text(15, 15, anchor="nw")
self.xb= Scrollbar(self.panelA,orient="horizontal", command=self.cl.xview)
self.xb.pack(side=BOTTOM,fill=X)
self.xb.config(command=self.cl.xview)
self.yb= Scrollbar(self.panelA,orient="vertical", command=self.cl.yview)
self.yb.pack(side=RIGHT,fill=Y)
self.yb.config(command=self.cl.yview)
self.cl.itemconfig(canvas_id,font=('Consolas',9), text="{}".format(self.printLabel()))
self.cl.configure(scrollregion = self.cl.bbox("all"))
self.cl.config(xscrollcommand=self.xb.set, yscrollcommand=self.yb.set)
self.cl.config(width=250,height=150)
self.cl.pack(side=LEFT,expand=True,fill=BOTH)
def main():
root = Tk()
root.title("Tk")
root.geometry('378x176')
app = Example(root)
app.pack(expand=True, fill=BOTH)
root.mainloop()
if __name__ == '__main__':
main()
The Hello World! should display without the brackets in the Canvas but the main issue is that when I click on the Button it just overlaps the canvas and prints out the append on to the Label.
The Label should be inside the Canvas.
Here's how to fix the "main issue" along with the "brackets issue". The latter is taken care of by using the string join() method as suggested in the comments.
The updatePanel() method has been modified so it first create a Label widget with the text you want displayed in it, followed by a Canvas "window" object specifying that widget as its contents. Code for the way you were attempting to do it was also removed from the other class methods.
from tkinter import *
class Example(Frame):
def __init__(self, root):
Frame.__init__(self, root)
self.buttonA()
self.viewingPanel()
def printLabel(self):
text = []
text.append('Hello')
text.append('World!')
return ' '.join(text)
def updatePanel(self):
label = Label(self, bg='white', padx=3, pady=3, anchor=NW,
text=self.printLabel())
label.place(relx=0.5, rely=0.5, anchor=CENTER)
self.cl.create_window(100, 100, window=label) # Put Label in a Canvas "window".
def buttonA(self):
self.firstPage = Button(self, text="Print Text", bd=1, anchor=CENTER, height=11,
width=13, command=lambda: self.updatePanel())
self.firstPage.place(x=0, y=0)
def viewingPanel(self):
self.panelA = Label(self, bg='white', width=65, height=13, padx=3, pady=3,
anchor=NW, text="")
self.panelA.place(x=100, y=0)
self.cl= Canvas(self.panelA, bg='WHITE', width=165, height=113, relief=SUNKEN)
canvas_id = self.cl.create_text(15, 15, anchor="nw")
self.xb= Scrollbar(self.panelA,orient="horizontal", command=self.cl.xview)
self.xb.pack(side=BOTTOM, fill=X)
self.xb.config(command=self.cl.xview)
self.yb= Scrollbar(self.panelA, orient="vertical", command=self.cl.yview)
self.yb.pack(side=RIGHT, fill=Y)
self.yb.config(command=self.cl.yview)
self.cl.itemconfig(canvas_id, font=('Consolas',9), text=self.printLabel())
self.cl.configure(scrollregion=self.cl.bbox("all"))
self.cl.config(xscrollcommand=self.xb.set, yscrollcommand=self.yb.set)
self.cl.config(width=250, height=150)
self.cl.pack(side=LEFT, expand=True, fill=BOTH)
def main():
root = Tk()
root.title("Tk")
root.geometry('378x176')
app = Example(root)
app.pack(expand=True, fill=BOTH)
root.mainloop()
if __name__ == '__main__':
main()
I want my label to be displayed in the same column as the picture. Both of them are generated with a click. I place them in the same column of the grid, but the image is displayed in the neighbour column. What's the reason and how to correct it?
Here's my simplified code.
from tkinter import *
from tkinter import ttk
class App(Frame):
def __init__(self, master):
ttk.Frame.__init__(self, master, padding='20')
self.grid(column=0, row=0, sticky=(N, W, E, S))
self.create_button()
def create_button(self):
self.button = ttk.Button(self,
text="Click",
width=12,
command=lambda: self.display_name_and_picture()
).grid(column=2, columnspan=2, row=1, sticky=NW)
def display_name_and_picture(self):
random_label = ttk.Label(self, font=(None, 16), text='random random')
random_label.grid(row=0, column=5, sticky=N)
random_image = PhotoImage(file='random.gif')
label = Label(image=random_image)
label.image = random_image
label.grid(row=1, column=5, sticky=NW)
root = Tk()
root.title("Random something...")
root.geometry("600x300")
app = App(root)
root.mainloop()
The culprit is this line
label = Label(image=random_image)
You create label without specifying its parent, so its parent defaults to root. But random_label has app as its parent, and app in turn has root as its parent. So label is gridded side by side with app --- inside root --- and not inside app as you wished. Just change the above line to
label = Label(self, image=random_image)
and you should be fine.
(Well, not totally fine. You should also fix the things people pointed out in comments.)
I monkeyed around with this for a while and produced something that does what you are asking. I would love to have somebody explain why this works. The columns look goofy.
Check it out:
from tkinter import *
from tkinter import ttk
class App(Frame):
def __init__(self, master):
ttk.Frame.__init__(self, master, padding='20')
self.grid(column=0, row=0, sticky=(N, W, E, S))
self.create_button()
def create_button(self):
self.button = ttk.Button(self,
text="Click",
width=12,
command=self.display_name_and_picture
)
self.button.grid(column=0, columnspan=2, row=0, sticky=NW)
def display_name_and_picture(self):
random_label = ttk.Label(self, font=(None, 16), text='random random')
random_label.grid(row=0, column=2)
random_image = PhotoImage(file='random.gif')
label = Label(image=random_image)
label.image = random_image
label.grid(row=1, column=0)
root = Tk()
root.title("Random something...")
root.geometry("600x300")
app = App(root)
root.mainloop()
I'm added canvas and a scroll bar to one of the frames in my script.
However somethings wrong cause the scroll bar is off (lower bottom is not visible) and the text I drew is off. Could anyone please tell me whats the problem ? I want the canvas to fill the whole frame (obviously without the scroll bar)
import sys
import os
if sys.version_info[0] < 3:
import Tkinter as tk
import ttk as ttk
else:
import tkinter as tk
import tkinter.ttk as ttk
#
# LeftMiddle
#
class LeftMiddle(tk.Frame):
def __init__(self, master=None):
self.parent = master
tk.Frame.__init__(self, self.parent, bg='bisque', borderwidth=1, relief="sunken")
self.__create_layout()
self.draw_text()
def __create_layout(self):
self.canvas = tk.Canvas(self, bg="green", relief=tk.SUNKEN)
self.canvas.config(width=20, height=10)
self.canvas.config(highlightthickness=0)
self.sbar = tk.Scrollbar(self, orient=tk.VERTICAL)
self.sbar.pack(side=tk.RIGHT, fill=tk.Y)
self.canvas.pack(side=tk.LEFT, expand="YES", fill=tk.BOTH)
def draw_text(self):
self.canvas.create_text(0, 0, text='1234567890', fill='red')
self.canvas.create_text(0, 25, text='ABCDEFGH', fill='blue')
#
# MainWindow
#
class MainWindow(tk.Frame):
def __init__(self, master=None):
self.parent = master
tk.Frame.__init__(self, self.parent, bg='bisque', borderwidth=1, relief="sunken")
self.__create_layout()
def __create_layout(self):
self.frame1 = tk.Frame(self, bg="yellow")
self.frame2 = tk.Frame(self, bg="blue")
self.frame3 = LeftMiddle(self) # tk.Frame(self, bg="green")
self.frame4 = tk.Frame(self, bg="brown")
self.frame5 = tk.Frame(self, bg="pink")
self.frame1.grid(row=0, column=0, rowspan=4, columnspan=8, sticky=(tk.N, tk.S, tk.W, tk.E))
self.frame2.grid(row=0, column=8, rowspan=4, columnspan=2, sticky=(tk.N, tk.S, tk.W, tk.E))
self.frame3.grid(row=4, column=0, rowspan=2, columnspan=5, sticky=(tk.N, tk.S, tk.W, tk.E))
self.frame4.grid(row=4, column=5, rowspan=2, columnspan=5, sticky=(tk.N, tk.S, tk.W, tk.E))
self.frame5.grid(row=5, column=0, rowspan=1, columnspan=10, sticky=(tk.N, tk.S, tk.W, tk.E))
for r in range(6):
self.rowconfigure(r, weight=1)
for c in range(10):
self.columnconfigure(c, weight=1)
#
# MAIN
#
def main():
root = tk.Tk()
root.title("Frames")
root.geometry("550x300+525+300")
root.configure(background="#808080")
root.option_add("*font", ("Courier New", 9, "normal"))
window = MainWindow(master=root)
window.pack(side="top", fill="both", expand=True)
root.mainloop()
if __name__ == '__main__':
main()
You have overlapping frames. Both self.frame3 and self.frame4 are in row 4 with a rowspan of 2, meaning they occupy rows 4 and 5. self.frame5 is also in row 5. So, self.frame5 is obscuring the bottom half of self.frame3, the frame that contains the canvas.
I don't understand why you have so many rowspans, they seem completely unnecessary unless you have some specific reason why you want multiple rows and columns but only single frames that span these rows and columns. Looking at the screenshot I see the need for only three rows.
The reason the text seems off is that by default the text is centered over the coordinate you give. You might want to look at the anchor option for the create_text method.