I have a GUI which I want to test with QTest.
I'm planning on having a separate thread which executes QTest::keyClick() test steps but I want to synchronize my test steps with QWidget::repaint() i.e. I want each QTest::keyClick() to happen only after the repaint (if any) caused by the repaint from the previous QTest::keyClick()
QTest::keyClick()
Repaint from step 1
QTest::keyClick()
Repaint from step 2
I do not want
QTest::keyClick()
QTest::keyClick()
Repaint from step 1
Repaint from step 2
QUESTION
How to make sure keyClick()s do not occur before paint event from previous keyClick()?
No need for synchronization: each call to keyClick() results in a call to qApp->processEvents()
static void simulateEvent(QWindow *window, bool press, int code,
Qt::KeyboardModifiers modifier, QString text, bool repeat, int delay=-1)
{
QEvent::Type type;
type = press ? QEvent::KeyPress : QEvent::KeyRelease;
qt_handleKeyEvent(window, type, code, modifier, text, repeat, delay);
qApp->processEvents();
}
which would trigger any paint events before the next QTest.keyClick().
Related
My goal is to show a brief "Please Wait..." dialog with an animated gif (spinner) in a Gtk::Dialog.
My problem is that when I do not use Gtk:Dialog::run(), the gif won't be animated, and when I do use the Gtk:Dialog::run() method it completely blocks my running code afterwards. And since I don't have any buttons in my dialog it would hang there indefinitely. Is there a way around that? I have had no success in getting the animated gif to work in a non-modal dialog, i.e without using the run() method.
I'm using gtkmm 3.0
Compile with : g++ examplewindow.cc main.cc -o main `pkg-config gtkmm-3.0 --cflags --libs`
main.cc
#include "examplewindow.h"
#include <gtkmm/application.h>
#include <iostream>
int main(int argc, char *argv[])
{
auto app = Gtk::Application::create("org.gtkmm.example");
ExampleWindow window;
//Shows the window and returns when it is closed.
//return app->make_window_and_run<ExampleWindow>(argc, argv);
return app->run(window);
}
examplewindow.h
#ifndef GTKMM_EXAMPLEWINDOW_H
#define GTKMM_EXAMPLEWINDOW_H
#include <gtkmm.h>
class ExampleWindow : public Gtk::Window
{
public:
ExampleWindow();
virtual ~ExampleWindow();
protected:
//Signal handlers:
void on_button_clicked();
//Child widgets:
Gtk::Box m_VBox;
Gtk::Box m_ButtonBox;
Gtk::Button m_Button;
};
#endif //GTKMM_EXAMPLEWINDOW_H
examplewindow.cc
#include "examplewindow.h"
#include <iostream>
ExampleWindow::ExampleWindow()
: m_VBox(Gtk::Orientation::ORIENTATION_VERTICAL),
m_ButtonBox(Gtk::Orientation::ORIENTATION_VERTICAL),
m_Button("Show Dialog")
{
set_title("Test animated gif");
set_default_size(800, 600);
add(m_VBox);
m_VBox.pack_start(m_ButtonBox);
m_ButtonBox.pack_start(m_Button);
m_Button.set_hexpand(true);
m_Button.set_halign(Gtk::Align::ALIGN_CENTER);
m_Button.set_valign(Gtk::Align::ALIGN_CENTER);
m_Button.grab_focus();
m_Button.signal_clicked().connect(sigc::mem_fun(*this, &ExampleWindow::on_button_clicked));
show_all_children();
}
ExampleWindow::~ExampleWindow()
{
}
void ExampleWindow::on_button_clicked()
{
Gtk::Dialog m_Dialog;
m_Dialog.set_transient_for(*this);
m_Dialog.set_size_request(200, 200);
m_Dialog.set_decorated(false);
Gtk::Image imageLoading = Gtk::Image();
imageLoading.property_pixbuf_animation() = Gdk::PixbufAnimation::create_from_file("gtkmm_logo.gif");
m_Dialog.get_vbox()->pack_start(imageLoading);
m_Dialog.show_all();
m_Dialog.run();
/******** This, below, never gets executed as run() is blocking the program...********/
// Dummy "long" operation
for (int i = 0; i <= 2010101010; i++)
{
if (i == 2010101010)
std::cout << "Done" << std::endl;
}
m_Dialog.response(Gtk::RESPONSE_ACCEPT);
m_Dialog.hide();
}
Let us look at the original problem. You created a dialog, called show() on it, did some long-running process, then closed the dialog. The process worked, but your program froze during the processing. Why is that?
A graphical interface works by processing messages (events). Some events run off a timer, such as the ones that tell an animation to go to the next frame. Some are generated as needed, such as the ones that tell an image to draw the current frame. These events need to be both triggered and processed to be effective. You triggered the appropriate events with your call to show_all(), but you did not give your program a chance to handle those events.
You used a button click to start your long-running process. That click is an event that was handled by your main event handling loop. That loop then waited for the click to be fully handled before moving on to the next event. However, you have your long-running process in the handler. The main event loop had to wait for that process to finish before it could handle new events, such as the ones to show and animate your image. You never gave your dialog a chance to do its job before you destroyed it.
Calling the dialog's run() method partially fixed the situation by starting a new event loop for the dialog. So even though the main event loop was still blocked by your click handler, new events could be handled. The dialog's event loop received the events required to show an animation, hence your program was again responsive. Unfortunately, run() blocked your long-running process, so we're not really any better off.
The simplest fix is to no longer completely block your main event loop. You could have your long-running process periodically allow events to be processed via Gtk::Main::iteration(). This function invokes an iteration of the main event loop, allowing your program to stay responsive. Pass it a false argument so that it only processes events if there are some to process (rather than waiting for an event to occur).
for (unsigned long i = 0; i <= 2010101010; i++)
{
if (i == 2010101010)
std::cout << "Done" << std::endl;
// Periodically process events
if ( i % 10000 == 0 ) // <---- after some suitable amount of work
if ( !Gtk::Main::iteration(false) ) // <---- allow events to be processed
// Abort the work.
break;
}
The return value is supposed to tell you if you should quit or not, but I didn't get this working in my test (and the return value seemed to have the opposite meaning compared to the documentation). Maybe the dialog itself was keeping the app alive? Eh, that can be the next question, once this part is working.
Other approaches would move your long-running process out of the click handler. If you let the click handler end quickly, the main event loop can do its job without the extra prompting from you. However, this requires a few adjustments so that the Gtk::Dialog outlives the call to on_button_clicked(). That's a bit of refactoring, but it might be worth the time. I'll present two options (without code).
You could have your work operate on multiple timeout signals. Divide your long-running process into smaller chunks, each chunk suitably sized for a callback. (How big is that? Not sure. For now, let's say at most a few milliseconds.) Have the button click event start the first timeout signal with a priority that allows the GUI to update. (As I recall, PRIORITY_DEFAULT_IDLE should work.) For the interval, I would try 0 if that does not overly confuse Gtk+. (I have not tried it, but it seems plausible.) If the 0-interval works, it might be wise to use connect_once() instead of connect(), and have each chunk schedule the next with another timeout. The final chunk would be responsible for closing the dialog.
You could move your long-running process to another thread. Multi-threaded programming has its own set of problems and sometimes a lot of setup, but this is something it is well-suited for. If your long-running process is in a different thread than your main event loop, the operating system becomes responsible for making sure each thread gets some CPU time. Your long-running process can chug away, and the main event loop would simultaneously be able to process events with no special intervention from you.
Final notes:
If your dialog is for one-way communication to the user, it seems more like a monologue than a dialogue. Excuse me, more like an ordinary window than a dialog. Also, I'll make sure you are aware of Gtk::ProgressBar, which "is typically used to display the progress of a long running operation." Just an option; preferring your image is understandable.
In my current project I need to perform some calculations when a specific button is pressed, and while I perform these calculations, I want to show a Gtk::MessageDialog that simply states that calculations are being performed. So, I initialize the MessageDialog like this (for the moment just ignore that I actually don't need the pointer here):
Gtk::MessageDialog *waitdialog;
Gtk::MessageDialog dia("Processing", false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_NONE, true);
dia.set_title("Wait.");
dia.set_transient_for(*(Gtk::Window *)this);
waitdialog = &dia;
Next I want to start a separate thread with the dialog:
std::thread dialog_thread(wait_dialog,waitdialog);
The wait_dialog method is defined as follows:
void wait_dialog(Gtk::MessageDialog *dialog){
dialog->run();
}
The problem now is, that even though the main window is darkened (because of set_transient_for), the message dialog is not visible. However, when I don't start a seperate thread, but just call waitdialog->run() instead, this will show the dialog properly (but will result in a loop).
So, the question is: why does the workaround with the separate thread not work? I can't make any sense of that :-(
GUI components are required to stay in the GUI loop. Your long running calculations belong in the thread. The calculation thread then signals back to the GUI thread to close the modal dialog. Also, you should use glib threads instead of std::threads. Here's how I would structure program:
// in header, member var dispatcher used to signal GUI thread
// and our member var thread
Glib::Dispatcher m_signalDone;
Glib::Thread* m_someThread;
...
// in constructor, hook up dispatcher event
m_signalDone.connect(sigc::mem_fun(this, &MyClass::OnDone));
...
// later when ready to kick off thread...
// show dialog or progess bar or something and kick off thread...
m_someThread = Glib::Thread::create(sigc::mem_fun(*this, &MyClass::CalcMethod), true);
...
void MyClass::CalcMethod()
{
// do your long running stuff...
// when done signal completion back to GUI
m_signalDone.emit();
}
...
void MyClass::OnDone()
{
// clean up dialog or progress bar or whatever
// kill thread
m_currentBackUpThread->join();
m_currentBackUpThread = NULL;
}
i receive screenshot as bitmap from socket and when i show only one of them it work
but when i put in loop ( show all receive image to make movie) i get hang
void ShowImageBuffer(char* buf,int sizeofimagebuffer )
{
QByteArray byte=QByteArray::fromRawData(buf, sizeofimagebuffer );
QPixmap image;
if(image.loadFromData(byte,"BMP"))
{
ui->label->setPixmap(image);
ui->label->update();
}
}
while(1)
{
ShowImageBuffer(buf, sizeofimagebuffer)
}
i must use separate thread?( but i think we will not use any thread to change GUI?)
what is best to make it real time?
The problem I suppose is that you're not returning to the event loop this way. The update() method you're using doesn't repaint the QWidget immediately. It schedules a request to update the area, which is unified with other pending requests if any is available. This request is processed when the execution returns to the event loop. It is clearly stated in the documentation.
You might want to use a QTimer and invoke the ShowImageBuffer method at a specific frame rate. When the ShowImageBuffer is finished, execution returns to the event loop thus giving the time to process the update() request. Also consider the improvement suggested by AJG85.
Otherwise you can have a look at the repaint() method which immediately invokes the paintEvent() method, but still I suppose you should set a specific frame rate to get a good result. I would go with the QTimer way.
I've a dialog displaying progress bar + some other data, and I also have a cancel button on this dialog. While this dialog is displayed there is potentially heavy computation going on, which is show on progress bar. This computation is started from withing this dialog code so I have:
Counting_Progress_Dialog::Counting_Progress_Dialog(QWidget *parent) :
QDialog(parent)
{
setupUi(this);
thread_ = new Threaded;//THIS IS THE THREAD IN WHICH COMPUTATION IS BEING PERFORMED
connect(thread_,SIGNAL(counter_value(int)),this,SLOT(update_progress_bar(int)));
connect(this,SIGNAL(rejected()),thread_,SLOT(terminate()),Qt::QueuedConnection);//
HERE I'M CONNECTING REJECTED ON DIALOG TO TERMINATE ON THREAD
}
void Counting_Progress_Dialog::start()
{
thread_->start(QThread::LowestPriority);
}
and I do invoke this in part of the program:
void My_Class::dummy_()
{
auto old_priority = this->thread()->priority();
this->thread()->setPriority(QThread::HighestPriority);
Counting_Progress_Dialog progress;
progress.start();//this will start thread
progress.exec();//this will enter it's event loop
progress.wait();//this will wait until thread is finished
this->thread()->setPriority(QThread::NormalPriority);
}
But despite all this, when I press cancel on my dialog, the whole application freezes. What am I doing wrong? How to make it behave correctly?
UPDATED:
void Counting_Progress_Dialog::wait()
{
thread_->wait();
}
I see that you are connecting using 2 different strategies. But if thread_ and this(counting dialog) are really within two separated threads then the connection will always be Qt::QueuedConnection. Well that's not the issue.
progress.exec();//this will enter it's event loop
Calling exec() suspend the execution of dummy_() until the dialog have to return. And when the dialog return your thread is terminated. So I don't see the purpose of
progress.wait();//this will wait until thread is finished
By the way which function is that? the only one I know is Qthread::wait(). I am pretty confident the issue is here...
edit:
progress.wait() is not the issue... But it is possible that the events sent by the thread are causing trouble in some way. Use the debugger or some qDebug() to see if update_progress_bar is called after you push cancel.
In my GUI, I have a list store treeview in my main window. When the user double clicks a row, a dialog pops up. The problem is that the data I populate in the dialog box takes a while to process, so what I've done is started a thread (using boost threads) to do the dialog box calculations.
In main:
.......
g_signal_connect (G_OBJECT (m_treeview), "row_activated", G_CALLBACK (m_row_activated),
(gpointer) main_window);
.......
In m_row_activated:
.........
// combo_box and dialog are GtkWidget* global variables
create_dialog(dialog, combo_box); // function creates the combobox
set_combo_box_with_loading_message;
gtk_widget_show_all (dialog);
thread m_thread (bind (&do_dialog_calculations, data1, data2, combobox));
.........
In do_dialog_calculations:
.........
// do_calculations takes about 15 seconds to complete
do_calculations(MyData data1, MyData data2, combobox);
gdk_threads_enter();
gtk_combo_box_append_text(...);
gdk_threads_leave()
Everything works fine (i.e. when the user double clicks a row, a dialog pops up immediately with a loading message and it is populated eventually when the thread returns), but my problem is when the user closes the dialog before do_calculations in do_dialog_calculations completes. If the dialog is destroyed, my combobox within it will be destroyed and my call to gtk_combo_box_append_text will seg fault.
I tried to test the combo box before updating it:
In do_dialog_calculations:
.........
do_calculations(MyData data1, MyData data2, combobox);
gdk_threads_enter();
if (GTK_IS_COMBO_BOX (combobox))
gtk_combo_box_append_text(...);
gdk_threads_leave()
but this results in a deadlock at the call to GTK_IS_COMBO_BOX. I think this is beause GTK_IS_COMBO_BOX probably calls gdk_threads_enter(). I've also tried testing for NULL
if (combobox == NULL)
but that doesn't seem to work either. Any suggestions on how to get around this problem?
UPDATE: The deadlock at GTK_IS_COMBO_BOX only occurs if I close the dialog immediately after it opens (i.e. before do_calculations() completes. If I just let the dialog sit, it will eventually update. Also, if I switch the combobox check before writing calling gdk_threads_enter():
if (GTK_IS_COMBO_BOX (combobox)
{
gdk_threads_enter();
gtk_combo_box_append_text(...);
gdk_threads_leave();
}
No deadlock occurs when I destroy the dialog before this code executes. However, I'm afraid of the rare possibility that the user will close the dialog after the GTK_IS_COMBO_BOX check completes.
PS - I use threads to do my dialog box calculations because the dialog boxes are non modal, and I want the user to be able to do other things with the main UI while the dialog boxes populate.
I think this is beause GTK_IS_COMBO_BOX probably calls gdk_threads_enter()
I don't think this is the case. These macros are generally pretty simple and I wouldn't expect it to take a lock. In fact, as far as I can tell the whole idea of gdk_threads_enter is that the library itself shouldn't call this, only code that knows it's running in another thread should.
Here's my idea: Did you forget to call g_thread_init and gdk_threads_init?
Also, one thing to keep in mind... By default, gdk_threads_enter is not using a recursive mutex. Though some people have religious objections to recursive mutexes, it's possible to have gdk_threads_enter use one:
static GStaticRecMutex my_gdk_lock;
static void my_gdk_lock_enter() {g_static_rec_mutex_lock(&my_gdk_lock);}
static void my_gdk_lock_leave() {g_static_rec_mutex_unlock(&my_gdk_lock);}
// ...
g_thread_init(NULL);
g_static_rec_mutex_init(&my_gdk_lock);
gdk_threads_set_lock_functions(G_CALLBACK(my_gdk_lock_enter),
G_CALLBACK(my_gdk_lock_leave));
gdk_threads_init();
// ...
Update: From your comment it sounds like you have a race condition between destroying the dialog and populating the combo box. One potential solution is that you bump up the reference count of the combo box (i.e. gtk_widget_ref) so that it doesn't get freed while your asynchronous worker is doing something. Then release it with gtk_widget_unref when the other thread no longer needs the pointer.