populate dropdown with sql python/tkinter - python

i use python 3, tkinter and mysql connector.
I want to make an optionMenu object with my sql data i tried to do like that :
mongroupe3 = LabelFrame(app, text='nouvelle div 3').pack()
Label(mongroupe3, text='groupe3').pack()
select_Classes = ("select id,nom from classe")
cursor.execute(select_Classes)
result = cursor.fetchall()
for row in result:
id = row[0]
nom = row[1]
mongroupe3 = Label(app, text=nom).pack()
v = StringVar()
v.set(nom)
om = OptionMenu(mongroupe3, v, nom)
om.pack()
the code has to show a dropdown menu with a list of the "nom" on my "classe" table but my option menu is in my for loop and is executed as many time as i have records in my classe table .
for exemple if i have 3 record i got 3 option menu with 3 different value.
o tried to put my optionMenu out of my for loop like that :
mongroupe3 = LabelFrame(app, text='nouvelle div 3').pack()
Label(mongroupe3, text='groupe3').pack()
select_Classes = ("select id,nom from classe")
cursor.execute(select_Classes)
result = cursor.fetchall()
for row in result:
id = row[0]
nom = row[1]
mongroupe3 = Label(app, text=nom).pack()
v = StringVar()
v.set(nom)
om = OptionMenu(mongroupe3, v, nom)
om.pack()
but i got only the last record i inserted on my table.
i cant put my for loop inside my OptionMenu cause of the following shema OptionMenu(parent, default variable, var)

You got what you did not expect for this reason:
You create only one label and you override it in each iteration (the same about the class variable v). So by the end, it is logic you get only one row (because, again, you have only one label widget).
Apart from this, you have to care about the widgets and where you place them: for example, I believe mongroup3 must be the parent widget of the row you want to create, unlike what you did.

Related

How can I get the value of a row in a column in SQLite 3 table with Tkinter Listbox widget?

I have Python program connected to SQLite 3 database with Tkinter on the frontend. My database table (subjectlist) consists of three columns: [id (unique interger), subject (text), serial (unique integer)]. Here is my program:
import sqlite3
from tkinter import *
conn = sqlite3.connect('database.db')
c = conn.cursor()
c.execute('SELECT COUNT() FROM subjectlist')
number = (c.fetchone()[0])
c.execute('SELECT * FROM subjectlist ORDER BY serial')
data = c.fetchall()
c.close
conn.close()
root = Tk()
listbox = Listbox(root)
listbox.pack()
for i in range(number):
listbox.insert(END, data[i][1])
def get_serial():
print(listbox.get(listbox.curselection()))
btn = Button(root, text="Show serial", command=lambda: get_serial())
btn.pack()
mainloop()
Currently at runtime when I click item on listbox (witch basically shows all subject column values) and then press Tkinter button I get the subject I clicked. Instead I want to get serial corresponding to the subject. Notice that subject column may have same value on two or more different rows. How would I go about achieving this?
Here is just one more example; If I have this table:
I want the GUI to show first all of the subjects in the listbox. Then I click "Cats" (on the the id row 3) and then I click the button, I want the program to print me serial 4.
Well, this should also be easy. But since there is a database in your code that I can't currently test this with it, I made some assumptions. Try this out:
def get_letter():
conn = sqlite3.connect('database.db')
c = conn.cursor()
ids = listbox.curselection()[0]+1 # Getting the index number starting from 1
c.execute('SELECT * FROM subjectlist WHERE `id`=?;',(ids,)) # Searching the database for items with selected items index
rows = c.fetchall()
print(rows[0]) # Print the first and hopefully the only item in the list
conn.close() # Close the connection
This should print the rows which corresponds to the id, and since usually id is a unique number, it will only print out one row. I'm also assuming that you have both the database and the listbox in the same order or this might not work.
You can use the index of the item inside listbox to get the serial from data:
import sqlite3
from tkinter import *
conn = sqlite3.connect('database.db')
c = conn.cursor()
c.execute('SELECT * FROM subjectlist ORDER BY serial')
data = c.fetchall()
c.close()
conn.close()
root = Tk()
listbox = Listbox(root)
listbox.pack()
for rec in data:
listbox.insert(END, rec[1])
def get_serial():
selected = listbox.curselection()
if selected:
idx = selected[0]
print(data[idx][2]) # print serial of selected item
btn = Button(root, text="Show serial", command=get_serial)
btn.pack()
mainloop()
Note that you should make sure data and listbox are synchronised.

Having problems pulling out check box values in a loop

I want to make a food selector page where food items being stored in a database are being printed out with check boxes but I'm having problems getting the value from the check boxes
def foodSelector():
editor = Tk()
editor.title('Food Items')
editor.geometry("400x400")
conn = sqlite3.connect('food.db')
c = conn.cursor()
c.execute("SELECT *, oid FROM foods")
records = c.fetchall()
def show():
myLabel = Label(editor, text=record).pack()
v = []
for i, record in enumerate(records):
v.append(IntVar(value=1))
query = Checkbutton(editor, text=record, variable = v[i])#record
query.pack()
myButton = Button(editor, text="Show Selection", command=show).pack()
Posting as an answer as its too big for comments:
from tkinter import *
editor = Tk()
editor.title('Food Items')
editor.geometry("400x400")
records = [(1,2,3,4),(3,2,1,3),(12,31,5,12)]
def show(rec):
global items
items = []
for i, record in enumerate(records):
if v[i].get()==1:
items.append(record)
def final():
Label(editor, text=items).pack()
v = []
for i, record in enumerate(records):
v.append(IntVar(value=0))
query = Checkbutton(editor, text=record, variable = v[i],command=lambda i=record:show(i))#record
query.pack()
myButton = Button(editor, text="Show Selection", command=final).pack()
editor.mainloop()
Is this what you wanted? I've used a random list of tuple here to mimic your records fetched from database. Keep in mind to not use the button as it will give the error.
Anyway for small parts of explanation, what lambda i=record:show(i) does is that, it holds the value of record(while iterating) in a parameter which is then passed to show()(when clicked on checkbutton) and then that is used in show(records) to put the text rec on the label, with text=rec.
In function show, it take variable record as label text, so it always show last value at end of for loop.
Example for show,
# records = ['Bread', 'Beef', 'Pork']
def show():
items = [record for i, record in enumerate(records) if v[i].get()==1]
text = 'Selected items: ' + ','.join(items) if items else 'No items selected !'
myLabel = Label(editor, text=text).pack()

How to delete from Mysql Database an item selected in a Listbox Python Tkinter

i have made a database with 3 columns(id, name, price) and since i can't display all the columns in the Listbox i am displaying only the column "name", so i want to be able to delete an item in the database based on the name of the product i am clicking on the Listbox.
here is the code, i hope it helps:
import tkinter
from tkinter import *
import mysql.connector
root = Tk()
root.geometry('700x500')
con = mysql.connector.connect(user = '', password = '', host = '127.0.0.1', database = 'anime')
concursor = con.cursor()
concursor.execute('select * from prod')
query = concursor.fetchall()
l = Listbox(root)
for x,y,z in query:
l.insert(x+1, y)
l.pack(anchor='w')
#function to delete prod
def delp():
sel_items = l.curselection()
for item in sel_items:
concursor.execute("delete from prod where name = ('{}')".format(item))
con.commit()
button1 = Button(root, text = 'prod del', command = delp)
button1.pack(anchor='w')
root.mainloop()
and this is the database picture:
the python code works only if i replace "name" with "id" in the query in the "delp" function, however when i select the 1st item in the listbox, it deletes the 2nd item int the database, and i want to be able to delete based on the name.
so i figured it out, so heres the answer for ones whom are/will be looking for this.
first we wont need the curselection()
we need to get the active item or Anchor and "string it" and then use it in the query with format.
heres the code to make sense
def delp():
sel_items = l.get(ACTIVE)
string_item = str(sel_items)
for item in sel_items:
concursor.execute("delete from prod where name = ('{}')".format(string_item))
con.commit()
this code will delete the items in the database based on the name select in the listbox.

Create different entries each loop python tkinter

I created a function to create entry boxes (tkinter) but I want to be able to access the data in the entry boxes after. Therefore, I can't call all the boxes the same name e.g. Entry1=Entry(Solo) - I have to have Entry1, Entry2 etc...
I thought that I might be able to store the Entry Names in an array but it doesn't work. I can't just store the data in a list because when creating the entry boxes there is no data to store yet.
[Update] I tried to store an entry box in an list but it's not supported.
TypeError: 'set' object does not support item assignment
This is a snippet of code from my program (the code works without the lines with inputlist[i] on it):
StudentsArray1 = c.fetchall()
c.execute('SELECT Count(*) from students WHERE solo = 1;')
x = c.fetchone()[0]
a = 0
rownum = 1
inputlist = []
for i in range(0, x):
label1 = Label(Solo, text=StudentsArray1[a][0], font=font4, bg="white")
label1.grid(row=rownum, column=1)
label2 = Label(Solo, text=StudentsArray1[a][1], font=font4, bg="white")
label2.grid(row=rownum, column=2)
inputlist[i] = Entry(Solo)
inputlist[i].grid(row=rownum, column=2)
rownum = rownum + 1
a = a + 1

Python Tkinter: Delete label not working

I have a GUI that contains all the database records. Upon clicking the "search" button, I want all the records to be cleared from the screen, and the records relevant to the user's search to appear. I am trying to reference my labels and call the "destroy" method like this: a.destroy() but the program is not recognizing these labels as widgets.
class DBViewer:
def __init__(self, rows):
# Display all records
row_for_records = 2
for record in rows:
a = tkinter.Label(self.main_window, text=record[0])
a.grid(row=row_for_records, column=0)
b = tkinter.Label(self.main_window, text=record[1])
b.grid(row=row_for_records, column=1)
# More labels here
self.display_rows(rows)
tkinter.mainloop()
def display_rows(self, rows):
# Where I'm having trouble
def main():
global db
try:
dbname = 'books.db'
if os.path.exists(dbname):
db = sqlite3.connect(dbname)
cursor = db.cursor()
sql = 'SELECT * FROM BOOKS'
cursor.execute(sql)
rows = cursor.fetchall()
DBViewer(rows)
db.close()
Edit: Separated widget assignment and grid but still facing have the same problem. Anyone want to help out a programming newbie?
In order to delete something, you must have a reference to it. You aren't saving references to the widgets you are creating. Try saving them to a list or dictionary.
When creating them:
...
self.labels = []
for record in rows:
a = Tkinter.Label(...)
self.labels.append(a)
...
When you want to delete them:
for label in self.labels:
label.destroy()

Categories

Resources