How to get context menus of the context menu for clicks in the QTableView header?

The following code example (highly dependent on here ) has a context menu context menu that will be displayed when a user clicks on cells in a table. Is it possible to have a different right-click context menu for right-clicking in the table header? If so, how can I change the code to include this?

import re import operator import os import sys from PyQt4.QtCore import * from PyQt4.QtGui import * def main(): app = QApplication(sys.argv) w = MyWindow() w.show() sys.exit(app.exec_()) class MyWindow(QWidget): def __init__(self, *args): QWidget.__init__(self, *args) self.tabledata = [('apple', 'red', 'small'), ('apple', 'red', 'medium'), ('apple', 'green', 'small'), ('banana', 'yellow', 'large')] self.header = ['fruit', 'color', 'size'] # create table self.createTable() # layout layout = QVBoxLayout() layout.addWidget(self.tv) self.setLayout(layout) def popup(self, pos): for i in self.tv.selectionModel().selection().indexes(): print i.row(), i.column() menu = QMenu() quitAction = menu.addAction("Quit") action = menu.exec_(self.mapToGlobal(pos)) if action == quitAction: qApp.quit() def createTable(self): # create the view self.tv = QTableView() self.tv.setStyleSheet("gridline-color: rgb(191, 191, 191)") self.tv.setContextMenuPolicy(Qt.CustomContextMenu) self.tv.customContextMenuRequested.connect(self.popup) # set the table model tm = MyTableModel(self.tabledata, self.header, self) self.tv.setModel(tm) # set the minimum size self.tv.setMinimumSize(400, 300) # hide grid self.tv.setShowGrid(True) # set the font font = QFont("Calibri (Body)", 12) self.tv.setFont(font) # hide vertical header vh = self.tv.verticalHeader() vh.setVisible(False) # set horizontal header properties hh = self.tv.horizontalHeader() hh.setStretchLastSection(True) # set column width to fit contents self.tv.resizeColumnsToContents() # set row height nrows = len(self.tabledata) for row in xrange(nrows): self.tv.setRowHeight(row, 18) # enable sorting self.tv.setSortingEnabled(True) return self.tv class MyTableModel(QAbstractTableModel): def __init__(self, datain, headerdata, parent=None, *args): """ datain: a list of lists headerdata: a list of strings """ QAbstractTableModel.__init__(self, parent, *args) self.arraydata = datain self.headerdata = headerdata def rowCount(self, parent): return len(self.arraydata) def columnCount(self, parent): return len(self.arraydata[0]) def data(self, index, role): if not index.isValid(): return QVariant() elif role != Qt.DisplayRole: return QVariant() return QVariant(self.arraydata[index.row()][index.column()]) def headerData(self, col, orientation, role): if orientation == Qt.Horizontal and role == Qt.DisplayRole: return QVariant(self.headerdata[col]) return QVariant() def sort(self, Ncol, order): """Sort table by given column number. """ self.emit(SIGNAL("layoutAboutToBeChanged()")) self.arraydata = sorted(self.arraydata, key=operator.itemgetter(Ncol)) if order == Qt.DescendingOrder: self.arraydata.reverse() self.emit(SIGNAL("layoutChanged()")) if __name__ == "__main__": main() 
+7
source share
2 answers

It turned out to be easier than I thought. Just as I add a popup menu for the QTableView widget itself, I can just get the title from the table object and then attach the context menu in the same way as in the regular context menu.

 headers = self.tv.horizontalHeader() headers.setContextMenuPolicy(Qt.CustomContextMenu) headers.customContextMenuRequested.connect(self.header_popup) 
+11
source

There is another potentially more powerful way to do this if you take a step and inherit a view, rather than just composing it. Does the user context menu work here? Yes, but why should something other than opinion know about it? It will also help to better shape your code to properly solve other problems. Currently, implementation does not provide encapsulation, cohesion, or separation of responsibilities. As a result, you will have one big blob, which is the antithesis of good design. I mention this because you seem to host all the Logic GUIs in this ever-growing core function, and for this reason you ended up putting a sort implementation inside your model, which makes no sense to me. (What if you have two representations of the model, you force them to sort the same way)

Is this more code? Yes, but it gives you more power, which I think is worth mentioning. Below I demonstrate how to handle the headers, as well as any cell you want. Also note that in my implementation, if there is some other widget that also defines the context menu event handler, it will potentially get a chance to get cracks when processing the event after mine; so if someone adds a handler only for certain cases, they can do this without complicating my code. Part of this is done if you handled the event or not.

Enough of my incoherent and thoughtful code here:

  #Alteration : instead of self.tv = QTableView... self.tv = MyTableView() .... # somewhere in your TableView object __init__ method # yeah IMHO you should be inheriting and thus extending TableView class MyTableView(QTableView): def __init__(self, parent = None): super(MyTableView, self).__init__() self.setContextMenuPolicy(Qt.DefaultContextMenu) ## uniform one for the horizontal headers. self.horizontalHeader().setContextMenuPolicy(Qt.ActionsContextMenu) ''' Build a header action list once instead of every time they click it''' doSomething = QAction("&DoSomething", self.verticalHeader(), statusTip = "Do something uniformly for headerss", triggered = SOME_FUNCTION self.verticalHeader().addAction(doSomething) ... return def contextMenuEvent(self, event) ''' The super function that can handle each cell as you want it''' handled = False index = self.indexAt(event.pos()) menu = QMenu() #an action for everyone every = QAction("I'm for everyone", menu, triggered = FOO) if index.column() == N: #treat the Nth column special row... action_1 = QAction("Something Awesome", menu, triggered = SOME_FUNCTION_TO_CALL ) action_2 = QAction("Something Else Awesome", menu, triggered = SOME_OTHER_FUNCTION ) menu.addActions([action_1, action_2]) handled = True pass elif index.column() == SOME_OTHER_SPECIAL_COLUMN: action_1 = QAction("Uh Oh", menu, triggered = YET_ANOTHER_FUNCTION) menu.addActions([action_1]) handled = True pass if handled: menu.addAction(every) menu.exec_(event.globalPos()) event.accept() #TELL QT IVE HANDLED THIS THING pass else: event.ignore() #GIVE SOMEONE ELSE A CHANCE TO HANDLE IT pass return pass #end of class 
+4
source

All Articles