I have a function that checks if a barcode is known to the warehouse. If so, the function grabs the row from the dataframe (occupied by an imported excel file) and will be inserted into a treeview with known items. If the barcode is unknown it will be inserted into a listbox.
The function works and does what it is supposed to do, but I want to expand it by updating the row in the treeview by increasing its quantity by 1 when adding the same barcode to the treeview. See the picture for the current behaviour. Known items treeview
# Function to process new entered barcodes by filtering known and unknown items and adding them to treeview
def scan_check(event):
scanned_item = scan_entry.get()
for code in df.iloc[:, 1]: # column with barcodes
if code == scanned_item:
for row in df.to_numpy().tolist(): # dataframe with item / barcode / item description / size / quantity
if scanned_item in row:
quantity_count = 1
row.insert(4, quantity_count)
scanTree.insert(parent='', index='end', value=row)
for child in scanTree.get_children():
if scanTree.item(child, option='values'[3]) in scanTree.get_children():
quantity_count += 1
scanTree.set(child, 'Quantity', quantity_count)
scan_entry.delete(0, tkinter.END)
break # to prevent adding item to unknown products listbox as well
else:
unknown_listbox.insert(tkinter.END, scanned_item)
scan_entry.delete(0, tkinter.END)
My question is: How would I write the if clause, after iterating throught the children, when I want to check if the added row from the dataframe is already in my treeview?
My attempts at the if clause did not work obviously. I was hoping anyone could help me with my problem. Thanks for reading.
You can simplify the logic:
Search the treeview first for the barcode, if found, update the quantity
If not found, search the dataframe. If found, insert new record into treeview, otherwise insert the barcode to the unknown listbox
def scan_check(event):
scanned_item = scan_entry.get().strip()
if scanned_item == '':
# do nothing if empty string is input
return
# search treeview
for child in scanTree.get_children():
row = scanTree.set(child)
if row['Barcode'] == scanned_item:
# update quantity
scanTree.set(child, 'Quantity', int(row['Quantity'])+1)
break # prevent executing else block
else:
# search dataframe
result = df.loc[df['Barcode'].astype(str) == scanned_item]
if result.empty:
# should check whether barcode already exists?
unknown_listbox.insert('end', scanned_item)
else:
scanTree.insert('', 'end', values=result.iloc[0].to_list()+[1])
scan_entry.delete(0, 'end')
Related
I want to build a URL-shortener which should work as follows:
shortener saves the original URL typed in by user, the automatically generated numeric ID of the original URL and the base 62-encoded version of the numeric ID.
with each new entered original URL, those 3 types of information are saved into a pandas data frame as columns;
the data frame is empty in the beginning; when the first row is inserted, a random numeric ID is generated; the IDs of following rows are incremented by 1
the process of insertion should have following logic:
User gets asks for input (the original URL)
a check is conducted, whether this URL is already contained in the database; if it is, the user gets asked to enter a new URL
if the URL is not contained in the database yet, the script checks if there are other entries in the database yet
if there are no entries, the entered URL gets inserted into the data frame with a randomly generated ID (which is used as the index of the data frame) and the encoded ID
if there are other entries available, the entered Url gets inserted into the data frame with an ID that is the last available ID in the data frame + 1
What I want to achieve is to operate on the same data frame without creating copies of the data frame with each new entry. So the main function should each time receive the same data frame and update it with new rows. However, when I execute the code below, and the script gets to the point of executing the line
database = database.append(row)
in the update_database-function, I get the following error:
UnboundLocalError: local variable 'database' referenced before assignment
This seems strange, because the variable database is defined on the global scope right on top of the main function, so each function within the main function should have access to it. Can anyone tell me where I'm going wrong?
import numpy as np
import pandas as pd
import string
import random
#create the 62-base for encoding
digits = [str(i) for i in list(np.arange(0,10))]
letters_upper = list(string.ascii_uppercase)
letters_lower = list(string.ascii_lowercase)
base_62 = digits + letters_upper + letters_lower
#create the empty database with ID as index
database = pd.DataFrame(columns = ['ID', 'URL_long', 'URL_short']).set_index("ID")
#create the 62-base encoded version of the ID
def encode_id(num,base, base_size):
result = []
while divmod(num,base_size) != (0,0):
el = base[divmod(num,base_size)[1]]
result.append(el)
num = divmod(num,base_size)[0]
result.reverse()
return "".join(result)
def main(df):
database = df
#asks user for input
def user_input():
print("exec user_input")
return input("Enter your URL: ")
#if the entered URL is not in the data frame yet, inserts the URL with ID and encoded ID
def update_database(passed_input_value):
print("exec_update_database")
#executes if the data frame is empty
if len(database) == 0:
first_id = int("".join(random.sample(string.digits,7)))
row = pd.Series({"URL_long": passed_input_value, "URL_short": encode_id(first_id,base_62,62)})
row.name = first_id
#executes if the data frame already has entries
else:
last_id_incr = int(df.index[-1]+1)
row = pd.Series({"URL_long": passed_input_value, "URL_short": encode_id(last_id_incr,base_62,62)})
row.name = last_id_incr
#appends the created row to the data frame
#this is where the error happens
database = database.append(row)
#checks if the entered URL is already in the data frame
#if it is, redirects to the beginning of the process
#if it's not, passes the value of the input to the update-function and executes it
def check_duplicates():
print("exec_check_duplicates")
input_value = user_input()
if input_value in database["URL_long"].unique():
url_available = database[database["URL_long"]==input_value].iloc[0]['URL_short']
print(f"URL already shortened: {url_available}.")
check_duplicates()
else:
update_database(input_value)
check_duplicates()
return database
main(database)
Firstly, When my cursor is in the entry box (entrybox.focus()), I would like to using F4 key on keyboard (GUI.bind('<F4>', directselection)) to select the first data in my treeview directly instead of using mouse.
I don't have any idea to create the function. I'm trying to use
def directselection(event = None):
treeviewtable.focus()
but it doesn't work.
Use .get_children() to get a list of row IDs and then use .selection_set() to select the first row:
def directselection(event=None):
# get all row IDs
idlist = treeviewtable.get_children()
if idlist:
# select and focus on first row
treeviewtable.selection_set(idlist[0])
treeviewtable.focus(idlist[0])
# transfer keyboard focus to treeviewtable
treeviewtable.focus_force()
I have the following piece of code I've been working on and I can only get to do the trick for the first row of the "input" table. I know there is clearly no relation between the two cursors on the nested loop but I have tried a nested while-for loop and obtained the same result. I am using python 3.4.
The idea is to compare rows in table input with rows in table comparetable and if the result given by the formula is less than 5 print the correspondents ids (row[0] & rowx[0]) of the given tables and the result of the formula in a new different table called output. The problem is it only works for the first record of the input table.
db_connection = pypyodbc.connect(connection_str)
db_connection2 = pypyodbc.connect(connection_str)
db_connection3 = pypyodbc.connect(connection_str)
db_cursor = db_connection.cursor()
db_cursor2 = db_connection2.cursor()
db_cursor3 = db_connection3.cursor()
db_cursor.execute("SELECT * FROM input")
db_cursor2.execute("SELECT * FROM comparetable")
for row in db_cursor:
for rowx in db_cursor2:
idoriginal = row[0]
idcomparable = rowx[0]
result = formula(float(row[1]), float(row[2]), float(rowx[1]), float(rowx[2]))
if result < 5:
db_cursor3.execute("INSERT INTO output (id, idcomparable, result) VALUES (?, ?, ?)",(idoriginal,
idcomparable, result))
db_cursor3.commit()
Any ideas? Hope is a silly thing. I have already checked this post but I still dont know how to solve it.
The for rowx in db_cursor2: "exhausts" db_cursor2 the first time through -- nothing is left to loop on the second and later times through the outer loop.
So, you must move the db_cursor2.execute("SELECT * FROM comparetable") to inside the outer loop for row in db_cursor:, just before the inner loop for rowx in db_cursor2: so the latter will have rows to loop upon over and over again!-)
I'm trying to update a microsoft word - 2010 table by deleting its contents (except the first row contents) using python and win32com client component. I even looked at the MSDN library (http://msdn.microsoft.com/en-us/library/bb244515.aspx ) and found that Delete could help me in this, something like this.
(Also had a look # How to read contents of an Table in MS-Word file Using Python? )
..///
# delete row 1 of table 1
doc.Tables(1).Rows(1).Delete
# delete cell 1,1 of table 1
doc.Tables(1).Cell(1, 1).Delete
..///
but on doing the above steps, the table row isn't deleted (neither the cell [1,1] is deleted). Is there something that I'm missing ? Any suggestions are welcome.
Python function for clearing the table contents is pasted herein with
..//
def updateTable(name):
#tell word to open the document
word.Documents.Open (IP_Directory_Dest + "\\" + name)
#open it internally (i guess...)
doc = word.Documents(1)
## doc.Content.Text = "This is the string to add to the document."
## doc.Content.MoveEnd()
## doc.Content.Select
## doc.Tables(1).Rows(2).Select
## Selection.InsertRowsBelow
## doc.Tables [1]. Rows [1]. Cells [1]. Range.Text = '123123 '
## doc.Tables [1]. Rows.Add () # add a line
# specifically select TABLE # 1
table = doc.Tables(1)
# count the number of rows in TABLE # 1
numRows = table.Rows.Count
# count number of columns
numCols = table.Columns.Count
print ('Number of Rows in TABLE',numRows)
print ('Number of Columns in TABLE',numCols)
# print the row 1 of TABLE # 1 -- for checking
print ('### 1 - CHECK this ONE ... ',table.Rows(1).Range.Text)
# delete row 1 of table 1
doc.Tables(1).Rows(1).Delete
# delete cell 1,1 of table 1
doc.Tables(1).Cell(1, 1).Delete
# again count the number of rows in table
numRows = table.Rows.Count
print numRows
# print the row 1 of TABLE # 1 -- after Deleting the first ROW --> for checking
print ('### 2 - CHECK this ONE ... ',table.Rows(1).Range.Text)
# get the number of tables
numberTables = doc.Tables.Count
# count the number of tables in document
print numberTables
#delete ALL tables
tables = doc.Tables
for table in tables:
# to get the content of Row # 1, Column # 1 of table
print table.Cell(Row =1, Column = 1).Range.Text
## table.Delete(Row =2)
# this one deletes the whole table (which is not needed)
#table.Delete()
#re-save in IP folder
doc.SaveAs(IP_Directory_Dest + "\\" + name)
#close the stream
doc.Close()
...///
Please ignore the commented out sections (I was also trying to make the stuff work)
All,
So this is what I have figured out. I'm going to share the code which I have written to fix it.
While doing this, I learned how to clear content of a table (specific row and column), how to add a row, get the count of columns and rows in a word table et al. Also I figured out, since there in not much documentation available on the API's used for python/win32 (except the MSDN library), the one way which i thought to get used to these APIs is to understand the VB code (mostly its present # MSDN http://msdn.microsoft.com/en-us/library/bb244515.aspx ) and try to make a corresponding similar code for python-win32 too. Thats my understanding.
..///
########################
#
# Purpose : To update the Table contents present in file
# # name : name of the document to process.
# # tableCount : Specific Table number to edit.
#
#######################
def updateTable(name,tableCount):
#tell word to open the document
word.Documents.Open (IP_Directory_Dest + "\\" + name)
#open it internally
doc = word.Documents(1)
# answer to Question # 2 (how to update a specific cell in a TABLE)
# clearing Table # 1, Row # 1, cell # 1 content
doc.Tables (1). Rows (1). Cells (1). Range.Text = ''
#Clearing Table # 1, Row # 1, Col # 4 content to blank
doc.Tables (1). Cell(1, 4). Range.Text = ''
# specifically select TABLE # tableCount
table = doc.Tables(tableCount)
# count the number of rows in TABLE # 1
numRows = table.Rows.Count
# count number of columns
numCols = table.Columns.Count
print ('Number of Rows in TABLE',numRows)
print ('Number of Columns in TABLE',numCols)
# NOTE : ROW # 1 WILL NOT BE DELETED, AS IT IS COMMON FOR BOTH IP AND IR
# delete and add the same number of rows
for row in range(numRows):
# add a row at the end of table
doc.Tables(tableCount).Rows.Add ()
# delete row 2 of table (so as to make the effect of adding rows equal)
doc.Tables(tableCount).Rows(2).Delete()
#re-save in IP folder
doc.SaveAs(IP_Directory_Dest + "\\" + name)
#close the stream
doc.Close()
..///
Add empty parentheses at the end of the line:
doc.Tables(1).Rows(1).Delete()
Those parentheses are necessary to call a method.
I have a scroll list on my window that I am going to insert 2 entry for each row, I am trying to understand how I can catch the entry that has been changed and update my array with this value.
I will explain what is my code:
I have an array that has 2 fields: Name and Description
Each row has 2 entry, Name and Description
When I am going to modify the row number 2 I want to update my object on my array:
rows[1].name = XXX rows[1].description = YYY
You might also want to consider using Gtk.TreeView with editable cells. The underlying Gtk.ListStore could replace your array.
But you can also use your existing entries and pass any data you want as "user data" to the callback for the "changed" signal.
def on_entry_changed(entry, data):
print("Row %d, Column %s - %s", data[0], data[1], entry.get_text())
for i in xrange(10):
name = Gtk.Entry()
name.connect("changed", on_entry_changed, (i, "name"))
description = Gtk.Entry()
description.connect("changed", on_entry_changed, (i, "description"))
# add your entries to a box or whatever