Qt:访问uitools/uiloader生成的小部件的信号/插槽
Qt: Accessing the signals/slots of a widget generated by uitools/uiloader
我的应用程序显示了一个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混合使用是完全有效的
- 运行时中生成的信号和槽的行为与其他信号和槽相同
- 小心管理小部件的使用寿命
- 如何将点击的信号和插槽添加到qt中的自定义按钮中
- 如何在没有信号的情况下从C++执行QML插槽
- 从Q_INVOKABLE功能或插槽中更改QQuick图像源
- 升压信号2将插槽传递到成员功能以断开连接
- 如何在qt中将信号和插槽与另一个对象连接 --解决了
- C++ 信号和插槽不工作:插槽不响应事件
- NS3 插槽混淆(需要帮助理解)
- QObject::连接无法将信号连接到*this*对象的插槽
- QT 插槽未在主线程上调用
- 通过插槽和信号在不同线程中的两个qt对象之间进行通信
- 控制带有信号/插槽的Qt QML滑动视图
- Qt 信号/插槽问题
- 当对话框中的任何小部件发出信号时,是否可以调用插槽
- 多个小部件的信号和插槽
- 如何在Qt小工具中删除带有插槽的按钮
- 从插槽访问中央小部件时出现Segfault
- Qt:访问uitools/uiloader生成的小部件的信号/插槽
- 动态创建自定义小部件的新实例并连接信号和插槽(Qt)
- Qt:如何为所有小部件和小部件类型实现通用基类信号/插槽功能(通过虚拟基类插槽)
- QT小部件插槽调用没有被单独的线程信号激活