Как я могу правильно выровнять контекстное меню, вызываемое правой кнопкой мыши, в PyQt?

#pyqt #contextmenu #alignment

#pyqt ( пикт ) #контекстное меню #выравнивание #pyqt #contextmenu

Вопрос:

В приведенном ниже примере кода (на который сильно повлияло здесь) контекстное меню, вызываемое правой кнопкой мыши, на самом деле не выровнено должным образом.

Как видно на скриншоте, результирующее меню находится совсем немного выше курсора мыши. Я бы ожидал, что верхний левый угол меню будет точно выровнен с указателем мыши.

Есть ли какой-нибудь способ приспособиться к этому?

Скриншот меню

 import re
import operator
import os
import sys
import sqlite3
import cookies
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()
  

Ответ №1:

позиция находится в координатах окна просмотра, поэтому, если вы используете

self.tableView.setContextMenuPolicy(Qt.CustomContextMenu)

чтобы вы не event перешли к popup , вы можете сделать следующее

action = menu.exec_(self.tableView.viewport().mapToGlobal(pos))

вместо этого.

Комментарии:

1. После нескольких часов поиска это работает для PyQt5

Ответ №2:

Это было немного сложно, но следуя примеру создания подклассов в этом примере вики и заменяя

   15         action = menu.exec_(self.mapToGlobal(event.pos()))
  

с помощью

   15         action = menu.exec_(event.globalPos())
  

верхний левый угол всплывающего меню будет точно соответствовать щелчку мыши.

Ответ №3:

Это будет работать для развернутых / уменьшенных окон. Меню будет сгенерировано в правом нижнем положении мыши.

 menu.exec_(self.mapToGlobal(self.mapFromGlobal(QtGui.QCursor.pos())))