从库 (DLL) 函数访问 ui 指针
Posted
技术标签:
【中文标题】从库 (DLL) 函数访问 ui 指针【英文标题】:Access ui pointer from a library (DLL) function 【发布时间】:2014-11-11 13:55:02 【问题描述】:大家好,我想从非成员函数更新 ui。除了将“this”指针作为我的非成员传递之外的任何帮助都是来自库的回调。
下面是我的代码:
主窗口.cpp
static void callback(QString result)
ui->textBrowser->append(result);
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
ui->setupUi(this);
MainWindow::~MainWindow()
delete ui;
void MainWindow::on_pushButton_clicked()
int a=1,b=2;
QLibrary myLib("myaddlib");
myLib.load();
add = (myadd)myLib.resolve("add_function");
add(callback, a, b);
所以我只需要能够从我的非成员回调中将数据附加到 UI。请帮帮我。
编辑:我不允许修改我的回调
根据评论进行编辑:callback
将在不同的线程中调用,因此从那里调用小部件方法会产生错误 'cannot send events to objects owned by a different thread'
【问题讨论】:
确保只从 UI (=main) 线程更新 UI。 嗨@JohannesS。我同意你的观点,但是对于我想从库回调更新 UI 的情况有什么解决方案? 可以修改回调签名吗?对于回调,能够传递一个额外的指针通常会很有帮助(通常是void*
,然后需要适当地转换)。
Manipulating QT Ui with different source files的可能重复
@MiyazawaKenji 好友不能设置为会员。但是我尝试创建全局指针 Ui::MainWindow *myui 并在构造函数中将 myui 分配给 ui 但它引发以下错误:'无法将事件发送到不同线程拥有的对象'。
【参考方案1】:
看你的评论
"我试图创建全局指针 Ui::MainWindow *myui 并分配 myui 到 ui 在构造函数中,但它抛出了以下错误: '不能向不同线程拥有的对象发送事件'。”
问题是,您的callback
在错误的线程中被调用。这是一个解决方案:
创建指向实际主窗口实例的全局指针,我们称之为MainWindow *mainWinInstance;
。另请注意,主窗口实例的寿命将比回调所针对的库/线程长,或者如果存在即使在主窗口被破坏后仍会调用callback
的风险,请使用QPointer
。
给MainWindow
添加如下槽方法:
MainWindow::appendText(const QString &text) // use const ref for efficiency
ui->textBrowser->append(result);
更改您的callback
以使用QMetaObject::invokeMethod
使用排队连接类型调用该方法:
static void callback(QString result)
bool r = QMetaObject::invokeMethod(mainWinInstance,
"appendText",
Qt::QueuedConnection,
Q_ARG(QString, result));
Q_ASSERT(r); // should only fail if there's a mistake in above code
使用Qt::QueuedConnection
很重要。它将方法调用放入目标对象的正确线程的事件队列中并立即返回。然后目标线程的事件循环将执行实际调用。
【讨论】:
嗨@hyde 我尝试了你的代码,但我收到了 r 的断言错误。不知道为什么它会失败我的意思是即使我调试时也没有其他错误消息。有什么进一步的帮助吗? @NaveenkumarKattarathanaiah 那么你的问题出在其他地方。这是一个工作示例:pastebin.com/64bM7qXX 你确定appendText
是可调用的方法吗?您是否记得在 .h 文件中使用 Q_OBJECT
宏,并运行 qmake,以及使 Qt 元对象系统工作所需的所有其他东西?
hi @hyde Q_OBJECT 宏存在,我运行 qmake 还是一样。它说“QObject::killTimers:定时器不能从另一个线程停止”。有什么帮助吗?而我的 'appendText' 是 Mainwindow 类中的一个公共成员函数。【参考方案2】:
你需要在单独的header中创建一个接口:
itextbrowseraccessor.h
class ITextBrowserAccessor
public:
void appendText(const QString& text) = 0;
从ITextBrowserAccessor
继承MainWindow
并实现方法:
void MainWindow::appendText(const QString& text)
ui->textBrowser->append(text);
在您的库源文件中包含itextbrowseraccessor.h
。将指向接口的指针传递给回调。
static void callback(ITextBrowserAccessor* accessor, QString result)
accessor->appendText(result);
【讨论】:
嗨@Ezee 感谢您的快速回复,根据您的解决方案,我必须修改我的库调用 add_function 以接受一个我不允许的参数。任何其他解决方案。 因此,您需要在问题中指定您还有哪些其他限制。你能修改什么? add_function 的主体?主窗口?在库中为接口创建一个 setter 怎么样?【参考方案3】:您可以尝试使用QApplication::topLevelWidgets()
查找主窗口。
找到主窗口后,使用mainWindow->findChild<QTextBrowser*>()
找到浏览器小部件,然后根据需要修改其内容。
编辑: 示例:
Q_ASSERT(QApplication::topLevelWidgets() == 1);
QWidget* mainWindow = QApplication::topLevelWidgets().first();
QTextEditor* editor = mainWindow->findChild<QTextBrowser*>();
Q_ASSERT(editor != NULL);
editor->append(QLatin1String("abc"));
【讨论】:
QWidget *mywidget; mywidget = QApplication::topLevelWidgets(); mywidget->mainwindow->ui->textBrowser->append("something"); @Ezee 在上面尝试过,但没有成功。您能否提供一些正确的示例代码如何获取主窗口。提前致谢。 嗨@Ezee 我收到断言错误。而且我的主窗口中有多个文本浏览器,我如何选择一个特定的?编辑:如果我评论该断言行并在下面调试是错误我得到“下级停止,因为它触发了异常”。这里有什么帮助吗? 发生了哪个断言?如果是第一个,请查看topLevelWidgets
返回的内容。要选择正确的编辑器,您可以使用findChild
的参数:findChild<QTextBrowser*>(objectName)
其中objectName
可以设置为Qt Designer
或代码至setObjectName
中的编辑器。
'topLevelWidgets' 返回主窗口地址,直到这里一切都很好。现在当我做这个 QTextEdit* editor = mainWindow->findChild这里提到的所有事情都对我不起作用。 通过创建一个具有向主窗口发送信号的公共功能的虚拟 Qobject 来解决它。创建这个虚拟 qobject 的实例并在我的回调中调用公共函数。
PS:我有一个限制,不能修改我的回调,因此上述解决方案。
【讨论】:
以上是关于从库 (DLL) 函数访问 ui 指针的主要内容,如果未能解决你的问题,请参考以下文章
如何从 MSVC 中的 libsndfile-1.dll 访问函数?
在具有 ui/非 ui 线程差异的 WPF 中使用 PInvoke