Struggleing to validate a user entry in tkinter - validation

Here is part of some code i create for a project in tkinter using sqlite3 as a database in python. Im trying to make it so that when a user enters their values into the entry fields it only accepts integer values, and tried to implement this into the validation function. Ive tried using the try and except method, but this still seems to allow all values to be added to the table. How else could i attempt to make this work?
def validation (self):
try:
int(self.inc.get()) and int(self.out.get()) == True
except ValueError:
self.message['text'] = 'Value must be a number!'
def adding (self):
if self.validation:
query = 'INSERT INTO data VALUES (?,?)'
parameters = (self.inc.get(), self.out.get())
self.run_query (query, parameters)
self.message ['text'] = 'Record [] added' .format (self.inc.get ())
self.inc.delete (0, END)
self.out.delete (0, END)
else:
self.message['text'] = 'Income or outgoing field is empty'
self.viewing_records()
def deleting (self):
self.message ['text'] = ''
try:
self.tree.item(self.tree.selection ()) ['values'][0]
except IndexError as e:
self.message['text'] = 'Please, select record!'
return
self.message['text'] = ''
Income = self.tree.item (self.tree.selection ()) ['text']
query = 'DELETE FROM data WHERE totalinc = ?'
self.run_query (query, (Income, ))
self.message['text'] = 'Record [] deleted.'.format(Income)
self.viewing_records()
def editing (self):
self.message['text'] = ''
try:
self.tree.item (self.tree.selection ())['values'][0]
except IndexError as e:
self.message['text'] = 'Please select record'
return
name = self.tree.item (self.tree.selection ())['text']
old_out = self.tree.item (self.tree.selection ())['values'][0]
self.edit_wind = Toplevel ()
self.edit_wind.title ("Editing")
Label (self.edit_wind, text = 'Old income:').grid (row = 0, column = 1)
Entry (self.edit_wind, textvariable = StringVar(self.edit_wind, value = name), state = 'readonly').grid(row = 0, column = 2)
Label (self.edit_wind, text = 'New income:').grid(row = 1, column = 1)
new_inc = Entry (self.edit_wind)
new_inc.grid (row = 1, column = 2)
Label (self.edit_wind, text = 'Old outgoing:').grid (row = 2, column = 1)
Entry (self.edit_wind, textvariable = StringVar(self.edit_wind, value = old_out), state = 'readonly').grid(row = 2, column = 2)
Label (self.edit_wind, text = 'New outgoing: ').grid(row = 3, column = 1)
new_out = Entry (self.edit_wind)
new_out.grid (row = 3, column = 2)
Button (self.edit_wind, text = 'Save changes', command = lambda: self.edit_records (new_inc.get(), name, new_out.get(), old_out)).grid (row = 4, column = 2, sticky = W)
self.edit_wind.mainloop()
def edit_records (self, new_inc, name, new_out, old_out):
query = "UPDATE data SET totalinc = ?, totalout = ? WHERE totalinc = ? AND totalout = ?"
parameters = (new_inc, new_out, name, old_out)
self.run_query (query, parameters)
self.edit_wind.destroy()
self.message['text'] = 'Record [] changed.' .format (name)
self.viewing_records()
if __name__ == '__main__':
wind = Tk()
application = Product (wind)
wind.mainloop()

str = '8'
if str.isdigit():
print(str)
I suggest taking a look at is isdigit().

Related

probleme de renitialisation d'un QtreeWidget

I am developing an application that allows to place orders with python and QtDesigner. I can't manage to place two commands in a row. The first command passes without any problem but when I want to place another command without closing the application, this error is displayed: "self.ui.treeWidgetcommand.topLevelItem(self.Line ).setText(0, str(Id))
AttributeError: 'NoneType' object has no attribute 'setText'".
def AddCommande(self):
QtWidgets.QTreeWidgetItem(self.ui.treeWidgetcommande)
Libelle = self.ui.comboBoxproduit.currentText()
Qte = int(self.ui.lineEditQteproduit.text())
Info = self.stock.GetProductName(Libelle)[0]
Id = str(int(Info[0]))
Pu = Info[1]
Total = int(Qte)*int(Pu)
data=(Libelle,Qte,Id,Pu,Total)
#print(data)
self.ui.treeWidgetcommande.topLevelItem(self.Ligne).setText(0, str(Id))
self.ui.treeWidgetcommande.topLevelItem(self.Ligne).setText(1, str(Libelle))
self.ui.treeWidgetcommande.topLevelItem(self.Ligne).setText(2, str(Qte))
self.ui.treeWidgetcommande.topLevelItem(self.Ligne).setText(3, str(Pu))
self.ui.treeWidgetcommande.topLevelItem(self.Ligne).setText(4, str(Total))
self.Ligne +=1
def ValiderCommande(self):
Client = self.ui.comboBoxclient.currentText()
IdClient = self.stock.GetClientIdByName(Client.split(" ")[0])
PrixTotal = 0
UniqueId = random.random()
Date = date.today()
Data = (IdClient,PrixTotal,Date,UniqueId)
if self.stock.AddCommande(Data) == 0:
for i in range(self.Ligne):
IdCommande = self.stock.GetClientIdByUniqueId(UniqueId)
Libelle = self.ui.treeWidgetcommande.topLevelItem(i).text(1)
IdProduit = self.ui.treeWidgetcommande.topLevelItem(i).text(0)
Pu = self.ui.treeWidgetcommande.topLevelItem(i).text(3)
Qte = self.ui.treeWidgetcommande.topLevelItem(i).text(2)
Total = int(self.ui.treeWidgetcommande.topLevelItem(i).text(4))
InfoData = (IdCommande, Libelle, Qte, Pu, Total)
data = (Qte,IdProduit)
if self.stock.AjoutInfoCommande(InfoData) == 0:
PrixTotal += Total
self.stock.UpdateQteStock(data)
if self.stock.UpdateCommande(PrixTotal,IdCommande) == 0:
self.ui.treeWidgetcommande.clear()
#self.ui.treeWidgetcommande.topLevelItem(self.Ligne).setHidden(True)
self.ui.lineEditQteproduit.setText(" ")
`
I would like after placing an order, reset my treeWidget array and be able to place other orders.

Adding Textfield in Tkinter

I want to programm a simple 'calculator'.. Some Input fields, then some calculations with those inputs in the background and then I want to see output.
In a simple form it looks like that:
a-e: are input fields.
f-h: output fields.
The calculations in the background and the showing of output goes well.
Now I want to have a line between e and f that says: Output
Without any entry field or sth.. just a textfield.
Can someone help me, how to insert a textfield on a specific place ?
This is the code:
fields = ('a', 'b', 'c', 'd', 'e',
'f (in EUR)', 'g (days per year)', 'h in %')
def initUI(self):
self.master.title("Calculator")
self.pack(fill=BOTH, expand=1)
Style().configure("TFrame", background="#111")
bardejov = ImageTk.PhotoImage(bard)
label1 = Label(self, image=bardejov)
label1.image = bardejov
label1.place(x=20, y=20)
def total_assets(entries):
# period rate:
a = (float(entries['a'].get()))
b = (float(entries['b'].get()))
c = (float(entries['c'].get()))
d = (float(entries['d'].get()))
e = (float(entries['e'].get()))
entries['f'].insert(0, calc1 )
entries['g'].insert(0, np.round( calc2 ) )
entries['h'].insert(0, calc3 )
#calc1,cal2,calc3 are some calculations with the input fields
def makeform(root, fields):
entries = {}
for field in fields:
print(field)
row = tk.Frame(root)
lab = tk.Label(row, width=50, text=field+": ", anchor='w')
ent = tk.Entry(row)
ent.insert(0, "0")
row.pack(side=tk.TOP,
fill=tk.X,
padx=5,
pady=5)
lab.pack(side=tk.LEFT)
ent.pack(side=tk.RIGHT,
expand=tk.YES,
fill=tk.X)
entries[field] = ent
ent.config(background="gray")
return entries
if __name__ == '__main__':
root = tk.Tk()
root.title("hello")
root.columnconfigure(1, weight=1)
#root.geometry("1000x300") #Width x Height
#root.grid_columnconfigure("50")
ents = makeform(root, fields)
b1 = tk.Button(root, text='do',
command=(lambda e=ents: total_assets(e)))
#ents.place(x=10, y=115, height=30, width=200)
b1.pack(side=tk.LEFT, padx=5, pady=5)
b2 = tk.Button(root, text = "reset",
command=(lambda e=ents: restart_program(e)))
b2.pack(side=tk.LEFT, padx=5, pady=5)
root.mainloop()
You can use below code to create a separator with text:
def create_separator(parent, text=None):
frame = tk.Frame(parent)
frame.columnconfigure(0, weight=1)
ttk.Separator(frame, orient=tk.HORIZONTAL).grid(row=0, column=0, sticky='ew')
if text:
tk.Label(frame, text=text).grid(row=0, column=0, padx=10, sticky='w')
return frame
Then inside the for loop of makeform():
for field in fields:
...
ent.config(background="gray")
if field == 'e':
# add a separator
sep = create_separator(root, 'Output')
sep.pack(side=tk.TOP, fill=tk.X, padx=5, pady=5)

tk.Entry validate command doesn't restore previous value when False returned

I have carefully reviewed answers to Interactively validating Entry widget content in tkinter, but my script fails to restore previous value if the validate command returns False. I captured %P and %s and print them out...They both show the same value.
import tkinter as tk
class Controller :
def __init__(self) :
i=10
j=20
# list comprehension
self.entry_widgets = [[None for col in range(j)] for row in range(i)]
#print(self.entry_widgets)
self.values = [["string"+str(row) + str(col) for col in range(10)] for row in range(20)]
#print(self.values)
class EnterBox(tk.Entry):
def __init__(self,*args,**kwargs):
#print (args)
self._modified = False
self._save = 0
self._raise = 1
self._lower = 2
frame, i,j, *newargs = args
self._content = tk.StringVar()
# tk.Entry.__init__(self,frame,*newargs,
# validate = 'focusout',
# validatecommand = vcmd,
# **kwargs)
tk.Entry.__init__(self,frame,*newargs,**kwargs)
vcmd = (self.register(self._revert), '%P', '%s')
ct.entry_widgets[i][j] = self
self.config(textvariable=self._content)
self.config(validate = "focusout")
self.config(validatecommand = vcmd )
x=(ct.values[i][j])
self.insert(0,x)
#self._content.set(x)
self.bind("<Return>",lambda event, x=self._save : self._action(event,x) )
self.bind("<Button-2>",lambda event, x=self._save : self._action(event,x) )
self.bind("<FocusIn>", lambda event, x=self._raise : self._action(event,x))
self.bind("<FocusOut>", lambda event, x=self._lower : self._action(event,x))
self.bind('<Button-3>', lambda event, x=self._lower : self._action(event,x))
self.grid(column=i+1,row=j+2)
def _revert(self,P,s):
print ("Hi There")
print(P)
print(s)
return False
def _action(self,event,action):
print(str(action)+' ' + str(event))
if action == self._save :
ct.values[i][j] = self._content.get()
self.config(bg='lightskyblue2')
self._modified = True
elif action == self._raise :
self.config(bg = 'light pink')
elif action == self._lower :
self.config(bg = 'gray80')
self._modified = False
else :
print('action value is bad action =>' + str(action))
if "__main__" == __name__ :
root = tk.Tk()
frame = tk.Frame()
i=j=0
ct = Controller()
root.grid()
frame.grid()
check = EnterBox(frame,i,j,width = 24)
check2 = EnterBox(frame,i+1,j,width = 24)
root.mainloop()
I have tried removing all other bindings, to no avail.
Interestingly, but a separate issue, If I use StringVar. set instead of self.insert, (see commented out line) the validate command runs once, and never again despite several focus changes. Using Python 3.8
The validation isn't designed to restore anything if the validation happens on focusout. The validation can only prevent characters from being added at the time they are added. You will have to add code to restore the previous value.

update_cells not working as expected

I have written this function:
def duplicate_sheet1(wb, title=None):
if title is None:
title = wb.sheet1.title + ' DUPLICATE'
wb._sheet_list = [wb.sheet1]
wb.add_worksheet(title, wb.sheet1.row_count, wb.sheet1.col_count)
wb._sheet_list = wb._sheet_list[::-1]
wb._sheet_list[0].update_cells(wb._sheet_list[1]._fetch_cells())
...everything works as expected upon inspection with a debugger except update_cells, when I _fetch_cells for worksheet 0 after running the code, the sheet is empty.
Apparently the list returned by _fetch_cells is not the same as what is expected by update_cells. This may be because _fetch_cells does not include empty cells in the returned list, update_cells may only work with a 1 or 2-D grid--I am unsure.
Here is the work-around I found, apologies as the code could could probably be improved:
def duplicate_sheet1(wb, title=None):
if title is None:
title = wb.sheet1.title + ' DUPLICATE'
wb._sheet_list = [wb.sheet1]
wb.add_worksheet(title, wb.sheet1.row_count, wb.sheet1.col_count)
wb._sheet_list = wb._sheet_list[::-1]
cell_list = build_cell_list(wb._sheet_list[0], wb._sheet_list[1])
wb._sheet_list[0].update_cells(cell_list)
def build_cell_list(new_worksheet, old_worksheet):
fetched = old_worksheet._fetch_cells()
max_row = fetched[-1].row
max_col = max([cell.col for cell in fetched])
cell_list = new_worksheet.range('A1:' + chr(max_col + 64) + str(max_row))
for cell in cell_list:
cell.value = next(
(
f.value for f in fetched
if f.col == cell.col and f.row == cell.row
),
'',
)
return cell_list

Check OptionMenu selection and update GUI

I'm working on a class project and I'm trying to take it beyond the requirements a little here (I'm doing my own homework, just need help improving it!) so I want to update the GUI based on certain selections the user makes instead of just having all irrelevent options available all the time (requirements are to just present the options).
I'm still new to Python and even more new to Tkinter so my only attempt has been the following:
#Step Type
ttk.Label(mainframe, text = "Step Type").grid(column = 1, row = 16)
type_entry = OptionMenu(mainframe, StepType, "Kill", "Explore" , "Conversation")
type_entry.grid(column = 2, row = 16, sticky = (E))
#Step Goal
if StepType.get() == "Kill":
ttk.Label(mainframe, text = "Required Kills").grid(column = 1, row = 17)
goal_entry = ttk.Entry(mainframe, width = 20, textvariable = StepGoal)
goal_entry.grid(column = 2, row = 17, sticky = (E))
elif StepType.get() == "Explore":
ttk.Label(mainframe, text = "Location ID").grid(column = 1, row = 17)
goal_entry = ttk.Entry(mainframe, width = 20, textvariable = StepGoal)
goal_entry.grid(column = 2, row = 17, sticky = (E))
elif StepType.get() == "Conversation":
ttk.Label(mainframe, text = "NPC ID").grid(column = 1, row = 17)
goal_entry = ttk.Entry(mainframe, width = 20, textvariable = StepGoal)
goal_entry.grid(column = 2, row = 17, sticky = (E))
Obviously what I want to do here is when the user selects one of the options from the menu, to display the corresponding entry box and label instead of having all 3 all the time.
Also looking for the same situation for CheckButton
Full working example: tested od 2.7.5 and 3.3.2
It use command= in OptionMenu to call function when user changed option.
import tkinter as ttk
#----------------------------------------------------------------------
def on_option_change(event):
selected = step_type.get()
if selected == "Kill":
goal_label['text'] = "Required Kills"
elif selected == "Explore":
goal_label['text'] = "Location ID"
elif selected == "Conversation":
goal_label['text'] = "NPC ID"
# show label and entry
#goal_label.grid(column=1, row=17)
#goal_entry.grid(column=2, row=17, sticky='E')
#----------------------------------------------------------------------
mainframe = ttk.Tk()
# Step Type
step_type = ttk.StringVar() # there is the rule: variable name lowercase with _
ttk.Label(mainframe, text="Step Type").grid(column=1, row=16)
type_entry = ttk.OptionMenu(mainframe, step_type, "Kill", "Explore" , "Conversation", command=on_option_change)
type_entry.grid(column=2, row=16, sticky='E')
step_type.set("Kill")
# Step Goal
step_goal = ttk.StringVar()
goal_label = ttk.Label(mainframe, text="Required Kills")
goal_label.grid(column=1, row=17)
goal_entry = ttk.Entry(mainframe, width=20, textvariable=step_goal)
goal_entry.grid(column=2, row=17, sticky='E')
# hide label and entry
#goal_label.grid_forget()
#goal_entry.grid_forget()
# --- star the engine ---
mainframe.mainloop()
BTW: you can use grid() and grid_forget() to show and hide elements.
EDIT: example with Radiobutton using trace on StringVar
import tkinter as ttk
#----------------------------------------------------------------------
def on_variable_change(a,b,c): # `trace` send 3 argument to `on_variable_change`
#print(a, b, c)
selected = step_type.get()
if selected == "Kill":
goal_label['text'] = "Required Kills"
elif selected == "Explore":
goal_label['text'] = "Location ID"
elif selected == "Conversation":
goal_label['text'] = "NPC ID"
#----------------------------------------------------------------------
mainframe = ttk.Tk()
# Step Type
step_type = ttk.StringVar() # there is the rule: variable name lowercase with _
ttk.Label(mainframe, text="Step Type").grid(column=1, row=16)
ttk.Radiobutton(mainframe, text="Kill", value="Kill", variable=step_type).grid(column=2, row=16, sticky='E')
ttk.Radiobutton(mainframe, text="Explore", value="Explore", variable=step_type).grid(column=3, row=16, sticky='E')
ttk.Radiobutton(mainframe, text="Conversation", value="Conversation", variable=step_type).grid(column=4, row=16, sticky='E')
step_type.set("Kill")
# use `trace` after `set` because `on_variable_change` use `goal_label` which is not created yet.
step_type.trace("w", on_variable_change)
# Step Goal
step_goal = ttk.StringVar()
goal_label = ttk.Label(mainframe, text="Required Kills")
goal_label.grid(column=1, row=17)
goal_entry = ttk.Entry(mainframe, width=20, textvariable=step_goal)
goal_entry.grid(column=2, row=17, sticky='E')
# --- star the engine ---
mainframe.mainloop()

Resources