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!
Related
So I have been trying to make a scrollable frame. I've had been searching and these 3 had the most impact:
https://stackoverflow.com/questions/16188420/tkinter-scrollbar-for-frame\
https://stackoverflow.com/questions/3085696/adding-a-scrollbar-to-a-group-of-widgets-in-tkinter\
How could I get a Frame with a scrollbar in Tkinter?
And I have come up with the following code:
from tkinter import *
class Test_tk_stuff():
def __init__(self):
self.screen = Tk()
self.screen.geometry("500x500")
self.structure()
self.screen.mainloop()
def scroller(self, canvas):
canvas.configure(scrollregion = canvas.bbox("all"))
def frame_expander(self, event):
canvas_width = event.width
self.canvas.itemconfig(self.frame, width = canvas_width)
def structure(self):
parent = Frame(self.screen)
parent.pack(expand = True, fill = BOTH)
self.canvas = Canvas(parent, bg = "green", highlightthickness = 0, relief = RAISED)
self.frame = Frame(self.canvas, bg = "blue")
myscrollbar = Scrollbar(parent, orient = "vertical", command = self.canvas.yview)
self.canvas.configure(yscrollcommand = myscrollbar.set)
myscrollbar.pack(side = RIGHT, fill = Y)
self.canvas.pack(side = LEFT, fill = BOTH, expand = True)
self.canvas.create_window((0, 0), window = self.frame)
# can't do: self.frame.pack(expand = True, fill = BOTH) because it will become unscrollable
# Event Bind
self.frame.bind("<Configure>", lambda event, canvas = self.canvas: self.scroller(self.canvas))
self.canvas.bind("<Configure>", self.frame_expander)
# initialize number of minimum columns
for num_columns in range(3):
self.frame.columnconfigure(num_columns, weight = 1)
a = "Button Text!"
# fill it to - 1.) test scrollbar 2.) actually using the frame inside
for place2 in range(10):
Button(self.frame, text = a, bg = "black", fg = "white").grid(row = place2, column = 1, sticky = "NSEW", ipady = 15)
if __name__ == "__main__":
Test_tk_stuff()
But somehow when I run it, it shows a _tkinter.TclError. I tried searching what that is and how to fix it, but, as you can see, I wasn't able to fix it.
Is there something wrong with my implementation?
Thanks in advance.
canvas.itemconfig() is used on canvas item returned by canvas.create_xxxxx() functions, not on tkinter widget (self.frame).
Save the canvas item ID for the self.frame and use it in canvas.itemconfig():
def frame_expander(self, event):
canvas_width = event.width
self.canvas.itemconfig(self.frame_item, width=canvas_width)
def structure(self):
...
self.frame_item = self.canvas.create_window((0, 0), window = self.frame)
...
use self.frame.config(width=canvas_width) instead of canvas.itemconfig()
I know i'm probably making a silly mistake but is there any reason that this isn't working? I am trying to delete a row in treeview by clicking on the row and then the delete button but for whatever reason the tv attribute isn't going into the function without an error.
class pageDeleteStaff(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text = "Delete Staff", font = NORMAL_FONT)
label.pack()
tv = ttk.Treeview(self, height = 10, columns = ("StaffID, Forename, Surname, Wage, NIN, Account Name, Account Number, Contract Hours, Usercode, Passcode"))
tv.pack()
tv.heading("#0", text = "StaffID")
tv.heading("#1", text = "Forename")
tv.heading("#2", text = "Surname")
tv.heading("#3", text = "Wage(£)")
tv.heading("#4", text = "NIN")
tv.heading("#5", text = "Account Name")
tv.heading("#6", text = "Account Number")
tv.heading("#7", text = "Contract Hours")
tv.heading("#8", text = "Usercode")
tv.heading("#9", text = "Passcode")
tv.column("#0", width = 100)
tv.column("#1", width = 170)
tv.column("#2", width = 170)
tv.column("#3", width = 150)
tv.column("#4", width = 170)
tv.column("#5", width = 200)
tv.column("#6", width = 150)
tv.column("#7", width = 150)
tv.column("#8", width = 150)
tv.column("#9", width = 150)
conn = sqlite3.connect('subway.db')
c = conn.cursor()
c.execute("SELECT * FROM staff")
staffData = c.fetchall()
for row in staffData:
tv.insert("", "end", text=row[0], values=row[1:])
print(row)
conn.close()
button4 = tk.Button(self, text="Delte", bg = 'grey', fg = 'black', font = SMALL_FONT, height = 2, width = 14,
command = self.deleteRow(controller))
button4.pack()
button4 = tk.Button(self, text="Menu", bg = 'grey', fg = 'black', font = SMALL_FONT, height = 2, width = 14,
command = lambda: controller.show_frame(pageLabour))
button4.pack()
def deleteRow(self, controller):
currentItem = self.tv.focus()
item = tv.item(currentItem)
if item.get("text") == "":
messagebox.showerror("ERROR", "No member of staff selected!")
else:
staffID = int(item.get("text"))
result = messagebox.askyesno("Are you sure?", "Are you sure you would like to delete?")
I'm getting an attribute error when calling tv into the function.
Here is my code
The function of this code is that it displays the list of records in a database with one of the fields being a button. When this button is clicked it brings the user to a new frame which displays the account information for that record.
The record information is displayed in the Record class as dat[0] and dat[1] and I have had problems with initializing this in the second class, I do not know how I would get this information to appear on the second screen.
Thanks for any help.
from tkinter import *
import sqlite3
import AddAccount
import RemoveAccount
import LogOnLib
import LoginMenu
import TrainerAccounts
class Records(object):
def __init__(self, window):
self.window = window
window.state("zoomed")
self.window.title('Trainer Accounts')
window.update_idletasks()
h = self.window.winfo_height()
w = self.window.winfo_width()
Center_h = h/2
Center_w = w/2
self.FrameRecords = Frame(window, bg = "PaleTurquoise1")
self.FrameRecords.place(x = Center_w , y = Center_h, anchor = "center", width = 1024, height = 300)
self.btn_Back = Button(self.FrameRecords, text = "Back", bg = "PaleTurquoise1", font =("Arial", "16"), command = self.Back, width = 20)
self.btn_Back.grid(row = 1, column = 4, columnspan = 5)
self.connection = sqlite3.connect(r"E:\Program\Accounts.db")
self.cur = self.connection.cursor()
self.btn_TrainerID = Label(self.FrameRecords, text = "Trainer ID", bg = "PaleTurquoise1", font =("Arial", "16"), width = 20)
self.btn_TrainerID.grid(row = 0, column = 1, columnspan = 1)
self.NameLabel = Label(self.FrameRecords, text = "Name", bg = "PaleTurquoise1", font =("Arial", "16"), width = 20)
self.NameLabel.grid(row=0, column=0)
self.showallrecords()
def showallrecords(self):
Data = self.readfromdatabase()
for index, dat in enumerate(Data):
self.row1 = Button(self.FrameRecords, text=dat[0],font =("Arial", "16"), command = self.account).grid(row=index+1, column=0)
self.row2 = Label(self.FrameRecords, text=dat[1],font =("Arial", "16")).grid(row=index+1, column=1)
def readfromdatabase(self):
self.cur.execute("SELECT * FROM Trainers")
return self.cur.fetchall()
self.btn_TrainerID = self.row1
self.NameLabel = self.row2
def Back(self):
self.FrameRecords.place_forget()
GUI = LoginMenu.Logged(self.window)
def account(self):
self.FrameRecords.place_forget()
GUI = TrainerAccounts.TrainerInfo(self.window, Records)
##########################################################################################################################################################################################
class TrainerInfo(Records):
def __init__(self, window, Records):
self.window = window
window.state("zoomed")
self.window.title('CHANGEEEE')
window.update_idletasks()
h = self.window.winfo_height()
w = self.window.winfo_width()
Center_h = h/2
Center_w = w/2
self.FrameTrainerInfo = Frame(window, bg = "PaleTurquoise1")
self.FrameTrainerInfo.place(x = Center_w , y = Center_h, anchor = "center", width = 1024, height = 300)
self.connection = sqlite3.connect(r"E:\Program\Accounts.db")
self.cur = self.connection.cursor()
self.showrecord()
def showrecord(self):
Data = self.Information()
for index, dat in enumerate(Data):
Label(self.FrameTrainerInfo, text=self.row1,font =("Arial", "16")).grid(row=index+1, column=0)
Label(self.FrameTrainerInfo, text=self.row2,font =("Arial", "16")).grid(row=index+1, column=1)
def Information(self):
self.cur.execute("SELECT * FROM Trainers WHERE row1 = '" + row1 + "'" + "AND row2 = '" + row2 + "'")
return self.cur.self.all()
Have you tried merging the two classes together ?
if you can push values from database you only need make a .insert on Entrybox
maybe like this
tkEntry = Entry( master)
tkEntry.pack() # .grid or .place, don't make this in obj create
tkEntry.insert( 0, dat[0] ) # 0 is the index and dat[0] is your value
I am trying to set up validation on text entry boxes. Three of the boxes need to only accept integers and one text as a postcode. I am not sure whether to do this in a function previously defined or when the entry boxes are created. Also how would i make the values from the text entry boxes be accessable in the function QuoteCreation. All my code is below.
from tkinter import *
class quote():
def __init__(self, master):
self.master=master
self.master.title("Quote Screen")
self.master.geometry("2100x1400")
self.master.configure(background = "white")
self.Borras = PhotoImage(file = "Borras.Logo.2.gif") #sets up image
self.Borras.image = self.Borras
self.BorrasLabel = Label(self.master, image = self.Borras, bg = "white")#puts image onto label
self.BorrasLabel.place(anchor=NW)
self.Title = Label(self.master, text = "New Quote", font = ("calibri", 20), bg = "White")
self.Title.place(x=650, y = 10)
self.SubmitButton = PhotoImage(file = "Submit.Button.gif") #sets up image
self.SubmitButton.image = self.SubmitButton
self.SubmitButtonLabel = Button(self.master, image = self.SubmitButton, bg = "white", command= self.QuoteCreation)#puts image onto a button
self.SubmitButtonLabel.place(x=900, y=290)
PostCodeVar = StringVar()
PostCodeEntry = Entry(master,width=50, font=20, textvariable=PostCodeVar)
PostCodeEntry.place(x = 20, y = 150)
PostCodeVar.set("Please enter the Post Code")
PostCodeValue = PostCodeVar.get()
HeightVar = StringVar()
HeightEntry = Entry(master, width=50, font=20, textvariable=HeightVar)
HeightEntry.place(x = 20, y = 220)
HeightVar.set("Please enter the Height")
HeightValue = HeightVar.get()
LengthVar = StringVar()
LengthEntry = Entry(master, width=50, font=20, textvariable=LengthVar)
LengthEntry.place(x = 20, y = 290)
LengthVar.set("Please enter the Length")
LengthValue = LengthVar.get()
PitchVar = StringVar()
PitchEntry = Entry(master, width=50, font=20, textvariable=PitchVar)
PitchEntry.place(x = 20, y = 360)
PitchVar.set("Please enter the Pitch")
PitchValue = PitchVar.get()
RiseVar = StringVar()
RiseEntry = Entry(master, width=50, font=20, textvariable=RiseVar)
RiseEntry.place(x = 20, y = 430)
RiseVar.set("Please enter the Rise")
RiseValue = RiseVar.get()
self.SubmitButton = PhotoImage(file = "Submit.Button.gif")
self.SubmitButton.image = self.SubmitButton
self.SubmitButtonLabel = Button(self.master, image = self.SubmitButton, bg = "white", command= self.QuoteCreation)#puts image onto a button
self.SubmitButtonLabel.place(x=900, y=290)
def on_button(self):
print(self.entry.get())
def QuoteCreation(self):
print(' ')
def quitWindow(self):
self.master.destroy()
def backToWelcome(self):
self.master.destroy()
You would set up separate functions to deal with the validation, when the submit button is pressed.
So, as an example, your submit button may look a bit like this:
submitButton = Button(master, text="Submit", command=validation)
The validation, in your case would then want to carry out these checks:
def validation():
postcode = PostCodeVar.get()
length = LengthVar.get()
pitch = PitchVar.get()
rise = RiseVar.get()
if postcodeCheck(postcode) == True and length.isdigit() == True and pitch.isdigit() == True and rise.isdigit() == True:
#carry out chosen process
In your case, you can try setting the postcode, length, pitch and height variables before calling the function, and setting them as global. The postcode should be created, and if it is okay, the function should then:
return True
...so it matches the outcome of the if statement.
I hope this is what you were looking for, and can adapt the example to your specific problem!
When i put widgets inside my canvas, the widgets bypass the canvas size. But when I scroll, the canvas won't move.
On top of that, the canvas auto re-sizes when I add more widgets. I thought the canvas isn't supposed to re-size because I already added a scroll bar?
Here's my code:
from tkinter import *
from tkinter import ttk
import mysql.connector
class CanvasScroll:
def on_resize(self,event):
self.canvas.config(width = 1185, height = 530)
def add_crew(self):
crewFrame = CrewFrame()
def callback_list(self):
index = self.notebook.index(self.notebook.select()) + 1
self.tmpframe = Try2(self.contentframe, index)
def __init__(self, master):
self.canvas = Canvas(master, width = 1185, height = 530, scrollregion = (0, 0, 1216, 700), bg = 'white', confine = True)
self.canvas.grid(row = 2, column = 0, sticky = 'news')
self.xscroll = ttk.Scrollbar(master, orient = HORIZONTAL, command = self.canvas.xview)
self.xscroll.grid(row = 3, column = 0, sticky = 'we')
self.yscroll = ttk.Scrollbar(master, orient = VERTICAL, command = self.canvas.yview)
self.yscroll.grid(row = 2, column = 1, sticky = 'ns')
self.canvas.config(xscrollcommand = self.xscroll.set, yscrollcommand = self.yscroll.set)
self.canvas.bind("<Configure>", self.on_resize)
self.option = ttk.Frame(master, height = 150, width = 1206)
self.option.grid(row = 0, column = 0, sticky = 'we', columnspan = 5)
self.addicon = PhotoImage(file = 'C:\\Users\\rain\\Desktop\\Fyosh!\\logo\\add.gif').subsample(2,2)
self.btnAdd = ttk.Button(self.option, image = self.addicon, text = 'Add Crew', compound = TOP, command = self.add_crew)
self.btnAdd.grid(row = 0, column = 0, padx = 50, pady = 5)
self.updateicon = PhotoImage(file = 'C:\\Users\\rain\\Desktop\\Fyosh!\\logo\\update.gif').subsample(2,2)
self.btnUpdate = ttk.Button(self.option, image = self.updateicon, text = 'Update Crew', compound = TOP)
self.btnUpdate.grid(row = 0, column = 1, padx = 50)
self.deleteicon = PhotoImage(file = 'C:\\Users\\rain\\Desktop\\Fyosh!\\logo\\delete.gif').subsample(2,2)
self.btnDelete = ttk.Button(self.option, image = self.deleteicon, text = 'Delete Crew', compound = TOP)
self.btnDelete.grid(row = 0, column = 2, padx = 50)
self.reloadicon = PhotoImage(file = 'C:\\Users\\rain\\Desktop\\Fyosh!\\logo\\Refresh.png').subsample(7,7)
self.reloadbtn = ttk.Button(self.option, image = self.reloadicon, command = self.callback_list, text = 'Load List', compound = TOP)
self.reloadbtn.grid(row = 0, column = 3, padx = 50)
self.tabframe = ttk.Frame(master, height = 20)
self.tabframe.grid(row = 1, sticky = 'we')
self.notebook = ttk.Notebook(self.tabframe)
self.notebook.grid(row = 0, column = 0, sticky = 'we')
db = mysql.connector.connect(user = 'root', password = '', host = 'localhost', database = 'fat2x_payroll')
cursor = db.cursor()
ships = ("SELECT * from tbl_shiplist")
try:
cursor.execute(ships)
result = cursor.fetchall()
self.tab = {}
for row in result:
self.tab[row[0]] = {
"shipname": ttk.Frame(self.notebook)
}
self.notebook.add(self.tab[row[0]]["shipname"], text = row[1])
db.close()
except:
messagebox.showinfo("alert", "ship list error")
self.canvas.label = ttk.Label(self.canvas, text = "hahahahahha").grid(column = 0)
self.canvas.label1 = ttk.Label(self.canvas, text = "hahahahahha").grid(column = 1)
self.canvas.label2 = ttk.Label(self.canvas, text = "hahahahahha").grid(column = 2)
self.canvas.label3 = ttk.Label(self.canvas, text = "hahahahahha").grid(column = 3)
self.canvas.label4 = ttk.Label(self.canvas, text = "hahahahahha").grid(column = 4)
self.canvas.label5 = ttk.Label(self.canvas, text = "hahahahahha").grid(column = 5)
self.canvas.label6 = ttk.Label(self.canvas, text = "hahahahahha").grid(column = 6)
self.canvas.label7 = ttk.Label(self.canvas, text = "hahahahahha").grid(column = 7)
self.canvas.label8 = ttk.Label(self.canvas, text = "hahahahahha").grid(column = 8)
def start():
master = Tk()
master.geometry("1206x690+10+10")
master.resizable(0,0)
master.title("Crew Panel")
canvasScroll = CanvasScroll(master)
if __name__ == "__main__":
start()
The canvas can only scroll objects that are embedded on the canvas. In the case of widgets, that means that you've called the create_window method of the canvas for the widgets you want to be affected by the scrollbar.
If you want to use grid to organize widgets, and you want those widgets to be in a scrollable container, the usual method is to put those widgets inside a frame, and then add the frame to the canvas with create_window. You can see an example of this technique in this answer: https://stackoverflow.com/a/3092341/7432
As for the auto-resize, this behavior is completely unrelated to using a scrollbar. If you use grid or pack on a widget, the parent will always resize unless you've turned geometry propagation off. It doesn't matter if it's a Canvas or a Frame or a Toplevel, or any other class of widget.