Receiving error: _tkinter.TclError: invalid command name ".!frame3.!treeview" - python

I am writing a simple program for a local business that will allow its users to easily keep track of their job service tickets. At one point, I had the program working with no errors. However, being that I am very new to programming and had typed a lot of messy code, I went back and decided to clean some of it up, as well as added better functionality. In doing so, I caused an error to occur, and I can't seem to fix it.
I have tried multiple ways to solve this issue, but all I have been able to determine is that if I remove a small block of code, it will essentially run as it should.
Here are the two definitions:
def gui_elements_remove(self, elements):
for element in elements:
element.destroy()
def Load(self):
self.gui_elements_remove(self.gui_elements)
var = self.FirstTree.focus()
treevar = self.FirstTree.item(var)
JobDF = pd.DataFrame(treevar, index = [0])
self.JobID = JobDF.iloc[0]['values']
cursor1 = self.cursor.execute("SELECT * from 'Service Tickets' WHERE JobID = ?", (self.JobID))
SP = cursor1.fetchall()
df = pd.DataFrame(SP, columns = [
'ServiceTicketsID',
'JobID',
'Ticket Number',
'Reason for Visit',
'Warranty/VPO',
'Date Ticket Received',
'Date of Service',
'Service Person'
])
columns = [
'ServiceTicketsID',
'Ticket Number',
'Reason for Visit',
'Warranty/VPO',
'Date Ticket Received',
'Date of Service',
'Service Person'
]
LoadFrame = Frame(self.root)
LoadFrame.pack(fill = BOTH, expand = True, padx = 50)
scroll = Scrollbar(LoadFrame, orient = VERTICAL)
SecondTree = ttk.Treeview(LoadFrame, yscrollcommand = scroll.set, columns = columns)
SecondTree['show'] = 'headings'
SecondTree.heading('#1', text = 'ServiceTicketsID')
SecondTree.heading('#2', text = 'Ticket Number')
SecondTree.heading('#3', text = 'Reason for Visit')
SecondTree.heading('#4', text = 'Warranty/VPO')
SecondTree.heading('#5', text = 'Date Ticket Received')
SecondTree.heading('#6', text = 'Date of Service')
SecondTree.heading('#7', text = 'Service Person')
SecondTree.column('#1', width = 0, stretch = NO)
SecondTree.column('#2', width = 75, stretch = YES, anchor = "n")
SecondTree.column('#3', width = 75, stretch = YES, anchor = "n")
SecondTree.column('#4', width = 75, stretch = YES, anchor = "n")
SecondTree.column('#5', width = 100, stretch = YES, anchor = "n")
SecondTree.column('#6', width = 100, stretch = YES, anchor = "n")
SecondTree.column('#7', stretch = YES, anchor = "n")
scroll.config(command = SecondTree.yview)
scroll.pack(side = RIGHT, fill = Y)
SecondTree.pack(fill = BOTH, expand = TRUE)
Maintree = df [[
'ServiceTicketsID',
'Ticket Number',
'Reason for Visit',
'Warranty/VPO',
'Date Ticket Received',
'Date of Service',
'Service Person'
]]
Maintree_rows = Maintree.to_numpy().tolist()
for row in Maintree_rows:
SecondTree.insert("", 0, values = row)
for col in columns:
SecondTree.heading(col, text=col, command=lambda _col=col: \
self.Treeview_sort_column(SecondTree, _col, False))
b1 = Button(LoadFrame, text = "Add")
b2 = Button(LoadFrame, text = "Update")
b3 = Button(LoadFrame, text = "Cancel")
b1.configure(command = lambda: self.Load_Add())
b2.configure(command = lambda: self.Load_Update())
#b3.configure(command = lambda: self.Cancel_Button(LoadFile, self.MainWindow, self.root))
b1.pack(side = LEFT, padx = 5, pady = 50)
b2.pack(side = LEFT, padx = 5, pady = 50)
b3.pack(side = LEFT, padx = 5, pady = 50)
There is more code but what I've provided should be more than enough, hopefully. The first method is called to clear out the frame from the previous method being run. The remainder is supposed to take the user's selection from the previous TreeView. However, upon running this code, I am given the error
_tkinter.TclError: invalid command name ".!frame3.!treeview"
If I comment out this block of code:
var = self.FirstTree.focus()
treevar = self.FirstTree.item(var)
JobDF = pd.DataFrame(treevar, index = [0])
self.JobID = JobDF.iloc[0]['values']
cursor1 = self.cursor.execute("SELECT * from 'Service Tickets' WHERE JobID = ?", (self.JobID))
SP = cursor1.fetchall()
df = pd.DataFrame(SP, columns = [
'ServiceTicketsID',
'JobID',
'Ticket Number',
'Reason for Visit',
'Warranty/VPO',
'Date Ticket Received',
'Date of Service',
'Service Person'
])
As well as the remaining code affiliated with it, the program runs correctly. The frame is cleared out and the new TreeView is brought in, as well as its headers.
As I said, I am brand new to programming and as such am new to using StackOverflow. With that said, I apologize if I've not provided enough information, posted incorrectly, etc. I also apologize in advance for any sloppiness you may find in the code lol.
I appreciate all input.

JobDF = pd.DataFrame(treevar, index = [0])
and
cursor1 = self.cursor.execute("SELECT * from 'Service Tickets' WHERE JobID = ?", (self.JobID))
were the culprits.
It should have been
JobDF = pd.DataFrame(treevar) #removed the index argument
and
cursor1 = self.cursor.execute("SELECT * from 'Service Tickets' WHERE JobID = ?", (self.JobID,)) #added a comma (self.JobID,))

Related

Streamlit Statefulness for Multi-Submit

I’m trying to compare two charts side by side that can be sourced from different datasets with the same structure.
This is what it looks like:
This is what the code looks like:
col1, col2 = st.columns(2)
#Left Column
with col1:
dataset1 = st.selectbox(
'Pick Type',
datasets)
df1 = pd.read_csv(os.path.join(".\\data\\",dataset1+".csv"), index_col=False)
option1 = st.selectbox(
'Pick Flight',
df1['log_id_full'].unique(),
key='option1')
col1_choice = list(df1.columns)
col1_choice.remove('timestamp')
columns1 = st.multiselect(
'Pick Values',
col1_choice,
key='col1'
)
if 'df1_filtered' not in st.session_state:
st.session_state.df1_filtered = df1.loc[df1['log_id_full'] == option1]
form1= st.form(key='form1')
submit1 = form1.form_submit_button('Submit')
if submit1:
st.line_chart(
st.session_state.df1_filtered,
x = 'timestamp',
y = columns1)
#Right column
with col2:
dataset2 = st.selectbox(
'Pick Type',
datasets,
key = 'dataset2')
df2 = pd.read_csv(os.path.join(".\\data\\",dataset2+".csv"), index_col=False)
option2 = st.selectbox(
'Pick Data',
df2['log_id_full'].unique(),
key = 'option2')
col2_choice = list(df2.columns)
col2_choice.remove('timestamp')
columns2 = st.multiselect(
'Pick Values',
col2_choice,
key='col2'
)
if 'df2_filtered' not in st.session_state:
st.session_state.df2_filtered = df2.loc[df2['log_id_full'] == option2]
form2 = st.form(key='form2')
submit2 = form2.form_submit_button('Submit')
if submit2:
st.line_chart(
st.session_state.df2_filtered,
x = 'timestamp',
y = columns2)
If I remove the submit button one chart always shows an error until it gets values selected so I basically just need each column to operate independently and preserve state.
The above is how I tried to add stateful-ness based on the documentation examples. But it still behaves such that when I submit one it reruns the app and removes the line chart.
I know there are other optimizations, best-practice improvements I can make but this app is just for a POC and I was hoping to demo this functionality
Remove the form and just replace it with checkbox.
submit1 = st.checkbox(...)
submit2 = st.checkbox(...)

How to read a SQL Stored Procedure into a pandas dataframe?

Thanks all for your suggestions. Adding self. before calling my functions fixed that issue. I'm now running into a different issue where the stored procedure I am using isn't getting properly read into the pandas dataframe. I don't get any errors, the tkinter window pops up like it should, but when the PDF gets generated, there is no data in the rows, just the column names and other formatting I've written in.
I added print(df) to the code to check if it was an issue with the data getting read from the dataframe to the PDF, but print(df) just returns Empty DataFrame.
from tkinter import *
import pyodbc
import pandas as pd
from reportlab.lib import colors
from reportlab.platypus import *
from reportlab.lib import styles
from reportlab.lib.units import inch
# Create connection
server = 'XXXXXXX'
database = 'XXXXXXX'
username = 'XXXXXXXX'
password = 'XXXXXXXXX'
try:
cnxn = pyodbc.connect('DRIVER={SQL Server};SERVER='+server+';DATABASE='+database+';UID='+username+';PWD='+password+'')
except:
raise NameError('unable to connect')
#save stored procedure to a global variable
storedProc = 'EXEC [presentation].[pdf_project] '
elements = []
class NewPDF:
def __init__(self):
window = tk.Tk()
window.title("Form Example")
window = tk.Frame(window)
window.grid(column=0,row=0, sticky=(tk.N,tk.W,tk.E,tk.S))
window.columnconfigure(0, weight = 1)
window.rowconfigure(0, weight = 1)
window.pack(pady = 100, padx = 100)
self.tkvar = tk.StringVar(window)
choices = {'2021','2020','2019','2018'}
self.tkvar.set('2021')
popupMenu = tk.OptionMenu(window, self.tkvar, *choices)
tk.Label(window, text = "Pick Year").grid(row = 1, column = 1)
popupMenu.grid(row = 1, column = 2)
tk.Label(window, text = "Customer").grid(row = 2, column = 1)
self.e1 = tk.Entry(window)
self.e1.grid(row = 2, column = 2)
self.param_year = self.tkvar.get()
self.param_cust = str(self.e1.get())
B = tk.Button(window, text = "Make PDF", command=self.make_df_and_pdf()).grid(row = 3, column = 1)
self.tkvar.trace('w', self.change_dropdown())
window.mainloop()
def change_dropdown(self, *args):
print(args)
def make_df_and_pdf(self):
#param_year = self.tkvar.get()
#param_cust = str(self.e1.get())
params = "'" + self.param_cust + "'," + self.param_year + ""
querystring = storedProc + params
df = pd.read_sql_query(querystring, cnxn)
lista = [df.columns[:,].values.astype(str).tolist()] + df.values.tolist()
#cust_list = (df['CUSTOMER'].unique())
#cust_list = cust_list.tolist()
#print(df)
styles = getSampleStyleSheet()
ts = [('ALIGN', (1,1), (-1,1), 'LEFT'),
('BOX', (0,0), (3,0), 2, colors.red),
('FONT', (0,0), (-1,0), 'Times-Bold'),
('GRID', (0,1), (-1,-1), 0.5, colors.grey)]
n_table = Table(lista, colWidths = (1.5*inch, 1.5*inch, 1.5*inch, 1.5*inch, 1.5*inch), repeatRows = 1)
table_style = TableStyle(ts)
n_table.setStyle(table_style)
PATH_OUT = "Desktop"
doc = SimpleDocTemplate(PATH_OUT + 'UserInputPDF_Test.pdf')
elements.append(Paragraph("CustomerTAT", styles['Title']))
elements.append(n_table)
doc.build(elements)
NewPDF()
EDIT: The stored procedure operates as it should within SQL. Here is the code for my stored procedure:
USE [XXXXX]
GO
/****** Object: StoredProcedure [presentation].[pdf_project] Script Date: 6/22/2021 4:31:20 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: XXXXXX
-- Create date:
-- Description:
-- =============================================
ALTER PROCEDURE [presentation].[pdf_project]
-- Add the parameters for the stored procedure here
#CustomerName varchar(50) = '',
#Year int = 0
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
SELECT CUSTOMER, [TAT Whole Days], [SHIP DATE], YEAR([SHIP DATE]) AS YEAR,
CASE
WHEN MONTH([SHIP DATE]) IN (1,2,3) THEN 'Q1'
WHEN MONTH([SHIP DATE]) IN (4,5,6) THEN 'Q2'
WHEN MONTH([SHIP DATE]) IN (7,8,9) THEN 'Q3'
ELSE 'Q4'
END AS QUARTER
FROM presentation.CustomerTAT
WHERE (YEAR([SHIP DATE]) = #Year or YEAR([SHIP DATE]) = #Year)
AND CUSTOMER = #CustomerName
END

Tkinter field in blank for Python

I need your help to figure out how to make the following coding:
My idea is to get a message if fields name and quantity are in blank at the time I execute a function through a button. I printed what is the value if name and quantity are in blank and program says it is .!entry and .!entry2 respectively.
Unfortunately it doesn't work. What I am doing wrong?
if name == .!entry and quantity == .!entry2:
message = Label(root, text = 'Name and Quantity required' , fg = 'red')
message.grid(row = 4, column = 0, columnspan = 2, sticky = W + E)
return
As #JordyvanDongen pointed out in the comments you should use entry.get() to get the text inside the the entry object.
I suggest this for you code, assuming the entry objects are named name and quantity:
if not name.get() or not quantity.get():
message = Label(root, text = 'Both name and quantity are required', fg = 'red')
message.grid(row = 4, column = 0, columnspan = 2, sticky = W + E)
return None
I changed the conditional to or so that if either of the fields are blank it will be triggered but you may want to change it back to and depending on your purposes..

Python sqlite3, tkinter display multible rows

I am new to python and recently i make dictionary using Python and Sqlite3 with tkinter.
When I run the code it returns multiple line in IDLE but in the GUI its only display the last result. I would like to display all the information in the GUI. Thanks you for any help and suggestion.
import tkinter
import sqlite3
class Dictionary:
def __init__(self,master =None):
self.main_window = tkinter.Tk()
self.main_window.title("Lai Mirang Dictionary")
self.main_window.minsize( 600,400)
self.main_window.configure(background = 'paleturquoise')
self.frame1 = tkinter.Frame()
self.frame2= tkinter.Frame(bg = 'red')
self.frame3 =tkinter.Text( foreground = 'green')
self.frame4 = tkinter.Text()
self.lai_label = tkinter.Label(self.frame1,anchor ='nw',font = 'Times:12', text = 'Lai',width =25,borderwidth ='3',fg='blue')
self.mirang_label = tkinter.Label(self.frame1,anchor ='ne',font = 'Times:12', text = 'Mirang',borderwidth ='3',fg ='blue')
self.lai_label.pack(side='left')
self.mirang_label.pack(side = 'left')
self.kawl_buttom= tkinter.Button(self.frame2 ,fg = 'red',font = 'Times:12',text ='Kawl',command=self.dic,borderwidth ='3',)
self.lai_entry = tkinter.Entry(self.frame2, width= 28, borderwidth ='3',)
self.lai_entry.focus_force()
self.lai_entry.bind('<Return>', self.dic,)
self.lai_entry.configure(background = 'khaki')
self.lai_entry.pack(side='left')
self.kawl_buttom.pack(side = 'left')
self.value = tkinter.StringVar()
self.mirang_label= tkinter.Label(self.frame3,font = 'Times:12',fg = 'blue',textvariable = self.value,justify = 'left',wraplength ='260',width = 30, height = 15,anchor = 'nw')
self.mirang_label.pack()
self.mirang_label.configure(background = 'seashell')
self.copyright_label = tkinter.Label(self.frame4,anchor ='nw',font = 'Times:12:bold',text = "copyright # cchristoe#gmail.com",width = 30,borderwidth = 3, fg = 'purple',)
self.copyright_label.pack()
self.frame1.pack()
self.frame2.pack()
self.frame3.pack()
self.frame4.pack()
tkinter.mainloop()
self.main_window.quit()
def dic(self, event = None):
conn = sqlite3.connect('C:/users/christoe/documents/sqlite/laimirangdictionary.sqlite')
c = conn.cursor()
kawl = self.lai_entry.get()
kawl = kawl + '%'
c.execute("SELECT * FROM laimirang WHERE lai LIKE ?", (kawl,))
c.fetchall
for member in c:
out = (member)
self.value.set(out)
print(out, )
dic=Dictionary()
You need to change:
c.execute("SELECT * FROM laimirang WHERE lai LIKE ?", (kawl,))
c.fetchall
for member in c:
out = (member)
self.value.set(out)
print(out, )
To:
for member in c.execute("SELECT * FROM laimirang WHERE lai LIKE ?", (kawl,)):
current_text = self.value.get()
new_text = current_text +( "\n" if current_text else "") +" ".join(member)
self.value.set(new_text)
The new version gets the current value of the StringVar value and then appends the results returned with a space inbetween for each row returned with each row on its own line. What you were doing however involved just updating the StringVar to be the current member object, and therefore it only ever had the last values.
This is how it looks with more than one line:

How can I put frames inside frames using tkinter?

I'm making a baseball program based on the strategy games produced by Avalon Hills from the 70s & 80s, and the last part is a gui. I've made all the code to run the game command line, and I've got the code to select lineups with a gui. I envision a 3by1 grid with a scoreboard on the first row, a text box displaying the result, out, home run, double play, etc., and the last row is divided between the pitcher and batter cards on the left side, and a frame of buttons. The frame will flip between an offence and defence frame. So first the defence frame comes up with options like pitching change, change position, and play ball. Play ball changes the frame to the offence options, which will be pinch hit, pinch run, steal, and so on. But how can I put the buttons inside a frame, then combine the player cards and buttons in another frame, and then add that to the main frame?
The DFrame & OFrame classes are inner classes (hence the "elf", not "self"). I've got the dynamic switching between the 2 Frames. My problem is breaking the DFrame main loop, it only plays the top of the first, and the self.roadOuts never increments. Here's what I've got:
while self.innings < 8.5 or self.homeScore == self.roadScore:
self.roadOuts = 0
while self.roadOuts < 3:
self.dFrame.mainloop()
class DFrame(Frame):
def __init__(elf, parent):
Frame.__init__(elf)
elf._playButton = Button(elf, text = 'Play Ball',
command = parent.oMenu)
elf._playButton.grid(row = 0, column = 0)
elf._pitchingButton = Button(elf, text = 'Pitching Change',
command = parent.pitchingChange)
elf._pitchingButton.grid(row = 1, column = 0)
elf._positionButton = Button(elf, text = 'Defensive Substitution',
command = parent.positionChange)
elf._positionButton.grid(row = 0, column = 1)
elf._alignButton = Button(elf, text = 'Change Positions',
command = parent.positionSwap)
elf._alignButton.grid(row = 1, column = 1)
elf._doubleButton = Button(elf, text = 'Double Switch',
command = parent.doubleSwitch)
elf._doubleButton.grid(row = 2, column = 0)
elf._walkButton = Button(elf, text = 'Intentional Walk',
command = parent.intentionalWalk)
elf._walkButton.grid(row = 2, column = 1)
elf._depthButton = Button(elf, text = 'Change Infield Depth',
command = parent.infieldDepth)
elf._depthButton.grid(row = 3, column = 0)
class OFrame(Frame):
def __init__(elf, parent):
Frame.__init__(elf)
elf._playButton = Button(elf, text = 'Play Ball',
command = parent.atBat)
elf._playButton.grid(row = 0, column = 0)
elf._pinchHitButton = Button(elf, text = 'Pinch Hit',
command = parent.pinchHit)
elf._pinchHitButton.grid(row = 1, column = 0)
elf._prfButton = Button(elf, text = 'Pinch Run First',
command = parent.pinchRunFirst)
elf._prfButton.grid(row = 0, column = 1)
elf._prsButton = Button(elf, text = 'Pinch Run Second',
command = parent.pinchRunSecond)
elf._prsButton.grid(row = 1, column = 1)
elf._prtButton = Button(elf, text = 'Pinch Run Third',
command = parent.pinchRunThird)
elf._prtButton.grid(row = 2, column = 1)
elf._stealButton = Button(elf, text = 'Steal',
command = parent.steal)
elf._stealButton.grid(row = 2, column = 0)
elf._bunt4HitButton = Button(elf, text = 'Bunt for a hit',
command = parent.bunt4AHit)
elf._bunt4HitButton.grid(row = 3, column = 0)
elf._hitNRunButton = Button(elf, text = 'Hit And Run',
command = parent.hitAndRun)
elf._hitNRunButton.grid(row = 4, column = 0)
elf._sacButton = Button(elf, text = 'Sacrifice',
command = parent.sacrifice)
elf._sacButton.grid(row = 4, column = 1)
elf._squeezeButton = Button(elf, text = 'Squeeze',
command = parent.squeeze)
elf._squeezeButton.grid(row = 3, column = 1)
the next method is called when the DFrame "play ball" button is clicked, and it makes the OFrame.
def oMenu(self):
self.dFrame.grid_forget()
self.dFrame.destroy()
self.oFrame = self.OFrame(self)
self.oFrame.grid(row = 1, column = 1)
self.oFrame.mainloop()
and at the end of an at bat, I have:
self.oFrame.grid_forget()
self.oFrame.destroy()
self.dFrame = self.DFrame(self)
self.dFrame.grid(row = 1, column = 1)
I'm not sure I understand your question. It seems like you know how to put one frame in another (It is really not much different than adding a Button to a frame -- or any other widget). I think what you're asking is how to dynamically switch which frame is being displayed at any given time.
You probably want the grid_forget method. using the play_ball button should cause the defense_frame to call it's grid_forget method, while re-gridding the the offense_frame. Of course, this would be pack_forget if you're using the pack geometry manager.
EDIT
Added a very rudimentary working example of the grid layout you described. It can probably be done a lot better, but this should get you started. (Particularly the switchOffenseDefense function and the switch_button button).
import Tkinter as tk
base=tk.Tk() #this is the main frame
root=tk.Frame(base) #Really this is not necessary -- the other widgets could be attached to "base", but I've added it to demonstrate putting a frame in a frame.
root.grid(row=0,column=0)
scoreboard=tk.Frame(root)
scoreboard.grid(row=0,column=0,columnspan=2)
###
#Code to add stuff to scoreboard ...
# e.g.
###
scorestuff=tk.Label(scoreboard,text="Here is the scoreboard")
scorestuff.grid(row=0,column=0)
#End scoreboard
#Start cards.
cards=tk.Frame(root)
cards.grid(row=1,column=0)
###
# Code to add pitcher and batter cards
###
clabel=tk.Label(cards,text="Stuff to add cards here")
clabel.grid(row=0,column=0)
#end cards
#Offense/Defense frames....
offense=tk.Frame(root)
offense.grid(row=1,column=1)
offense.isgridded=True #Dynamically add "isgridded" attribute.
offense_label=tk.Label(offense,text="Offense is coolest")
offense_label.grid(row=0,column=0)
defense=tk.Frame(root)
defense.isgridded=False
defense_label=tk.Label(defense,text="Defense is coolest")
defense_label.grid(row=0,column=0)
def switchOffenseDefense():
print "Called"
if(offense.isgridded):
offense.isgridded=False
offense.grid_forget()
defense.isgridded=True
defense.grid(row=1,column=1)
else:
defense.isgridded=False
defense.grid_forget()
offense.isgridded=True
offense.grid(row=1,column=1)
switch_button=tk.Button(root,text="Switch",command=switchOffenseDefense)
switch_button.grid(row=2,column=1)
root.mainloop()

Categories

Resources