could somebody see what is causing the ItemChanged signal to fire three times instead of just once?
The intention is to:
- load data from an external source
- create a tree widget with the top level items corresponding to the columns of the data
- tick which column the user should see in a table
- create a table showing only the selected columns
I managed to fix the code so that it ignores the square selection (were only some of the children are selected) and to also ignore any changes to the child-items. The signal though still fires 3 times for every time a heading is either selected or dis-selected.
import pandas as pd
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import *
from PyQt5.Qt import Qt
import sys
df = pd.DataFrame()
column_names = ['something1', 'different1', 'nothing1']
df_2 = pd.DataFrame(columns = column_names)
df= df.append (df_2)
df.loc[len(df)] = [2, 4, 5]
df.loc[len(df)] = [6, 7, 8]
df.loc[len(df)] = [9, 10, 11]
df.loc[len(df)] = [12, 13, 14]
class My_test(QtWidgets.QFrame):
def __init__ (self, df,*args, **kw):
super(My_test, self).__init__(*args, **kw)
# Import the datafrarme
self.df = df
self.horizontalLayout_2 = QtWidgets.QVBoxLayout()
#Define the table
self.table = QtWidgets.QTableWidget()
#Define the tree
self.tree = QtWidgets.QTreeWidget(self)
self.tree.setHeaderLabel("Station Data")
#Complete the Gui
self.horizontalLayout_2.addWidget(self.table)
self.horizontalLayout_2.addWidget(self.tree)
self.setLayout(self.horizontalLayout_2)
# Create the top level tree items (the same as the table headers)
for name in df.columns:
top_level_item_KD = QTreeWidgetItem([name])
top_level_item_KD.setFlags(top_level_item_KD.flags() | Qt.ItemIsUserCheckable | Qt.ItemIsTristate )
# self.tree.itemChanged['QTreeWidgetItem*','int'].connect(self.my_func)
#Connect the itemChanged signal to the its slot
self.tree.itemChanged.connect(self.return_checked_headers)
#Populate each header with the appropriate data
for data_point in df[name]:
child_KD = QTreeWidgetItem([str(data_point)])
child_KD.setFlags(child_KD.flags() | Qt.ItemIsTristate | Qt.ItemIsUserCheckable)
child_KD.setCheckState(0, Qt.Unchecked)
top_level_item_KD.addChild(child_KD)
self.tree.addTopLevelItem(top_level_item_KD)
def return_checked_headers(self,item ,column_index):
#column_index is not used in the function. It is kept in the definition for a reminder of what is the second option of the "itemChanged" signal
my_selected_columns = []
#Identify how many data columns have been generated
top_level_count = self.tree.topLevelItemCount()
#check if change is on a top level item
if item.parent() == None:
#ignore the change if it is a square (value of 1)
if item.checkState(0)!=1:
for index in range(self.tree.topLevelItemCount()):
tree_item = self.tree.topLevelItem(index)
if tree_item.checkState(0) !=0:
my_header = tree_item.text(0)
my_selected_columns.append(my_header)
self.update_table(self.df,my_selected_columns, self.table)
def update_table(self, df, headers, table):
print ('you are in the updating fucntion')
self.df = df
self.headers = headers
self.table = table
viewing_df = []
viewing_df =self.df[self.headers]
column_count = len(self.headers)
row_count = len(viewing_df)
#Update row and column numbers to reflect input data
self.table.setRowCount(len(viewing_df.index))
self.table.setColumnCount(len(self.headers))
#Create the column headings
self.table.setHorizontalHeaderLabels(self.headers)
#Transfer data
for row_number, row_data in enumerate(viewing_df.values):
for column_number, datum in enumerate(row_data):
#Convert numerical data to 1 decimal place and ensure it is converted to a string
if type(datum)==float or int:
datum= str(round(datum,1))
self.table.setItem(row_number, column_number,QTableWidgetItem(datum))
#Run the GUI
app = QtWidgets.QApplication (sys.argv)
my_window = My_test(df)
my_window.show()
app.exec_()
That happens because you connected the signal for every cycle of
for name in df.columns
. Since you've three columns, the signal is connected three times, meaning that each time the data is changed (by checking the item), the function is called three times.Just put the connection outside the for cycle and you'll have only one call.