I am creating a SQlite3 GUI interface in tkinter (ttk), but i am stuck in half way. Please see the below code
import sqlite3
from tkinter import ttk
import tkinter as tk
root = tk.Tk()
conn = sqlite3.connect('contact.sqlite3')
conn_cursor = conn.cursor()
res = conn.execute("SELECT name FROM sqlite_master WHERE type='table';")
tables = tuple([tbl_name[0] for tbl_name in res])
table_chooser = ttk.Combobox(root)
table_chooser['values'] = tables
table_chooser.grid(row=0, column=0)
def view_table():
selected_table = 'contacts' #table_chooser.get()
table_names = conn.execute(f'''SELECT * FROM pragma_table_info('%{selected_table}%')''')
column_header = [nam[1] for nam in table_names]
stringvar_header = [tk.StringVar() for _ in column_header]
alldb = conn.execute(f'''SELECT * FROM {selected_table} ''').fetchall()
stringvar = [[tk.StringVar() for _ in alldb[0]] for _ in alldb]
def header_entry(i):
stringvar_header[i].set(column_header[i])
return ttk.Entry(root, textvariable=stringvar_header[i], state="disabled").grid(row=1, column=i)
def create_entry(i, j):
stringvar[i][j].set(alldb[i][j])
return ttk.Entry(root, textvariable=stringvar[i][j]).grid(row=i+2, column=j)
h_entry = [header_entry(i) for i in range(len(column_header))]
entry = [[create_entry(i, j) for j in range(len(alldb[0]))] for i in range(len(alldb))]
view_table()
#ttk.Button(root,text="view",command=view_table).grid(row=0,column=2)
root.mainloop()
the problem is that, if we run the below code we can see only empty cells of Entry widget, but if we write the contents of view_table() function directly in the code(.ie without using function ) it is working fine
and you can see all the contents of table 'contacts'. in Entry widget.
so what is happening when using function and without function
Related
I cannot for the life of me find any post or documentation that explains why the Treeview heading is off by one column from the rest of the data.
Even the documentation I found shows this issue in an example but does not describe the issue.
import tkinter as tk
import tkinter.ttk as ttk
import random
n, m = 40, 10
table = []
for i in range(n):
line = []
# line.append('')
# This adds a blank string to start of row data to off set the data.
# It is my current work around but does not look great.
for j in range(m):
line.append(random.randint(0, 999))
table.append(line)
class Demo(tk.Tk):
def __init__(self):
super().__init__()
tree = ttk.Treeview(self)
tree.pack()
for i in range(n):
tree.insert('', 'end', text=table[i][0], values=table[i][1:])
tree['columns'] = list(range(m - 1))
headings = list('ABCDEFGHI')
for j in range(m - 1):
tree.column(j, width=50, anchor='e')
tree.heading(j, text=headings[j])
if __name__ == '__main__':
Demo().mainloop()
As you can see the headings are off by one column. I cannot figure out why this is or how to fix it properly.
I did manage a bad workaround that appends an empty string to the start of the row data so it lines up with the correct headings but this cannot be the correct or best way of fixing this.
Am I missing something here? Is this normal for Treeview?
As I know first column has special meaning - it can be used to display tree with nested elements - like on image in tkdoc - and it may need special method to set header.
tree.heading('#0', text='Hello')
Result:
You may also use "#1", "#2", etc. to set other headers.
You can use f'#{j}' in loop.
headings = list('ABCDEFGHIJ')
for j in range(m):
tree.column(f'#{j}', width=50, anchor='e')
tree.heading(f'#{j}', text=headings[j])
Full working code:
import tkinter as tk
import tkinter.ttk as ttk
import random
n, m = 40, 10
table = []
for i in range(n):
line = []
# line.append('')
# This adds a blank string to start of row data to off set the data.
# It is my current work around but does not look great.
for j in range(m):
line.append(random.randint(0, 999))
table.append(line)
class Demo(tk.Tk):
def __init__(self):
super().__init__()
tree = ttk.Treeview(self)
tree.pack()
for i in range(n):
tree.insert('', 'end', text=table[i][0], values=table[i][1:])
tree['columns'] = list(range(m - 1))
headings = list('ABCDEFGHIJ')
for j in range(m):
tree.column(f'#{j}', width=50, anchor='e')
tree.heading(f'#{j}', text=headings[j])
#tree.heading('#0', text='Hello')
if __name__ == '__main__':
Demo().mainloop()
Doc: Column identifiers
I've got some code in python using tkinter which retrieves the name of a room and uses that to to insert into an SQL database the room name and which site it belongs to. However when I run the code its not retrieving the room name from the entry box.
Can anyone help?
def addroom():
global screen14
global roomsinsite
roomsinsite = StringVar()
screen14 = Tk()
screen14.geometry("300x250")
screen14.title("Insert rooms")
Label(screen14, text = "Insert room name:", bg = "LightSkyBlue1", width = "300", height = "2").pack()
Label(screen14, text = "").pack()
roomsinsite_entry = Entry(screen14, textvariable = roomsinsite)
roomsinsite_entry.pack()
Button(screen14, text = "Register room", width = "12", height = "1", command = insertroom).pack()
def insertroom():
sitename4_info = sitename2.get()
print(sitename4_info)
roomname1_info = roomsinsite.get()
print(roomname1_info)
cursor = cnn.cursor()
# SQL to select the siteID and insert rooms for that paticular site.
siteID_fetch3 = "SELECT siteID FROM Sites WHERE siteName = %s"
cursor.execute(siteID_fetch3, [sitename4_info])
siteID_fetch3 = cursor.fetchall()
# print out the values retrieved
print(siteID_fetch3[0][0])
insertRooms = "INSERT INTO `rooms`(`siteID_fk2`, `roomname`) VALUES (%s,%s)"
insertRooms_val = (siteID_fetch3[0][0], roomname1_info)
cursor.execute(insertRooms, insertRooms_val)
# print out the rows inserted.
print(cursor.rowcount)
cnn.commit()
You are probably having more than one Tk in your code, which means your StringVar does not know which Tk to belong to. So here there are three possible solutions:
Avoid using more than one Tk and replace all child windows with Toplevel, so:
screen14 = Toplevel()
roomsinsite = StringVar()
If you are adamant that you want to use more than one instance of Tk then you can specify master for each StringVar, like:
screen14 = Tk()
roomsinsite = StringVar(master=screen14)
To be honest, I wouldn't use StringVar with entry widgets except when I want to use trace, here if the only purpose of using StringVar is for getting the value of the entry widget, then remove it and use get() method of the entry widget, like:
roomname1_info = roomsinsite_entry.get()
The combination of first and third method seems like best practice, if you ask me. Also here, even if you are not using more than one Tk, one of the above methods would certainly solve the problem(as far as something is inputted inside the entry and then the insertroom() is called).
So I want to run the Tk() and import there some values. Then these values will be checked from a function that will replacing the value in a particular cell in excel. This cell that matches with one imported entry. My challenge here is that the function works and replacing if I put manually the values inside the program BUT how can I make to read the values from the Tk() that I enter? I made a button so the function will be running after I have imported the values in the entry fields but still not. The "sntake" and "sngive" in the replace() function seems to not working... What am I missing?
Code:
from tkinter import *
import pandas as pd
from xlutils.copy import copy
from openpyxl import *
from openpyxl.utils.cell import get_column_letter
import openpyxl
app = Tk()
app.geometry("500x500")
app.title("S/N Management")
heading = Label(text="S/N Management",fg="black",bg="green",width="500",height="3",font="10")
heading.pack()
sngive_text = Label(text="S/N of the delivered ")
sntake_text = Label(text="S/N of the recieved ")
sngive_text.place(x=15,y=80)
sntake_text.place(x=15,y=160)
sngive = StringVar()
sntake = StringVar()
sngive_entry = Entry(textvariable=sngive,width="30")
sntake_entry = Entry(textvariable=sntake,width="30")
sngive_entry.place(x=15,y=100)
sntake_entry.place(x=15,y=180)
def replace():
wb = openpyxl.load_workbook('Tracker.xlsx')
wb.sheetnames
sheet = wb["serials"]
amountOfRows = sheet.max_row
amountOfColumns = sheet.max_column
for i in range(amountOfColumns):
for k in range(amountOfRows):
cell = str(sheet[get_column_letter(i+1)+str(k+1)].value)
if( str(cell) == sntake):
newCell = sngive
sheet[get_column_letter(i+1)+str(k+1)]=newCell
wb.save('tracker_updated.xlsx')
button = Button(app,text="Submit Data",command=replace,width="30",height="2",bg="grey")
button.place(x=140,y=420)
mainloop()
I have been trying to use tkinter CheckButton widget to edit items in a list - each item of the list is a new checkbutton. I want a save method to save the data to a text file, and a load method to load the info from the text file and mark the checkbutton boxes depending on the items in the list.
Here is my code so far, but the list doesn't seem to change when I check the buttons and update the list/file
Here is my code, I need to know why the list isn't updating when I check the boxes:
import tkinter.messagebox as box
modulesMain = Tk()
moduleChecks = []
def SaveChanges():
# Clear the text file
modules = open("modules.txt", "w") #Write mode to overwrite the whole file
modules.write("") # Put in blank text
modules.close()
modules = open("modules.txt", "a") # Append mode to append the file
for item in moduleChecks:
modules.write(item + "\n")
print(moduleChecks)
appButton = Checkbutton(modulesMain, text = "Test", variable = moduleChecks[0]).grid()
searchButton = Checkbutton(modulesMain, text = "Test", variable = moduleChecks[1]).grid()
Save = Button(modulesMain, text = "Save Changes", command = SaveChanges).grid()
The variable for each of your checkboxes needs to be an IntVar. Your moduleCheck list is currently un-initialised so when you try to access the elements inside it, you'll get an error.
In the below code (modified from yours), I've initialised the moduleCheck to contain two IntVars.
When you press the save button, it will print to the console the current state of the check boxes.
import tkinter as tk
modulesMain = tk.Tk()
moduleChecks = [tk.IntVar() for i in range(2)]
def SaveChanges():
for idx,item in enumerate(moduleChecks):
print(f"{idx} = {item.get()}")
appCheck = tk.Checkbutton(modulesMain, text = "App", variable = moduleChecks[0])
appCheck.grid()
searchCheck = tk.Checkbutton(modulesMain, text = "Check", variable = moduleChecks[1])
searchCheck.grid()
saveButton = tk.Button(modulesMain, text = "Save Changes", command = SaveChanges)
saveButton.grid()
modulesMain.mainloop()
Here is a basic solution.
import tkinter as tk
root = tk.Tk()
#list of options. doubles as checkbox label text
#appending to this list is all that is necessary to create more options
opts = ['App', 'Search']
#create an StringVar for every option
#this way there doesn't have to be type conversions for reading/writing files
vars = [tk.StringVar(value='0') for _ in opts]
#create checkboxes for every option
for opt, var in zip(opts, vars):
tk.Checkbutton(root, text=opt, variable=var, onvalue='1', offvalue='0').grid(sticky='w')
#write the checkbox values to file
def save_options_state():
with open("modules.txt", "w") as file:
file.write(''.join([v.get() for v in vars]))
#set checkbox values from file
def load_options_state():
with open("modules.txt", "r") as file:
for n, var in zip(list(file.read().strip()), vars):
var.set(n)
#save/load buttons
tk.Button(root, text="save", command=save_options_state).grid()
tk.Button(root, text="load", command=load_options_state).grid(row=2, column=1)
root.mainloop()
Here's a runnable example which I think shows how to do everything you've asked about. It assumes the modules.txt file contains data in the following format, where each line consists of a module's name and whether it's checked or not.
Mod1,0
Mod2,0
Mod3,0
The data in the file is used to create a list of Checkbuttons — one for each module — and initially sets to its current status as indicated in the file.
Here's the sample code:
from tkinter import *
import tkinter.messagebox as box
dataFilename = "modules.txt"
moduleChecks = []
def ReadModules():
with open(dataFilename, "r") as file:
for line in (line.strip() for line in file):
moduleName, moduleState = line.split(',')
moduleChecks.append((moduleName, int(moduleState)))
print('read:', moduleChecks)
def SaveChanges():
print('writing:', moduleChecks)
with open(dataFilename, "w") as file:
for checkButton in moduleCheckbuttons:
moduleName = checkButton.cget('text')
moduleState = checkButton.var.get()
file.write(','.join((moduleName, str(moduleState))) + '\n')
ReadModules()
modulesMain = Tk()
moduleCheckbuttons = []
# Create a Checkbutton for each module based on moduleChecks values.
for moduleName, moduleState in moduleChecks:
intVar = IntVar(value=moduleState)
checkButton = Checkbutton(modulesMain, text=moduleName, variable=intVar)
checkButton.var = intVar # Attach reference to variable.
checkButton.grid()
moduleCheckbuttons.append(checkButton)
saveButton = Button(modulesMain, text="Save Changes", command=SaveChanges)
saveButton.grid()
modulesMain.mainloop()
I am trying to later iterate through the data inside of the Treeview. I will then hope to be able to sort through it.
from tkinter import *
from tkinter.ttk import *
import pickle
root = Tk()
def treeData(event):
children = tree.get_children()
print(children)
entry = StringVar()
a = Entry(root, textvariable=entry)
a.grid(column=0,row=0)
a.bind("<Key>", function)
file_data = []
file = open('data.dat', 'rb')
while True:
try:
file_data.append(pickle.load(file))
except EOFError:
break
file.close()
column_names = ("Column 1", "Column 2")
tree = Treeview(root, columns=column_names)
tree['show'] = 'headings'
for x in file_data:
a = tree.insert('', 'end', values=x)
for col in column_names:
tree.heading(col, text=col)
tree.grid(column=0, row=1)
In the function, called 'treeData' when I print(children) it outputs a list that looks similar to this - ('I001', 'I002', 'I003', 'I004')
I am hoping someone will know how to convert these pieces of data into what is actually shown in the row of the Treeview?
Thanks,
What you are asking is documented in the official tkinter documentation for the Treeview widget.
The get_children method returns a list of item IDs, one for each child. The item method of the treeview will return a dictionary of data for a given item. Thus, you can iterate over the values with something like this:
for child in tree.get_children():
print(tree.item(child)["values"])