1

hi all i want to update ui from a non member function. Any help other than passing 'this' pointer as my non member is a callback from library.

Below is my code :

mainwindow.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);
}

So all I need is I should be able to append data to UI from my non member callback. Please help me.

EDIT: i am not permitted to modify my callback

EDIT based on comment: callback will be called in a different thread, so calling a widget method from there gives error 'cannot send events to objects owned by a different thread'

hyde
  • 60,639
  • 21
  • 115
  • 176
  • Be sure to only update the UI from the UI (=main) thread. – Johannes S. Nov 11 '14 at 14:12
  • hi @JohannesS. I agree with your point but any solution for my case where i want to update UI from a library callback? – Naveen kumar Katta rathanaiah Nov 11 '14 at 14:32
  • Can you modify the callback signature? For callbacks, it's usually helpful to be able to pass an additional pointer (usually `void*` that then needs to be cast appropriately). – Johannes S. Nov 11 '14 at 14:39
  • possible duplicate of [Manipulating QT Ui with different source files](http://stackoverflow.com/questions/9694474/manipulating-qt-ui-with-different-source-files) – BaCaRoZzo Nov 11 '14 at 14:46
  • Probably your answer is [here](http://stackoverflow.com/questions/9694474/manipulating-qt-ui-with-different-source-files). Friend keyword is the way. – BaCaRoZzo Nov 11 '14 at 14:47
  • `Ui::MainWindow * ui` can be a public member, or you can use the `friend` keyword –  Nov 12 '14 at 02:46
  • 1
    @MiyazawaKenji friend cannot be set to a member. However i tried to create global pointer Ui::MainWindow *myui and assigned myui to ui in the constructor but it throws me following error : 'cannot send events to objects owned by a different thread'. – Naveen kumar Katta rathanaiah Nov 12 '14 at 06:40
  • Based on comment, this question has `callback` call in a different thread, so linked question is not a duplicate. – hyde Nov 13 '14 at 21:57

4 Answers4

2

Going by your comment

"i tried to create global pointer Ui::MainWindow *myui and assigned myui to ui in the constructor but it throws me following error : 'cannot send events to objects owned by a different thread'."

Problem is, your callback gets called in the wrong thread. Here's one solution:

  1. Create global pointer to the actual main window instance, let's call it MainWindow *mainWinInstance;. Also be careful that the main window instance will live longer than the library/thread for which the callback is for, or use QPointer if there's risk that callback gets called even after the main window was destructed.

  2. Add following slot method to MainWindow:

    MainWindow::appendText(const QString &text) { // use const ref for efficiency
        ui->textBrowser->append(result);
    }
    
  3. Change your callback to invoke that method using QMetaObject::invokeMethod using queued connection type:

    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
    }
    

    Using Qt::QueuedConnection is impotant. It will place the method invocation in the event queue of the correct thread of the target object and return immediately. The event loop of the target thread will then do the actual call.

hyde
  • 60,639
  • 21
  • 115
  • 176
  • hi @hyde i tried ur code and i am getting assert error for r. Got no clue why it fails i mean no other error messages even when i debug. Any further help? – Naveen kumar Katta rathanaiah Nov 14 '14 at 09:21
  • @NaveenkumarKattarathanaiah Then your problem is somewhere else. Here's a working example: http://pastebin.com/64bM7qXX – hyde Nov 18 '14 at 15:03
  • Are you certain `appendText` is invokable method? Did you remember to use `Q_OBJECT` macro in the .h file, and run *qmake*, and all the other stuff needed to make Qt meta object system work? – hyde Nov 18 '14 at 15:04
  • hi @hyde Q_OBJECT macro is present and i had run qmake still the same. It says "QObject::killTimers: timers cannot be stopped from another thread". Any help? And my 'appendText' is a public member function in Mainwindow class. – Naveen kumar Katta rathanaiah Nov 27 '14 at 10:33
1

You need to create an interface in a separate header:

itextbrowseraccessor.h

class ITextBrowserAccessor
{
public:
  void appendText(const QString& text) = 0;
}

Inherit MainWindow from ITextBrowserAccessor and implement the method:

void MainWindow::appendText(const QString& text)
{
   ui->textBrowser->append(text);
}

Include itextbrowseraccessor.h in your library source file. Pass a pointer to the interface to the callback.

static void callback(ITextBrowserAccessor* accessor, QString result) 
{
  accessor->appendText(result);
}
Ezee
  • 4,214
  • 1
  • 14
  • 29
  • hi @Ezee thanks for ur quick reply, as per your solution I have to modify my library call add_function to accept one more perameter which I am not permitted. Any other solution. – Naveen kumar Katta rathanaiah Nov 11 '14 at 14:29
  • So you need to specify in your question what other restrictions do you have. What can you modify? Body of add_function? MainWindow? What about creating a setter for the interface in the library? – Ezee Nov 11 '14 at 14:40
0

You can try to find the main window using QApplication::topLevelWidgets().

Once you have found the main window, use mainWindow->findChild<QTextBrowser*>() to find the browser widget and then modify its content as you need.

EDIT:
example:

Q_ASSERT(QApplication::topLevelWidgets() == 1);
QWidget* mainWindow = QApplication::topLevelWidgets().first();
QTextEditor* editor = mainWindow->findChild<QTextBrowser*>();
Q_ASSERT(editor != NULL);
editor->append(QLatin1String("abc"));
Ezee
  • 4,214
  • 1
  • 14
  • 29
  • QWidget *mywidget; mywidget = QApplication::topLevelWidgets(); mywidget->mainwindow->ui->textBrowser->append("something"); @Ezee itried above but didnot work out. Can you provide some correct sample code how to get mainwindow. Thanks in advance. – Naveen kumar Katta rathanaiah Nov 12 '14 at 07:03
  • hi @Ezee i am getting assertion error. And moreover i am having multiple text browsers in my mainwindow, how do i get to choose one particular? EDIT: If i comment that assert line and debug below is the error i m getting "the inferior stopped because it triggered an exeption". Any help here? – Naveen kumar Katta rathanaiah Nov 12 '14 at 09:56
  • Which assert happens? if the first one, look what is returned by `topLevelWidgets`. To choose a right editor you can use an argument of `findChild`: `findChild(objectName)` where `objectName` can be set to an editor in `Qt Designer` or in code wich `setObjectName`. – Ezee Nov 12 '14 at 09:58
  • 'topLevelWidgets' returns mainwindow address, things ar fine until here. Now when i did this QTextEdit* editor = mainWindow->findChild(readtag_textBrowser); it throws compiler error 'readtag_textBrowser' is undeclared identifier. 'readtag_textBrowser' is my object name where i want to edit. – Naveen kumar Katta rathanaiah Nov 12 '14 at 10:04
  • hi @Ezee getting error "cannot send events to objects owned by a different thread". So Qt is running my callback in different thread and there is no way I can access ui? – Naveen kumar Katta rathanaiah Nov 12 '14 at 10:41
  • Looks like the code in the library is executed in a different thread. I can't move futher without having a working sample. – Ezee Nov 12 '14 at 10:47
0

None of the things worked for me which were mentioned here. Solved it by creating a dummy Qobject which has a public function to send signal to mainwindow. Create an instance of this dummy qobject and call the public function inside my callback.

PS: I have a limitation not to modify my callback hence the above solution.