内部C ++对象(PySide2.QtGui.QContextMenuEvent)已在Qtableview上删除

问题描述 投票:1回答:1

我有一个非常简单的程序,可以打开一个数据库并加载一个表视图。

所以:

布局文件称为TestLayouts.py

# -*- coding: utf-8 -*-

################################################################################
## Form generated from reading UI file 'basic.ui'
##
## Created by: Qt User Interface Compiler version 5.14.2
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################

from PySide2.QtCore import (QCoreApplication, QMetaObject,
                            QRect)
from PySide2.QtWidgets import *


class Ui_MainWindow(object) :
    def setupUi(self, MainWindow) :
        if not MainWindow.objectName() :
            MainWindow.setObjectName(u"MainWindow")
        MainWindow.resize(1034, 803)
        self.centralwidget = QWidget(MainWindow)
        self.centralwidget.setObjectName(u"centralwidget")
        self.pushButton = QPushButton(self.centralwidget)
        self.pushButton.setObjectName(u"pushButton")
        self.pushButton.setGeometry(QRect(920, 730, 89, 25))
        self.tableView = QTableView(self.centralwidget)
        self.tableView.setObjectName(u"tableView")
        self.tableView.setGeometry(QRect(10, 20, 1001, 711))
        MainWindow.setCentralWidget(self.centralwidget)
        self.statusbar = QStatusBar(MainWindow)
        self.statusbar.setObjectName(u"statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)

        QMetaObject.connectSlotsByName(MainWindow)
    # setupUi

    def retranslateUi(self, MainWindow) :
        MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", u"MainWindow", None))
        self.pushButton.setText(QCoreApplication.translate("MainWindow", u"PushButton", None))
    # retranslateUi

我的主文件为tests.py

import sys
import webbrowser

from PySide2 import QtWidgets, QtGui
from PySide2.QtCore import QCoreApplication, QSortFilterProxyModel
from PySide2.QtCore import Slot
from PySide2.QtSql import QSqlDatabase, QSqlQueryModel
from PySide2.QtWidgets import QMenu, QAction
from TestLayouts import Ui_MainWindow


class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow) :

    def __init__(self, parent=None) :
        super(MainWindow, self).__init__()

        self.setupUi(self)
        self.showMaximized()
        self.pushButton.clicked.connect(self._basic)

    def _basic(self) :
        db = QSqlDatabase.addDatabase("QSQLITE")
        db.setDatabaseName("data.sqlite")
        db.open()
        sourceModel = QSqlQueryModel()
        sourceModel.setQuery(
            "SELECT id,url FROM database",
            db)
        proxyModel = QSortFilterProxyModel(self)
        proxyModel.setSourceModel(sourceModel)
        self.tableView.setModel(proxyModel)
        self.tableView.setSortingEnabled(True)


    @Slot()
    def closeEvent(self, event) :
        super(MainWindow, self).closeEvent(event)
        QCoreApplication.instance().quit()

    def contextMenuEvent(self, event) :
        self.menu = QMenu(self)
        openlinkAction = QAction('Open Link In A Browser', self)
        openlinkAction.triggered.connect(lambda : self.openInANewTab(event))
        self.menu.addAction(openlinkAction)
        self.menu.popup(QtGui.QCursor.pos())

    def openInANewTab(self, event) :
        self.row = self.tableView.rowAt(event.pos().y())
        self.col = self.tableView.columnAt(event.pos().x())
        self.cell = self.tableView.item(self.row, self.col)
        cellText = self.cell.text()
        webbrowser.open(cellText)


if __name__ == "__main__" :
    app = QtWidgets.QApplication([])
    w = MainWindow()
    w.show()
    sys.exit(app.exec_())

一旦运行程序并单击加载,数据库就会加载到视图中。右键单击菜单将按预期显示,但是单击后会收到错误消息。

RuntimeError: Internal C++ object (PySide2.QtGui.QContextMenuEvent) already deleted.

我看过几个线程like this one here,它说

如果QObject在Python中超出范围,它将被删除。您必须注意保留对该对象的引用:将其存储为您周围的对象的属性,例如self.window = QMainWindow()将父级QObject传递给对象的构造函数,使其由父级拥有]

我不确定如何实现。任何帮助表示赞赏。

python qtableview pyside2
1个回答
1
投票

说明:

在contextMenuEvent方法中,您正在创建一个弹出窗口,并且正在显示该弹出窗口,但是它消耗的时间很少,因此当用户选择一个选项时,该方法已经完成,因此Qt删除了“事件”对象,因为不再需要。但是您正在尝试访问由Qt删除的项目,从而导致此类错误。

解决方案:

有几种解决方案:

  • 使用exec_()而不是popup(),以便contextMenuEvent方法无法完成执行,因此不会删除“事件”对象,此外QTableView没有item()方法,因此它将抛出另一个例外,因此请使用index方法:

    def contextMenuEvent(self, event):
        self.menu = QMenu(self)
        openlinkAction = QAction("Open Link In A Browser", self)
        openlinkAction.triggered.connect(lambda: self.openInANewTab(event))
        self.menu.addAction(openlinkAction)
        self.menu.exec_(QtGui.QCursor.pos())
    
    def openInANewTab(self, event):
        gp = self.mapToGlobal(event.pos())
        vp = self.tableView.viewport().mapFromGlobal(gp)
        index = self.tableView.indexAt(vp)
        if index.isValid():
            cellText = index.data()
            if isinstance(cellText, str):
                webbrowser.open(cellText)
    
  • 从文本中获取信息,并在显示弹出窗口之前将其传递给lambda,因此不再需要使用该事件:

    def contextMenuEvent(self, event):
        gp = self.mapToGlobal(event.pos())
        vp = self.tableView.viewport().mapFromGlobal(gp)
        index = self.tableView.indexAt(vp)
        if not index.isValid():
            return
        self.menu = QMenu(self)
        cellText = index.data()
        openlinkAction = QAction("Open Link In A Browser", self)
        openlinkAction.triggered.connect(
            lambda *args, text=cellText: self.openInANewTab(text)
        )
        self.menu.addAction(openlinkAction)
        self.menu.popup(QtGui.QCursor.pos())
    
    def openInANewTab(self, text):
        if isinstance(text, str):
            webbrowser.open(text)
    
最新问题
© www.soinside.com 2019 - 2025. All rights reserved.