如何在关闭的QDialog和新打开的QMainWindow之间共享对象



我有一个应用程序,它为某个流打开一个QDialog,当它关闭时,QMainWindow就会打开。

在QDialog中,我创建了一些对象,希望将其作为指向QMainWindow的指针(或其他方式)传递。例如,我创建了一个SysTray对象,该对象需要从QMainWindow更改其状态。最好的方法是什么?单身汉?

更新
在实现了sulotion之后,另一个问题是rises,QDialog是否从内存中清除,也就是说,调用了它的析构函数?我认为情况并非如此。我必须进行一些对象清理,那里的析构函数从未调用

一种方法是将QMainWindow作为父级在堆上进行分配。QObject层次结构将负责释放内存,并且您将在QMainWindow的生存期内访问该对象。

如果在任何时候您确信不再需要QDialog或共享对象,则可以调用deleteLater。

示例:

class MyDialog : public QDialog
{
    Q_OBJECT
    QObject* _objToShare;
public:    
    QObject* objToShare() const { return _objToShare; }
    MyDialog(QObject* parent = 0) : QDialog(parent)
    {
        _objToShare = new QObject(this); // either use this as the parent
                                         // or free by hand in the destructor
    }
    // ...
 };
 class MyWindow : public QMainWindow
 {
     Q_OBJECT
     // ...
     QObject* ptrToSharedObj; // alternatively you can hold a pointer
                              // to your MyDialog instance
 };

你在哪里使用你的QDialog:

 MyDialog* d = new MyDialog(this);
 d->exec();
 ptrToSharedObj = d->objToShare();
 // don't delete d explicitly, the QObject hierarchy will take care of that

一种更好的(可能对内存更友好)方法是使用共享指针实现。Qt有多种智能指针类,其中之一就是QSharedPointer。它基本上是一个线程安全的、引用计数的指针,这意味着如果你获取一个指向QDialog的共享对象的指针,只要有其他QSharedPointer指向它,它就不会被释放。请记住,如果你不小心,这可能会导致循环引用。

示例:

class MyDialog : public QDialog
{
    Q_OBJECT
    QSharedPointer<QObject> _objToShare; 
                                 // ^ don't delete in the destructor
public:
    QSharedPointer<QObject> objToShare() const { return _objToShare; }
    MyDialog(QObject* parent = 0) : QDialog(parent)
    {
        _objToShare = QSharedPointer<QObject>(new QObject); 
        // no parent to avoid destruction when the dialog is destructed
    }
    // ...
 };
 class MyWindow : public QMainWindow
 {
     Q_OBJECT
     // ...
     QSharedPointer<QObject> ptrToSharedObj;
 };

你在哪里使用你的QDialog:

MyDialog d;
d.exec();
ptrToSharedObj = d.objToShare(); 

从这一点开始,即使d超出范围(即销毁),您仍然会有一个指向共享数据的有效指针(当然,除非您明确地做了一些使其无效的事情)。

您有很多不同的选项。其中一些可能不适用于您的情况:

情况1:呼叫者最清楚(即程序性编程)

定义一个可以控制排序的控制器。然后,该控件可以充当中介,并在对话框关闭后获取数据:

void FlowController::displayFlow()
{
  MyDialog *dialog = new MyDialog();
  YourSharedData *data = NULL;
  int result = dialog->exec();
  if (result == QDialog::Accepted) {
    data = dialog->sharedDataAccessor();
  }
  MyMainWindow *window = new MyMainWindow();
  window->setSharedData(data);
  window->exec();
}

案例2:消息传递

在前面创建对话框和主窗口。连接相应的信号和插槽,让它们彼此都不知道。这通常更容易测试和保持解耦:

void AppLauncher::launch()
{
  MyDialog *dialog = new MyDialog();
  MyMainWindow *window = new MyMainWindow();
  window->connect(
    dialog,
    SIGNAL(dialogResult(int, const SharedData&)),
    SLOT(setDialogResult(int,const SharedData&))
    );
}
void MyMainWindow::setDialogResult(int result, const SharedData& sharedData)
{
  if (result == Dialog::Accepted) // something domain-specific would be better
  {
    this->processSharedData(sharedData); // do whatever with it.
  }
}

案例3:共享状态依赖

预先定义您的共享数据,并使其成为每个表单的依赖项。然后,每个表单定义状态,根据该状态采取行动,或两者的组合:

void AppLauncher::launch()
{
  SharedData *data = this->createSharedData();
  MyDialog *dialog = new MyDialog(data);
  MyMainWindow *window = new MyMainWindow();
  dialog->exec(); // does what it needs with shared data
  window->exec(); // checks shared data and acts accordingly
}

案例4:MVC/MVP/MVM

您可以将您的共享状态定义为一个模型,它们都可以根据该模型进行操作。这可能与上面的第三种情况没有太大区别。

案例4:辛格尔顿或全球状态

它会起作用,但请不要这样做:)。

在QDialog中,您有一个所有者,可能是QMainWindow。(如果没有,您可以在那里添加它,或者获取父对象,直到您进入QMainWindow类)。您可以在创建新对象时参考它。。。

您可以将QApplication子类化,使其保持指向您的systray对象或其他应该在"应用程序范围"内可用的对象的指针。

在Qt应用程序中,只有一个QApplication实例,可以使用qApp宏在任何地方使用。

请参阅此邮件列表讨论,其中还包含一个示例。

最新更新