实现 Qt 中的自包含类

73 阅读2分钟

当您想要构建完全自包含的类时,可能会遇到一些问题,可能原因是您缺少一些众所周知的基础知识。考虑以下示例:

class Main_Window (QtGui.QMainWindow):
    def __init__ (self, parent=None):
        QtGui.QWidget.__init__(self, parent)
        self.ui = Ui_bookingSystemMain()
        self.ui.setupUi(self)

        # Connect slots
        QtCore.QObject.connect(self.ui.submitRecord, QtCore.SIGNAL("clicked()"), self.__clickSubmitRecord)
        QtCore.QObject.connect(self.ui.btnListBookings, QtCore.SIGNAL("clicked()"), self.__show_list)

    def __clickSubmitRecord (self):
        global bookings

        name = self.ui.edtName.text()
        event = str(self.ui.comEvent.currentText())
        amount = self.ui.spinBox.value()

        if name == '':
            QtGui.QMessageBox.warning(self, "Error", "Please enter a name!")
        elif amount == 0:
            QtGui.QMessageBox.warning(self, "Error", "You can't reserve 0 tickets!")
        elif event == '':
            QtGui.QMessageBox.warning(self, "Error", "Please choose an event!")
        else:
            bookings.append(Booking(name, event, amount))
            QtGui.QMessageBox.information(self, "Booking added", "Your booking for " + str(amount) + " ticket(s) to see " + event + " in the name of " + name + " was sucessful.")
            self.__clear_widgets()

    def __clear_widgets (self):
        self.ui.edtName.clear()
        self.ui.comEvent.setCurrentIndex(-1)
        self.ui.spinBox.setValue(0)

    def __show_list (self):
        listdialog = List_Window(self)
        listdialog.show()

这个类实现了另一个模块中描述的 UI。clickSubmitRecord() 方法使用全局“booking”列表并向其中添加内容——现在这个类肯定不应该与 UI 之外的任何事情有任何关系?

2. 解决方案

您可以使用以下步骤来实现良好的面向对象方式:

  1. 在窗口对象中定义一个“bookingAdded”信号。
  2. 让外部对象之一(可能是调用 UI 的那个)将槽连接到此信号。
  3. 在 clickSubmitRecord 中触发此信号,并将新的预订数据随信号一起传递给外部对象。

这样,UI 对象就不需要了解任何外部信息,而所有外部对象需要了解的 UI 只是它公开的信号。如果您使用队列连接到信号,这也可能有助于线程安全。

以下是用 C++ 编写的一个示例:

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);

public slots:
    void onBookingAdded(const Booking& booking);

private:
    Ui::MainWindow ui;

    // ...
};

class ExternalObject
{
public:
    ExternalObject();

public slots:
    void onBookingAdded(const Booking& booking);

private:
    // ...
};

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    ui.setupUi(this);

    connect(ui.submitRecordButton, &QPushButton::clicked, this, &MainWindow::onBookingAdded);
}

void MainWindow::onBookingAdded(const Booking& booking)
{
    // Do something with the booking data
}

ExternalObject::ExternalObject()
{
    connect(this, &ExternalObject::bookingAdded, this, &ExternalObject::onBookingAdded);
}

void ExternalObject::onBookingAdded(const Booking& booking)
{
    // Do something with the booking data
}

在这个示例中,MainWindow 类定义了一个“bookingAdded”信号。ExternalObject 类将一个槽连接到此信号。当用户单击添加预订按钮时,onBookingAdded() 方法将在 MainWindow 类中触发,并将新的预订数据随信号一起传递给 ExternalObject 类。这样,MainWindow 类就不需要了解任何外部信息,而 ExternalObject 类只需要了解 MainWindow 类公开的信号。