如何使用标头、类定义和构建器 XML 外部文件在 C++ 中正确创建简单的 GTK 应用程序?

问题描述 投票:0回答:1

我已经学会了如何使用 C++ 和 gtkmm-3.0 创建一个简单的 GTK 应用程序。

到目前为止,我已经了解了两种可能的方法,我想要第三种方法,即这两种方法的合并。

第一种方法:3 个文件(头文件、构造函数、主文件)。代码中的小部件

在这种方法中,我编写了三个文件:

  1. window.h
    MainWindow 类的声明位置
#pragma once
#include <gtkmm.h>

class MainWindow : public Gtk::Window
{
public:
 MainWindow();
};
  1. window.cpp
    类的构造函数写在哪里
#include "window.h"

MainWindow::MainWindow()
{
  set_title("Simple application");
  set_default_size(200, 200);
}

  1. main.cpp
    app->run() 启动窗口并运行 “事件循环”
#include "window.h"

int main(int argc, char* argv[])
{
  auto app = Gtk::Application::create("org.gtkmm.window");

  MainWindow w;

  return app->run(w);
}

第二种方法:1个代码文件。 XML 文件中的小部件

在这种方法中,我只有一个“代码”文件,

main.cpp

#include <gtkmm.h>
#include <iostream>

Gtk::Window* gWindow = nullptr;

int main(int argc, char const *argv[])
{
    auto app = Gtk::Application::create("org.gtkmm.window");

    auto gladeBuilder = Gtk::Builder::create();

    gladeBuilder->add_from_file("builder.ui");

    gladeBuilder->get_widget("MainWindow", gWindow);

    app->run(*gWindow);
    return 0;
}

然后我有一个 XML 文件

builder.ui
,其中定义了小部件:

<?xml version="1.0" encoding="UTF-8"?>
<interface>
  <requires lib="gtk+" version="3.22"/>
  <object class="GtkWindow" id="MainWindow">
    <property name="default_width">400</property>
    <property name="default_height">400</property>
  </object>
</interface>

第一种方法的优点是将代码分离在不同的文件中,这在编写更复杂的应用程序时非常有用,例如有多个窗户。 第二种方法的优点是将小部件放在单独的文件中,并且可以使用图形应用程序来设计更复杂的 UI。

所以,我想要第三种方法,将这两种方法合并起来,结合所有优点。

我看到的问题是如何为主窗口编写类,因为它既位于

window.cpp
代码文件中,又位于 XML
builder.ui
文件中。

我尝试了一个天真的想法,将

window.cpp
文件修改为:

#include "window.h"


MainWindow::MainWindow()
{
    auto builder = Gtk::Builder::create();

    builder->add_from_file("builder.ui");

    builder->get_widget("MainWindow", (*this) );
}

即在代码和 XML 文件中定义相同的

MainWindow
类,但我知道这太天真了,而且确实出现了错误。

我有一些用 Python 编写 GUI 的经验,而我是用 C++ 编写 GUI 的新手,非常感谢任何帮助!

c++ xml gtk gtkmm
1个回答
0
投票

经过一些研究,包括 Stack Overflow 中的一些问题(这里这里这里),我为一个简单的应用程序编写了以下代码,其中包含一些小部件和一些连接的信号,这满足了问题:

myWindow.h

#pragma once

#include <gtkmm/window.h>
#include <gtkmm/builder.h>
#include <gtkmm/button.h>
#include <gtkmm/entry.h>
#include <gtkmm/label.h>

class MainWindow : public Gtk::Window
{
public:
    MainWindow(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& p_builder);

protected:
    Glib::RefPtr<Gtk::Builder> m_builder;
    Glib::RefPtr<Gtk::Button> m_button;
    Glib::RefPtr<Gtk::Entry> m_entry;
    Glib::RefPtr<Gtk::Label> m_label;

    // Signal handler:
    void on_button_clicked();
};

myWindow.cpp

#include "myWindow.h"
#include <gtkmm/builder.h>
#include <gtkmm/button.h>
#include <iostream>

MainWindow::MainWindow(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& p_builder)
: Gtk::Window(cobject), m_builder{p_builder}
{

    Gtk::Button* p_button = nullptr; // Create a raw pointer to a button
    Gtk::Entry* p_entry = nullptr; // Create a raw pointer to an entry field
    Gtk::Label* p_label = nullptr; // Create a raw pointer to a label


    m_builder->get_widget("button1", p_button); // Retrieve the button from XML file
    m_button = Glib::RefPtr<Gtk::Button>(p_button); // Assign to m_button

    m_builder->get_widget("entry1", p_entry); // Retrieve the entry from XML file
    m_entry = Glib::RefPtr<Gtk::Entry>(p_entry); // Assign to m_entry

    m_builder->get_widget("label1", p_label); // Retrieve the label from XML file
    m_label = Glib::RefPtr<Gtk::Label>(p_label); // Assign to m_label


    // connect the button click event to the function
    m_button->signal_clicked().connect(sigc::mem_fun(*this, &MainWindow::on_button_clicked));
}

void MainWindow::on_button_clicked()
{
    m_label->set_label(m_entry->get_text());
}

main.cpp

#include <iostream>
#include <cstring>
#include <gtkmm/application.h>
#include <gtkmm/builder.h>
#include "myWindow.h"

int main(int argc, char** argv)
{
    // Create an application object. This is mandatory since it initializes
    // the toolkit. If you don't do this, widgets won't show.
    auto app = Gtk::Application::create(argc, argv);

    // Since you want to use the builder, you need to instantiate one. You
    // can do this directly using your UI file.
    auto builder = Gtk::Builder::create_from_file("builder.ui");

    // At this point, you are ready to create your window. The builder is
    // going to create an instance for you, and you get a handle to this
    // istance to later use.
    // First, you create a MainWindow pointer, which points to nothing. It
    // is going to be used by the builder to get you the handle.
    MainWindow* window = nullptr;

    // Then, using you builder instance. You call get_widget_derived and
    // pass the pointer to get the handle.
    builder->get_widget_derived("mainWindow", window);

    // Check if the window was created successfully
    if (window) {

        // You then show the window by using you handle (i.e window).
        app->run(*window);
    } else {
        std::cerr << "Failed to create main window." << std::endl;
        return 1;
    }

    return 0;
}

(此代码中的精彩评论来自@BobMorane,写在this问题的答案中)

builder.ui

<?xml version='1.0' encoding='UTF-8'?>
<interface>
  <requires lib="gtk+" version="3.20"/>
  <object class="GtkWindow" id="mainWindow">
    <property name="default-height">300</property>
    <property name="default-width">400</property>
    <property name="title">Simple copy</property>
    <child>
      <object class="GtkGrid" id="grid">
        <property name="visible">True</property>
        <child>
          <object class="GtkLabel" id="label1">
            <property name="visible">True</property>
            <property name="width-request">2</property>
          </object>
          <packing>
            <property name="height">2</property>
            <property name="left-attach">0</property>
            <property name="top-attach">0</property>
            <property name="width">2</property>
          </packing>
        </child>
        <child>
          <object class="GtkEntry" id="entry1">
            <property name="visible">True</property>
            <property name="width-request">2</property>
          </object>
          <packing>
            <property name="left-attach">0</property>
            <property name="top-attach">3</property>
            <property name="width">2</property>
          </packing>
        </child>
        <child>
          <object class="GtkButton" id="button1">
            <property name="label">copy</property>
            <property name="visible">True</property>
            <property name="width-request">2</property>
          </object>
          <packing>
            <property name="left-attach">0</property>
            <property name="top-attach">2</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
</interface>

此代码构建并运行,创建一个带有标签、按钮和输入字段的窗口。 单击按钮会将字段中输入的文本复制到标签。 关键方面之一是用于处理不同小部件的指针的数据类型。

© www.soinside.com 2019 - 2024. All rights reserved.