PySide2 QDialog возможная ошибка - PullRequest
1 голос
/ 04 июня 2019

У меня есть код ниже.

В PyQt5 все нажатия кнопок работают как положено.Под PySide2 они не.

Любое объяснение, это ошибка PySide2?

import os
import sys
import xlrd
from PySide2 import QtCore, QtGui, QtWidgets
# from PyQt5 import QtCore, QtGui, QtWidgets


class ExcelDialog(QtWidgets.QDialog):

    def __init__(self, excel_file=None, items=()):
        """
        Constructor
        :param excel_file: excel file to list
        :param items: items to show if the file is none
        """

        QtWidgets.QDialog.__init__(self)

        self.setObjectName("ExcelSelectionDialog")
        self.resize(272, 229)
        self.setMaximumSize(QtCore.QSize(272, 229))
        self.setModal(True)
        self.verticalLayout = QtWidgets.QVBoxLayout(self)
        self.verticalLayout.setContentsMargins(1, 1, 1, 1)
        self.verticalLayout.setObjectName("verticalLayout")
        self.sheets_list = QtWidgets.QListWidget(self)
        self.sheets_list.setFrameShape(QtWidgets.QFrame.StyledPanel)
        self.sheets_list.setObjectName("sheets_list")
        self.verticalLayout.addWidget(self.sheets_list)
        self.frame = QtWidgets.QFrame(self)
        self.frame.setFrameShape(QtWidgets.QFrame.NoFrame)
        self.frame.setFrameShadow(QtWidgets.QFrame.Raised)
        self.frame.setObjectName("frame")
        self.horizontalLayout = QtWidgets.QHBoxLayout(self.frame)
        self.horizontalLayout.setContentsMargins(1, 1, 1, 1)
        self.horizontalLayout.setObjectName("horizontalLayout")
        spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout.addItem(spacerItem)
        self.cancelButton = QtWidgets.QPushButton(self.frame)
        self.cancelButton.setObjectName("cancelButton")
        self.horizontalLayout.addWidget(self.cancelButton)
        self.acceptButton = QtWidgets.QPushButton(self.frame)
        self.acceptButton.setObjectName("acceptButton")
        self.horizontalLayout.addWidget(self.acceptButton)
        self.verticalLayout.addWidget(self.frame)

        self.retranslateUi(self)
        QtCore.QMetaObject.connectSlotsByName(self)

        # click
        self.acceptButton.clicked.connect(self.accepted)
        self.cancelButton.clicked.connect(self.rejected)
        self.sheets_list.doubleClicked.connect(self.accepted)

        self.excel_sheet = None

        self.sheet_names = list()
        if excel_file is not None:
            if os.path.exists(excel_file):
                self.fill_from_file(excel_file=excel_file)
            else:
                self.sheets_list.addItems(items)
        else:
            self.sheets_list.addItems(items)

    def fill_from_file(self, excel_file):
        """

        :param excel_file:
        :return:
        """
        if excel_file is not None:
            xls = xlrd.open_workbook(excel_file, on_demand=True)
            self.sheet_names = xls.sheet_names()
            self.sheets_list.addItems(self.sheet_names)

            if len(self.sheet_names) > 0:
                self.excel_sheet = 0

    def accepted(self):
        """

        :return:
        """
        if len(self.sheets_list.selectedIndexes()):
            self.excel_sheet = self.sheets_list.selectedIndexes()[0].row()
        print('Accepted: self.excel_sheet: ', self.excel_sheet)

        self.close()

    def rejected(self):
        """

        :return:
        """
        print('Rejected: self.excel_sheet: ', self.excel_sheet)
        self.close()

    def retranslateUi(self, ExcelSelectionDialog):
        """

        :param ExcelSelectionDialog:
        :return:
        """
        ExcelSelectionDialog.setWindowTitle(QtWidgets.QApplication.translate("ExcelSelectionDialog", "Excel sheet selection", None, -1))
        self.cancelButton.setText(QtWidgets.QApplication.translate("ExcelSelectionDialog", "Cancel", None, -1))
        self.acceptButton.setText(QtWidgets.QApplication.translate("ExcelSelectionDialog", "Accept", None, -1))


if __name__ == "__main__":
    excel_file = None
    app = QtWidgets.QApplication(sys.argv)
    window = ExcelDialog(excel_file, items=['A', 'B', 'C'])
    window.show()
    sys.exit(app.exec_())

Кстати, я на PySide 5.12.3

1 Ответ

1 голос
/ 05 июня 2019

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

Прежде всего я упростил MCVE до следующего:

from PySide2 import QtCore, QtGui, QtWidgets
# from PyQt5 import QtCore, QtGui, QtWidgets


class Dialog(QtWidgets.QDialog):
    def __init__(self, parent=None):
        super(Dialog, self).__init__(parent)

        list_widget = QtWidgets.QListWidget()
        list_widget.addItems(list("ABC"))

        accept_button = QtWidgets.QPushButton("Accept")
        cancel_button = QtWidgets.QPushButton("Cancel")

        lay = QtWidgets.QVBoxLayout(self)
        hlay = QtWidgets.QHBoxLayout()
        hlay.addStretch()
        hlay.addWidget(accept_button)
        hlay.addWidget(cancel_button)
        lay.addWidget(list_widget)
        lay.addLayout(hlay)

        accept_button.clicked.connect(self.accepted)
        cancel_button.clicked.connect(self.rejected)

    def accepted(self):
        print("accepted")

    def rejected(self):
        print("rejected")

Еще одна вещь, которую следует иметь в виду, это то, что QDialog имеет сигнал accepted(), который вызывает это странное поведение. Также имейте в виду, что может быть установлена ​​связь между сигналом с вызываемым, слотом и другим сигналом.

Моя гипотеза состоит в том, что PySide2 сначала устанавливает соединение со слотами и сигналами, прежде чем устанавливать соединения с обычными функциями python, и это проверяется с помощью следующего кода:

    # ...
    # create connections
    QtCore.QObject.connect(self, QtCore.SIGNAL("accepted()"), self, QtCore.SLOT("accepted_test()"))
    QtCore.QObject.connect(self, QtCore.SIGNAL("rejected()"), self, QtCore.SLOT("rejected_test()"))

def accepted_test(self):
    print("accepted_test")

def rejected_test(self):
    print("rejected_test")

def accepted(self):
    print("accepted")

def rejected(self):
    print("rejected")
# ...

Если вы нажмете кнопки «Принять» и «Отмена», вы получите:

accepted_test
rejected_test

С другой стороны, PyQt5, похоже, не имеет той же иерархии, поэтому он предпочитает соединение с функцией python.

Укажите, что ошибка субъективна, поскольку в документах явно не указано и зависит от поведения, ожидаемого каждым, возможно, это запланировано PySide2, поскольку для них это правильно, и то же самое для PyQt5.

В этом случае существует обходное решение: сделать принятую и отклоненную часть QMetaObject с помощью декоратора @QtCore.Slot():

    # ...
    accept_button.clicked.connect(self.accepted)
    cancel_button.clicked.connect(self.rejected)

@QtCore.Slot()
def accepted(self):
    print("accepted")

@QtCore.Slot()
def rejected(self):
    print("rejected")
# ...

Но моя личная рекомендация, которую можно считать хорошей практикой, - не создавать методы, которые уже используют базовый класс.

...