Qt:访问uitools/uiloader生成的小部件的信号/插槽

Qt: Accessing the signals/slots of a widget generated by uitools/uiloader

本文关键字:小部 插槽 信号 访问 uitools uiloader Qt      更新时间:2023-10-16

我的应用程序显示了一个MainWindow ui,它在运行时获得了新的ui部分。主窗口的ui是在编译时创建的,而新的小部件是在运行时加载的。(qrc内有.ui,还有uiloader)

我的问题是:尽管Qt报告QObject::connect()全部成功,但我无法触发运行时小部件的信号和插槽。

我在这里和其他地方读了很多关于信号和插槽的故障排除建议(比如这20个技巧),但我开始怀疑这是使用uitools/uiloader获取运行时ui元素的独特之处。这是因为主窗口的信号和插槽都可以工作。

我在Windows 7 Professional上使用Qt 5.2.1、Qt Creator 3.0.1。我也使用boost线程,没有QThread。

图片千言万语。

MainWindow.ui

主窗口,包含一个按钮和一个文本编辑

ChildWidget.ui

子窗口小部件,看起来很像主窗口,有一个额外的标签

组合

主窗口垂直布局内的2个小部件

组合在一起,两个子窗口位于主窗口的垂直布局内

在这个程序的例子中,我在主窗口中硬编码了两个子窗口的派生。

我将主窗口的PushButton点击信号连接到test()插槽,该插槽在主窗口的文本框上写入"OH PUSH"。相同的信号连接到所有子窗口的test()插槽,这些插槽应该在其所有文本框上执行相同的操作。什么也没有出现。

程序中隐藏着一个Boost::线程,它带有一个自定义的QObject。其信号每2秒触发一次,并且同样连接到所有test()时隙。只有主窗口显示"OH PUSH"。

我还尝试将Toggle Pause按钮的点击与所有插槽连接起来。在这种情况下,单击时甚至不会触发信号。

目前还没有代码,因为我希望这是一个专业人士一看到就能快速识别的简单问题。如果不是这样的话,我会显示代码。


编辑:在编写包含代码的编辑时,我无意中发现了解决方案。我将在这里展示这篇文章,并发布解释为什么我的程序不起作用。

主要玩家有MainController、MainWindow、ChildController、ChildWidget

/*
main spawns a mainController.
mainController::ctor spawns a QApplication.
mainController::ctor spawns a MainWindow.
The MainWindow spawns its ui.  ( `ui->setupUI( this );` )
// back in main, after spawning MainWindow
main calls mainController's init()
mainController::init() spawns a ChildController.
ChildController::ctor spawns a boost::thread that idles.
mainController::init() spawns a ChildWidget.* // see below for code
mainController::init() connects MainWindow with all ChildWidgets, as well as all signals/slots**. // see below for the code
mainController::init() loops to spawn another pair of ChildController/ChildWidget, if required.
// back in main, after calling mainController's init()
main calls mainController's run()
mainController::run() calls MainWindow's show()
mainController::run() calls (every) ChildController's unpause(), so the boost::threads stop idling and starts emitting signals every 2 seconds.
mainController::run() calls QApplication's exec() and blocks till the end of the program.
*/

*生成ChildWidget。

class ChildWidget : public QWidget
{
    Q_OBJECT
public:
    ChildWidget( QWidget* parent = 0 );
    QWidget* get_pointer_to_ui();
public slots: 
    void slot_push_text( std::string text );
    void slot_push_image( const std::vector< unsigned char > image );
    void slot_test();
private slots:
    void on_buttonPause_clicked();
private:
    QPlainTextEdit* uiText;
    QLabel* uiImage;
    QPushButton* uiPauseButton;
    QWidget* ui;
private:
    QWidget* load_ui(); // called on construct
};
ChildWidget::ChildWidget( QWidget* parent )
    : QWidget( parent )
{
    QWidget* widg = load_ui();
    uiImage = widg->findChild< QLabel* >( "labelImage" );
    uiText = widg->findChild< QPlainTextEdit* >( "textConsole" );
    uiPauseButton = widg->findChild< QPushButton* >( "buttonPause" );
    ui = widg;
    QMetaObject::connectSlotsByName( this );
}
QWidget* ChildWidget::load_ui()
{
    QUiLoader loader;
    QFile file(":/widgets/ChildWidget.ui");
    file.open(QFile::ReadOnly);
    QWidget *widg = loader.load(&file, this);
    file.close();
    return widg;
}

**主控制器连接所有信号和插槽。

void MainController::init()
{
    //FIXME. hard code to spawn two for now.
    for( int i = 0; i < 2; ++i )
    { // routine spawning the child controllers
        /* **** construct the child controllers, which internally contains a small module that emits sigs **** */
        boost::shared_ptr< ChildController > v1 = boost::shared_ptr< ChildController >( new ChildController );
        /* **** each child controller is to have 1-1 corresponding ui element. **** */
        /* We shall spawn ONE ChildWidget for each ChildController.
         * Each ChildWidget itself contains pointer to a widget generated via runtime uic.
         * This widget needs to be passed to The Main Controller so it can be added to its layout.
        */
        boost::shared_ptr< ChildWidget > newWidget = boost::shared_ptr< ChildWidget >( new ChildWidget( w.get() ) );
        // a new ChildWidget is created, whose parent is defined to be the MainWindow, w
        // the main window will now attempt to incorporate the new widget in its layout
        QWidget* newWidgUi = newWidget->get_pointer_to_ui(); // dangerous? returns a pointer to the ui loaded with `loader.load(&file, this)` above
        this->w->add_view( newWidgUi ); // calls MainWindow's VerticalLayout addWidget()

        // all signals and slots are to be connected next.
        if( newWidget != 0 )
        {
            // if main window button pressed, send text to this ChildWidget's text window
            bool isTest = QObject::connect( w.get(), &MainWindow::sig_test,
                                          newWidget.get(), &ChildWidget::slot_push_text );
            // if main window button pressed, send text to the main window's text window.
            QObject::connect( w.get(), &MainWindow::sig_test,
                                          w.get(), &MainWindow::slot_test );
            // if the boost::thread signals, deliver some text to this ChildWidget
            QObject::connect( v1->widget.get(), &ChildController::sig_push_text,
                              newWidget.get(), &ChildWidget::slot_push_text );
            // if this child window's button is pressed, show some text on the main window.
            QObject::connect( v1->widget.get(), &ChildController::sig_push_text,
                         this->w.get(), &MainWindow::slot_test );
            this->vControllers.push_back( v1 );
        }
    }
    // all the connections above succeed.
    // but this is where my problem was.
    // newWidget was not stored beyond this function, so boost::shared_ptr destroys it
}

其他注意事项:我在MainController.cpp 中完成了以下操作

Q_DECLARE_METATYPE( std::string )
Q_DECLARE_METATYPE( std::vector< unsigned char > )

并添加

qRegisterMetaType< std::string >( "std::string" );
qRegisterMetaType< std::string >( "std::vector< unsigned char >" );

在MainController的ctor中。

我把这作为main()中的第一行

Q_INIT_RESOURCE( dynwidgets ); // dynwidgets.qrc contains the ChildWindow's .ui file

起初,我把代码放在这里,但它变得非常混乱,所以现在它只是感兴趣区域的一个片段,其余部分是描述性文本

原来我犯了一个与Qt无关的基本错误。

当通过QUiLoader生成ui元素时,我附带了一个配套小部件的生成。这个小部件用于从ui发出信号,并在触发其插槽时对ui进行更改。

所有连接都已正确建立,但不会因为附带的小部件被意外删除而触发,从而使所有连接无效。

  • 将编译时ui与运行时加载的ui混合使用是完全有效的
  • 运行时中生成的信号和槽的行为与其他信号和槽相同
  • 小心管理小部件的使用寿命