I have two GTK windows
Normal (main) window that runs animation, draws stuff in callback registered by gtk_widget_add_tick_callback().
At some point secondary window is created that runs modal loop:
void show_modal()
{
GtkWindow* gw = gtkwindow(this);
if( parent() )
gtk_window_set_transient_for(gw, gtkwindow( parent() ));
gtk_widget_show(GTK_WIDGET(gw));
gtk_window_set_modal(gw,TRUE);
gtk_window_set_keep_above(gw,TRUE);
this->update_window_state(gool::WINDOW_SHOWN);
while( this->is_valid_window() )
{
if(this->_window_state == WINDOW_HIDDEN) break;
if(this->_window_state == WINDOW_STATE_NA) break;
gtk_main_iteration(); // gtk_main_iteration_do(true);
}
}
Problem: Animation in main window works fine until show_modal() is invoked. It appears as gtk_main_iteration(); blocks ticks added by gtk_widget_add_tick_callback() function. As soon as I close secondary window and so while() {gtk_main_iteration();} loop exits then animations in main window start running again.
Any idea of how to make "animation friendly" modal loops in GTK?
UPDATE: it appears as gtk_main_iteration(); blocks not only ticks but any updates of any windows other than "current" - they are simply frozen. What is the reasoning of such GTK behavior?
UPDATE #2:
gtk_dialog_run(); behaves exactly as gtk_main_iteration(); - locks any updates on any window in process other than active window.
It seems to be by definition: link
gboolean gtk_main_iteration (void);
Runs a single iteration of the mainloop. If no events are waiting to be processed GTK+ will block until the next event is noticed. If you don’t want to block look at gtk_main_iteration_do() or check if any events are pending with gtk_events_pending() first.
The explanation suggests to use gtk_main_iteration_do(FALSE) if you don't want blocking:
gboolean gtk_main_iteration_do (gboolean blocking);
Runs a single iteration of the mainloop. If no events are available either return or block depending on the value of blocking:
TRUE if you want GTK+ to block if no events are pending
As for gtk_dialog_run: it also blocks by design link
gint gtk_dialog_run (GtkDialog *dialog);
Blocks in a recursive main loop until the dialog either emits the “response” signal, or is destroyed.[...]
I read about people solving this using multiple threads: handle the GUI in the main thread and do background work in another one. There's an article about it here that might be useful.
I assume that show_modal is called from a callback or other activity in main context. You could try adding your modal window into main context using invoke or signal_idle.
This way execution of show_modal will end.
#include <gtkmm.h>
#include <string>
int main()
{
auto Application = Gtk::Application::create();
Gtk::Window window;
Gtk::Window* window2;
Gtk::Button button;
window.add(button);
//I hope timeout behaves similar to ticks. I have no idea how animations in GTK work
int i=0;
Glib::MainContext::get_default()->signal_timeout().connect([&]()->bool{
button.set_label(std::to_string(i++));
return true;
}, 1000);
button.signal_clicked().connect([&]{
Glib::MainContext::get_default()->invoke([&]()->bool{
window2 = new Gtk::Window;
window2->set_modal(true);
window2->set_keep_above(true);
window2->signal_delete_event().connect([&](GdkEventAny* any_event)->bool{
delete window2;
return false;
});
window2->show_all();
return false;
});
});
window.show_all();
return Application->run(window);
}
Related
I want to run a window in another thread (ie not the main thread) using Centurion (which uses SDL).
Here is how i run the thread:
auto _THR_WIN_HWND_MAINLOOP = ([&HWND_instance]() {
HWND_instance._mainloop_();
});
// starts the window thread.
std::thread _thr(_THR_WIN_HWND_MAINLOOP);
_thr.detach();
Then my _mainloop_() function:
const cen::sdl sdl;
// main loop here.
cen::window hwnd;
cen::renderer renderer = hwnd.make_renderer();
hwnd.show();
cen::event_handler handler;
while (__flag_open) {
while (handler.poll()) {
if (handler.is<cen::quit_event>()) {
__flag_open = false;
break;
}
}
renderer.clear_with(cen::colors::white);
renderer.fill_rect(cen::frect{ 25.0, 500.0, 125.0, 75.0 });
renderer.present();
}
Just a note, the cen::window constructor calls SDL_Init().
The problem is is that on the off chance i run my program, it closes after a second or so. if i JOIN the thread instead of detaching it the window stays open. However i need the window to run in the background as i have other code that must be run. Is there any way to have a background thread that fully handles the SDL window as if it was its own program?
NOTE: No errors are thrown, the window just closes after a second or so, on the off chance?!
It turns out i was not labeling my HWND_instance class variable as static, and that variable held almost everything to do with SDL. So when it got destroyed, so did the SDL instances:
before:
_CE_WINDOW_HWND HWND_instance(s.get()); // create the instance.
auto _THR_WIN_HWND_MAINLOOP = ([&]() {
HWND_instance._mainloop_();
});
// starts the window thread.
std::thread _thr(_THR_WIN_HWND_MAINLOOP);
_thr.detach();
After:
static _CE_WINDOW_HWND HWND_instance(s.get()); // create the instance.
auto _THR_WIN_HWND_MAINLOOP = ([&]() {
HWND_instance._mainloop_();
});
// starts the window thread.
std::thread _thr(_THR_WIN_HWND_MAINLOOP);
_thr.detach();
As you can see, now the variable does not get destroyed after the function ends, preserving the lifetime of SDL and the class.
I want to implement a GUI that receives messages from an external device. The "advancedReceiveExample" is waiting for messages. Once it has received one, it does stuff with it, saves it and terminates.
I want to make my function wait for new messages after receiving one as long as the button is toggled.
I have tried this so far:
void MainWindow::on_pushButton_clicked()
{
if (ui.pushButton->isChecked()) {
ui.pushButton->setText("Stop Receiving");
ui.label_3->setText("Receiving...");
advancedReceiveExample(ui.comboBox->currentIndex() + 1);
}
else
{
ui.pushButton->setText("Start Receiving");
ui.label_3->setText("Not Receiving");
}
}
This works perfectly fine but as mentioned above it only receives one message. If I do that:
void MainWindow::on_pushButton_clicked()
{
if (ui.pushButton->isChecked()) {
ui.pushButton->setText("Stop Receiving");
ui.label_3->setText("Receiving...");
while (1)
{
advancedReceiveExample(ui.comboBox->currentIndex() + 1);
}
}
else
{
ui.pushButton->setText("Start Receiving");
ui.label_3->setText("Not Receiving");
}
}
it blocks the function because the state of the button can only be change after the function "on_pushButton_clicked()" has terminated.
Visual Studio 2019
C/C++
EDIT: Okay, I have understood the problem of blocking the thread. Multithreading might be the right option but I am very unexperienced regarding this topic. The <QThread> could be possible. How would you use it?
Do you have suggestions which other library could be used?
Note QT is event-based. If you keep your computer busy inside some function without returning to the main loop frequently, your GUI will freeze.
What you need to do is slice your action that you want to do into small bits that can repeatedly return to the main loop in order to keep the GUI responsive. (Another method yould be to swap out your action into a separate thread and handle it in parallel, killing the thread when the button is released)
Probably the simplest method to do what you want is with timers that you arm in the PushButton::clicked slot, and then check in the timer event whether the button is still pressed, and, if yes, do a bit of your action, save state and re-arm the timer to have you return.
Something along the lines of the following pseudo code should work and execute what you want to do in slices every 10ms:
MainWindow::onPushButtonClicked () {
// do the action, or, alternatively, start a
// parallel thread that does it
do_a_bit_of_action();
// sets up a timer to call onTimer after 10ms
QTimer::singleShot (10, this, SLOT(onTimer()));
}
MainWindow::onTimer () {
// check if button is still held down
if (pushButton.down) {
// re-arm timer
Timer::singleShot (10, this, SLOT(onTimer()));
// do some more action bits
do_a_bit_of_action();
}
else {
// kill optional background thread here
}
}
You can try it with:
while(ui.pushButton->isChecked()){
*your function*
}
I need to create a simple GUI which displays images, the images in this example can change and the GUI will need to update it's contents.
I wrote the following update function in my widget class:
void myClass::updatePic() {
QPixmap pix("./pic.png");
int width = ui->picLabel->width();
int height = ui->picLabel->height();
ui->picLabel->setPixmap(pix.scaled(width,height,Qt::KeepAspectRatio));}
I try to use it in the following manner:
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
myClass w;
w.show();
sleep(3);
w.updatePic();
sleep(3);
w.updatePic();
sleep(3);
return a.exec();}
But the window just opens and does not display the images until we get to the a.exec() line, and then it opens the last image. What am I doing wrong?
EDIT:
Clarification, the trigger for changing the images comes from an external program (specifically, the gui will be a node in ros, and will be triggered by another node). Is there a way to push a button not from the gui via an external program? the timer will work but I dislike this "busy wait" style solutions.
Thanks for the suggestions so far
exec runs the QT event loop, which includes rendering widgets.
So move your updatePic call into your widget and activate it by for example a button or in the show event
At first learn more about event loop. In particular, you must know that all events like paintEvent or resizeEvent are usually called on corresponding events handle. The events handle is usually called by the event loop, i.e. inside of exec function.
Let's unite answers of #MohaBou and #RvdK. You need to handle timer shots after the exec call. Use QObject::timerEvent for this.
myClass::myClass()
{
<...>
// This two variables are members of myClass.
_timerId = startTimer(3000);
_updatesCount = 0;
}
myClass::~myClass()
{
<...>
// For any case. As far as I remember, otherwise the late event
// may be handled after the destructor. Maybe it is false, do
// not remember...
if (_timerId >= 0) {
killTimer(_timerId);
_timerId = - 1;
}
}
myClass::timerEvent(QTimerEvent *event)
{
if (event->timerId() == _timerId) {
if (_updatesCount < 2) {
updatePic();
++_updatesCount;
} else {
killTimer(_timerId);
_timerId = - 1;
}
}
}
The startTimer method here adds especial timer event to the event query every 3 seconds. As all events, it may be handled only when the event loop will take control and all earlier events are handled. Because of it you can have a duration if many "heavy" events are handled.
EDIT: sorry, I didn't understand #MohaBou at first read. His answer with explicit QTimer is also good enough (but I still don't understand a part about modality).
The function exec also renders the child widgets. exec() blocks the application flow while show() doesn't. So, exec is mainly used for modal dialogs.
I recommend to update it in your custom witget by using a refresh timer. Use a QTimer to update the image every 3 secs:
QTimer* timer = new QTimer(this);
timer->setInterval(3000);
connect(timer, SINGAL(timeout()), this, SLOT(updatPicture()));
Update your picture in your custom slot:
MainWindow::updatePicture() {
updatePic()
}
If you want, you could use a lambda function:
connect(timer, &QTimer::timeout, this, [&w]() {
updatePic()
});
I have a wxwindows application and in the onclick event of a button, I have a very long process, for example I have something such as this:
for(int i=1;i<100;i++)
{
sleep(1000);
gaugeProgress->SetValue(i);
*textOutput<<i;
}
Running this code, stops UI to be responsive. I add
Refresh();
Update();
just after
*textOutput<<i;
but it did not work.
is there any way that I can pump the events?
I am working on Windows using VS 20102
In those cases I use wxYield() like this:
for(int i = 1; i < 100; i++)
{
// sleep() freezes the program making it unresponsible.
// sleep(1000);
gaugeProgress->SetValue(i);
*textOutput << i;
// wxYield stops this function execution
// to process all the rest of stocked events
// including the paint event and resumes immediately.
wxYield();
}
This stops the current process and lets the application to process the message stack like the paint event.
But I think that the proper way to do this should be using threads.
You can add a wxTimer member in your wxwindows, start it in the window constructor, as such:
m_timer.Start(1000);
then capture the timer event with a function, for example:
void mywindow::OnTimer(wxTimerEvent& event)
{
Refresh();
Update();
}
Make sure you connect the event to the wxTimer member.
I'm trying to create a "responsive gui", which basically means that I have an app, and on the main window there is a button. After I press this button I want the "progress bar window" to get displayed which will show the progress of the work being done, and of course this work is being done in separate thread.
Unfortunately my approach with starting a new thread in ctor of this progress_bar window doesn't seems to work and I got frozen gui.
Here is the link to this project so you can download it and run without the need for copying and pasting anything: http://www.mediafire.com/?w9b2eilc7t4yux0
Could anyone please tell me what I'm doing wrong and how to fix it?
EDIT
progress_dialog::progress_dialog(QWidget *parent) :
QDialog(parent)
{/*this is this progress dialog which is displayed from main window*/
setupUi(this);
working_thread_ = new Threaded;
connect(working_thread_,SIGNAL(counter_value(int)),progressBar,SLOT(setValue(int)),Qt::QueuedConnection);
working_thread_->start();//HERE I'M STARTING THIS THREAD
}
/*this is run fnc from the threaded class*/
void Threaded::run()
{
unsigned counter = 0;
while(true)
{
emit counter_value(counter);
counter = counter + 1 % 1000000;
}
}
Independently from the fact that tight looping is bad, you should limit the rate at which you make changes to the main GUI thread: the signals from your thread are queued as soon they are emitted on the main thread event loop, and as the GUI can't update that fast, repaint events are queued rather than executed in real time, which freezes the GUI.
And anyways updating the GUI faster than the screen refresh rate is useless.
You could try something like this:
void Threaded::run()
{
QTime time;
time.start();
unsigned counter = 0;
// initial update
emit counter_value(counter);
while(true)
{
counter = (counter + 1) % 1000000;
// 17 ms => ~ 60 fps
if(time.elapsed() > 17) {
emit counter_value(counter);
time.restart();
}
}
}
Do you try to start the thread with a parent object?