Can't select entire row of data from QTableWidget - python

Problem Statement
I'm trying to select rows of data from my QtableWidget and print them out to my console just so I can test some things, with the end goal being able to plot the data. However I can never grab the whole row of data.
Background
I have made a GUI that can embed several QTableWidgets by importing a specifically formatted CSV file. The goal is to be able to pull data from multiple rows from the same or different tables and then plot them in a side by side fashion. Where each row of data will be its own dataset and have its own plot, but there will be multiple plots on the same figure.
To complete this task I have made a window called CompareWindow that opens when a Qpushbutton called "Compare" is pressed. The window prompts the user to type in the names of the tables and the respective rows from that table they wish to plot.
After this information is submitted I have dictionary that I can reference which has saved all the QTableObjects that have been instantiated. Where the keys are the names given to the tables which are connected to their corresponding Table Object.
Problem
The two main methods I have tried to grab the row data areā€¦
The first idea was using TableObject.selectRow() command I would iterate through the rows I wanted, but whenever I did this to it would return a nonetype.
The second method I tried was to iterate a given rows columns so it would fill a list one by one by appending the item values. However when I did this it only filled the list with the same number repeatedly, which was the first cell in my Qtable.
Even when I explicitly called a certain row or column I would get the same output. The output being pulled is .12, the number from the first cell in my CSV file.
Here is the code in question I'm having problems with.
def initiateMultiPlot(self, tableV, rowV, PlotV):
"""
1. Match TableName values with the key values in our TableDB
2. When we find a match look at that key's corresponding Table Object, and iterate
through that objects rows and select the rows specified by rowV
3.Call plot for those values
"""
#calls my class and creates a blank figure where eventually we will plot data on
f = CreateFigure.FigureAssembly()
print("")
for i in tableV:
"""
tableV: is list of strings that represent assigned tablenames [Table1, Table2, Table3]
rowV: is a list, containing lists representing rows from corresponding Tables the user wishes to plot.
for example [[1,2],[3,4],[1]] means rows 1,2 from table1, rows 3,4 from table2... so on
PlotV: is a string that is ethier "box" or "whisker" to tell what method to plot. Default right now
is to do a simple boxplot
"""
print("Creating table instance")
#Table Dictionary is setup so the names of the Tables (tableV) are the keys of the dictionary
#and the actual table objects are referenced by these keys
self.TableOBJ = self.TableDictionary[i]
print("Data Type for the table object is..................{}".format(type(self.TableOBJ)))
#empty list that will store our row data
self.Elements = []
try:
for rows in rowV:
for i in rows:
print("rowV value is... {}".format(rowV))
print("current row list{}".format(rows))
print("i value is {}".format(i))
print("itterating")
for j in range(self.TableOBJ.columnCount()):
print("i value is ...{}".format(i))
print("j value is .... {}".format(j))
#FIRST idea try selecting entire row of data
print("i value is ...{}".format(i))
print("j value is .... {}".format(j))
#entire row returns none-type
EntireRow = self.TableOBJ.selectRow(i)
print(EntireRow)
#selecteditems
#SECOND idea try using for loop and iterating through every value in a row
item = self.TableOBJ.itemAt(i,j)
#explicit call for (row 1, col 1) and (row 3, col 3), both which output .12
print(self.TableOBJ.itemAt(1,1).text())
print(self.TableOBJ.itemAt(3,3).text())
print("printing item...... {}".format(item))
element = item.text()
print(element)
#list of .12
self.Elements.append(element)
#elements = [self.TableOBJ.item(i, j).text() for j in range(self.TableOBJ.columnCount()) if
# self.TableOBJ.item(i, j).text() != ""]
#print(elements)
except Exception as e:
print(e)
print(self.Elements)
Here is my GitHub link containing all my files: https://github.com/Silvuurleaf/Data-Visualize-Project
The problem occurs in my file Perspective.py in the method initiateMultiPlot. The file CompareWindow.py sends a signal to my Perspective.py and is connected to initateMultiPlot. Please inquire if anything requires more in depth explanation.

According to the documentation:
QTableWidgetItem *QTableWidget::itemAt(int ax, int ay) const
Returns the item at the position equivalent to QPoint(ax, ay) in the
table widget's coordinate system, or returns 0 if the specified point
is not covered by an item in the table widget.
That is, returns the given item x and y which are graphical coordinates with respect to QTableWidget, and clearly is not what you are looking for.
You must use the item():
QTableWidgetItem *QTableWidget::item(int row, int column) const
Returns the item for the given row and column if one has been set;
otherwise returns 0.
But in your case will not work unless you do the following change:
class CreateTable(QTableWidget):
....
for j in range(0, m):
self.item = QTableWidgetItem(str(round(ValList[j], 6)))
# print("{}, {}".format(i, j))
self.setItem(i, j, self.item)
to:
class CreateTable(QTableWidget):
....
for j in range(0, m):
item = QTableWidgetItem(str(round(ValList[j], 6)))
# print("{}, {}".format(i, j))
self.setItem(i, j, item)
That is, you change your self.item to item.
The problem is that at first glance the error is rather difficult, the QTableWidget class has an item() function, but when you use the self.item statement you are replacing that call, ie when python reads that statement it will use the attribute and not the function , So you get the error:
TypeError 'xxx' object is not callable

Related

python comparing two lists and retaining second list index

I am taking a user input of "components" splitting it into a list and comparing those components to a list of available components generated from column A of a google sheet. Then what I am attempting to do is return the cell value from column G corresponding the Column A index. Then repeat this for all input values.
So far I am getting the first value just fine but I'm obviously missing something to get it to cycle back and to the remaining user input components. I tried some stuff using itertools but wasn't able to get the results I wanted. I have a feeling I will facepalm when I discover the solution to this through here or on my own.
mix = select.split(',') # sets user input to string and sparates elements
ws = s.worksheet("Details") # opens table in google sheet
c_list = ws.col_values(1) # sets column A to a list
modifier = [""] * len(mix) # sets size of list based on user input
list = str(c_list).lower()
for i in range(len(mix)):
if str(mix[i]).lower() in str(c_list).lower():
for j in range(len(c_list)):
if str(mix[i]).lower() == str(c_list[j]).lower():
modifier[i] = ws.cell(j+1,7).value # get value of cell from Column G corresponding to Column A for component name
print(mix)
print(modifier)
You are over complicating the code by writing C like code.
I have changed all the loops you had to a simpler single loop, I have also left comments above each code line to explain what it does.
# Here we use .lower() to lower case all the values in select
# before splitting them and adding them to the list "mix"
mix = select.lower().split(",")
ws = s.worksheet("Details")
# Here we used a list comprehension to create a list of the "column A"
# values but all in lower case
c_list = [cell.lower() for cell in ws.col_values(1)]
modifier = [""] * len(mix)
# Here we loop through every item in mix, but also keep a count of iterations
# we have made, which we will use later to add the "column G" element to the
# corresponding location in the list "modifier"
for i, value in enumerate(mix):
# Here we check if the value exists in the c_list
if value in c_list:
# If we find the value in the c_list, we get the index of the value in c_list
index = c_list.index(value)
# Here we add the value of column G that has an index of "index + 1" to
# the modifier list at the same location of the value in list "mix"
modifier[i] = ws.cell(index + 1, 7).value

How to reference a Class List with another class?

I am setting up a script that will extract data from excel and return it in lists. Right now I am trying to be able to reorganized the data into smaller lists that have a common attribute. (Such as: A list that has the indices of the rows that contained, 'Pencil') I keep having the smaller list returning None.
I've checked and the lists that extract the data are working fine. But I cant seem to get the smaller lists working.
#Create a class for the multiple lists of Columns
class Data_Column(list):
def Fill_List (self,col): #fills the list
for i in range(sheet.nrows):
self.append(sheet.cell_value(i,col))
#Create a class for a specific list that has data of a common artifact
class Specific_List(list):
def Find_And_Fill (self, listy, word):
for i in range (sheet.nrows):
if listy[i] == word:
self.append(I)
#Initiate and Populate lists from excel spreadsheet
date = Data_Column()
date.Fill_List(0)
location = Data_Column()
location.Fill_List(1)
name = Data_Column()
name.Fill_List(2)
item = Data_Column()
item.Fill_List(3)
specPencil = Specific_List()
print(specPencil.Find_And_Fill(item,'Pencil'))
I expected a List that contained the indices where 'Pencil' was found such as [1,6,12,14,19].
The actual output was: None
I needed to take the print out of the very last line.
specPencil.Find_And_Fill(item,'Pencil')
print(specPencil)
I knew it was a simple fix

Best search algorithm to find 'similar' strings in excel spreadsheet

I am trying to figure out the most efficient way of finding similar values of a specific cell in a specified column(not all columns) in an excel .xlsx document. The code I have currently assumes all of the strings are unsorted. However the file I am using and the files I will be using all have strings sorted from A-Z. So instead of doing a linear search I wonder what other search algorithm I could use as well as being able to fix my coding eg(binary search etc).
So far I have created a function: find(). Before the function runs the program takes in a value from the user's input that then gets set as the sheet name. I print out all available sheet names in the excel doc just to help the user. I created an empty array results[] to store well....the results. I created a for loop that iterates through only column A because I only want to iterate through a custom column. I created a variable called start that is the first coordinate in column A eg(A1 or A400) this will change depending on the iteration the loop is on. I created a variable called next that will get compared with the start. Next is technically just start + 1, however since I cant add +1 to a string I concatenate and type cast everything so that the iteration becomes a range from A1-100 or however many cells are in column A. My function getVal() gets called with two parameters, the coordinate of the cell and the worksheet we are working from. The value that is returned from getVal() is also passed inside my function Similar() which is just a function that calls SequenceMatcher() from difflib. Similar just returns the percentage of how similar two strings are. Eg. similar(hello, helloo) returns int 90 or something like that. Once the similar function is called if the strings are above 40 percent similar appends the coordinates into the results[] array.
def setSheet(ws):
sheet = wb[ws]
return sheet
def getVal(coordinate, worksheet):
value = worksheet[coordinate].value
return value
def similar(first, second):
percent = SequenceMatcher(None, first, second).ratio() * 100
return percent
def find():
column = "A"
print("\n")
print("These are all available sheets: ", wb.sheetnames)
print("\n")
name = input("What sheet are we working out of> ")
results = []
ws = setSheet(name)
for i in range(1, ws.max_row):
temp = str(column + str(i))
x = ws[temp]
start = ws[x].coordinate
y = str(column + str(i + 1))
next = ws[y].coordinate
if(similar(getVal(start,ws), getVal(next,ws)) > 40):
results.append(getVal(start))
return results
This is some nasty looking code so I do apologize in advance. The expected results should just be a list of strings that are "similar".

Unreachable code block (in python) with html templater

I'm having trouble creating a fiddly html table in python 3.4. The templater is html 1.16. Here's a simplified version of the problem: I would like to traverse a list. For each list item, I would like to write the data to a html table. The table should be two columns wide.
from html import HTML
#create html object
h = HTML()
comments=["blah1",
"blah2",
"blah3"
]
#create table object
c_table = h.table.tbody
for i, comment in enumerate(comments):
#create row if we are at an odd index
if i % 2 != 0:
row = c_table.tr
row.td(comment)
else:
#it is intended to add another <td> to the current row here
#but because the row was declared in the if block, it is out of scope
row.td(comment)
#write the html output now
print(h)
The difficulty is with the templater, specifically: accessing the row object for the second cell of the row without causing the </tr> closing tag. I have to create new cells through the row object, otherwise if I call c_table.tr.td it closes the row with </tr> and starts a new one.
Can anyone clever think of any code trickery that achieves what I'm trying to do in these circumstances?
Your comment is simply incorrect. Python does not have block scope, and the row that is defined in the if block is accessible in the else.
In fact, you can take the td out of the if block, and remove the else altogether.
You can't access that row object, because it was created inside the first if. In order to access it in your "else", you'll have to create it outside both clauses, which doesn't help you achieve your goal.
Try dividing the list into "chunks" - a list of lists with 2 objects each.
h = HTML()
comments=["blah1",
"blah2",
"blah3",
"blah4",
"blah5"
]
fixed_list = []
for i in xrange(0, len(comments), 2):
fixed_list.append(comments[i:i+2])
Now fixed list looks like this -
[["blah1", "blah2"], ["blah3", "blah4"], .....]
And now you can easily iterate over that list, and create a row for each list -
#create table object
body = h.body
tb = body.table
for comments_list in fixed_list:
row = tb.tr
for comment in comments_list:
row.td(comment)
print h

Python Query Result in QTreeWidget

I am working on python plugins.I used PyQt4 Designer.
I want to list query result into QTreeWidget.
My code is as follows:
c = self.db.con.cursor()
self.db._exec_sql(c, "select est from bio")
for row in c.fetchall():
item_value=unicode(row[0])
top_node1 = QTreeWidgetItem(item_value)
self.treeWidget.insertTopLevelItem(0, top_node1)
The query returns the values as:
But when i list these values into QTreeWidget using above code,it is shown as below :
Only first character is shown.If i change '0' to some other number in self.treeWidget.insertTopLevelItem(0, top_node1) ,nothing appears in QTreeWidget.
How do i do it????
thanx.
If you take a look at the documentation for a QTreeWidgetItem, you will see there are a number of possible constructors for creating an instance. Though none of which it seems you are using in a way that is going to give you desirable results. The closest match to the signature you are providing is:
QTreeWidgetItem ( const QStringList & strings, int type = Type )
What this is probably doing is taking your string (I am assuming row[0] is a string because I don't know which drivers you are using) and applying it as a sequence, which would fullfill the requiremets of QStringList. Thus what you are getting is populating multiple columns of your item with each letter of your string value. If this is what you wanted, then you would n eed to tell your widget to show more columns: self.treeWidget.setColumnCount(10). But this isn't what you are looking for I am sure.
More likely what you should be trying is to create a new item, then add the value to the desired column:
item = QTreeWidgetItem()
item.setText(0, unicode(row[0]))
self.treeWidget.insertTopLevelItem(0, item)
You can use the default constructor with no arguments, set the text value of the first column to your database record field value, and then add that item to the tree. You could also build up a list of the items and add them at once:
items = []
for row in c.fetchall():
item = QTreeWidgetItem()
item.setText(0, unicode(row[0]))
items.append(item)
self.treeWidget.insertTopLevelItems(0, items)
Your first aproach could be corrected just add a list to the widgetitem not a string like this:
top_node1 = QTreeWidgetItem([item_value])

Categories

Resources