How to befriend QEventLoop and winapi event loop - c++

I have MyApp class with winapi event loop in it
class MyApp : public QObject {
Q_OBJECT
public:
int WINAPI exec() {
MSG msg;
int nRetVal;
while ((nRetVal = ::GetMessage(&msg, nullptr, 0, 0)) != 0 && nRetVal != -1) {
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
return msg.wParam;
}
};
When I start my program, QObject's events won't process (can't invoke any slot and hence send signals).
Somewhere on the internet I found kind of a solution - create a QApplication object, so it somehow "hooks up" to winapi message loop and it worked:
int main(int _argc, char* _argv[]) {
QApplication qa(_argc,_argv);
UNREFERENCED_PARAMETER(qa);
MyApp mapp();
return mapp.exec();
}
But it seems to me that design is fragile and a hack. And I don't like that I'm using some undocumented feature and never call QApplication.exec() method.
Can you tell me how to do it the right way? Namely, I want to call only one exec method (I guess it will be the one of QApplication, but where should I put my winapi loop?)
Thanks for any help

Related

GTK+ Use one handler for multiple widgets

I have a callback function as follows:
void handle(GtkWidget *widget, gpointer data) {...}
Since I have a lot of widgets for this window, I would like to use this callback as the only handler to avoid writing a bunch of small functions. Initially I thought of using an enum that's stored in the UI class which wraps around the window, and then I would test for it as follows:
UIClass::Signal signal = (UIClass::Signal) data;
switch (signal) {
case UIClass::main_button:
// handle
case UIClass::check_box:
...
}
But the compiler refuses the cast in the first line of that snippet.
Is there a standard way to accomplish this? Or was GTK+ designed to have one handler for every widget?
Store a pointer to a widget in class and pass whole object to the handler:
#include <gtk/gtk.h>
#include <iostream>
struct UIClass
{
GtkWidget* widget1, *widget2, *box;
UIClass()
{
widget1 = gtk_button_new_with_label("button1");
widget2 = gtk_button_new_with_label("button2");
box = gtk_hbox_new(true, 10);
gtk_box_pack_start(GTK_BOX(box), widget1, 0 ,0, 1);
gtk_box_pack_start(GTK_BOX(box), widget2, 0 ,0, 1);
}
static void handle(GtkWidget* sender, UIClass* uiClass)
{
if(sender == uiClass->widget1)
std::cout<<"widget1"<<std::endl;
else if(sender == uiClass->widget2)
std::cout<<"widget2"<<std::endl;
else
std::cout<<"something else"<<std::endl;
}
void connect()
{
g_signal_connect(widget1, "clicked", G_CALLBACK(UIClass::handle), this);
g_signal_connect(widget2, "clicked", G_CALLBACK(UIClass::handle), this);
}
};
int main(int argc, char *argv[])
{
gtk_init (&argc, &argv);
GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
UIClass ui;
ui.connect();
gtk_container_add(GTK_CONTAINER(window), ui.box);
gtk_widget_show_all(window);
gtk_main();
return 0;
}

How to update modeless dialogbox

I have a modeless dialog box running in a separate thread. I want to update this dialog box from my main program. I tried with creating custom message UPDATE = 0x8001 (in this range WM_APP - 0xBFFF) and associated handler for that message and calling postthreadmessage(). But that is not working. My code look like this.
int _tmain(int argc, _TCHAR* argv[])
{
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), SW_SHOW))
{
std::cout<<"Fatal Error: MFC initialization failed\n";
}
else
{
std::thread th2(ModelessThreadFunc);
DWORD thid = GetThreadId(th2.native_handle());
std::cout<<PostThreadMessage(thid,UPDATE,0,0);
th2.join();
}
return 0;
}
int ModelessThreadFunc()
{
dialog *dial = new dialog;
assert(dial->Create(dialog::IDD));
dial->ShowWindow(SW_SHOWNORMAL);
MSG msg;
while((::GetMessage(&msg, NULL, 0,0) != 0))
{
::peekmessage(&msg,NULL,0x8000,0x8002,0x0001);
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
return 0;
}
Can anyone explain problem with above logic? My aim is to update dialog box outside its thread. Any more ideas are welcome. Thank you.
PostThreadMessage is documented to fail if the destination thread creates any windows. You should PostMessage to the dialog HWND. You might also need to use AfxBeginThread instead of std::thread because you need the MFC message pump that is built in to CWinThread.
In general, the approach you are taking is not recommended. All GUI should be in the main thread, and secondary threads used for time-consuming operations. This avoids many awkward problems.

Qt - get data and forward them on serial connection

I am trying to develop a simple Qt application.
After I press a "START" button, the application should continuosly retrieves data from a device (using third party libraries) and forward them as soon as possible on a serial connection.
The (ugly) software I used till now was a console application that ran in a sequential way and got data frame as soon as they are made available by the host, using the following cycle:
while(1)
{
[...]
while( MyClient.GetFrame().Result != Result::Success )
{
Sleep( 200 );
std::cout << ".";
}
[... pack and send on serial]
}
I was wondering which is the more convinient way to implement this in Qt, in order to keep the GUI responsive but also with the minimum possible latency between getFrame and the serial write function.
Should I use a timer triggered SLOT? QtConcurrent namespace? QRunnable?
Which are the main advantages and disadvantages of each of these approaches?
Thanks
for your help!
Since the existing library forces you to poll for data, the only thing to do is to run it on a timer. It's your choice as to if the object that does this job will run in the main thread, or in a worker thread. There's no need to use Qt Concurrent nor QRunnable - using a QObject makes life somewhat simpler since you can easily provide feedback to the GUI.
For example, making some assumptions about your client's API:
class Worker : public QObject {
Client m_client;
QSerialPort m_port;
QBasicTimer m_timer;
void processFrame() {
if (m_client.GetFrame().Result != Result::Success) return;
QByteArray frame = QByteArray::fromRawData(
m_client.GetFrame().Data, m_client.GetFrame().Size);
... process the frame
if (m_port.write(frame) != frame.size()) {
... process the error
}
}
void timerEvent(QTimerEvent * ev) {
if (ev->timerId() == m_timer.timerId()) processFrame();
}
public:
Worker(QObject * parent = 0) : QObject(parent) {}
Q_SLOT bool open(const QString & name) {
m_port.close();
m_port.setPortName(name);
if (!m_port.open(name)) return false;
if (!m_port.setBaudRate(9600)) return false;
if (!m_port.setDataBits(QSerialPort::Data8)) return false;
... other settings go here
return true;
}
Q_SLOT void start() { m_timer.start(200, this); }
Q_SLOT void stop() { m_timer.stop(); }
...
}
/// A thread that's always safe to destruct
class Thread : public QThread {
using QThread::run; // lock the default implementation
public:
Thread(QObject * parent = 0) : QThread(parent) {}
~Thread() { quit(); wait(); }
};
int main(int argc, char ** argv) {
QApplication app(argc, argv);
Worker worker;
Thread thread; // worker must be declared before thread!
if (true) {
// this code is optional, disabling it should not change things
// too much unless the client implementation blocks too much
thread.start();
worker.moveToThread(&thread);
}
QPushButton button("Start");
QObject::connect(&button, &QPushButton::clicked, [&worker]{
// Those are cross-thread calls, they can't be done directly
QMetaObject::invoke(&worker, "open", Q_ARG(QString, "COM1");
QMetaObject::invoke(&worker, "start");
});
button.show();
return app.exec(argc, argv);
}

Controls not rendering in modeless dialog box MFC

UPDATE: Possible solution. Declaring the CWait dialog in the header seems to solve this.
UPDATE2: Message pump may be the culprit. Explicit "pumping" seems to resolve problem.
Im trying to display a modal "Please Wait" Dialog box while some functions execute in an application. The dialog I want to display is this:
Im using this code to call the dialog box.
CWait dialog;
dialog.Create(IDD_WAIT);
dialog.SetWindowTextW(L"Geocoding");
dialog.ShowWindow(SW_SHOW);
mastImageGeocode(); //Working here
slvImageGeocode();
interfImageGeocode();
cohImageGeocode();
dialog.CloseWindow();
What ends up displaying is this:
I cant seem to figure out why the controls don't render.
I tried manually processing the message loop after initialization of the dialog with this approach:
MSG stMsg;
while (::PeekMessage (&stMsg, NULL, 0, 0, PM_REMOVE))
{
::TranslateMessage (&stMsg);
::DispatchMessage (&stMsg);
}
Did not really work.
I also tried the pointer approach
Cwait * dialog; //THis is in header
dialog = new CWait(this);
dialog->Create(IDD_WAIT);
dialog->SetWindowTextW(L"Geocoding");
dialog->ShowWindow(SW_SHOW);
mastImageGeocode(); //Some work here
slvImageGeocode();
interfImageGeocode();
cohImageGeocode();
dialog->CloseWindow();
delete dialog;
Am I doing something wrong.
Thanks for the help.
Update: If I call it inside the individual functions, it works fine!
It sounds like you're not updating your dialog from your main processing loop. I've put a cut down version of my MFC progress dialog below. Note that it is necessary to call SetProgress regularly to update the screen. As a more general point, if you're using modeless dialogs in MFC, you need to call OnUpdate(FALSE) for them to refresh, and ensure they have enough time to do so. Quite often when I think I need a modeless dialog, I'm actually served better by splitting the task into separate threads, i.e. the processing part gets placed in its own worker thread. YMMV.
class CProgressDialog : public CDialog
{
public:
CProgressDialog(LPCTSTR Name,int Max,CWnd* pParent = NULL);
CProgressDialog(UINT NameID,int Max,CWnd* pParent = NULL);
virtual ~CProgressDialog();
int m_Max;
void SetProgress(int Progress);
void SetRange(int Range);
enum { IDD = IDD_PROGRESS_DIALOG };
CProgressCtrl m_Progress;
protected:
virtual void DoDataExchange(CDataExchange* pDX);
protected:
virtual BOOL OnInitDialog();
DECLARE_MESSAGE_MAP()
};
CProgressDialog::CProgressDialog(LPCTSTR Name,int Max,CWnd* pParent /*=NULL*/)
: CDialog(CProgressDialog::IDD, pParent)
{
Create(CProgressDialog::IDD, pParent);
SetWindowPos(&wndTop,1,1,0,0,SWP_NOSIZE | SWP_SHOWWINDOW);
SetWindowText(Name);
m_Max = Max;
}
CProgressDialog::~CProgressDialog()
{
DestroyWindow();
}
void CProgressDialog::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDC_PROGRESS, m_Progress);
}
BEGIN_MESSAGE_MAP(CProgressDialog, CDialog)
END_MESSAGE_MAP()
BOOL CProgressDialog::OnInitDialog()
{
CDialog::OnInitDialog();
m_Progress.SetRange32(0,m_Max);
return TRUE;
}
void CProgressDialog::SetProgress(int Progress)
{
m_Progress.SetRange32(0,m_Max);
m_Progress.SetPos(Progress);
UpdateData(FALSE);
}
void CProgressDialog::SetRange(int Range)
{
m_Max = Range;
}
You need to manually pump messages not just at the beginning of the dialog, but after updates as well.
Something like this:
void CProgressDialog::SetProgress(int Progress)
{
m_progressBar.SetPos(Progress);
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
Use:
m_progressBar.SetPos(Progress+1);
m_progressBar.SetPos(Progress);
and it will show. Don't ask me why ...
PS.: attention when reaching the last Progressstep!

SFML and GTK+ - GtkFileChooserDialog

I'm writing an app using SFML and I want to use GTK+ to create file chooser dialogs. I have this code:
gtk_init(&argc, &argv);
GtkWidget *dialog;
dialog = gtk_file_chooser_dialog_new ("Open file...", NULL, GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
gtk_dialog_run (GTK_DIALOG (dialog));
And the dialog is showing, but it doesn't get destroyed :(
In the gtk_dialog_run documentation there is a note
After gtk_dialog_run() returns, you are responsible for hiding or destroying the dialog if you wish to do so.
So the dialog should not get destroyed automaticaly, the programmer must do it.
EDIT:
The another problem is that you are not running GTK main loop (gtk_main() or its variant) so the GTK can not deal with events necessary to destroy a widget (no part of GTK is running in the time the events are present). The sollution for this is in answer to another question using gtk_idle_add() to invoke function after gtk_main()
is called. In this function the dialog is shown, the result is given to the caller, the dialog is destroyed and gtk_main_quit() is called to terminate GTK main loop.
However, gtk_idle_add() is deprecated in GTK+2.6 and is not present in GTK+3.0, so g_idle_add() should be used instead. Your code could be somethink like
struct fch_result {
gint response;
// other information to return like filename,...
};
static gboolean fch_dialog(gpointer user_data)
{
struct fch_result *result = (struct fch_result *) user_data;
GtkWidget *dialog = gtk_file_chooser_dialog_new ( ... );
result->response = gtk_dialog_run (GTK_DIALOG(dialog));
// now add other information to result
gtk_widget_destroy(dialog);
gtk_main_quit(); // terminate the gtk_main loop called from caller
return FALSE;
}
int main(int argc, char** argv)
{
gtk_init(&argc, &argv);
struct fch_result data;
g_idle_add(fch_dialog, &data);
gtk_main();
// continue with the program
return 0;
}