【问题标题】:Qt: Accessing the signals/slots of a widget generated by uitools/uiloaderQt:访问由 uitools/uiloader 生成的小部件的信号/插槽
【发布时间】:2014-09-09 04:51:48
【问题描述】:

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

我的问题:尽管 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 click 信号连接到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

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

【问题讨论】:

  • 如果您展示了子小部件的创建和信号/插槽连接,也许会很好。

标签: c++ qt


【解决方案1】:

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

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

所有连接均已正确建立,但不会仅仅因为伴随小部件被意外删除而触发,从而使所有连接无效。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-10-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-11
    • 2016-10-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多