I have a Python/Tkinter program and I am trying to increment some numbers that are at the end of a variable used for tk.Entry boxes.
For example, self.output0, self.output1, self.output2, etc
(Sorry, my code is long so I tried shortening it up for an example.)
self.entries = []
self.output0 = tk.Entry(self, width=149, justify='center', bd='0', bg='#E0E0E0')
self.output0.grid(column=0, row=6, columnspan=5, padx=1)
self.output1 = tk.Entry(self, width=149, justify='center', bd='0', bg='#E0E0E0')
self.output1.grid(column=0, row=7, columnspan=5, padx=1)
I grab the users input with another entry box such as 'self.first_entry' and run a SQL query on the user input.
For example:
class Adder(ttk.Frame):
"""The adders gui and functions."""
def __init__(self, parent, *args, **kwargs):
ttk.Frame.__init__(self, parent, *args, **kwargs)
self.root = parent
self.init_gui()
def authenticate(self):
return pypyodbc.connect('Driver={SQL Server};Server=MYSERVERNAME;Database=MYDATABASENAME;Trusted_Connection=yes;')
def calculate(self):
firstname = str(self.first_entry.get()) # converts user entry into variable
lastname = str(self.last_entry.get())
license = str(self.lic_entry.get())
try:
connection = self.authenticate()
except pypyodbc.Error as ex:
sqlstate = ex.args[0]
if sqlstate == '28000':
self.output0.delete(0, END)
self.output0.insert(0,"You do not have access.")
cursor = connection.cursor()
if (firstname and not lastname and not license): # "You entered first name."
SQLCommand = ("SELECT LASTNAME, FIRSTNAME, LICNUMBER "
"FROM MyTABLEName " # table name
"with (nolock)"
"WHERE FIRSTNAME LIKE ?")
Values = [firstname + '%']
else:
SQLCommand = ("SELECT LASTNAME, FIRSTNAME, LICNUMBER "
"FROM MyTABLEName " # table name
"(nolock)"
"WHERE FIRSTNAME LIKE ? AND LASTNAME LIKE ? AND LICNUMBER LIKE ?")
Values = [firstname + '%', lastname + '%', '%' + license + '%']
cursor.execute(SQLCommand,Values)
results = cursor.fetchmany(10)
if results:
self.output0.delete(0, END) # clears entry
self.output0.insert(0,results[0]) # enters results in entry
self.output1.delete(0, END) # clears entry
self.output1.insert(0,results[1]) # enters results in entry
self.output2.delete(0, END) # clears entry
self.output2.insert(0,results[2]) # enters results in entry
self.output3.delete(0, END) # clears entry
self.output3.insert(0,results[3]) # enters results in entry
self.output4.delete(0, END) # clears entry
self.output4.insert(0,results[4]) # enters results in entry
connection.close()
else:
self.output0.delete(0, END)
self.output0.insert(0,"There are no records for this input.")
The above will output 5 rows from the database.
I am wanting to shorten the code up. How can I take the below and write it so that I do not have to repeat
self.output0.delete(0, END) # clears entry
self.output0.insert(0,results[0]) # enters results in entry
self.output1.delete(0, END) # clears entry
self.output1.insert(0,results[1]) # enters results in entry
2
2
3
3
...etc
I want to increment the outputX and the number in results[x]
This below is what I have been trying but it is not working:
cursor.execute(SQLCommand,Values)
results = cursor.fetchmany(10)
self.outputs = [self.output0, self.output1, self.output2, self.output3, self.output4]
for output in self.outputs:
for i in range(0, 4):
if results:
output.delete(0, END) # clears entry
output.insert(0,results[i]) # enters results in entry
else:
self.output0.delete(0, END)
self.output0.insert(0,"There are no records for this input.")
How can I increment the number in "resuls[x]" so that I get a different row from the SQL query? It keeps printing the same row on each line.
The problem is that you're trying to index your cursor. Try converting it to a list first and then running the function.
results = list(cursor.fetchmany(10))
One easy and not so intrusive solution would be to use getattr:
def update_fields(self, prefix, values):
for x, _ in enumerate(values):
attr = getattr(self, prefix + str(x))
attr.delete(0, END)
attr.insert(0, values[x])
And then you'd just:
results = cursor.fetchmany(10)
if results:
self.update_fields('output', results)
Please note that getattr will raise an AttributeError if self has no declared field named outputX
You can ignore undeclared fields by swallowing the exception:
def update_fields(self, prefix, values):
for x, _ in enumerate(values):
try:
attr = getattr(self, prefix + str(x))
attr.delete(0, END)
attr.insert(0, values[x])
except AttributeError:
pass
# You can also use continue
Related
I have this code that I'm following from a video. This is a function that gets activated when a button is pressed. In some parts I want to erase the previous output in a label every time the button is pressed:
# Search customers
def search_customers():
search_customers = Tk()
search_customers.title("Search Customers")
search_customers.geometry("1300x600")
searched_label = Label(search_customers)
searched_label.grid(row=2, column=0)
test = Label(search_customers)
test.grid(row=3, column=0)
def search_now():
# searched_label = Label(search_customers)
# searched_label.grid(row=2, column=0)
selected = drop.get() # This is a Combobox
if selected == 'Search By...':
sql = ""
test['text'] = 'You forgot to pick an option'
elif selected == 'Last Name':
sql = "SELECT * FROM customers WHERE last_name = %s"
elif selected == 'Email Address':
sql = "SELECT * FROM customers WHERE email = %s"
elif selected == 'Customer ID':
sql = "SELECT * FROM customers WHERE user_id = %s"
searched = search_box.get()
name = (searched, )
result = my_cursor.execute(sql, name)
if selected == "Search By...":
result = ''
else:
result = my_cursor.fetchall()
if not result:
result = "Record Not Found"
test['text'] = ''
searched_label['text'] = result
elif result:
test['text'] = ''
searched_label['text] = ''
searched_label = Label(search_customers)
for index, x in enumerate(result):
num = 0
index += 2
for y in x:
searched_label = Label(search_customers, text=y)
searched_label.grid(row=index, column=num)
num += 1
The thing is, every time the code reaches this statement: searched_label['text'] = '', it says: variable referenced before the assignment but that doesn't happen with test['text'] = '' even though both labels are created in the same scope.
The only way it worked was to create searched_label inside the search_now() (see the commented lines and let's pretend to uncomment them and comment the ones above).
With the lines uncommented inside search_now(), when it reaches this statement: if not result, it sets searched_label['text'] = result without a problem, but when it reaches the last elif, it doesn't set searched_label['text'] = '', actually, let's say the code was run and it first reached the if not result: statement so when the button is press again and it reaches the last elif it doesn't erase the previous output with searched_label['text] = ''.
In this last elif, I tried reached_label.grid_remove() and creating the label again but the previous output still remains so it mixes with the new output.
Thanks in advance, I'm still learning and I hope my question is clear enough
If you want to change the texts of Label widgets regularily, it pays to use the parameter textvariable. It takes a StringVar() object that can be changed by any function. As soon as the StringVar gets a new value, the label changes.
I do not see the whole of your program, but this is how it works in general:
def search_customers():
search_customers = Tk()
search_customers.title("Search Customers")
search_customers.geometry("1300x600")
global labeltext
labeltext = StringVar() ## Initiate a string variable
labeltext.set("This is the mutable text")
searched_label = Label(search_customers,textvariable=labeltext)
searched_label.grid(row=2, column=0)
test = Button(search_customers,text="Change it",command=change)
test.grid(row=3, column=0)
def change():
labeltext.set("This is a new text")
If the program gets more complicated, you might also consider defining the dialog box as a new class, iheriting from Frame. There, you can define methods that have access to all widgets and variables without the need of global variables.
I can't help you. Actually, need more information. I am using match case statements instead of if/else.
Code:
def search_now():
searched_label = Label(search_customers)
searched_label.grid(row=2, column=0)
selected = drop.get() # This is a Combobox
match selected:
case 'Search By...':
sql = ""
test['text'] = 'You forgot to pick an option'
case 'Last Name':
sql = "SELECT * FROM customers WHERE last_name = %s"
case 'Email Address':
sql = "SELECT * FROM customers WHERE email = %s"
case 'Customer ID':
sql = "SELECT * FROM customers WHERE user_id = %s"
searched = search_box.get()
name = (searched, )
result = my_cursor.execute(sql, name)
if selected == "Search By...":
result = ''
else:
result = my_cursor.fetchall()
if not result:
result = "Record Not Found"
#test['text'] = ''
searched_label.config{text=result)
elif result:
#test['text'] = ''
searched_label.config(text='')
#searched_label = Label(text=search_customers)
for index, x in enumerate(result):
num = 0
index += 2
for y in x:
#searched_label = Label(search_customers, text=y)
searched_label.config(text=y)
Let me know if this work.
I am trying to query a database based upon user input, if the data exist then print the data, otherwise prompt entering new data. Upon query, the code only returns one time.
I've tried to use the while True: statement, it queries the data based upon the original input repeatedly.
I would like to query based on input, return a result, then reset query based on new user input. Can't seem to figure this one out. Any help would be appreciated.
user_input = input("Scan ID: ")
def read_from_db():
try:
c.execute("SELECT * FROM users WHERE barcode LIKE %s", ("%" + user_input + "%",))
result = c.fetchall()
if result is not None:
print ('Name:' , result[0][1], '| barcode: ' , result[0][3], ' | crew position: ' , result[0][4])
except:
print ("Register new user")
def main():
read_from_db()
if __name__ == '__main__':
try:
db = MySQLdb.connect("localhost","user","pw","database")
c= db.cursor()
except:
print ("not working...")
This did the trick -
while True:
user_input = input("Scan ID: ")
read_from_db = c.execute("SELECT * FROM users WHERE barcode LIKE %s", ("%" + user_input + "%",))
result = c.fetchall()
#print (result)
today = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
print (today)
if len(result) == 1:
print ('Name:' , result[0][1], '| barcode: ' , result[0][3], ' | crew position: ' , result[0][4])
c.execute("INSERT INTO Data (first_name,last_init,crew,time) VALUES (%s,%s,%s,%s)", (result[0][1],result[0][2][:1],result[0][4],today,))
db.commit()
else:
print ("Register new user")
fn = input("First Name: ")
ln = input("Last Name: ")
bc = input("Scan ID: ")
cp = input("IP or Student: ")
c.execute("INSERT INTO users (first_name,last_name,barcode,crew_position) VALUES (%s,%s,%s,%s)", (fn,ln,bc,cp,))
db.commit()
SELECT *
FROM users
WHERE barcode
LIKE CONCAT('%',user_input,'%');
I'm relatively new to Python and currently working on a 2nd tutorial, specifically working with SQLite.
In my attached code (specifically the function Update), I have 3 while loops and several breaks within just this simple function. I am thinking that this is not good practice and I am looking for advice to make it less vulnerable to crashing and/or more compact.
What are the side effects of having too many break statements?
def updateData():
global cursor
sql = 'SELECT name FROM sqlite_master WHERE type = "table" ORDER BY name'
cursor.execute(sql)
rows = cursor.fetchall()
print("These are the existing tables in the system:")
tableList = []
for row in rows:
print(row[0])
tableList.append(row[0])
while True:
table = input("Enter table to update: ")
if table in tableList:
while True:
phoneLIST = searchDB()
if len(phoneLIST) == 0:
option = tryMessage()
if not option:
break
else:
numID = int(input("Enter the ID number you want updated: "))
sql = 'PRAGMA table_info(%s)' % table
cursor.execute(sql)
rows = cursor.fetchall()
print("These are the existing columns in %s table:" % table)
colList = []
for row in rows:
print(row[1])
colList.append(row[1])
while True:
column = input("Enter the column name of ID: %d you want updated: " % numID)
column = column.upper()
if column in colList:
if column == 'BOD' or column == 'PID':
print("You can't change Birth of Date OR PID")
option = tryMessage()
if not option:
break
else:
if column == 'PHONE':
newEntry = checkPhone()
elif column == 'POSTAL':
newEntry = checkPostal()
else:
newEntry = input("Enter new information for column %s: " % column)
sql = 'UPDATE %s SET %s = "%s" WHERE PID = %d' % (table, column, newEntry, numID)
cursor.execute(sql)
displayOneEntry(numID)
commitMessage()
break
else:
print("Column not in the table")
break
break
else:
print("Table not in the database")
option = tryMessage()
if not option:
break
There's nothing wrong with while True: loops; they're the natural way of doing something over and over until an error happens or the user decides to quit.
In my opinion this function is a bit clumsy, because it's working at many different levels of detail. It might be better to reorganize it into separate functions for tables, column IDs, and values, so that each function is concerned just with its own stuff and doesn't worry about higher- or lower-level details.
I have refactored as follows, eliminating the nested while True:
Thanks again #JG!
{other functions ...}
def getTable():
global cursor
sql = 'SELECT name FROM sqlite_master WHERE type = "table" ORDER BY name'
cursor.execute(sql)
rows = cursor.fetchall()
tableList = []
print("These are the available tables: ")
for row in rows:
print(row)
tableList.append(row[0])
while True:
tableName = input("Enter table to update: ")
if tableName in tableList:
return tableName
break
else:
print("Table not in the database")
# provide option to re-enter information
option = tryMessage()
if not option:
break
def getColumn(tableName, numID):
global cursor
sql = 'PRAGMA table_info(%s)' % tableName
cursor.execute(sql)
rows = cursor.fetchall()
print("These are the existing columns in %s table:" % tableName)
colList = []
for row in rows:
print(row[1])
colList.append(row[1])
while True:
colName = input("Enter the column name of ID: %d you want updated: " % numID)
colName = colName.upper()
if colName in colList:
return colName
else:
print("Column not in the table")
# provide option to re-enter information
option = tryMessage()
if not option:
break
def getID(idList):
while True:
try:
numID = int(input("Enter the ID number you want updated: "))
except ValueError:
print('Enter valid number')
continue
if numID in idList:
return numID
else:
print("Wrong ID")
# admin use only
def updateData():
global tempPassword
passWord = input("Enter password: ")
if passWord == tempPassword:
global cursor
# Displays valid tables
tableName = getTable()
idName = getIDName(tableName)
while True:
idList = searchDB()
# provide option to re-enter information
if len(idList) == 0:
option = tryMessage()
if not option:
break
else:
numID = getID(idList)
colName = getColumn(tableName, numID)
if colName == 'BOD' or colName == idName or colName == 'STATUS':
print("You can't change this field")
# provides option to re-enter information
option = tryMessage()
if not option:
break
else:
if colName == 'PHONE':
# checks format for phone input
newEntry = checkPhone()
elif colName == 'POSTAL':
# checks format for postal code input
newEntry = checkPostal()
elif colName == 'POSITION_ID':
# checks to ensure ID is valid
newEntry = checkPositionID()
else:
newEntry = input("Enter new information for column %s: " % colName)
sql = 'UPDATE %s SET %s = "%s" WHERE %s = %d' % (tableName, colName, newEntry, idName, numID)
cursor.execute(sql)
# display the updated entry for confirmation
displayOneEntry(idName, numID)
# provide option to commit changes
commitMessage(idName)
break
else:
print("Access requires correct password")
{menu ...}
I'm trying to close the ODBC connection and I'm not sure about the best way to implement this.
My program runs but I'm wanting to close the connection properly with connection.close(). Here is my original:
import pypyodbc
def queryfirst():
return ("SELECT FIRSTNAME, LASTNAME "
"FROM dbo.MAIN "
"WHERE FIRSTNAME = ?")
def sqlfirst():
firstname = "Josh"
if True:
connection = pypyodbc.connect('Driver={SQL Server};Server=;Database=;Trusted_Connection=yes;')
cursor = connection.cursor()
SQLCommand = queryfirst()
Values = [firstname]
cursor.execute(SQLCommand,Values)
return cursor.fetchmany(2)
def calculate():
results = sqlfirst()
if results:
print (results[0]) # prints the first and last name
calculate()
I've tried this:
import pypyodbc
def queryfirst():
return ("SELECT FIRSTNAME, LASTNAME "
"FROM dbo.V_LICMAIN_IT "
"WHERE FIRSTNAME = ?")
def sqlfirst(closeit):
firstname = "Josh"
if True:
connection = pypyodbc.connect('Driver={SQL Server};Server=;Database=;Trusted_Connection=yes;')
cursor = connection.cursor()
SQLCommand = queryfirst()
Values = [firstname]
cursor.execute(SQLCommand,Values)
return cursor.fetchmany(1)
connection.close() = closeit
def calculate():
results = sqlfirst()
if results:
print (results[0]) # prints the first and last name
sqlfirst(closeit)
calculate()
The above says:
connection.close() = closeit
SyntaxError: can't assign to function call
And this with no luck:
import pypyodbc
def queryfirst():
return ("SELECT FIRSTNAME, LASTNAME "
"FROM dbo.MAIN "
"WHERE FIRSTNAME = ?")
def closeconn():
return connection.close()
def sqlfirst():
firstname = "Josh"
if True:
connection = pypyodbc.connect('Driver={SQL Server};Server=;Database=;Trusted_Connection=yes;')
cursor = connection.cursor()
SQLCommand = queryfirst()
Values = [firstname]
cursor.execute(SQLCommand,Values)
return cursor.fetchmany(2)
testname = closeconn()
def calculate():
results = sqlfirst()
if results:
print (results[0]) # prints the first and last name
closeconn()
calculate()
The above says:
return connection.close()
NameError: name 'connection' is not defined
UPDATE: Below is my full code:
import os
import pypyodbc
import tkinter
from tkinter import ttk
from tkinter import messagebox
from tkinter import BOTH, END, LEFT
import traceback
class Adder(ttk.Frame):
"""The adders gui and functions."""
def __init__(self, parent, *args, **kwargs):
ttk.Frame.__init__(self, parent, *args, **kwargs)
self.root = parent
self.init_gui()
def queryfirst(self):
return ("SELECT LASTNAME, FIRSTNAME, ID "
"FROM dbo.TABLENAME " # table name
"WHERE FIRSTNAME = ?")
def connect(self):
return pypyodbc.connect('Driver={SQL Server};Server=;Database=;Trusted_Connection=yes;')
def sqlfirst(self):
firstname = str(self.first_entry.get())
lastname = str(self.last_entry.get())
license = str(self.lic_entry.get())
if (firstname and not lastname and not license): # "You entered first name."
try:
connection = self.connect()
except pypyodbc.Error as ex:
sqlstate = ex.args[0]
if sqlstate == '28000':
self.output0.delete(0, END)
self.output0.insert(0,"You do not have access.")
cursor = connection.cursor()
SQLCommand = self.queryfirst()
Values = [firstname]
cursor.execute(SQLCommand,Values)
return cursor.fetchmany(10)
# connection.close() # !!!!!! <<< this is what I'm working on
def calculate2(self):
results = self.sqlfirst()
if results:
self.output2.delete(0, END)
self.output2.insert(0,results[2])
def calculate1(self):
results = self.sqlfirst()
if results:
self.output1.delete(0, END)
self.output1.insert(0,results[1])
def calculate(self):
results = self.sqlfirst()
if results:
self.output0.delete(0, END)
self.output0.insert(0,results[0])
self.calculate1()
self.calculate2()
def init_gui(self):
"""Builds GUI."""
self.root.title('Verify')
self.root.option_add('*tearOff', 'FALSE')
# Input Boxes and Button
self.first_entry = tkinter.Entry(self, width=28) # first input box
self.first_entry.grid(sticky='', column=1, row=1)
self.output0 = tkinter.Entry(self, width=150, bd=0,)
self.output0.grid(column=0, row=6, columnspan=5, padx=10)
self.output0.bind("<Key>", lambda e: "break")
self.output1 = tkinter.Entry(self, width=150, bd=0,)
self.output1.grid(column=0, row=7, columnspan=5, padx=10)
self.output1.bind("<Key>", lambda e: "break")
self.output2 = tkinter.Entry(self, width=150, bd=0,)
self.output2.grid(column=0, row=8, columnspan=5, padx=10)
self.output2.bind("<Key>", lambda e: "break")
self.blank.grid(row=16,)
if __name__ == '__main__':
root = tkinter.Tk()
Adder(root)
root.resizable(width=False, height=False) # locks window from being resized
root.mainloop()
Looks like you got an exception and masked it because sqlstate was not '28000'.
try:
connection = self.connect()
except pypyodbc.Error as ex:
sqlstate = ex.args[0]
if sqlstate == '28000':
self.output0.delete(0, END)
self.output0.insert(0,"You do not have access.")
else:
self.output0.insert(0,"Some other database error ({})".format(
ex.message
))
else:
cursor = connection.cursor()
SQLCommand = self.queryfirst()
Values = [firstname]
cursor.execute(SQLCommand,Values)
try:
return cursor.fetchmany(10)
finally:
connection.close()
Also note that any line after a return statement will not be executed unless it is inside a finally block.
I'm creating a program that allows the user to select a category and enter a value to calculate a charge. I would like to validate the text entry using my own validation file. However, when I run the program and enter nothing in the text entry, the error window keeps popping up over and over again. In addition, when I run the program and enter a valid number in the entry field, the charge comes out to 0.0, even though I have defined the calculation for the total charge.
Here is the program:
import tkinter
import tkinter.messagebox
import ValidationFile
validationObject = ValidationFile.ValidationClass ()
class MyGUI:
def __init__ (self):
self.main_window = tkinter.Tk ()
self.top_frame = tkinter.Frame (self.main_window)
self.middle_frame = tkinter.Frame (self.main_window)
self.bottom_frame = tkinter.Frame (self.main_window)
self.phone_var = tkinter.IntVar ()
self.phone_var.set (1)
self.pb1 = tkinter.Radiobutton (self.top_frame, \
text = 'Daytime (6:00 AM - 5:59 PM)', variable = self.phone_var, \
value = 0.12)
self.pb2 = tkinter.Radiobutton (self.top_frame, \
text = 'Evening (6:00 PM - 11:59 PM)', variable = self.phone_var, \
value = 0.07)
self.pb3 = tkinter.Radiobutton (self.top_frame, \
text = 'Off-Peak (Midnight - 5:50 AM)', variable = self.phone_var, \
value = 0.05)
#pack phone buttons
self.pb1.pack ()
self.pb2.pack ()
self.pb3.pack ()
#create input and output buttons
self.txtInput = tkinter.Entry (self.middle_frame, \
width = 10)
self.value = tkinter.StringVar ()
self.lblOutput = tkinter.Label (self.middle_frame, \
width = 10, textvariable = self.value)
self.txtInput.pack()
self.lblOutput.pack ()
#create OK buton and QUIT button
self.ok_button = tkinter.Button (self.bottom_frame, \
text = 'OK', command = self.show_choice)
self.quit_button = tkinter.Button (self.bottom_frame, \
text = 'QUIT', command = self.main_window.destroy)
#pack the buttons
self.ok_button.pack (side = 'left')
self.quit_button.pack (side = 'left')
#pack the frames
self.top_frame.pack ()
self.middle_frame.pack ()
self.bottom_frame.pack ()
#start the mainloop
tkinter.mainloop ()
def show_choice (self):
choice = self.phone_var.get ()
value = -1
while value == -1:
valueEntry = self.txtInput.get()
if valueEntry == '':
value = -1
tkinter.messagebox.showinfo (title = 'ERROR', \
message = 'Please enter a valid number.')
else:
value = validationObject.checkFloat (valueEntry)
total = choice * value
self.value.set (total)
#create instance of MyGUI class
my_GUI = MyGUI ()
Here is the validation file:
#create validation class
class ValidationClass:
def checkFloat (self, inputString):
try:
result = float (inputString)
except Exception:
return -1
if result < 0:
return -1
else:
return result
def checkInteger (self, inputString):
try:
result = int (inputString)
except Exception:
return -1
if result < 0:
return -1
else:
return result
You made an infinite loop with while value == -1:. Nowhere in that loop do you pause to allow the user to try again. You don't need the loop at all:
def show_choice (self):
valueEntry = self.txtInput.get()
value = validationObject.checkFloat(valueEntry)
if value == -1:
tkinter.messagebox.showinfo (title = 'ERROR', \
message = 'Please enter a valid number.')
else:
choice = self.phone_var.get()
total = choice * value
self.value.set (total)
Once you fix that you will have another problem: you use float values in your options but the variable is an IntVar, which can only handle integers. So "choice" will always be 0. You need to use DoubleVar instead.