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()
Related
I'm working on a tkinter database program for landscape architects to assist with selecting plants.
I have a search function which retrieves some entries with a for loop based on some user inputs, namely checkboxes for things like sun or shade (these variables are stored in search_current).
I'm trying to make delete buttons appear next to database entries, but I cannot make the button actually delete the corresponding entry. Each button will just delete the entry for the last result in the loop.
I know the problem is my delete function is being redefined in each iteration to the latest value in the loop, but I'm not sure how to approach the problem.
Here's the code.
def search():
for widgets in result_frame.winfo_children():
widgets.destroy()
conn = sqlite3.connect('plant_records.db')
c = conn.cursor()
c.execute("SELECT *, oid FROM plants")
records = c.fetchall()
for record in records:
countVar = 0
for i in search_current:
if record[i] == 1:
countVar += 1
if countVar == len(search_current):
result = tk.Frame(result_frame, bg='white', highlightbackground="Azure2", highlightthickness=1,
height=25, width=500)
result.pack()
result.grid_propagate(False)
result_name = tk.Label(result, bg='white', text=record[0])
result_name.grid(row=0, column=0)
del_button = tk.Label(result, text="Delete", bg="White", fg='Blue', cursor="hand2")
del_button.bind("<Button-1>", lambda del_button=del_button: delete())
del_button.grid(row=0, column=1)
n = record[-1]
def delete():
b = str(n)
conn = sqlite3.connect('plant_records.db')
c = conn.cursor()
c.execute('DELETE FROM plants WHERE oid = ' + b)
conn.commit()
conn.close()
conn.commit()
conn.close()
Please help, I'm quite new to programming and I've been stuck on this one for about a week, I've been reading through similar questions and answers and I can't get my head around it.
Without really understanding it, I've been trying to appropriate the solutions for my purpose, and that hasn't worked out.
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.
I am able to create one Select (portfolio) widget and one dependent Multiselect (keywords) widget (keywords change based on portfolio selected). However, i am struggling to retrieve the values of Multiselect. I am not sure how to pass the populated 'keywords' widget from get_folio_terms() to the main section so that it reflects in the multiselect. Any help on this is appreciated.
PS: The code correctly displays the multiselect with the right values populated from get_folio_terms().
Here is the code:
keywords = MultiSelect(title="Keywords", options=[])
def get_folio_terms(attrname, old, new):
connection = pymysql.connect(host='localhost',
user='root',
password='root',
db='qa_env',
charset='utf8mb4',
cursorclass=pymysql.cursors.DictCursor)
with connection.cursor() as cursor:
sqlstmt1 = "SELECT * from portfolioterms where portfolio_name = "
sql = sqlstmt1 + "'" + portfolio.value + "';" # handle Keyerror later
print(sql)
cursor.execute(sql)
result = cursor.fetchall()
if cursor.rowcount > 0:
dfinput = pd.DataFrame.from_dict(result)
print(dfinput)
keywords = MultiSelect(title="Keywords", options=list(dfinput['businessterm']))
layout.children[1] = keywords
else:
keywords = MultiSelect(title="Keywords", options=[]) # reset to empty
layout.children[1] = keywords
def update_portfolio():
print(portfolio.value, keywords.value)
def update(attrname, old, new):
print(keywords.value)
# set up select widget
portfolio = Select(title="portfolio", options=['ERT','Trading', 'Wealth'])
portfolio.on_change('value', get_folio_terms)
keywords.on_change('value', update)
kwbutton = Button(label='Select Keywords',width=150,button_type="success")
kwbutton.on_click(update_portfolio)
layout = column(portfolio,keywords,kwbutton)
curdoc().add_root(layout)
You are overwriting your initial keywords widget in your function, which you then pass to the children. That works, but the global keywords is not overwritten, but remains the old one. Refer to the new one by referring directly to the child:
print(portfolio.value, layout.children[1].value)
A cleaner way of doing this is not to replace the widget with a new one, but only replacing the options:
keywords.options=list(dfinput['businessterm'])
And remove the overwrite of the children as it is not necessary anymore.
layout.children[1] = keywords
When doing it in this way, you can use you old print statements again.
print(portfolio.value, keywords.value)
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.
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.