An example on run GUI in a sub-thread and send update command from main thread.

If you have a cool piece of software to share, but you are not hosting it officially yet, please dump it in here. If you have code snippets that are useful, please donate!
Post Reply
kilasuelika
In need of some credit
In need of some credit
Posts: 7
Joined: Tue Sep 24, 2019 1:02 pm

An example on run GUI in a sub-thread and send update command from main thread.

Post by kilasuelika »

Recently I develop an example that call wxwidgets from a console application and then update GUI through commands. I want to share with you.

The basic idea is run wxWidgets GUI in a single thread. This thread is managed by a manager. The manager stores a pointer of a wxApp and then it can call member of the wxApp to post event to update gui. In the manager class, an atomic_flag is used to ensure thread is correctly initialized.

So the GUI is running in a sub-thread and we are sending commands from main thread. This structure can be used for incorporating wxWidgets in a library.

main code:

Code: Select all

#include <iostream>
#include "libwx.hpp"
#include <thread>

int main()
{
	GUIManager manager;
	manager.init_gui_thread();

	manager.update();

	manager.clean();
}
libwx.hpp:

Code: Select all

#pragma once
#include "wx/wx.h"
#include <thread>
#include <atomic>

wxDEFINE_EVENT(wxEVT_MY_UPDATE_UI, wxThreadEvent);

class myFrame : public wxFrame {
public:
	myFrame() :wxFrame((wxFrame*)NULL, -1, "myFrame", wxPoint(50, 50), wxSize(800, 600)) {

		Bind(wxEVT_MY_UPDATE_UI, &myFrame::updateUI, this);
	};

	void updateUI(wxThreadEvent& evt) {
		wxStaticText* text = new wxStaticText(this, wxID_ANY, "Updated");
	}
};

class TestApp : public wxApp {
public:
	myFrame* frame;

	bool OnInit() override {
		frame = new myFrame;
		frame->Show();
		return true;
	};

	void add_static_text() {
		wxThreadEvent evtcustom(wxEVT_MY_UPDATE_UI);
		wxQueueEvent(frame, evtcustom.Clone());

	};
};

struct GUIManager {
	TestApp* app;

	std::jthread* thread;
	//Use a lock to ensure the gui thread is initialized only once.
	std::atomic_flag lock;

	GUIManager() {
		app = new TestApp();
	};

	void init_gui_thread() {
		if (lock.test()) {
			return;
		}
		else {
			thread = new std::jthread([this]() {
				wxApp::SetInstance(app);
				int argCount = 0;
				char* argv[1];
				wxEntryStart(argCount, argv);
				wxTheApp->CallOnInit();
				lock.test_and_set();
				lock.notify_all();
				wxTheApp->OnRun(); wxEntryCleanup();
				});
		};
	};
	void clean() {
		if (lock.test()) {
			thread->join();
		};
	};

	void update() {
		//If lock==false, then block until notified.
		lock.wait(false);

		app->add_static_text();
		std::cout << "Update finished." << std::endl;
	};

	~GUIManager() {
		delete thread;
	}
};
libwx.cpp:

Code: Select all

#include "libwx.hpp"
#include <iostream>

IMPLEMENT_APP_NO_MAIN(TestApp);
jpo234
Experienced Solver
Experienced Solver
Posts: 70
Joined: Tue Feb 25, 2020 11:34 am

Re: An example on run GUI in a sub-thread and send update command from main thread.

Post by jpo234 »

Or you could just call wxGetApp().CallAfter ([]{/* do something here */}); instead of jumping through all these hoops.
Notice that it is safe to use CallAfter() from other, non-GUI, threads, but that the method will be always called in the main, GUI, thread context.
Post Reply