I want the following layout in TkInter:
4 listboxes next to eachother, a canvas next to them, filled with buttons (one button for every element in the listboxes)
The problem is, that the canvas can be scrolled with the listboxes, also on it's own, but even higher than the point where it has content, which means, you can scroll it to the very top, but the listbox belonging to it won't even move, and they are de-synched already. So question is, how can I give a point for the scrolling where it can not go higher?
The code I have is the following:
from Tkinter import *
def Scroll2Gether(*args):
list1.yview(*args)
list2.yview(*args)
list3.yview(*args)
list4.yview(*args)
ButtonLine.yview(*args)
master = Tk()
EndLayer = Canvas(master)
scroll = Scrollbar(orient="vertical", command=Scroll2Gether)
UpperLayer = Frame(EndLayer, bg = '#000fff000')
list1 = Listbox(UpperLayer, bg = "#B2B2B2",width = 60)
list3 = Listbox(UpperLayer, bg = "#B2B2B2",width = 60)
list4 = Listbox(UpperLayer, bg = "#E5E5E5",width = 60)
list2 = Listbox(UpperLayer, bg = "#E5E5E5",width = 60)
Framm = Frame(UpperLayer, bg = "#ffa300", width = 30, )
Address1 = Label(list1, text = "OWNER1", bg= "#E5E5E5",width = 60,font=("Helvetica", 8,"bold"))
Address2 = Label(list2, text = "OWNER2", bg= "#B2B2B2", width = 60,font=("Helvetica", 8,"bold"))
Address3 = Label(list3, text = "MailDL", bg= "#E5E5E5", width = 60,font=("Helvetica", 8,"bold"))
Address4 = Label(list4, text = "ExpirationDate", bg= "#B2B2B2", width = 60,font=("Helvetica", 8, "bold"))
ButtonLine = Canvas(Framm, bg = "#E5E5E5", width = 30)
Address1.pack(side = TOP)
Address2.pack(side = TOP)
Address3.pack(side = TOP)
Address4.pack(side = TOP)
list1.pack(side = LEFT, fill = "y")
list2.pack(side = LEFT, fill = "y")
scroll.pack(side="right",fill="y")
list3.pack(side = LEFT, fill = "y")
list4.pack(side = LEFT, fill = "y")
Framm.pack(side=LEFT)
ButtonLine.pack(side = BOTTOM)
UpperLayer.pack()
EndLayer.pack()
x = 0
photo=PhotoImage(file="email.gif")
for i in ("qwertzuiopasfghjklyxcvbnm"):
i = Button(text="Mail")
i.config(image = photo, width = 20, height = 20)
Button_Window = ButtonLine.create_window(1, 22+x, anchor = NW, window = i)
x = x+26
Thanks for any help in advance.
SOLVED! I found out that it makes no sense to group 3 columns, so I created lines which are canvas and created canvases in them for every input in a loop.
Related
I don't know what to say because I am clueless on why this is not working.
The first button appears but the image does not the second and third button does not appear.
from tkinter import *
Master = Tk()
Master.geometry("1408x768")
Master.configure(background = "#000000")
# Top
Top = Frame(Master)
Top.configure(background = "#1C1C1C", width = 1024.0, height = 384.0)
Top.place(x = 0.0, y = -5.684341886080802e-14)
Nextimg = PhotoImage(file = "Next.png")
Next = Button(master = Top, background = "#0084FF", image = Nextimg)
Next.place(x = 624.0, y = 551.0, width = 100, height = 50)
# Bottom
Bottom = Frame(Master)
Bottom.configure(background = "#8C8C8C", width = 1024.0, height = 384.0)
Bottom.place(x = 0.0, y = 384.0)
Nextimg = PhotoImage(file = "Next.png")
Next = Button(master = Bottom, background = "#0084FF", image = Nextimg)
Next.place(x = 624.0, y = 551.0, width = 100, height = 50)
# Dashboard
Dashboard = Frame(Master)
Dashboard.configure(background = "#252525", width = 384.0, height = 768.0)
Dashboard.place(x = 1024.0, y = 0.0)
Continueimg = PhotoImage(file = "Continue.png")
Continue = Button(master = Dashboard, background = "#FF8900", image = Continueimg)
Continue.place(x = 1091.0, y = 359.0, width = 250, height = 50)
Your primary issue here appears that you are expecting your widget placement to be relative to the root window but in fact they are relative to the frames they are placed in.
Try this. Change all your buttons X/Y to 20 and see what I mean.
You will see all the buttons show up in the top left corner of each frame they are assigned to.
One issue is that you are using place to manage widgets and this is very bad for code maintenance. It is much better to use grid() and/or pack() to build your windows for several reasons.
Automatic weights to adjust placement based on window size and easy of maintainability as your code grows.
The issue you will see with your 1st and 2nd buttons is the 1st button does not show the image due to the reference being reassigned.
If you want to use the same image for both just save a reference once.
In this case you can remove the 2nd Nextimg = from your code.
See what happens when we change the placement to 20's
from tkinter import *
Master = Tk()
Master.geometry("1408x768")
Master.configure(background = "#000000")
Top = Frame(Master)
Top.configure(background = "#1C1C1C", width = 1024.0, height = 384.0)
Top.place(x = 0.0, y = -5.684341886080802e-14)
Nextimg = PhotoImage(file = "Next.png")
Next = Button(master = Top, background = "#0084FF", image = Nextimg)
Next.place(x = 20, y = 20, width = 100, height = 50)
Bottom = Frame(Master)
Bottom.configure(background = "#8C8C8C", width = 1024.0, height = 384.0)
Bottom.place(x = 0.0, y = 384.0)
# Nextimg = PhotoImage(file = "Next.png") Removed this as it is why your image does not show up on the first button.
Next = Button(master = Bottom, background = "#0084FF", image = Nextimg)
Next.place(x = 20, y = 20, width = 100, height = 50)
Dashboard = Frame(Master)
Dashboard.configure(background = "#252525", width = 384.0, height = 768.0)
Dashboard.place(x = 1024.0, y = 0.0)
Continueimg = PhotoImage(file = "Continue.png")
Continue = Button(master = Dashboard, background = "#FF8900", image = Continueimg)
Continue.place(x = 20, y = 20, width = 250, height = 50)
Master.mainloop()
Note that I replaced your images with grey squares since I did not have your image to use.
Results:
A more conventional way to write this would be to use grid() or pack().
Also I have rewritten the code to more closely fit PEP* standards.
See below example:
import tkinter as tk
master = tk.Tk()
master.geometry("1408x768")
master.configure(background="#000000")
top = tk.Frame(master)
top.grid_propagate(False)
top.configure(background="#1C1C1C", width=1024.0, height=384.0)
top.grid(row=0, column=0)
nextimg = tk.PhotoImage(file="gray.png")
tk.Button(master=top, background="#0084FF", image=nextimg).grid(row=0, column=0)
bottom = tk.Frame(master)
bottom.grid_propagate(False)
bottom.configure(background="#8C8C8C", width=1024.0, height=384.0)
bottom.grid(row=1, column=0)
tk.Button(master=bottom, background="#0084FF", image=nextimg).grid(row=0, column=0)
dashboard = tk.Frame(master)
dashboard.grid_propagate(False)
dashboard.configure(background = "#252525", width = 384.0, height = 768.0)
dashboard.grid(row=0, column=1, rowspan=2)
continueimg = tk.PhotoImage(file="gray.png")
tk.Button(master=dashboard, background="#FF8900", image=continueimg).grid(row=0, column=0)
master.mainloop()
Results:
You can also manage spacing of the widgets from the edges if you like. That can be done by providing a padx/pady parameter to the grid().
I am trying to get the canvas to center of the available space but it keeps sticking to North part of the screen... Here is an image
And here is the code
def newsetup(filelocation):
global width, height
global stage, img_id, imgtk
for widgets in root.winfo_children():
widgets.destroy()
root.config(bg = '#454545')
iconsframewidth = int(screen_width / 20)
iconsframe = Frame(root, width = iconsframewidth, bg = '#2a2a2a')
iconsframe.pack(side = 'left', expand = False, fill = 'y')
iconsframe.pack_propagate(0)
sep1frame = Frame(root, bg = '#1a1a1a', width = 10, relief = 'sunken')
sep1frame.pack(side = 'left', expand = False, fill = 'y')
optionsframe = Frame(root, bg = '#2a2a2a', height = 100)
optionsframe.pack(side = 'top', expand = False, fill = 'x')
optionsframe.pack_propagate(0)
sep2frame = Frame(root, bg = '#1a1a1a', height = 10, relief = 'sunken')
sep2frame.pack(side = 'top', expand = False, fill = 'x')
propertyframe = Frame(root, bg = '#2a2a2a', width = 150)
propertyframe.pack(side = 'right', expand = False, fill = 'y')
propertyframe.pack_propagate(0)
sep3frame = Frame(root, bg = '#1a1a1a', width = 10, relief = 'sunken')
sep3frame.pack(side = 'right', expand = False, fill = 'y')
stageframe = Frame(root, bg = '#454545')
stageframe.pack(side = 'top', expand = True, fill = 'both')
stageframe.pack_propagate(0)
stage = Canvas(stageframe, width = width, height = height)
stage.pack(anchor = CENTER)
root.update()
pencilbutton = Button(iconsframe, image = pencilimg, borderwidth = 0, bg = '#2a2a2a', fg = '#2a2a2a', relief = 'flat')
pencilbutton.pack(anchor = W)
imgtk = ImageTk.PhotoImage(Image.open(filelocation))
img_id = stage.create_image(stage.winfo_width() / 2, stage.winfo_height() / 2, image = imgtk)
stage.image = imgtk
stage.bind('<Configure>', PhotoEditing.stageresize)
I have tried using anchors it has not worked... I have an idea put the canvas in the frame but the frame does not fill the entire blank area
TL;DR add expand=True when packing the canvas.
The packer works by placing widgets along one side of the available empty space, and centers it in the space that is allocated. By default the allocated space will be as small as possible to fit the widget.
When packing something along the top or bottom, if you want it to be in the center vertically you need to tell the packer to expand the space allocated to be all of the remaining space. The widget will then be centered both horizontally and vertically in the remaining space.
I also recommend to always explicitly set the side so that it's more obvious what your intent is.
stage.pack(side="top", anchor = CENTER, expand=True)
so, I'm trying to create a contacts book app for a school project using Tkinter. here's how it works:
user gives me a database(.db) and using sqlite3,u should put them automatically in labels and grid them to the screen but, I want to make a class out of each contact so I can use some functionality on them. so I tried making a class using this code(no functions yet):
class User(Label):
def __init__(self,name, last_name, phone_number, email, id, row):
self.frame = Frame(second_frame)
self.frame.grid(row = row, column = 0)
self.id_label = Label(self.frame, text = id, font = sub_font, bg = background_color, fg = sub_font_color)
self.id_label.grid(row = row, column = 0)
self.name_label = Label(self.frame, text = name, font = sub_font, bg = background_color, fg = sub_font_color)
self.name_label.grid(row = row, column = 1)
self.last_name_label = Label(self.frame, text = last_name, font = sub_font, bg = background_color, fg = sub_font_color)
self.last_name_label.grid(row = row, column = 2)
self.phone_number_label = Label(self.frame, text = phone_number, font = sub_font, bg = background_color, fg = sub_font_color)
self.phone_number_label.grid(row = row, column = 3)
self.email_label = Label(self.frame, text = email, font = sub_font, bg = background_color, fg = sub_font_color)
self.email_label.grid(row = row, column = 4)
and I tested it and it all worked out in my (root) screen. but I wanted to make a frame so I could use scrollbars for the app but for that to happen, I should have made an main frame (named second_frame).
when I tried using it in that main frame(second_frame), it didn't work and put all the variables in column 1.
here is how i make classes:
conn = sqlite3.connect("contacts_book.db")
cursor = conn.cursor()
cursor.execute("SELECT *,oid FROM contacts")
contacts = cursor.fetchall()
sub_row = design_row + 1
for contact in contacts:
cont = User(contact[0], contact[1], contact[2], contact[3], contact[4], sub_row)
sub_row += 1
conn.commit()
conn.close()
and here's how I made the second_frame:
# create a main_frame
main_frame = Frame(root, bg = background_color)
main_frame.pack(fill = BOTH , expand = 1)
# create a canvas
my_canvas = Canvas(main_frame, bg = background_color)
my_canvas.pack(side = LEFT,fill = BOTH, expand = 1)
# add scrollbar to canvas
my_scrollbar = ttk.Scrollbar(main_frame , orient = VERTICAL, command = my_canvas.yview)
my_scrollbar.pack(side = RIGHT, fill = Y)
# config scrollbar
my_canvas.configure(yscrollcommand = my_scrollbar.set, bg = background_color)
my_canvas.bind("<Configure>", lambda e: my_canvas.configure(scrollregion = my_canvas.bbox("all")))
# create another frame in canvas
second_frame = Frame(my_canvas, bg = background_color)
# add that new frame in the canvas
my_canvas.create_window((0,0), window = second_frame, anchor = "nw")
what am I doing wrong? is that because I put them in second_frame or something? thanks for your help!
import Tkinter
import tkMessageBox
from Tkinter import *
CanvasHeight = 500
CanvasWidth = 600
Canvas width and height set to 10x the maximum of the variables.
IsGraphHidden = 0
MainWindow = Tkinter.Tk()
This is the window for all the sliders, and is defined as "MainWindow" for later use.
Strength = DoubleVar()
Multiple = DoubleVar()
Time = DoubleVar()
All of the variables set to DoubleVar, because of the Tkinter plugin.
It needs it's own special floats, integers and strings to work.
They can be accessed as normal variables by using VARIABLE.get()
coords = []
lastcoords = [0,0]
This is what we'll use to continue the line instead of just having a bunch of lines drawing themselves from the corner of the screen.
Plot = DoubleVar()
StrengthScale = Scale( MainWindow, variable = Strength, orient = HORIZONTAL,label="Strength")
MultipleScale = Scale( MainWindow, variable = Multiple, from_ = float(0), to = float(1), resolution = float(0.01), orient = HORIZONTAL, label="Multiple")
TimeScale = Scale( MainWindow, variable = Time, orient = HORIZONTAL, from_ = int(0), to = int(120), label="Time")
These are the procedures for the buttons, as well as the rest of the code.
def Calculate():
answer = float(Strength.get())*float(Multiple.get())
tkMessageBox.showinfo("Answer:", answer)
def PrepPlot():
global IsGraphHidden
global coords
global lastcoords
lastcoords0 = lastcoords[0]
lastcoords1 = lastcoords[1]
coords.append(lastcoords0)
coords.append(lastcoords1)
coords.append(Time.get()*5)
coords.append(Strength.get()*Multiple.get()*5)
lastcoords = Time.get()*5
lastcoords = Strength.get()*Multiple.get()*5
if IsGraphHidden == 0:
Graph = Canvas(MainWindow, width = CanvasWidth, height = CanvasHeight, bg = "white")
Graph.create_line(coords, fill = "black")
Graph.grid(row=5, column=1)
else:
Graph.destroy()
Graph.delete("all")
Graph.create_line(coords, fill = "black")
Graph.grid(row=5,column=1)
IsGraphHidden = 1
def DisplayPoints():
PointWindow = Tkinter.Tk()
Text = Label(PointWindow, textvariable = "Hi there", relief=RAISED)
Text.pack()
PointWindow.mainloop() #Work in progress, nothin' to see here.
Button = Tkinter.Button(MainWindow, text= "Calculate",command = Calculate)
PrepButton = Tkinter.Button(MainWindow, text = "Plot", command = PrepPlot) #The text is the text on the button.
DisplayButton = Tkinter.Button(MainWindow, text = "Display Plots", command = DisplayPoints)
MultipleScale.grid(row=0,column=0)
StrengthScale.grid(row=1,column=0)
TimeScale.grid(row=1,column=2)
PrepButton.grid(row=2,column=1)
Button.grid(row=4,column=1)
DisplayButton.grid(row=3,column=1)
MainWindow.mainloop()
I need some help with the float object getitem error, I'm doing this code for work experience at Manchester university...
You replaced the lastcoords list with a floating point value:
lastcoords = Time.get()*5
lastcoords = Strength.get()*Multiple.get()*5
so that next time around the line:
lastcoords0 = lastcoords[0]
raises your exception as you cannot use subscription on a floating point value.
I think you wanted to set a new list instead:
lastcoords = [Time.get() * 5, Strength.get() * Multiple.get() * 5]
Currently, I'm preforming the following code to delete the child widgets on the gui
for child in infoFrame.winfo_children():
child.destroy()
However, the gui will not add another child to the gui. For example, neither lines of the following code
people.place(in_ = gui, x = 1, y = 1, width = 422, height = 449)
people.grid(row = 0, column = 0)
place a label on the gui. I'm using the following code to create the label
people = Label(text = "Default", fg = "black", bg = "white")
EDIT I was asked to add my gui code, so here it is:
def initializeGui(name = "Default"):
GUI = Tk()
GUI.geometry("423x450+200+200")
GUI.title(name)
return GUI
def buttonAnswers(): #This is what I'm focusing on
gui.title("Answers")
for child in gui.winfo_children():
child.destroy()
return True
people = Label(text = "Default", fg = "black", bg = "white")
#people.place(in_ = gui, x = 1, y = 1, width = 422, height = 449)
people.grid(row = 0, column = 0)
def buttonTest(): #This will be the same as the button above but will open a different gui
gui.title("Test")
for child in gui.winfo_children():
child.destroy()
return True
question = Label(text = "Do you want to see the Answers or take the Test?", fg = "black", bg = "white")
question.grid(row = 0, column = 1)
checkAns = Button(gui, text = "Answers", command = buttonAnswers, fg = "black", width=10)
checkAns.grid(row = 1, column = 0)
gui = initializeGui("School Test")
label = Label(text = "Do you want to see the Answers or take the Test?", fg = "black", bg = "white")
label.grid(row = 0, column = 1)
answers = Button(gui, text = "Answers", command = buttonAnswers, fg = "black", width=10)
questions = Button(gui, text = "Test", command = buttonTest, fg = "black", width=10)
answers.grid(row = 1, column = 0)
questions.grid(row = 1, column = 2)`
The solution to the issue was the following:
def buttonAnswers(): #This is what I'm focusing on
gui.title("Answers")
for child in gui.winfo_children():
child.destroy()
return True
people = Label(text = "Default", fg = "black", bg = "white")
#people.place(in_ = gui, x = 1, y = 1, width = 422, height = 449)
people.grid(row = 0, column = 0)
contained a return True underneath the for loop, preventing the program from continuing. As such, removing the return True allowed the program to continue with the script and add the other children to the form.
def buttonAnswers(): #This is what I'm focusing on
gui.title("Answers")
for child in gui.winfo_children():
child.destroy()
people = Label(text = "Default", fg = "black", bg = "white")
#people.place(in_ = gui, x = 1, y = 1, width = 422, height = 449)
people.grid(row = 0, column = 0)
You have a return statement after you destroy the widgets but before you add any new widgets. The code to add the new widgets is never getting executed.