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

时间:2014-09-09 04:51:49

标签: c++ qt

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

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

我已经在这里和其他地方阅读过很多关于信号和插槽的故障排除建议(比如这些20 tips)但是我开始怀疑这是使用uitools / uiloader来获取运行时ui元素的独特之处。这是因为主窗口的信号和插槽都能正常工作。

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

图片讲千言万语。

MainWindow.ui

main window, contains a button and a text edit

ChildWidget.ui

child widget, which looks a lot like main window, with an extra label

组合。

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

combined, two child widgets sit inside the main window's Vertical Layout

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

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

程序中隐藏的是带有自定义QObject的Boost :: Thread。其信号每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

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

1 个答案:

答案 0 :(得分:1)

事实证明我犯了一个与Qt无关的基本错误。

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

所有连接都已正确建立,但不会因为意外删除了随播广告素材而导致所有连接无效。

  • 将编译时ui与运行时加载的ui
  • 混合在一起是完全有效的
  • 在运行时生成的信号和插槽的行为与任何其他
  • 相同
  • 仔细管理小部件的生命周期