Trying to Grab Data Entry from Treeview to Create a command - python

Hi Guys currently working on POS system I almost have it finished just trying to create a Remove Item Function I'm using a Treeviewer List to display my item currently in the cart. I'm trying to make a Remove Item function and I want to get the price of the Item that is currently selected in the Treeviewer list and - the value of the item off the Total.
Just want help with getting data from the Treeview list I can do everything else. Thanks
This is my Treeview List
scroll_x = Scrollbar(ChangeButtonFrame, orient=HORIZONTAL)
scroll_y = Scrollbar(ChangeButtonFrame, orient=VERTICAL)
self.POS_records=ttk.Treeview(ChangeButtonFrame, height=20,columns=("Item","Qty","Amount"),
xscrollcommand=scroll_x .set, yscrollcommand=scroll_y .set)
scroll_x.pack(side=BOTTOM, fill=X)
scroll_y.pack(side=RIGHT, fill=Y)
self.POS_records.heading("Item",text="Item")
self.POS_records.heading("Qty",text="Qty")
self.POS_records.heading("Amount",text="Amount")
self.POS_records['show']='headings'
self.POS_records.column("Item",width=120)
self.POS_records.column("Qty",width=100)
self.POS_records.column("Amount",width=100)
self.POS_records.pack(fill=BOTH, expand=1)
self.POS_records.bind("<ButtonRelease-1>")
As seen when a button is pressed it enters the value defined in that function
here is an example
def Coffee1():
ItemCost = 3.70
Tax = 10
self.POS_records.insert("", tk.END, values=("Americano", "1", "3.70"))
for child in self.POS_records.get_children():
ItemCost += float(self.POS_records.item(child, "values")[2])
SubTotal_Input.set(str('$%.2f'%(ItemCost-3.70)))
GST_Input.set(str('$%.2f'%(((ItemCost-3.70)* Tax)/100)))
Total_Input.set(str('$%.2f'%((ItemCost-3.70) + ((ItemCost-3.70)* Tax)/100)))
I Have what is currently selected in my function as of now just don't know how to continue.
(Currently what I've done in the function)
def RemoveItem():
selected_item = (self.POS_records.selection())
self.POS_records.delete(selected_item)
Im trying to create a remove item function that grabs the price from the treeview and then ill use that data to calculate the SubTotal_Input, GST_Input and Total_Input.
Thanks Any help appreciated :)

Related

tkinter treeview open state is not up to date when callback is triggered

I am currently working on an tkinter application that uses a treeview element to display data in a hierarchy. In part of this application, I have the need to update data for items. Unfortunately, getting this data is expensive (in time and processing), so I now only update items that are currently visible.
The problem I am having is that when I bind a (left click) event to an item and then query the treeview for open items to try and determine what is visible, previously opened items are returned, but the currently opened item is not (until a subsequent item is clicked).
I saw this question about querying (and setting) the open state, but it doesn't seem to cover my needs.
I have the following simplified code, which demonstrates the problem:
## import required libraries
import tkinter
import tkinter.ttk
import random
import string
## event handler for when items are left clicked
def item_clicked(event):
tree = event.widget
## we assume event.widget is the treeview
current_item = tree.item(tree.identify('item', event.x, event.y))
print('Clicked ' + str(current_item['text']))
## for each itewm in the treeview
for element in tree.get_children():
## if it has elements under it..
if len(tree.get_children(element)) > 0:
## get the element name
element_name = tree.item(element)['text']
## check if it is open
if tree.item(element)['open'] is True:
print('Parent ' + element_name + ' is open')
else:
print('Parent ' + element_name + ' is closed')
## make the window and treeview
root = tkinter.Tk()
root.title('Treeview test')
tree = tkinter.ttk.Treeview(root, selectmode = 'browse', columns = ('num', 'let'))
tree.heading('#0', text = 'Name')
tree.heading('num', text = 'Numbers')
tree.heading('let', text = 'Letters')
tree.bind('<Button-1>', item_clicked)
tree.pack()
## populate the treeview with psuedo-random data
parents = ['']
for i in range(100):
id = str(i)
## higher probability that this won't be under anything
if random.randrange(50) > 40:
parent = random.choice(parents)
else:
parent = ''
## make up some data
tree.insert(parent, 'end', iid = id, text = 'Item ' + id)
tree.set(id, 'num', ''.join(random.choices(string.digits, k=10)))
tree.set(id, 'let', ''.join(random.choices(string.ascii_uppercase, k=10)))
parents.append(id)
## main event loop (blocking)
root.mainloop()
This code will generate a treeview with 100 items in a random heirachy. Clicking on any item will print a list of items that have children and are currently expanded (open). The issue is, as with my main code, not reporting the latest item opened (i.e. it is one step behind).
Adding update() to the treeview before querying its state (at the start of the item_clicked function) does not resolve the issue.
How can I accurately get the open state of the items in the event handler of the code above?
Is it an issue that the event is being fired before the item actually expands (opens), and if so, why is an update() not fixing this?
Does an alternative function exist for treeview widgets to return a list of currently visible items?
Running on Windows 10 with Python 3.7.3 (Tk version 8.6.9).
Changing the event binding from <Button-1> to <ButtonRelease-1> in the code above resolves the issue.
It should be noted in the above code that the open/close state will only be printed for items with children that have no parent (i.e. it won't descend in to the tree). To resolve this, you simply need to call tree.get_children(item_id) on each item with children recursively.

How to properly make OPEN and SAVE function with Tkinter?

I am making a simple shopping list application.
Whenever I try to save the content from the listbox shopping_list it saves as a tuple ('.., .., ...,') in the .txt file the function creates.
When I use the open button, the listbox displays the text as a tuple.
Example:
If in the entry field I write something like pizza and add it to the listbox and save it. When I try to open the same file into the listbox, the listbox now displays: ('pizza'). How can I get the listbox to display only pizza?
The code:
from tkinter import *
from tkinter import messagebox
from tkinter import filedialog
from PIL import ImageTk, Image
def add_item():
'''
This function takes the input from the entry field
and places the item in a shopping list.
:return: Adds to list
'''
if len(user_input.get()) == 0: # Checks if user typed into the entry field.
messagebox.showwarning("Warning", "No keys were detected from the Entry field") # Send warning for no input.
# Else statement for adding the user input to shopping list.
else:
shopping_list.insert(END, user_input.get())
user_input.delete(0, END) # Deletes whatever the user wrote in the entry box after button is clicked.
def remove_item():
'''
This function deletes the selected items when REMOVE button is clicked
'''
try:
selected = shopping_list.curselection()
for item in selected[::-1]: # Fetches all selected items from shopping list
shopping_list.delete(item)
# Make a warning if the shopping list is empty or items are not selected when REMOVE button is clicked.
except TclError:
messagebox.showwarning("Warning", "The shopping list is either empty \nor you didn't select an item")
def open_list():
'''
This function opens an existing shopping list that was previously saved.
:return: Opens older saved files.
'''
root.file = filedialog.askopenfilename(initialdir="./", title="Select a file",
filetypes=(("Txt files", "*.txt"), ("All files", "*,*")))
with open(root.file, "r") as file:
data_content = file.read()
shopping_list.delete(0, END)
shopping_list.insert(END, data_content)
def save_file():
'''
This function saves the whole shopping list and writes the content to the "shopping_list.txt" file.
:return: None
'''
text_file = filedialog.asksaveasfilename(defaultextension=".txt")
try:
with open(text_file, "w") as file:
file.write(str(shopping_list.get(0, END)))
except FileNotFoundError:
messagebox.showinfo("Alert", "No file was saved")
# backup eldre løsning:
"""
def save_file():
'''
This function saves the whole shopping list and writes the content to the "shopping_list.txt" file.
:return: None
'''
root.filename = filedialog.asksaveasfilename()
with open("shopping_list.txt", "w") as f:
for i in shopping_list.get(0, END):
f.write(i+"\n")
"""
# Root files
root = Tk()
root.title("Skoleoppgave OBLIG 5")
root.iconbitmap('favicon.icns')
root.geometry("500x500")
root.resizable(0, 0)
# TODO: Entry widget ENTRY 1
# Creating an Entry widget so user can add items to shopping-list.
user_input = Entry(root, relief=SUNKEN)
user_input.pack()
user_input.place(x=250, y=20, anchor=N)
# Todo: Buttons widget ADD ITEM 2
# Make a Button-widget that adds the user input from entry field to the shopping list.
add_entry = Button(root, text="ADD ITEM", command=add_item, padx=10, pady=5)
add_entry.pack()
add_entry.place(x=158, y=57)
# Todo: Listbox widget 4
# Creating Listbox for shopping list items
shopping_list = Listbox(root, selectmode=EXTENDED)
shopping_list.pack()
shopping_list.place(x=160, y=100)
"""
# Opens last saved shopping list when program starts.
try:
read = open("shopping_list.txt","r")
list_file = read.readlines()
read.close()
for data in list_file:
shopping_list.insert(0, END, data)
except FileNotFoundError:
read = open("shopping_list.txt","w")
"""
# Todo: Buttons widget REMOVE 3
# Make a Button-widget that deletes the selected item from shopping list
remove_entry = Button(root, text="REMOVE", command=remove_item, padx=10, pady=5)
remove_entry.pack()
remove_entry.place(x=160, y=285)
# Todo: Buttons widget OPEN 5
# Make a Button-widget that deletes the selected item from shopping list
open_file = Button(root, text="OPEN", command=open_list, padx=10, pady=5)
open_file.pack()
open_file.place(x=160, y=285)
# Todo: Buttons widget SAVE 6
# Make a Button-widget that deletes the selected item from shopping list
save_file = Button(root, text="SAVE", command=save_file, padx=10, pady=5)
save_file.pack()
save_file.place(x=282, y=57)
root.mainloop()
As mentioned in the comments, you are trying to put a string into a method that is asking for a tuple, that will give an error.
so one way is that you add functionality both in reading and writing to write a data format to the file that you control yourself. tip is trying to replicate one to one what you see in the listbox to the textfile. It looks like your "eldre losning" will put one item per line. So the thing is to read the file so that you get a list of strings where every row becomes an item in the list. Hint... .split on strings works good for that.
Or, just deal with the string you already have in the file.
One way to do that is to add/modify the following.
from ast import literal_eval # added import for safe evals
# modified open
with open(root.file, "rt") as file:
data_content = literal_eval(file.read())
shopping_list.delete(0, END)
shopping_list.insert(END, *data_content)
Side note #1, you don't need .pack() if you use .place.
Side note #2, next time around, mention that you are doing homework upfront.
Side note #3, if you use what's in this answer - you might have to do some reading before you can explain why it could be done like this to be believed that you came up with it yourself :-)
Good luck with your studies and learning.

Focusing on next entry box after typing a number in current entry box

I'm trying to see if there's a way to type a number between 1 and 4 into an entry box, then go to the next entry box (with the number entered into the box; the code below skips to the next entry without entering anything)
I'm creating a program that will take item-level data entry to be computed into different subscales. I have that part working in different code, but would prefer not to have to hit tab in between each text entry box since there will be a lot of them.
Basic code:
from tkinter import *
master = Tk()
root_menu = Menu(master)
master.config(menu = root_menu)
def nextentrybox(event):
event.widget.tk_focusNext().focus()
return('break')
Label(master, text='Q1',font=("Arial",8)).grid(row=0,column=0,sticky=E)
Q1=Entry(master, textvariable=StringVar)
Q1.grid(row=0,column=1)
Q1.bind('1',nextentrybox)
Q1.bind('2',nextentrybox)
Q1.bind('3',nextentrybox)
Q1.bind('4',nextentrybox)
Label(master, text='Q2',font=("Arial",8)).grid(row=1,column=0,sticky=E)
Q2=Entry(master, textvariable=StringVar)
Q2.grid(row=1,column=1)
Q2.bind('1',nextentrybox)
Q2.bind('2',nextentrybox)
Q2.bind('3',nextentrybox)
Q2.bind('4',nextentrybox)
### etc for rest of questions
### Scale sums, t-score lookups, and report generator to go here
file_menu = Menu(root_menu)
root_menu.add_cascade(label = "File", menu = file_menu)
file_menu.add_separator()
file_menu.add_command(label = "Quit", command = master.destroy)
mainloop()
Thanks for any help or pointers!
The simplest solution is to enter the event keysym before proceeding to the next field.
In the following example, notice how I added a call to event.widget.insert before moving the focus:
def nextentrybox(event):
event.widget.insert("end", event.keysym)
event.widget.tk_focusNext().focus()
return('break')

Changing entry text in tkinter, if the entry is created from list

I created 12 Entry boxes using a for loop with a default value of N/A. Any change in the text of entry is detected through .trace method.
I want to use reset button to make all the text on the Entry Boxes back to N/A
from tkinter import *
root = Tk()
t_diesel_price_EnF_variable = ["JanVar", "FebVar", "MarVar", "AprVar","MayVar","JuneVar","JulyVar","AugVar","SeptVar", "OctVar", "NovVar", "DecVar"]
t_diesel_price_EnF_values = ["N/A", "N/A","N/A", "N/A","N/A", "N/A","N/A", "N/A","N/A", "N/A","N/A", "N/A"]
def EnFChanging(*events):
for EnF in range(0,len(t_diesel_price_EnF_variable)):
t_diesel_price_EnF_values[EnF]=t_diesel_price_EnF_variable[EnF].get()
try:
t_diesel_price_EnF_values[EnF] = float(t_diesel_price_EnF_values[EnF])
except ValueError:
pass
print(t_diesel_price_EnF_values)
for EnF in range(0,len(t_diesel_price_EnF_values)):
t_diesel_price_EnF_variable[EnF] = StringVar(root , value = "N/A")
t_diesel_price = Entry(root , textvariable = t_diesel_price_EnF_variable[EnF], width = 10).pack()
t_diesel_price_EnF_variable[EnF].trace("w",EnFChanging)
def ChangeText():
for EnF in range(0, len(t_diesel_price_EnF_values)):
t_diesel_price[EnF].delete(0,END)
t_diesel_price[EnF].insert(0,"N/A")
return
b1 = Button(root, text = "Reset" , command = ChangeText).pack()
root.mainloop()
When I press the button it gives an error t_diesel_price[EnF].delete(0,END)
TypeError: 'NoneType' object is not subscriptable
What should I do now, Please ignore the basic errors of programming as I am a Mechanical Engineer with not a programming back ground. And I have to make a lot of other boxes too for my energy calculator.
You trying treat t_diesel_price as an Entry (and as a list) when your variable is None.
First of all I suggest you to install some IDE (e.g. PyCharm) and place break points to see whats wrong with variable!
Your problem occures because you create and pack your widget in one line! So t_diesel_price is None because pack() always returns None (link).
Just split your declaration and packing to:
t_diesel_price = Entry(root , textvariable = t_diesel_price_EnF_variable[EnF], width = 10)
t_diesel_price.pack()
After that it's works for me, except this fact that t_diesel_price is last created entry and the value changes only in it. So I assume that you need another list to iterate over entries:
...
# another fresh list
t_diesel_price_EnF_entries = list()
...
# declare entry
t_diesel_price = Entry(root, textvariable=t_diesel_price_EnF_variable[EnF], width=10)
# pack entry
t_diesel_price.pack()
# append entry to list
t_diesel_price_EnF_entries.append(t_diesel_price)
...
def ChangeText():
# iterate over entries
for diesel_price in t_diesel_price_EnF_entries:
diesel_price.delete(0,END)
diesel_price.insert(0,"N/A")
...
Alternatively you can iterate over StringVar's if you don't wanna store your entries at all:
def ChangeText():
# iterate over stringvars
for EnF in range(len(t_diesel_price_EnF_variable)):
t_diesel_price_EnF_variable[EnF].set('N/A')
And you can make it more readable as in example with entry iterating:
def ChangeText():
# iterate over stringvars
for string_var in t_diesel_price_EnF_variable:
string_var.set('N/A')
Cheers!

Using list defined in one function in another function. Python 2.7

I have a problem with the following code. Now I'm very new to programming in general, and most of the code is copied off the internet, and I adjusted it so it would work the way I want it to. So if there is no easy way of solving it, that's ok. Maybe you can just point out some topics of programming or python, that I should read about.
I try to explain it anyways. I have defined the function query(), that makes some changes to sqlite databases. The input is a list. That function works just fine if I use it just by itself.
Now I'm trying to have an Interface, where I can define, what should be inside that list, depending on what checkboxes are checked. Then I want to execute the function with that specific list, when I press a button. The checkboxes are generated just fine, the button, too. Also, when I check or uncheck the buttons, it updates the list just fine, and it shows the new updated list in the interpreter.
The problem is, that the button does not work:
1. It doesn't use the new updated list, instead it uses an empty list ()
2. When I input a predefined list, that is not empty, it automatically runs query() without me even clicking the button.
I may not have explained this well, but I hope you understand what my problem is.
Thanks for the help
`
def chkbox_checked():
for ix, item in enumerate(cb):
opt[ix]=(cb_v[ix].get())
print opt
def query(opt):
import sqlite3
connection = sqlite3.connect("gather.sqlite")
cursor1 = connection.cursor()
cursor1.execute('Drop table IF EXISTS matches')
cursor1.execute('CREATE TABLE matches(date TEXT, team1 TEXT, team2 TEXT, league TEXT)')
cursor1.execute('DELETE FROM "main"."matches"')
for i in range(0, len(opt)):
a=opt[i]
cursor1.execute('INSERT INTO matches (date, team1, team2, league) SELECT * FROM gather WHERE team1=? or team2=? or league=?', (a,a,a,))
cursor1.execute('Drop table IF EXISTS matchessorted')
cursor1.execute('CREATE TABLE matchessorted(date TEXT, team1 TEXT, team2 TEXT, league TEXT)')
cursor1.execute('DELETE FROM "main"."matchessorted"')
cursor1.execute('INSERT INTO matchessorted (date, team1, team2, league) SELECT * FROM matches ORDER BY date')
connection.commit()
import Tkinter as tk
from Tkinter import *
opt = []
root = tk.Tk()
mylist = [
'name1',
'name2',
'name3'
]
cb = []
cb_v = []
for ix, text in enumerate(mylist):
cb_v.append(tk.StringVar())
off_value=0
cb.append(tk.Checkbutton(root, text=text, onvalue=text,offvalue=off_value,
variable=cb_v[ix],
command=chkbox_checked))
cb[ix].grid(row=ix, column=0, sticky='w')
opt.append(off_value)
cb[-1].deselect()
label = tk.Label(root, width=20)
label.grid(row=ix+1, column=0, sticky='w')
button1 = Button(root, text = "Calculate", command = query(opt))
button1.grid(column=1, row=0, sticky=W)
root.mainloop()
`
A couple of points about how to structure your code: You need to write a function that populates the list based on your selection. It can return a list call 'options' and when you want to execute the code inside the query, you call the function that constructs the options list. The query function will have a statement like this:
options = get_options() #assuming the function that populates the options is called get_options
and then you execute the query function's code.
button1 = Button(root, text = "Calculate", command = query(opt))
This calls query(opt) immediately, before you create your Button, and passes the result of that call (None) to the Button constructor as the command argument. What you really want is a function that, when called, executes query(opt). Something like this:
def calculate_clicked():
query(opt)
button1 = Button(root, text = "Calculate", command = calculate_clicked)
or this:
button1 = Button(root, text = "Calculate", command = lambda : query(opt))

Categories

Resources