PyQt6 How to get Tableview header name/text from item on click - python

I've got a gui app in PyQt6 that just has a button and table view in it. Once pressed, the button connects TableModel, which inherits from QtCore.QAbstractTableModel, to the table view populates the data a does a bit of formatting. Great.
What I now want is to be able to access the header names (just the text thats in the headers), when a cell in the table is clicked. I've successfully connected a function that just prints out item.row() and item.column() and this is functioning correctly. I can't for the life of me get the header info though. Any help would be great
Code so far
I'm just posting the most relevant parts of the code, as it's long and spread over multiple files. Just lmk if there's more you need to see.
class PrimaryWindow(QtWidgets.QMainWindow):
"""docstring for PrimaryWindow"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.dbloc=r'test.sqlite'
self.conn=sqlite3.connect(self.dbloc)
#do all the connections
self.ui.DataButton.clicked.connect(self.get_data)
self.ui.DataTable.clicked.connect(self.Get_new_view)
def get_data(self):
#get some data from db
self.tmodel = TableModel(self.data)
self.ui.DataTable.setModel(self.tmodel)
def Get_new_view(self, item):
print('clicked',item.row(),item.column())
class TableModel(QtCore.QAbstractTableModel):
def __init__(self, data):
super(TableModel, self).__init__()
self._data = data
def data(self, index, role):
if role == Qt.ItemDataRole.BackgroundRole:
colors={
20:'#da172e', #red
15:'#ede60b', #yellow
10:'#0bed10', #green
99:'#0b6ded', #blue
5: '#FFFFFF' #white
}
value = self._data.iloc[index.row(),index.column()]
if value == 0:
return #don't color 0 values
else:
return QtGui.QColor(colors[max(i for i in colors if i<=value)]) #returns threshold value as key to color dict, sends string of hex color code
if role == Qt.ItemDataRole.DisplayRole:
# See below for the nested-list data structure.
# .row() indexes into the outer list,
# .column() indexes into the sub-list
return str(self._data.iloc[index.row(),index.column()])
def rowCount(self, index):
# The length of the outer list.
return len(self._data)
def columnCount(self, index):
# The following takes the first sub-list, and returns
# the length (only works if all rows are an equal length
return len(self._data.columns)
def headerData(self, section, orientation, role):
# section is the index of the column/row.
if role == Qt.ItemDataRole.DisplayRole:
if orientation == Qt.Orientation.Horizontal:
return str(self._data.columns[section])
if orientation == Qt.Orientation.Vertical:
return str(self._data.index[section])
def flags(self, index):
return Qt.ItemFlag.ItemIsSelectable|Qt.ItemFlag.ItemIsEnabled|Qt.ItemFlag.ItemIsEditable

Related

Change background of model index for QAbstractTableModel in PySide6

I would like to change the background color of specific index on my table, but only after a specific task is completed.
I know that I can use the Background role to change the color in my Table model, but I want to change the background color on external factors and not based on changes to the table itself. For example, the code below shows a basic example of a QTableView with 6 rows displayed in a QWidget. Inside the main app I am able to change the text of specific indexes using setData as seen below.
model.setData(model.index(2, 0), "Task Complete")
Here is the full code:
import sys
from PySide6.QtWidgets import (
QApplication, QWidget, QTableView, QVBoxLayout
)
from PySide6.QtCore import Qt, QAbstractTableModel
from PySide6.QtGui import QBrush
class TableModel(QAbstractTableModel):
def __init__(self, data):
super().__init__()
self._data = data
def data(self, index, role=Qt.DisplayRole):
# display data
if role == Qt.DisplayRole:
try:
return self._data[index.row()][index.column()]
except IndexError:
return ''
def setData(self, index, value, role=Qt.EditRole):
if role in (Qt.DisplayRole, Qt.EditRole):
# if value is blank
if not value:
return False
self._data[index.row()][index.column()] = value
self.dataChanged.emit(index, index)
return True
def rowCount(self, index):
return len(self._data)
def columnCount(self, index):
return len(self._data[0])
def flags(self, index):
return super().flags(index) | Qt.ItemIsEditable
class MainApp(QWidget):
def __init__(self):
super().__init__()
self.window_width, self.window_height = 200, 250
self.setMinimumSize(self.window_width, self.window_height)
self.layout = {}
self.layout['main'] = QVBoxLayout()
self.setLayout(self.layout['main'])
self.table = QTableView()
self.layout['main'].addWidget(self.table)
model = TableModel(data)
self.table.setModel(model)
# THIS IS WHERE THE QUESTION IS
model.setData(model.index(2, 0), "Task Complete") # Change background color instead of text
model.setData(model.index(5, 0), "Task Complete") # Change background color instead of text
if __name__ == '__main__':
data = [
["Task 1"],
["Task 2"],
["Task 3"],
["Task 4"],
["Task 5"],
["Task 6"],
]
app = QApplication(sys.argv)
myApp = MainApp()
myApp.show()
try:
sys.exit(app.exec())
except SystemExit:
print('Closing Window...')
I have tried to change the setData function to use the Qt.BackgroundRole instead of Qt.EditRole, but that does not work for changing the color. The result is that the code runs, but nothing happens.
I want to be able to fill the background with whatever color I choose based on the specific index I pick. However, I want this code to reside inside the MainApp class and not in the TableModel Class.
Suggestions Tried
Added code to data()
if role == Qt.BackgroundRole:
return QBrush(Qt.green)
Changed setData()
def setData(self, index, value, role=Qt.BackgroundRole):
if role in (Qt.DisplayRole, Qt.BackgroundRole):
# if value is blank
if not value:
return False
self._data[index.row()][index.column()] = value
self.dataChanged.emit(index, index)
return True
Changed setData in MainApp too
model.setData(model.index(5, 0), QBrush(Qt.green))
This resulted in highlighting the entire table in green instead of specific index.
If you want to set different colors for each index, you must store the color information in another data structure and return the corresponding value for the index.
Both data() and setData() must access different values depending on the role (see the documentation about item roles), meaning that you must not use self._data indiscriminately for anything role. If you set the color for a row/column in the same data structure you use for the text, then the text is lost.
A simple solution is to create a list of lists that has the same size of the source data, using None as default value.
class TableModel(QAbstractTableModel):
def __init__(self, data):
super().__init__()
self._data = data
rows = len(data)
cols = len(data[0])
self._backgrounds = [[None] * cols for _ in range(rows)]
def data(self, index, role=Qt.DisplayRole):
if not index.isValid():
return
elif role in (Qt.DisplayRole, Qt.EditRole):
return self._data[index.row()][index.column()]
elif role == Qt.BackgroundRole:
return self._backgrounds[index.row()][index.column()]
def setData(self, index, value, role=Qt.EditRole):
if (
not index.isValid()
or index.row() >= len(self._data)
or index.column() >= len(self._data[0])
):
return False
if role == Qt.EditRole:
self._data[index.row()][index.column()] = value
elif role == Qt.BackgroundRole:
self._backgrounds[index.row()][index.column()] = value
else:
return False
self.dataChanged.emit(index, index, [role])
return True
Note: you should always ensure that data has at least one row, otherwise columnCount() will raise an exception.
Then, to update the color, you must also use the proper role:
model.setData(model.index(5, 0), QBrush(Qt.green), Qt.BackgroundRole)
Note that if you don't need to keep the data structure intact (containing only the displayed values), a common solution is to use dictionaries.
You could use common dictionary that has the role as key and the data structure as value:
class TableModel(QAbstractTableModel):
def __init__(self, data):
super().__init__()
rows = len(data)
cols = len(data[0])
self._data = {
Qt.DisplayRole: data,
Qt.BackgroundRole: [[None] * cols for _ in range(rows)]
}
# implement the other functions accordingly
Otherwise, use a single structure that uses unique dictionaries for each item:
class TableModel(QAbstractTableModel):
def __init__(self, data):
super().__init__()
self._data = []
for rowData in data:
self._data.append([
{Qt.DisplayRole: item} for item in rowData
])
def data(self, index, role=Qt.DisplayRole):
if not index.isValid():
return
data = self._data[index.row()][index.column()]
if role == Qt.EditRole:
role = Qt.DisplayRole
return data.get(role)
def setData(self, index, value, role=Qt.EditRole):
if (
not index.isValid()
or role not in (Qt.EditRole, Qt.BackgroundRole)
):
return False
self._data[index.row()][index.column()][role] = value
self.dataChanged.emit(index, index, [role])
return True

Qtableview is not updating when table model updates

I am writing a gui application for processing multiple Excel files into a single file. The main window displays a table to which the user can add or remove files to be collected in the larger file. Each row will represent a file with columns for different parameters (data to be pulled from that file.) I've been working on implementing a QAbstractTableModel for this purpose, which works great, although I've not been able to update the table view. When a new row is added to my array of data, a new row is added to the table view but each column is empty. I'm not sure why this is as I've confirmed that the data array is updating as it should. Example:
class _tableModel(QAbstractTableModel):
def __init__(self, data=None):
QAbstractTableModel.__init__(self)
self.data = data
self.load_data(data)
def load_data(self, data):
self.input_files = data[:,0]
self.input_sheets = data[:,1]
self.column_count = 2
self.row_count = len(self.input_sheets)
def rowCount(self, parent=QModelIndex()):
return self.row_count
def columnCount(self, parent=QModelIndex()):
return self.column_count
def headerData(self, section, orientation, role):
if role != Qt.DisplayRole:
return None
if orientation == Qt.Horizontal:
return ("File", "Sheet")[section]
else:
return "{}".format(section)
def data(self, index, role=Qt.DisplayRole):
column = index.column()
row = index.row()
if role == Qt.DisplayRole:
if column == 0:
file = str(self.input_files[row])
return file
elif column == 1:
return str(self.input_sheets[row])
elif role == Qt.BackgroundRole:
return QColor(Qt.white)
elif role == Qt.TextAlignmentRole:
return Qt.AlignRight
return None
def appendRowData(self, data):
self.beginInsertRows(QModelIndex(), self.rowCount(), self.rowCount())
self.data = np.concatenate((self.data, data), axis=0)
self.endInsertRows()
def setData(self, index, value, role=Qt.EditRole):
if role == Qt.EditRole:
self.data[index.row()][index.column()] = value
self.dataChanged.emit(index, index)
return True
class Widget(QWidget):
def __init__(self, data=None):
QWidget.__init__(self)
self.main_layout = QVBoxLayout()
self.addFileButton = QPushButton('Add File')
self.addFileButton.clicked.connect(self.addFileDialog)
self.main_layout.addWidget(self.addFileButton)
self.model = _tableModel(data)
self.table_view = QTableView()
self.table_view.setModel(self.model)
self.horizontal_header = self.table_view.horizontalHeader()
self.vertical_header = self.table_view.verticalHeader()
self.horizontal_header.setSectionResizeMode(
QHeaderView.ResizeToContents
)
self.vertical_header.setSectionResizeMode(
QHeaderView.ResizeToContents
)
self.horizontal_header.setStretchLastSection(True)
size = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
size.setHorizontalStretch(1)
self.table_view.setSizePolicy(size)
self.main_layout.addWidget(self.table_view)
self.setLayout(self.main_layout)
def addFileDialog(self):
self.fileWizard = QDialog()
self.wiz_layout = QFormLayout()
self.fileWizard.setLayout(self.wiz_layout)
self.selectExcelFile = QPushButton('Select Excel File')
self.selectExcelFile.clicked.connect(self.selectexcel)
self.wiz_layout.addRow(self.selectExcelFile)
self.selectedFileDisplay = QTextEdit()
self.wiz_layout.addRow(self.selectedFileDisplay)
self.sheet = QLineEdit()
self.wiz_layout.addRow('Sheet Name: ', self.sheet)
self.add_to_table = QPushButton('Add to File Table')
self.wiz_layout.addWidget(self.add_to_table)
self.add_to_table.clicked.connect(self._addTableEntry)
self.fileWizard.show()
def selectexcel(self):
self.filename = QFileDialog.getOpenFileName(self)
self.filename = self.filename[0]
self.selectedFileDisplay.setText(self.filename)
pass
def _addTableEntry(self):
row = self.model.rowCount()
data = np.array([[self.filename, self.sheet.text()]])
self.model.appendRowData(data)
class _mainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.generalLayout = QGridLayout()
data = np.array([['File name here','Sheet name here']])
self._centralWidget = Widget(data)
self.setCentralWidget(self._centralWidget)
self._centralWidget.setLayout(self.generalLayout)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = _mainWindow()
window.show()
sys.exit(app.exec_())
Both rowCount and columnCount must be dynamic, since they return the current extent of the model.
You're setting those values as static, so, while the model "accepts" the insertion of rows (as shown in the view), it's not able to access them because the row and column count don't reflect the updated model size.
def rowCount(self, parent=QModelIndex()):
return len(self.data[:,1])
Note that you're also not updating the self.data but in fact replacing it. Since you're using self.input_files for data(), it will probably result in an exception, as those arrays are not actually updated.
So, you either always use self.data anywhere with the correct slices, or use python properties to access input_files and input_sheets.
class _tableModel(QAbstractTableModel):
# ...
#property
def input_files(self):
return self.data[:,0]
# ...

How to have multiple columns in a QComboBox with a QAbstractTableModel

I've seen questions similar to this one but they are aimed at QTableView. This is not using that,, this is just for a dropdown (QComboBox) with a custom QAbstractTableModel, which needs to have 2 columns.
BIG UPDATE
(Note: Legacy code has been deleted as this is a better approach on the same question, and legacy code was confusing as hell).
Okay, so trying to catch up with what #eyllanesc explained, I changed this from a QAbstractListModel to a QAbstractTableModel. The result is:
class ModelForComboboxesWithID(QAbstractTableModel):
"""Create our basic model"""
def __init__(self, program, records):
super(ModelForComboboxesWithID, self).__init__()
self._data = records
self.program = program
self.path_images = program.PATH_IMAGES
def rowCount(self, index: int = 0) -> int:
"""The length of the outer list. Structure: [row, row, row]"""
if not self._data:
return 0 # Doubt: Do we need to return this if self._data is empty?
return len(self._data)
def columnCount(self, index: int = 0) -> int:
"""The length of the sub-list inside the outer list. Meaning that Columns are inside rows
Structure: [row [column], row [column], row [column]]"""
if not self._data:
return 0 # Doubt: Do we need to return this if self._data is empty?
return len(self._data[0])
def data(self, index, role=None):
"""return the data on this index as data[row][column]"""
# 1 - Display data based on its content (this edits the text that you visually see)
if role == Qt.DisplayRole:
value = self._data[index.row()][index.column()]
return value
# 2 - Tooltip displayed when hovering on it
elif role == Qt.ToolTipRole:
return f"ID: {self._data[index.row()][1]}"
Which I set this way:
def eventFilter(self, target, event: QEvent):
if event.type() == QEvent.MouseButtonPress:
if target == self.Buscadorcombo_cliente:
records = ... # my query to the database
set_combo_records_with_ids(self.program, target, records)
target.currentIndexChanged.connect(self.test)
def set_combo_records_with_ids(program, combobox: QComboBox, records):
"""Clear combobox, set model/data and sort it"""
combobox.clear()
model = ModelForComboboxesWithID(program, records)
combobox.setModel(model)
combobox.model().sort(0, Qt.AscendingOrder)
combobox.setModelColumn(0)
The result of this works almost perfect:
On the dropdown(Combobox) it displays the name.
If you hover on an item, it displays the ID.
Now I am able to get any data of it this way.
def test(self, index):
data_id = self.Buscadorcombo_cliente.model().index(index, 1).data()
data_name = self.Buscadorcombo_cliente.model().index(index, 0).data()
print(data_id)
print(data_name)
You have to set a QTableView as a view:
from PySide2 import QtGui, QtWidgets
def main():
import sys
app = QtWidgets.QApplication(sys.argv)
w = QtWidgets.QWidget()
combo = QtWidgets.QComboBox()
model = QtGui.QStandardItemModel(0, 2)
for i in range(10):
items = []
for j in range(model.columnCount()):
it = QtGui.QStandardItem(f"it-{i}{j}")
items.append(it)
model.appendRow(items)
combo.setModel(model)
view = QtWidgets.QTableView(
combo, selectionBehavior=QtWidgets.QAbstractItemView.SelectRows
)
combo.setView(view)
view.verticalHeader().hide()
view.horizontalHeader().hide()
header = view.horizontalHeader()
for i in range(header.count()):
header.setSectionResizeMode(i, QtWidgets.QHeaderView.Stretch)
lay = QtWidgets.QVBoxLayout(w)
lay.addWidget(combo)
lay.addStretch()
w.resize(640, 480)
w.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()

How to lazy load changing data into a QColumnView (with PyQt)?

I have a hierarchical data source for a QColumnView I want to fill. The data source loads the data from a server using a REST interface.
Lets say the hierarchy looks like this:
Car_Manufacturer -> Car_Type -> Specific_Model -> Motor_Type
I have to use a QColumnView to display this (since it is a customer requirement). The behavior is supposed to be like this:
When the program starts, it loads the Car_Manufacturer from the server. When one of the Car_Manufacturer items is clicked, the Car_Type items for the selected Car_Manufacturer is loaded from the server and displayed in a new column. When the Car_Manufacturer is clicked again, the data has to be fetched again from the server and the column has to be updated. When Car_Type is clicked, the Specific_Model items for this Car_Manufacturer and Car_type have to be queried from the server and loaded into a new column... and so on.
The datasource has this api:
datasource.get_manufacturers(hierarchy) # hierarchy = []
datasource.get_car_type(hierarchy) # hierarchy = [manufacturer, ]
datasource.get_specific_model(hierarchy) # hierarchy = [manufacturer, car_type]
datasource.get_motor_type(hierarchy) # hierarchy = [manufacturer, car_type, specific_model ]
Where each element in the hierarchy is a string key representation of the item. When an item is clicked it has to inform a controller about this with the hierarchy of the curernt item.
How can I get the QColumnView to update the children of one item when the item is clicked using the datasource? How can this stay flexible when a new hierarchy layer is added or removed?
Here is an example which implements a custom DirModel.
The method _create_children is called lazily and should return a list of instances which implement AbstractTreeItem.
import sys
import os
import abc
from PyQt4.QtCore import QAbstractItemModel, QModelIndex, Qt, QVariant
from PyQt4.QtGui import QColumnView, QApplication
class TreeModel(QAbstractItemModel):
def __init__(self, root, parent=None):
super(TreeModel, self).__init__(parent)
self._root_item = root
self._header = self._root_item.header()
def columnCount(self, parent=None):
if parent and parent.isValid():
return parent.internalPointer().column_count()
else:
return len(self._header)
def data(self, index, role):
if not index.isValid():
return QVariant()
item = index.internalPointer()
if role == Qt.DisplayRole:
return item.data(index.column())
if role == Qt.UserRole:
if item:
return item.person
return QVariant()
def headerData(self, column, orientation, role):
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
try:
return QVariant(self._header[column])
except IndexError:
pass
return QVariant()
def index(self, row, column, parent):
if not self.hasIndex(row, column, parent):
return QModelIndex()
if not parent.isValid():
parent_item = self._root_item
else:
parent_item = parent.internalPointer()
child_item = parent_item.child_at(row)
if child_item:
return self.createIndex(row, column, child_item)
else:
return QModelIndex()
def parent(self, index):
if not index.isValid():
return QModelIndex()
child_item = index.internalPointer()
if not child_item:
return QModelIndex()
parent_item = child_item.parent()
if parent_item == self._root_item:
return QModelIndex()
return self.createIndex(parent_item.row(), 0, parent_item)
def rowCount(self, parent=QModelIndex()):
if parent.column() > 0:
return 0
if not parent.isValid():
parent_item = self._root_item
else:
parent_item = parent.internalPointer()
return parent_item.child_count()
class AbstractTreeItem(object):
__metaclass__ = abc.ABCMeta
def __init__(self, parent=None):
self._children = None
self._parent = parent
#abc.abstractmethod
def header(self):
#return ["name"]
raise NotImplementedError(self.header)
#abc.abstractmethod
def column_count(self):
#return 1
raise NotImplementedError(self.column_count)
def parent(self):
return self._parent
#abc.abstractmethod
def _create_children(self):
# subclass this method
return []
def row(self):
if self._parent:
return self._parent._children.index(self)
return 0
#property
def children(self):
if self._children is None:
self._children = self._create_children()
return self._children
def child_at(self, row):
return self.children[row]
#abc.abstractmethod
def data(self, column):
#return ""
raise NotImplementedError(self.data)
def child_count(self):
count = len(self.children)
return count
class DirPathModel(AbstractTreeItem):
def __init__(self, root="/", parent=None):
super(DirPathModel, self).__init__(parent)
self._root = root
def _create_children(self):
print "walking into", self._root
children = []
try:
entries = os.listdir(self._root)
except OSError:
# no permission etc
entries = []
for name in entries:
fn = os.path.join(self._root, name)
if os.path.isdir(fn):
children.append(self.__class__(fn, self))
return children
def data(self, column):
#assert column == 0
return os.path.basename(self._root)
def header(self):
return ["name"]
def column_count(self):
return 1
def main():
app = QApplication(sys.argv)
view = QColumnView()
view.setWindowTitle("Dynamic Column view test")
view.resize(1024, 768)
root = DirPathModel("/")
model = TreeModel(root)
view.setModel(model)
view.show()
return app.exec_()
if __name__ == "__main__":
sys.exit(main() or 0)
Asuming you can't bring all data at once and filter it out, you'll have to modify the item model (adding and removing rows) on the go based on whatever the user has selected from the QColumnView.
There's more than one way to remove the items:
You can use the index of the selected column and remove all items "on the left" of this column.
You can remove items based whose parent (or grandparent) matches the selection being made
Any option you take, you'll have to mirror the relationship between items in some way. That or implement from QAbstractItemModel, which I think would be an overkill.

How to re-arrange QTableView's Columns Order

The code below creates a single QTableView driven by Model/Proxy framework.
The self.headerNames list-variable declared in source-model stores the names of the Header's Columns. The number of names in this list is used by the same source model's columnCount() method to return the number of column in a view:
def columnCount(self, parent=QModelIndex()):
return len(self.headerNames)
The Proxy model's headerData() access this self.headerNames variable via source model:
sourceModel=self.sourceModel()
On if role==Qt.DisplayRole the Proxy retrieves and returns the name of the column to QTableView:
return QVariant( sourceModel.headerNames[column] )
There is a right-click menu implemented on header-column right-click.
That portion is working fine. But since I could not find any examples on how others do it I had to design how it would work myself. I would appreciate if you find it could be improved.
What I want to implement next is the ability to re-arrange the column in arbitrary order. But I am not sure where to start.
P.s. Please disregard the names of the Items displayed in QTableView.
I wanted to keep the code as simple as possible focusing on Header/Column subject only here.
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys
class Model(QAbstractTableModel):
def __init__(self, parent=None, *args):
QAbstractTableModel.__init__(self, parent, *args)
self.items = ['Item_A_001','Item_A_002','Item_B_001','Item_B_002']
self.headerNames=['Column 0','Column 1','Column 2','Column 3','Column 4','Column 5','Column 6','Column 7']
def rowCount(self, parent=QModelIndex()):
return len(self.items)
def columnCount(self, parent=QModelIndex()):
return len(self.headerNames)
def data(self, index, role):
if not index.isValid(): return QVariant()
elif role != Qt.DisplayRole:
return QVariant()
row=index.row()
if row<len(self.items):
return QVariant(self.items[row])
else:
return QVariant()
class Proxy(QSortFilterProxyModel):
def __init__(self):
super(Proxy, self).__init__()
def filterAcceptsRow(self, row, parent):
return True
def headerData(self, column, orientation, role=Qt.DisplayRole):
sourceModel=self.sourceModel()
if role==Qt.TextAlignmentRole:
if orientation==Qt.Horizontal:
return QVariant(int(Qt.AlignHCenter|Qt.AlignVCenter))
return QVariant(int(Qt.AlignHCenter|Qt.AlignVCenter))
if role==Qt.DisplayRole:
if orientation==Qt.Horizontal:
return QVariant( sourceModel.headerNames[column] )
else:
return QVariant()
else:
return QVariant()
return QVariant(int(column+1))
class MyWindow(QWidget):
def __init__(self, *args):
QWidget.__init__(self, *args)
tableModel=Model(self)
proxyModel=Proxy()
proxyModel.setSourceModel(tableModel)
self.tableview=QTableView(self)
self.tableview.setModel(proxyModel)
self.tableview.horizontalHeader().setStretchLastSection(True)
self.tableview.setSelectionMode(QAbstractItemView.MultiSelection)
header=self.tableview.horizontalHeader()
header.setContextMenuPolicy(Qt.CustomContextMenu)
header.connect(header, SIGNAL("customContextMenuRequested(QPoint)" ), self.headerRightClicked)
self.resHeaderMenu=QMenu(self)
for column in range(proxyModel.columnCount()):
columnName=proxyModel.headerData(column, Qt.Horizontal).toPyObject()
actn=QAction('%s'%columnName, self.resHeaderMenu, checkable=True)
actn.setChecked(True)
actn.triggered.connect(self.resHeaderMenuTriggered)
self.resHeaderMenu.addAction(actn)
layout = QVBoxLayout(self)
layout.addWidget(self.tableview)
self.setLayout(layout)
def headerRightClicked(self, QPos):
parentPosition=self.tableview.mapToGlobal(QPoint(0, 0))
menuPosition=parentPosition + QPos
self.resHeaderMenu.move(menuPosition)
self.resHeaderMenu.show()
def resHeaderMenuTriggered(self, arg):
print 'resHeaderMenuTriggered', arg
for i, actn in enumerate(self.resHeaderMenu.actions()):
if not actn.isChecked():
self.tableview.setColumnHidden(i, True)
if __name__ == "__main__":
app = QApplication(sys.argv)
w = MyWindow()
w.show()
sys.exit(app.exec_())
In case you really want to re-arrange the columns yourself:
You can move or swap sections via the moveSection and swapSections methods of the horizontalHeader.
headerView.moveSection(x, y)
will move column x such that it's column y afterwards, while
headerView.swapSections(x,y)
will - obviously - swap the positions of the two columns.
Thank you for the right-click menu implemented on header-column right-click example. Right looking for the same function as you were. Just one thing that the column should be shown when the tick is re-checked, i.e. your resHeaderMenuTriggered() should be written as:
def resHeaderMenuTriggered(self, arg):
print('resHeaderMenuTriggered', arg)
for i, actn in enumerate(self.resHeaderMenu.actions()):
if not actn.isChecked():
self.proxyView.setColumnHidden(i, True)
else:
self.proxyView.setColumnHidden(i, False)

Categories

Resources