简体   繁体   English

多线程 gtkmm 应用程序的最简单示例

[英]The simplest possible example for a multi-threaded gtkmm application

I'm developing a stopwatch application in c++ and gtkmm.The first approach was to integrate the stopwatch loop with main event loop,but that caused delays in the stopwatch loop,So I decided to use two threads, one for GUI the other is for the stopwatch counter.我正在 c++ 和 gtkmm 中开发秒表应用程序。第一种方法是将秒表循环与主事件循环集成,但这会导致秒表循环延迟,所以我决定使用两个线程,一个用于 GUI,另一个用于秒表计数器。 the GUI thread and and stopwatch thread -which counts the number of seconds passed-, the stopwatch thread updates the label after each second. GUI 线程和秒表线程 - 计算经过的秒数 - 秒表线程每秒更新 label。 I've read that I've to use specific techniques to use multi threaded gtkmm program.我读过我必须使用特定技术来使用多线程 gtkmm 程序。 I also checked this example which i didn't understand.我还检查了这个我不明白的例子 Can any one tell me what shall I do in the stopwatch thread to make it update the GUI without crashing.谁能告诉我我应该在秒表线程中做什么以使其更新 GUI 而不会崩溃。

Here's the code,这是代码,

#include <ctime>
#include <thread>
#include <iostream>
#include <sstream>
#include <gtkmm.h>

using namespace std;
using namespace Gtk;
using namespace std::chrono;

class Timer
{
    bool condition;
    Label *label_ptr;

private:
    void startTimer()
    {
        condition = true;
        auto t0 = high_resolution_clock::now();
        int x = 0;
        while (condition)
        {
            cout << condition << endl;
            auto t1 = high_resolution_clock::now();
            int duration = duration_cast<seconds>(t1 - t0).count();
            if (duration - x == 1)
            {
                x = duration;
                //cout << duration_cast<seconds>(t1 - t0).count() << "sec" << endl;
                stringstream moment;
                moment << to_string(duration_cast<seconds>(t1 - t0).count());
                moment << ": 0 s";

                label_ptr->set_label(moment.str());
            }
            if (g_main_context_pending(NULL))
            {
                g_main_context_iteration(NULL, true);
            }
        }
    }

public:
    //This is the function which start the thread
    void start_timer(){
        thread t0(sigc::mem_fun(this , &Timer::startTimer));
    }

    Timer(Label &label)
    {
        condition = 0;
        label_ptr = &label;
    }

    void stop_timer()
    {
        label_ptr->set_text("0 : 0");
        condition = false;
    }
};

int main(int argc, char *argv[])
{
    auto app = Application::create(argc, argv, "Timer");

    if (!g_thread_supported())
        g_thread_init(NULL);

    gdk_threads_init();

    gdk_threads_enter();

    Window window;
    HBox mainBox;
    window.add(mainBox);
    window.set_title("Timer & Stopwatch");
    window.set_default_size(240, 100);
    window.set_border_width(10);
    Label label;
    VButtonBox buttons;
    Separator sep0;

    mainBox.pack_start(buttons, PACK_EXPAND_PADDING, 20);
    mainBox.pack_start(sep0, PACK_SHRINK);
    mainBox.pack_end(label, PACK_EXPAND_PADDING);

    label.set_size_request(120, 100);

    Button start, stop;
    buttons.add(start);
    buttons.add(stop);
    start.set_border_width(5);
    stop.set_border_width(5);

    start.set_label("Start");
    stop.set_label("Stop");

    label.set_text("0 : 0");

    Timer timer(label);
    start.signal_clicked().connect(sigc::mem_fun(timer, &Timer::start_timer));

    window.show_all_children();

    gdk_threads_leave();

    return app->run(window);
}

if your requirement is just to count number of seconds elapsed, you do not need a worker thread.如果您的要求只是计算经过的秒数,则不需要工作线程。 Also you cannot modify any GUI element (in your case the 'label') from a worker thread since Gdk is not thread safe.此外,您不能从工作线程修改任何 GUI 元素(在您的情况下为“标签”),因为 Gdk 不是线程安全的。 You will then need to use Glib::Dispatcher to notify the main thread of a 'tick' event and then increment a counter from the main loop.然后,您将需要使用 Glib::Dispatcher 通知主线程“tick”事件,然后从主循环中增加一个计数器。

Instead of all this you can simply use Glib::SignalTimeout and connect a function to it like below.而不是所有这些,您可以简单地使用Glib::SignalTimeout并将 function 连接到它,如下所示。

In the function that handles starting of the stop-watch在处理秒表启动的 function 中

//Connect to timeout
m_connectionStopWatch = Glib::SignalTimeout::connect(sigc::mem_fun(*this, &CStopWatchClass::OneSecondElapsed), 1000);

//Reset counter
m_nCounter = 0;

The function that is called every second每秒调用的 function

bool CStopWatchClass::OneSecondElapsed(void)
{
  //Increment counter
  m_nCounter++;

  //TODO Update label

  return true;
}

In the function that stops the stop watch在停止秒表的 function

m_connectionStopWatch.disconnect();

m_connectionStopWatch and m_nCounter shall be class private (or protected or even public though not preferred) members. m_connectionStopWatchm_nCounter应为 class 私有(或受保护或什至公共,但不是首选)成员。

If you are not particular about having a delay of up to 0.99... seconds in starting your stop watch you can even use Glib::SignalTimeout::connect_seconds which loads your application even less.如果您不特别想在启动秒表时延迟最多 0.99... 秒,您甚至可以使用Glib::SignalTimeout::connect_seconds来减少应用程序的负载。 In any case a 1 second timeout is no problem at all, I myself am using a 20 millisecond timeout for a similar graphical application.在任何情况下,1 秒的超时都不是问题,我自己正在为类似的图形应用程序使用 20 毫秒的超时。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM