PyQt5 QTableWidget showing only first line and column - python-3.x

I have a QTableWidget populated with a list of lists. Each inner list have eight elements and my table have a ninth control column calculated after the others are loaded.
I can read and print to console the content of any cell of the table like print(self.tAccounts.item(52,3).text()), so I think there is no problem with the data, but the table shows only the cell's content for the first line and column in the table, leaving the others bank.
I should be making a mistake in some place, but I can't see.
Using PyQt 5 and Python 3.
The constructor
class Table(QWidget):
def __init__(self, parent=None):
super(Table, self).__init__(parent)
self.accounts = [] # The source is created in the constructor\
# and populate in other member function
self.tAccounts = QTableWidget(0,9)
self.tAccounts.setSortingEnabled(True)
self.tAccounts.setHorizontalHeaderLabels(['1','2','3','4','5','6','7','8','9'])
self.tAccounts.resizeColumnsToContents()
self.tAccounts.verticalHeader().hide()
The member function:
def loadDay(self):
for row, account in enumerate(self.accounts):
self.tAccounts.insertRow(row)
for col in range(8):
self.tAccounts.setItem(row, col, QTableWidgetItem(str(accounts[col])))
self.tAccounts.item(row,col).setTextAlignment(Qt.AlignRight)
self.tAccounts.setItem(row, 8, QTableWidgetItem('')) # defined for further use

Finally I found it.
The problem is in enabling the sorting in the constructor. Seems the default sorting is Z-A. Changing the sort to A-Z by clicking in the header of the empty table solves the bug but the best solution is to move the line self.tAccounts.setSortingEnabled(True) to the end of the loadDay function. Seems to be a clash between the ever changing row number because of enabled sorting and the updating algorithm of QTableWidget

Related

I'm trying to remove certain words from a column on each row in my dataframe

I'm still trying to understand how pandas works please bear with me. In this exercise, I,m trying to access a particular column ['Without Stop Words'] on each row which has a list of words. I wish to remove certain words from each row of that column. the words to be removed have been specified in a dictionary called {'stop_words_dict'}. here's my code, but the dataframe seems to be unchanged after running it.
def stop_words_remover(df):
# your code here
df['Without Stop Words']= df['Tweets'].str.lower().str.split()
for i, r in df.iterrows():
for word in df['Without Stop Words']:
if word in stop_words_dict.items():
df['Without Stop Words'][i] = df['Without Stop Words'].str.remove(word)
return df
this is how the input looks like
INPUT
EXPECTED OUTPUT
In Pandas, it's generally a bad idea to loop over your dataframe row by row to try to change rows. Instead, try using methods like .apply().
An example for stopwords, together with list comprehension:
test['Tweets'].apply(lambda x: [item for item in x if item not in stop_words_dict.items()])
See https://stackoverflow.com/a/29523440/12904151 for more context.

Getting index of every optionmenu from multiple optionmenus

Given multiple optionmenu with stringvar trace,how to get the index of every option menu's first option?
self.path_list=[]
def load(self,inputt):
for i in range(0,self.x):
self.variable = StringVar(root)
self.variable.set("abc")
self.variable.trace('w',partial(self.rb_mode,i=i))
w=OptionMenu(root,self.variable,"Hello","World")
w.grid(row=i+100,column=1)
self.opt_menu.append(self.variable.get())
def rb_mode(self,*args,inx):
self.variable.get.__setitem__(i)
if(self.variable.get()=="Hello"):
do something
Here as shown,the drop down box index should be mapped with the corresponding file path
There are multiple dynamic drop down lists. Each drop down box index should be mapped with the corresponding path.
Am getting the output like:
World(printing 2 times which is the 2nd option,on clicking the 1st option)

Return the reference of Tkinter control variable

I am not an experienced programmer so advance apologies if I am asking a basic question. I am using a for loop to print tkinter Entries and trying to return the reference of these Entries to the calling function. But when I try to get the text typed in the entry it always returns empty string? So my question is whether it is possible to return the reference of the control variable in python ? or I am using a wrong approach?
def data_entry_txtfield(self,rn,cn,pu,pd):
# Creates the Entry to enter data - rn is the row and cn is column
# pd and pu are padding up and padding down
entry = tk.StringVar()
tk.Entry(self.inputlab,width=32,bg=entrycolor,textvariable=entry)
entry.grid(column=cn,row=rn,pady=(pu,pd))
return entry
tbtlocationentry = self.data_entry_txtfield(9,4,0,12)
text = tbtlocationentry.get()
print(text)`
There are a lot of errors in this code like the bottom line your using self.data_entry_txtfield to call the function but you dont need the self. unless your using in a class. Also the entry needs a variable name to be able to change stuff inside it. I've fixed some errors and here is the code
def data_entry_txtfield(self,rn,cn,pu,pd):
# Creates the Entry to enter data - rn is the row and cn is column
# pd and pu are padding up and padding down
entry = tk.StringVar()
ety = tk.Entry(self.inputlab,width=32,bg=entrycolor,textvariable=entry)
ety.grid(column=cn,row=rn,pady=(pu,pd))
return entry
tbtlocationentry = self.data_entry_txtfield(9,4,0,12)
text = tbtlocationentry.get()
print(text)
I've left in the self. because i'm assuming your using this inside a class

Slow loop aggregating rows and columns

I have a DataFrame with a column named 'UserNbr' and a column named 'Spclty', which is composed of elements like this:
[['104', '2010-01-31'], ['215', '2014-11-21'], ['352', '2016-07-13']]
where there can be 0 or more elements in the list.
Some UserNbr keys appear in multiple rows, and I wish to collapse each such group into a single row such that 'Spclty' contains all the unique dicts like those in the list shown above.
To save overhead on appending to a DataFrame, I'm appending each output row to a list, instead to the DataFrame.
My code is working, but it's taking hours to run on 0.7M rows of input. (Actually, I've never been able to keep my laptop open long enough for it to finish executing.)
Is there a better way to aggregate into a structure like this, maybe using a library that provides more data reshaping options instead looping over UserNbr? (In R, I'd use the data.table and dplyr libraries.)
# loop over all UserNbr:
# consolidate specialty fields into dict-like sets (to remove redundant codes);
# output one row per user to new data frame
out_rows = list()
spcltycol = df_tmp.column.get_loc('Spclty')
all_UserNbr = df_tmp['UserNbr'].unique()
for user in all_UserNbr:
df_user = df_tmp.loc[df_tmp['UserNbr'] == user]
if df_user.shape[0] > 0:
open_combined = df_user_open.iloc[0, spcltycol] # capture 1st row
for row in range(1, df_user.shape[0]): # union with any subsequent rows
open_combined = open_combined.union(df_user.iloc[row, spcltycol])
new_row = df_user.drop(['Spclty', 'StartDt'], axis = 1).iloc[0].tolist()
new_row.append(open_combined)
out_rows.append(new_row)
# construct new dataframe with no redundant UserID rows:
df_out = pd.DataFrame(out_rows,
columns = ['UserNbr', 'Spclty'])
# convert Spclty sets to dicts:
df_out['Spclty'] = [dict(df_out['Spclty'][row]) for row in range(df_out.shape[0])]
The conversion to dict gets rid of specialties that are repeated between rows, In the output, a Spclty value should look like this:
{'104': '2010-01-31', '215': '2014-11-21', '352': '2016-07-13'}
except that there may be more key-value pairs than in any corresponding input row (resulting from aggregation over UserNbr).
I withdraw this question.
I had hoped there was an efficient way to use groupby with something else, but I haven't found any examples with a complex data structure like this one and have received no guidance.
For anyone who gets similarly stuck with very slow aggregation problems in Python, I suggest stepping up to PySpark. I am now tackling this problem with a Databricks notebook and am making headway with the pyspark.sql.window Window functions. (Now, it only takes minutes to run a test instead of hours!)
A partial solution is in the answer here:
PySpark list() in withColumn() only works once, then AssertionError: col should be Column

Write values from Pandas DataFrame columns into tkinter TreeView/Table Columns

I want to write values from a dataframe into a tkinter treeview/Table, I am not able to do this.
my code:
#Setting up tkinter window.
root = Tk()
tree = ttk.Treeview(root)
#taking file input through a dialog box from the user.
file = filedialog.askopenfile(parent=root,mode='rb',title='Choose a xlsx file')
#readinf the excel file selected by the user and then creating a dataframe of that file.
xls = pd.read_excel(file)
df = pd.DataFrame(xls)
#taking all the columns heading in a variable"df_col".
df_col = df.columns.values
#all the column name are generated dynamically.
tree["columns"]=(df_col)
counter = len(df)
#generating for loop to create columns and give heading to them through df_col var.
for x in range(len(df_col)):
tree.column(x, width=100 )
tree.heading(x, text=df_col[x])
#generating for loop to print values of dataframe in treeview column.
for i in range(counter):
tree.insert('', 0, values=(df[df_col[x]]][i]))
It is not printing the columns and showing the KeyError:0.
Output Required:
The first argument of tree.column() should be the column name, which you assigned with:
tree["columns"]=(df_col)
The problem is that you have named the columns using a string, but you are attempting to access them using integers in:
for x in range(len(df_col)):
tree.column(x, width=100 )
tree.heading(x, text=df_col[x])
Above, you are attempting to access tree.columns(0), instead of tree.columns('Company'), hence the key error.
Try instead:
for x in range(len(df_col)):
tree.column(df_col[x], width=100)
tree.heading(df_col[x], text=df_col[x])
Note that df_col is an ndarray, not a dataframe, which is why df_col[x] works correctly (df[x] would give a key error). This is because df.columns.values returns an ndarray. As a side note, it may be a bit confusing to name an ndarray df_col.
There are also a few issues with your insert. The second argument should correspond to the index of the entry you wish to address. One solution is then to use a row index as the second argument, followed by a row label as text="rowLabel", followed by a list of values for the row:
tree.insert('', i, text=rowLabels[i], values=df.iloc[i,:].tolist())
Where rowLabels should be defined as whatever you want to use in the first column of the table. I would suggest using an index column from the spreadsheet here, if possible. It could be defined by:
rowLabels = df.iloc[:,indexColumn].tolist()
or:
rowLabels = df.index.tolist()
The latter is viable if df has named indices defined by a column during the spreadsheet import. In the former, indexColumn is an int referring to a column number in df that contains unique identifiers.
The option values=df.iloc[i,:].tolist() converts all columns of the ith row into a list, and, since we have passed an index value (the second argument) that gets larger, the call will insert a new row every loop (from the python tkinter docs entry on Treeview --> insert: "if index is greater than or equal to the current number of children, it is inserted at the end").
Finally, I am not sure if you did not post the end of your code, but, in order for the tree to show up, you will also need to use pack, grid, etc.
tree.pack()
or
tree.grid(row=0, column=0)
References:
https://docs.python.org/3/library/tkinter.ttk.html#tkinter.ttk.Treeview
This helpful example makes a few of the steps clear:
https://knowpapa.com/ttk-treeview/
As I was reading over your code. I noticed at the end line you have an extra bracket #:
df[df_col[x]]]
for i in range(counter):
tree.insert('', 0, values=(df[df_col[x]]][i]))
I would assume that would explain the KeyError.

Resources