Populate Treeview with info from sql? - python-3.x

I would like to ask for some advice on the code I have below:
from tkinter import *
from tkinter import ttk
import sqlite3
root = Tk()
root.geometry("500x500")
root.title("Inventory Balance")
def db():
global conn, mycursor
conn = sqlite3.connect('MyStock.sql3')
mycursor = conn.cursor()
def data():`
tree.delete(*tree.get_children())
mycursor.execute("SELECT * FROM ItemCode")
for row in myscursor:
tree.insert('', 'end', values=row[1:6])
conn.close()
frame = Frame(root)
frame.pack()
tree = ttk.Treeview(frame, columns = (1,2,3,4,5), height = 20, show = "headings")
tree.pack(side = 'top')
tree.heading(1, text="ItemCode")
tree.heading(2, text="Description")
tree.heading(3, text="Category")
tree.heading(4, text="Unit")
tree.heading(5, text="Quantity")
tree.column(1, width = 100)
tree.column(2, width = 100)
tree.column(3, width = 100)
tree.column(4, width = 100)
tree.column(5, width = 100)
# Inserting Scrollbar
scroll = ttk.Scrollbar(frame, orient="vertical", command=tree.yview)
scroll.pack(side = 'right', fill = 'y')
tree.configure(yscrollcommand=scroll.set)
root.mainloop()
I will later have to use this as a main screen, add buttons and have a constantly updating treeview showing as stock is used and then updated via another python tkinter script.
My main issue is that it reads the correct column (ItemCode, Description, Category, Unit, Quantity) but the information contained in the database does not display in the treeview.
Please help and do not hesitate to ask for any more information where required.
I edited the script as recommended and still end up with this:
enter image description here
Thank you again

There are two issues in your code:
using info when inserting record into treeview. row should be used
treeview.insert(...) expects two positional arguments: parent and index
So change:
info = mycursor.fetchall()
for row in info:
tree.insert(values=(info[1], info[2], info[3], info[4], info[5]))
to:
for row in mycursor:
tree.insert('', 'end', values=row[1:6])

Related

Is there a way to write to a qTableWidget more than once?

I am trying to create a qTableWidget in PyQt5 that will allow me to make a selection with a checkbox that filters a Pandas DataFrame and then show that data in the GUI.
I can get it to run the code the first time, and it presents the data just as I would expect.
When you try and refresh it however it won't load the new data into the qTableWidget.
There isn't any error messages, and if you print the DataFrame it is loading the new data as I would expect, it just isn't showing up in the GUI.
I've searched for similar issues on here, but can't find anyone having the same problems I am getting.
I need it to refresh the table in the same GUI as in my real data, the DataFrame is generated from an API call which when selecting a new group will pull the most recent data and could be ran an indefinite number of times.
Here is what I've got up to, I've tried to add comments to explain as much as I can do
import sys
from PyQt5 import QtWidgets, QtGui, QtCore
import pandas as pd
class TableWidget(QtWidgets.QTableWidget):
def __init__(self, tab2_df, parent=None):
QtWidgets.QTableWidget.__init__(self,parent)
tab2_df = tab2_df
nRows = len(tab2_df.index)
nColumns = len(tab2_df.columns)
self.setRowCount(nRows)
self.setColumnCount(nColumns)
for i in range(self.rowCount()):
for j in range(self.columnCount()):
x = "{}".format(tab2_df.iloc[i, j])
self.setItem(i, j, QtWidgets.QTableWidgetItem(x))
print(tab2_df) #validating that the new df is received
class Window(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__()
self.title = "Reporting Test"
self.left = 400
self.top = 50
self.width = 1300
self.height = 700
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
#initialize window with tabs
self.table_widget = MyTableWidget(self)
self.setCentralWidget(self.table_widget)
self.show()
class MyTableWidget(QtWidgets.QWidget):
def __init__(self, parent):
super(QtWidgets.QWidget, self).__init__(parent)
self.layout = QtWidgets.QVBoxLayout(self)
self.tabs = QtWidgets.QTabWidget()
self.tab1 = QtWidgets.QWidget() #Group select Tab
self.tab2 = QtWidgets.QWidget() #DataFrame Tab
# Name and add first Tab to Layout
self.tabs.addTab(self.tab1,"Group Select")
self.layout.addWidget(self.tabs)
self.setLayout(self.layout)
# Sample DataFrame for demonstation
self.tab1.df = pd.DataFrame({'Name':['Tom', 'Jack', 'Steve', 'Ricky'],'Age':[28,34,29,42]})
# Filter dataframe into a list to show as checkboxes
self.tab1.group = self.tab1.df[["Name"]]
self.tab1.groupList = self.tab1.group['Name'].tolist()
self.tab1.listLabel = ["",] * len(self.tab1.groupList)
self.tab1.grid = QtWidgets.QGridLayout()
self.tab1.setLayout(self.tab1.grid)
# Populate the checkboxes with the list
for i, v in enumerate(self.tab1.groupList):
self.tab1.groupList[i] = QtWidgets.QCheckBox(v)
self.tab1.listLabel[i] = QtWidgets.QLabel()
self.tab1.grid.addWidget(self.tab1.groupList[i], i, 0)
# Add the checkboxes into the tab
self.tab1.button = QtWidgets.QPushButton("Select Group")
self.tab1.button.clicked.connect(self.checkboxChanged)
self.tab1.labelResult = QtWidgets.QLabel()
self.tab1.grid.addWidget(self.tab1.button, i+1, 0, 1,2)
self.tab1.setLayout(self.tab1.grid)
def checkboxChanged(self):
# Clear the previous tab
self.tabs.clear()
# Add a new tab for the loaded data
self.tabs.addTab(self.tab2,"Loaded Data")
# Match the ticked checkbox to the DataFrame and filter to a new DataFrame
self.tab1.labelResult.setText("")
for i, v in enumerate(self.tab1.groupList):
self.tab1.listLabel[i].setText("True" if v.checkState() else "False")
self.tab1.labelResult.setText("{}, {}".format(self.tab1.labelResult.text(),
self.tab1.listLabel[i].text()))
self.tab1.groupList2 = self.tab1.group['Name'].tolist()
checked2 = str(self.tab1.labelResult.text()).split(',')
result = list(filter(None, checked2))
checked_list = {"Name":self.tab1.groupList2, "checked":result}
checked_list_df = pd.DataFrame(checked_list)
checked_list_filtered_df = checked_list_df[checked_list_df.checked.str.contains("true", case=False)]
self.tab1.filteredGroup_df = checked_list_filtered_df
group_select_df = pd.merge(self.tab1.group, self.tab1.filteredGroup_df, on="Name", how="inner")
group_select_list = group_select_df["Name"].tolist()
tab2_df = self.tab1.df[self.tab1.df["Name"].isin(group_select_list)]
# Populate the filtered DataFrame onto a TableWidget and populate this into the tab
self.tab2.tableWidget = TableWidget(tab2_df, self)
# Set headings and style for TableWidget
self.tab2.tableWidget.setHorizontalHeaderLabels(("Name", "Age"))
stylesheet3 = "::section{font: bold 18px}"
self.tab2.tableWidget.horizontalHeader().setStyleSheet(stylesheet3)
# Add button to refresh the data with a new group filter
self.tab2.button = QtWidgets.QPushButton("Refresh", self.tab2)
self.tab2.button.clicked.connect(self.refresh_button)
self.tab2.layout = QtWidgets.QVBoxLayout()
self.tab2.layout.addWidget(self.tab2.tableWidget)
self.tab2.layout.addWidget(self.tab2.button)
self.tab2.setLayout(self.tab2.layout)
# Refresh button that will clear the previous TableWidget and load the Group Select Tab to generate new data
def refresh_button(self):
# Clear the previous tab
self.tabs.clear()
# Clear the TableWidget
self.tab2.tableWidget.setRowCount(0)
# Load the Group Select Tab to select the new group
self.tabs.addTab(self.tab1,"Group Select")
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
I'm sure a lot of this could be done much more elegantly and I'm trying to learn as I go, but I just can't figure this bit out.

Scrollbar in tree view in tkinter

I am trying to implement treeview in tkinter with scrollbar. I have got success in placing scrollbar but unable to place it in proper place. Following is full code:
from tkinter import*
from tkinter import ttk
import sqlite3
class Product:
db_name = 'Gateway_Log.db'
def __init__(self,wind):
self.wind=wind
self.wind.title('Device details')
self.tree = ttk.Treeview (height=25, columns=2)
self.tree.heading('#0',text = 'Name', anchor=W)
self.tree.heading(2, text='Price', anchor=W)
vsb = ttk.Scrollbar(wind, orient="vertical")
vsb.configure(command=self.tree.yview)
self.tree.configure(yscrollcommand=vsb.set)
self.tree.pack()
vsb.pack()
self.viewing_records()
def run_query(self, query, parameters =()):
with sqlite3.connect (self.db_name) as conn:
cursor = conn.cursor()
query_result=cursor.execute(query, parameters)
conn.commit()
return query_result
def viewing_records(self):
records = self.tree.get_children()
for element in records:
self.tree.delete(element)
query= 'SELECT * FROM Device_Status'
db_rows=self.run_query(query)
for row in db_rows:
self.tree.insert('',0, text = row[0], values = row[1])
if __name__ == '__main__':
wind=Tk()
application = Product(wind)
wind.mainloop()
Following output I am getting:
I want scrollbar after price field not at bottom side.
You need to indicate the side where you want to pack your widget and also which direction it should fill.
self.tree.pack(side="left")
vsb.pack(side="right",fill="y")

Multiple tkinter labels in notebook tab are not expanding to full length of window

I'm trying to equally distribute three objects/widgets across one row in a ttk notebook tab, but the three objects only expand half of the window.
I'm not sure what controls the number of columns within a tab since columnsspan in tab.grid(row=0, columnspan=3) doesn't appear to change anything. I've also tried various values for rows and columns for every object with .grid. This is only an issue for notebook tabs, rather than a single window.
#!/usr/bin/env python3
from tkinter import ttk
from tkinter import *
root = Tk()
root.title('Title')
root.resizable(width=FALSE, height=FALSE)
root.geometry('{}x{}'.format(750, 750))
nb = ttk.Notebook(root)
nb.grid(row=0, column=0)
# Add first tab
tab1 = ttk.Frame(nb)
#tab1.grid(row=0, column=0)
nb.add(tab1, text='Setup')
# Add row label
lb1 = ttk.Label(tab1, text = 'Parent Directory:')
lb1.grid(row = 1, column = 1)
# Add text entry
txt1 = ttk.Entry(tab1)
txt1.grid(row = 1, column = 2)
# Add selection button
btn1 = ttk.Button(tab1, text="Select")
btn1.grid(row=1, column=3)
root.mainloop()
I'm expecting the columns to span the full length of the window, instead of half the length of the window.
In order to do this using grid you need to use the Frame.columnconfigure([column#], minsize=[minsize]) function.
If you want the text box and button to stretch to fill the space, use the sticky option. (Sticky doesn't really do anything with the label)
Code:
#!/usr/bin/env python3
from tkinter import ttk
from tkinter import *
root = Tk()
root.title('Title')
root.resizable(width=FALSE, height=FALSE)
root.geometry('{}x{}'.format(750, 750))
nb = ttk.Notebook(root, width=750)
nb.grid(row=0, column=0)
# Add first tab
tab1 = ttk.Frame(nb)
#tab1.grid(row=0, column=0)
nb.add(tab1, text='Setup')
# Change the sizes of the columns equally
tab1.columnconfigure(1, minsize=250)
tab1.columnconfigure(2, minsize=250)
tab1.columnconfigure(3, minsize=250)
# Add row label
lb1 = ttk.Label(tab1, text = 'Parent Directory:')
lb1.grid(row = 1, column = 1,sticky=(E,W))
# Add text entry
txt1 = ttk.Entry(tab1)
txt1.grid(row = 1, column = 2,sticky=(E,W))
# Add selection button
btn1 = ttk.Button(tab1, text="Select")
btn1.grid(row=1, column=3,sticky=(E,W))
root.mainloop()
Image of result

Horizontal scroll bar not scrolling tkinter

Having trouble with this horizontal scroll bar... I've pieced this much together but could be completely wrong. The scroll bar is there where I want it to be it's just unusable and wont scroll anywhere.
There are about 83 columns and I can only see 15 of them.
import databasefile
import sqlite3
from tkinter import *
from tkinter.ttk import *
conn = sqlite3.connect("test_db.db")
cursor = conn.cursor()
returns = cursor.execute("SELECT * FROM Order_Number ORDER BY id DESC")
variables = [1,2,3,4,5,6,7,8,9,0,11,12,13,14,15,16,17,18,19,20]
class App(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.CreateUI()
self.grid(sticky = (N,S,W,E))
parent.grid_rowconfigure(1, weight = 1)
parent.grid_columnconfigure(1, weight = 1)
parent.wm_title("Database")
parent.maxsize(width=1200,height=600)
parent.resizable(width=0,height=0)
def CreateUI(self):
tv = Treeview(self)
tv['columns'] = variables
# Scroll Bar section
vsb = Scrollbar(root,orient="horizontal",command=tv.xview)
tv.configure(xscrollcommand=vsb.set)
vsb.place(x=0,y=210,height=20, width=1200)
tv.column("#0", anchor="w",width=25)
for item in variables:
tv.heading(str(item),text=str(item))
tv.column(str(item),anchor='center',width=75)
tv.grid(sticky = (N,S,W,E))
self.grid_rowconfigure(index=1, weight = 1)
self.grid_columnconfigure(index=1, weight = 1)
for row in returns:
tv.insert('','end',values=(row))
root = Tk()
App(root)
root.mainloop()

Changing entry box background colour in tkinter

So I've been working on this program and I'm finding it very hard to figure out what's wrong. I'm fairly new to tkinter so this may be quite minor.
I'm trying to get the program to change the entry box's background colour when the check button is pressed. Or even better if somehow I can change it dynamically it would be even better.
This is my code at the moment:
TodayReading = []
colour = ""
colourselection= ['green3', 'dark orange', "red3"]
count = 0
def MakeForm(root, fields):
entries = []
for field in fields:
row = Frame(root)
lab = Label(row, width=15, text=field, font=("Device",10, "bold"), anchor='center')
ent = Entry(row)
row.pack(side=TOP, padx=5, fill=X, pady=5)
lab.pack(side=LEFT)
ent.pack(side=RIGHT, expand=YES, fill=X)
entries.append((field, ent))
return entries
def SaveData(entries):
import time
for entry in entries:
raw_data_point = entry[1].get()
data_point = (str(raw_data_point))
TodayReading.append(data_point)
c.execute("CREATE TABLE IF NOT EXISTS RawData (Date TEXT, Glucose REAL, BP INTEGER, Weight INTEGER)")
c.execute("INSERT INTO RawData (Date, Glucose, BP, Weight) VALUES (?, ?, ?, ?)", (time.strftime("%d/%m/%Y"), TodayReading[0], TodayReading[1] , TodayReading[2]))
conn.commit()
conn.close()
def DataCheck():
if ((float(TodayReading[0])>=4 and (float(TodayReading[0])<=6.9))):
colour = colourselection[count]
NAME OF ENTRY BOX HERE.configure(bg=colour)
Thanks for the help. Someone may have answered it already but like i said I'm new to tkinter so if i've seen it already, I haven't figured out how to implement it.
Please see my example below:
from tkinter import *
class App:
def __init__(self, root):
self.root = root
self.var = StringVar() #creates StringVar to store contents of entry
self.var.trace(mode="w", callback=self.command)
#the above sets up a callback if the variable containing
#the value of the entry gets updated
self.entry = Entry(self.root, textvariable = self.var)
self.entry.pack()
def command(self, *args):
try: #trys to update the background to the entry contents
self.entry.config({"background": self.entry.get()})
except: #if the above fails then it does the below
self.entry.config({"background": "White"})
root = Tk()
App(root)
root.mainloop()
So, the above creates an entry widget and a variable which contains the contents of that widget.
Every time the variable is updated we call command() which will try to update the entry background colour to the contents of the entry (IE, Red, Green, Blue) and except any errors, updating the background to White if an exception is raised.
Below is a method of doing this without using a class and using a separate test list to check the value of the entry:
from tkinter import *
root = Tk()
global entry
global colour
def callback(*args):
for i in range(len(colour)):
if entry.get().lower() == test[i].lower():
entry.configure({"background": colour[i]})
break
else:
entry.configure({"background": "white"})
var = StringVar()
entry = Entry(root, textvariable=var)
test = ["Yes", "No", "Maybe"]
colour = ["Red", "Green", "Blue"]
var.trace(mode="w", callback=callback)
entry.pack()
root.mainloop()

Resources