Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

How to automatically add some actions to context menus



  • I am thinking through how to approach what I want to achieve. I think my desire might be "impossible", but while I am investigating I wonder if a Qt expert can point me in the right direction as to what is or is not achievable.

    Qt 5.7 GUI. I have a large body of already-written code, across many files, so changes are to be kept to a minimum.

    • Many pages have a QTableView. I have written code to export any table to CSV, PDF etc. I wish to offer this on the right-click context menu for every such table view. I am looking for a generic solution.

    • All my occurrences of QTableView are wrapped in my own JTableView(QTableView) sub-class. So we can do whatever we like in that sub-class.

    • Pages which have a JTableView may or may not already have their own specific context menu.

    • Regardless of whether the specific page has its own menu & items or not, I should like my "table export" items to appear at the bottom of the menu.

    If a page does have its own table view context menu, it will have code like this:

    self.tableView = JTableView(self)
    self.tableView.setContextMenuPolicy(Qt.CustomContextMenu)
    self.tableView.customContextMenuRequested.connect(self.openContextMenu)
    
    def openContextMenu(self, position):
        menu = JMenu(self)  # JMenu is sub-class of QMenu
    
        action = menu.addAction("...")
        action.triggered.connect(...)
    
        menu.exec_(self.tableView.viewport().mapToGlobal(position))
    

    So:

    • If the page's table does not have its own context menu, all I have to do is copy the above code principle directly into my JTableViews.

    • However, if a page does have the above, I assume I am in trouble? I won't be able to intercept that menu creation to append my own entries to the bottom, will I?

    So, for a generic case which should include the second possibility as well as the first, what approach can I take? I realise if I re-write all callers' code I can achieve something, but is there anything I can do which will work "generically", with as little separate-file code changes as feasible??



  • Well here is the outline of what I have implemented, bearing in mind my desire to make as little code changes as possible and to provide the basic export facility quite "transparently" & automatically to all existing JTableViews which do not have their own context menus to add in.

    Step 1 is just to make JTableView's constructor go setContextMenuPolicy(Qt.CustomContextMenu) and set its own slot on customContextMenuRequested to its own openContextMenu() method which displays its export custom menu. At this point all existing JTableViews have the export menu without them even knowing about it/changing any of their code. That deals with 50% of client cases, with 0 lines of code changes.

    Step 2 is to handle those callers which already have their own customContextMenuRequested slot registered (after the JTableView has been created and registered its own) with their own menu. The problem is that will lead to 2 separate menus being shown, one after the other, and the caller-specific one won't be called till after the JTableView's one has executed, so it will be too late to do anything about that at that point.

    So I provide a JTableView.replaceCustomContextMenuRequested() which accepts the caller's own slot as a parameter: it disconnect()s its own slot first and then registers the caller's slot in its place. The caller's slot code builds its own menu as before, but now instead of displaying it directly it passes it to JTableView's openContextMenu() which merges that with its own export menu and then displays it for the caller. This deals with the remaining 50% of client cases, at just 2 lines of code changes per client.

    Ugly? Possibly. But achieves desired with minimal changes, and it's not that bad is it?


  • Lifetime Qt Champion

    Hi,

    In that case, shouldn't you have a base widget that contains your custom tableview and context menu handler ?

    You could also have a method that generates a list of actions to add to the menu and on the widgets that have more action, re-implement that method to return the defaults values plus the one they need.



  • Well here is the outline of what I have implemented, bearing in mind my desire to make as little code changes as possible and to provide the basic export facility quite "transparently" & automatically to all existing JTableViews which do not have their own context menus to add in.

    Step 1 is just to make JTableView's constructor go setContextMenuPolicy(Qt.CustomContextMenu) and set its own slot on customContextMenuRequested to its own openContextMenu() method which displays its export custom menu. At this point all existing JTableViews have the export menu without them even knowing about it/changing any of their code. That deals with 50% of client cases, with 0 lines of code changes.

    Step 2 is to handle those callers which already have their own customContextMenuRequested slot registered (after the JTableView has been created and registered its own) with their own menu. The problem is that will lead to 2 separate menus being shown, one after the other, and the caller-specific one won't be called till after the JTableView's one has executed, so it will be too late to do anything about that at that point.

    So I provide a JTableView.replaceCustomContextMenuRequested() which accepts the caller's own slot as a parameter: it disconnect()s its own slot first and then registers the caller's slot in its place. The caller's slot code builds its own menu as before, but now instead of displaying it directly it passes it to JTableView's openContextMenu() which merges that with its own export menu and then displays it for the caller. This deals with the remaining 50% of client cases, at just 2 lines of code changes per client.

    Ugly? Possibly. But achieves desired with minimal changes, and it's not that bad is it?


Log in to reply