如何根据 PyQt5 中的用户输入添加对象?

How can I add objects based on user input in PyQt5?

我正在使用 PyQt5 和 SQL 制作一个基本的联系人应用程序。打开应用程序后,我想在屏幕上显示所有联系人。所以我想为每个联系人向 UI 添加一些小部件,但我不知道在开始时会添加多少小部件(因为用户可能会通过添加或删除联系人来更改联系人数量)所以我不能手动添加它们。我用 for 循环尝试过,但由于对象的名称必须彼此不同,所以它没有用。

代码如下:

from PyQt5 import QtCore, QtGui, QtWidgets
import sqlite3

class Contact:
    def __init__(self, id, first_name, last_name, phone_numbers, note = ""):
        self.id = id
        self.first_name = first_name
        self.last_name = last_name
        self.phone_numbers = phone_numbers
        self.note = note


    def getBoxInfo(self):
        return f"{self.first_name}, {self.last_name}"


    def getAllInfo(self):
        if self.note != "":
                return f"{self.first_name}, {self.last_name}, {self.phone_numbers}, {self.note}"
        else:
                return f"{self.first_name}, {self.last_name}, {self.phone_numbers},  "


#setting the connection to the database
connection = sqlite3.connect('contacts.db')

cursor = connection.cursor()

#getting 'user' and 'phone_number' datas from the database
cursor.execute("SELECT * FROM users")
connection.commit()

Users = cursor.fetchall()

cursor.execute("SELECT * FROM phone_numbers")
connection.commit()

PhoneNumbers = cursor.fetchall()

#Putting together the 'user' and 'phone_number' datas in the 'Contact' class subject and adding them to an array
Contacts = []
for i in range(len(Users)):
    newContact = Contact(Users[i][0], Users[i][1], Users[i][2], list(), Users[i][3])
    if newContact.note == None:
        newContact.note = ""
    
    for j in PhoneNumbers:
        if j[1] == Users[i][0]:
            newContact.phone_numbers.append(j[2])

        Contacts.append(newContact)

#Creating the UI
class Ui_MainWindow(object):
    def setupUi(self, MainWindow, Contacts):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(1200, 675)

        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")

        #The part I tried to create widgets as many as 'users'
        for i in range(len(Contacts)):
            self.widget = QtWidgets.QWidget(self.centralwidget)
            self.widget.setGeometry(QtCore.QRect(30, 30, 241, 81))
            self.widget.setObjectName(f"widget{i}")

            self.pushButton = QtWidgets.QPushButton(self.widget)
            self.pushButton.setGeometry(QtCore.QRect(150, 20, 75, 23))
            self.pushButton.setObjectName(f"pushButton{i}")

        MainWindow.setCentralWidget(self.centralwidget)

        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 816, 21))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)

        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

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

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.pushButton.setText(_translate("MainWindow", "PushButton"))


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)

    MainWindow = QtWidgets.QMainWindow()

    ui = Ui_MainWindow()
    ui.setupUi(MainWindow, Contacts)

    MainWindow.show()
    sys.exit(app.exec_())

这个问题与对象名称无关,而是因为小部件不断添加到相同的 固定 坐标。结果是您有重叠的小部件,您只能查看最后添加的小部件。

虽然您可以使用增加的 y 位置,但这不会很好地工作:即使假设您要调整主 window 基于最后添加的小部件位置,出于多种原因,使用固定几何形状被认为是一种不好的做法;在这种特定情况下,您最终可能会得到一个太高的 window,如果您尝试调整它的大小,一些小部件将被隐藏。

解决方案是使用layout managers,确保所有内容都正确显示,并且window永远不会小于其子项所需的大小。

对于这种情况,QVBoxLayout 可能是一个不错的选择。

请注意,编辑 pyuic 生成的文件也被认为是一种不好的做法,因此我建议您在 Designer 中使用 empty main window 重新启动项目,保存UI,用 pyuic 生成文件 agan,然后在主脚本中导入该文件。在以下示例中,我假设您将该文件命名为 mainWindow_ui.py:

from PyQt5 import QtWidgets
from mainWindow_ui import Ui_MainWindow

# ...
# contacts loading code...
# ...

class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(self)

        layout = QtWidgets.QVBoxLayout(self.centralWidget())
        self.contactWidgets = []

        for i, contact in enumerate(Contacts):
            contactWidget = QtWidgets.QWidget()
            layout.addWidget(contactWidget)
            contactLayout = QtWidgets.QHBoxLayout(contactWidget)
            edit = QtWidgets.QLineEdit(contact.first_name)
            pushButton = QtWidgets.QPushButton(f"Edit {i}")
            contactLayout.addWidget(edit)
            contactLayout.addWidget(pushButton)
            self.contactWidgets.append((edit, pushButton))

现在,这里唯一的问题是,如果您有很多联系人,window 会长高。为避免这种情况,您可以使用 QScrollArea,但必须将其添加到中央小部件。您可以直接在 Designer 中执行此操作,因此将 QScrollArea 拖到主要 window,然后右键单击 window 的 empty space 和select 从“布局”子菜单中“垂直布局”,用 pyuic 重建 UI 并更改布局构造函数:

class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(self)

        layout = QtWidgets.QVBoxLayout(self.scrollAreaWidgetContents)
        self.contactWidgets = []

        for i, contact in enumerate(Contacts):
            # same as above
            # ...

        # a "spacer" at the bottom, in case there are not enough contact 
        # widgets to vertically fill the scroll area
        layout.addStretch()

为了更好地理解如何使用 UI 文件(记住,pyuic 文件应该 永远不会 被修改),阅读更多关于关于 using Designer.

的官方指南