二十一、Gtk4-Template XML and composite widget

文章详细介绍了如何重构tfeapplication.c,将其拆分为更小的文件,如主窗口、首选项对话框和警告对话框。通过使用模板XML文件和GSettings,创建了复合小部件,提高了代码组织性和可维护性。同时,展示了如何处理对话框的响应,如警告对话框的确认操作。
摘要由CSDN通过智能技术生成

前一节中的tfe程序不太好,因为很多东西都塞到了tfeapplication .c中。以及tfeapplication .c中的许多静态变量。文件tfeapplication.c应该被分成几个文件。

  • tfeapplication.c只有与应用程序相关的代码。
  • 一个文件用于主窗口
  • 一个文件用于首选项对话框的文件
  • 一个用于警告对话框的文件

首选项对话框由ui文件定义。它有GtkBox, GtkLabel和GtkFontButton。这样的小部件可以定义为复合小部件。复合组件是:

  • 构件的子对象(不是子构件)。例如,首选项组合构件是GtkDialog的子对象。
  • 可以从模板XML构建复合小构件。构件是用模板标签定义的,而不是对象标签。

下一小节将展示如何构建首选项对话框。

Preference dialog

首先,编写一个模板XML文件。

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <interface>
 3   <template class="TfePref" parent="GtkDialog">
 4     <property name="title">Preferences</property>
 5     <property name="resizable">FALSE</property>
 6     <property name="modal">TRUE</property>
 7     <child internal-child="content_area">
 8       <object class="GtkBox" id="content_area">
 9         <child>
10           <object class="GtkBox" id="pref_boxh">
11             <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
12             <property name="spacing">12</property>
13             <property name="margin-start">12</property>
14             <property name="margin-end">12</property>
15             <property name="margin-top">12</property>
16             <property name="margin-bottom">12</property>
17             <child>
18               <object class="GtkLabel" id="fontlabel">
19                 <property name="label">Font:</property>
20                 <property name="xalign">1</property>
21               </object>
22             </child>
23             <child>
24               <object class="GtkFontButton" id="fontbtn">
25               </object>
26             </child>
27           </object>
28         </child>
29       </object>
30     </child>
31   </template>
32 </interface>
33 

模板标记指定一个复合小构件。class值是对象的名称。它是TfePref。parent值指定复合构件的父类。因此。TfePref是GtkDialog的一个子类。parent属性是可选的。但建议指定它。其他行和以前一样。

TfePref类的定义类似于TfeTextView。有两个文件tfeprefer.h和tfeprefer.c。

文件tfepre.h定义了类型并声明了公共函数。这些定义是公开的,对任何C文件都是开放的。

 1 #ifndef __TFE_PREF_H__
 2 #define __TFE_PREF_H__
 3 
 4 #include <gtk/gtk.h>
 5 
 6 #define TFE_TYPE_PREF tfe_pref_get_type ()
 7 G_DECLARE_FINAL_TYPE (TfePref, tfe_pref, TFE, PREF, GtkDialog)
 8 
 9 GtkWidget *
10 tfe_pref_new (void);
11 
12 #endif /* __TFE_PREF_H__ */
13 
  • 6:定义一个类型TFE_TYPE_PREF,它是一个宏,被tfe_pref_get_type()取代。
  • 7:宏G_DECLAER_FINAL_TYPE扩展为:
    • 函数tfe_pref_get_type()被声明。
    • TfePrep类型定义为typedef struct _TfePrep TfePrep。
    • TfePrepClass类型定义为struct {GtkDialogClass *parent;}的typedef类型。
    • 定义了两个函数TFE_PREF()和TFE_IS_PREF()。
  • 9-10: tfe_pref_new创建一个新的TfePref对象。

文件tfepre .c包含:

  • struct _TfePrep结构
  • G_DEFINE_TYPE宏
  • Initialize初始化和dispose释放函数
  • 公共函数
 1 #include <gtk/gtk.h>
 2 #include "tfepref.h"
 3 
 4 struct _TfePref
 5 {
 6   GtkDialog parent;
 7   GSettings *settings;
 8   GtkFontButton *fontbtn;
 9 };
10 
11 G_DEFINE_TYPE (TfePref, tfe_pref, GTK_TYPE_DIALOG);
12 
13 static void
14 tfe_pref_dispose (GObject *gobject) {
15   TfePref *pref = TFE_PREF (gobject);
16 
17   g_clear_object (&pref->settings);
18   G_OBJECT_CLASS (tfe_pref_parent_class)->dispose (gobject);
19 }
20 
21 static void
22 tfe_pref_init (TfePref *pref) {
23   gtk_widget_init_template (GTK_WIDGET (pref));
24   pref->settings = g_settings_new ("com.github.ToshioCP.tfe");
25   g_settings_bind (pref->settings, "font", pref->fontbtn, "font", G_SETTINGS_BIND_DEFAULT);
26 }
27 
28 static void
29 tfe_pref_class_init (TfePrefClass *class) {
30   GObjectClass *object_class = G_OBJECT_CLASS (class);
31 
32   object_class->dispose = tfe_pref_dispose;
33   gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (class), "/com/github/ToshioCP/tfe/tfepref.ui");
34   gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), TfePref, fontbtn);
35 }
36 
37 GtkWidget *
38 tfe_pref_new (void) {
39   return GTK_WIDGET (g_object_new (TFE_TYPE_PREF, NULL));
40 }
41 
  • 4-9:定义结构体struct _TfePref。每个TfePref实例都有自己的结构数据。该结构使用到:
    • 一个GSettings实例
    • 一个FontButton实例
  • 11: G_DEFINE_TYPE宏。宏扩展为:
    • 类初始化函数tfe_pref_class_init的声明
    • 实例初始化函数tfe_pref_init的声明
    • 静态变量tfe_pref_parent_class指向父类(GtkDialogClass)结构。
    • tfe_pref_get_type()函数的定义
  • 13-19: tfe_pref_dispose函数。它在销毁过程中被调用,并释放对其他对象的所有引用。有关销毁过程的进一步信息,请参阅第11节。
  • 17: g_clear_object通常用于dispose处理程序。g_clear_object (&pref->gsettings)做:
    • g_object_unref(pref->gsettings)
    • pref->settings = NULL
  • 21-26:实例初始化函数。
  • 23:函数gtk_widget_init_template创建并初始化子构件。这些构件是基于在gtk_widget_class_set_template_from_resource函数中创建的模板创建的。
  • 24:创建GSettings实例并将其指针分配到pref->settings中。实例引用一个id为com.github.ToshioCP.tfe的GSetting。
  • 25:绑定GSettings数据字体和pref->fontbtn (GtkFontButton)的字体属性。元素pref->fontbtn指向GtkFontButton,它是ui文件中fontbtn的实例。该关系是由gtk_widget_class_bind_template_child函数建立的。
  • 28-35:类初始化函数。
  • 32:设置dispose处理程序。
  • 33: gtk_widget_class_set_template_from_resource函数将XML文件(tfepref.ui)中的描述与小部件关联起来。目前还没有创建实例。它只是使类识别对象的结构。这就是为什么XML文件中的顶级标签不是<object>,而是<template>。实例稍后将在gtk_widget_init_template函数中创建。
  • 34: gtk_widget_class_bind_template_child宏绑定结构成员(struct _TfePref中的fontbtn)和XML文件中的id fontbtn。两个名称必须相同。这个绑定是在成员和模板(不是实例)之间。
  • 37-40:函数tfe_pref_new创建一个TfePref实例。

现在,使用这个对话框非常简单。调用者只是创建这个对象并显示它。

TfePref *pref;
pref = tfe_pref_new ();
gtk_window_set_transient_for (GTK_WINDOW (pref), win); /* win is the main window */
gtk_window_present (GTK_WINDOW (pref));

当用户单击关闭按钮时,该实例将自动销毁。这是所有。如果您想再次显示对话框,只需创建并显示它。
在这里插入图片描述

Alert dialog

它几乎与偏好对话框相同。

它的ui文件是:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <interface>
 3   <template class="TfeAlert" parent="GtkDialog">
 4     <property name="title">Are you sure?</property>
 5     <property name="resizable">FALSE</property>
 6     <property name="modal">TRUE</property>
 7     <child internal-child="content_area">
 8       <object class="GtkBox">
 9         <child>
10           <object class="GtkBox">
11             <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
12             <property name="spacing">12</property>
13             <property name="margin-start">12</property>
14             <property name="margin-end">12</property>
15             <property name="margin-top">12</property>
16             <property name="margin-bottom">12</property>
17             <child>
18               <object class="GtkImage">
19                 <property name="icon-name">dialog-warning</property>
20                 <property name="icon-size">GTK_ICON_SIZE_LARGE</property>
21               </object>
22             </child>
23             <child>
24               <object class="GtkLabel" id="lb_alert">
25               </object>
26             </child>
27           </object>
28         </child>
29       </object>
30     </child>
31     <child type="action">
32       <object class="GtkButton" id="btn_cancel">
33         <property name="label">Cancel</property>
34       </object>
35     </child>
36     <child type="action">
37       <object class="GtkButton" id="btn_accept">
38         <property name="label">Close</property>
39       </object>
40     </child>
41     <action-widgets>
42       <action-widget response="cancel" default="true">btn_cancel</action-widget>
43       <action-widget response="accept">btn_accept</action-widget>
44     </action-widgets>
45   </template>
46 </interface>
47 

头文件是:

1 #ifndef __TFE_ALERT_H__
2 #定义__TFE_ALERT_H__
3.
4 #包含<gtk/gtk.h>
5
6 #define TFE_TYPE_ALERT ()
7 G_DECLARE_FINAL_TYPE (tfe_alert, tfe_alert, TFE, ALERT, GtkDialog)
8
9无效
10 tfe_alert_set_message (TfeAlert *alert, const char *message);
11
12个空
tfe_alert_set_button_label (TfeAlert *alert, const char *label);
14
15 GtkWidget *
16 tfe_alert_new (void);
17
18 #endif /* __TFE_ALERT_H__ */
19

有三个公共功能。函数tfe_alert_set_message和tfe_alert_set_button_label设置警告对话框的标签和按钮名称。例如,如果你想在用户试图关闭页面时显示一个警告框,而不保存内容,可以这样设置:

tfe_alert_set_message (alert, "Contents aren't saved yet.\nAre you sure to close?");
tfe_alert_set_button_label (alert, "Close");

函数tfe_alert_new创建了一个TfeAlert对话框。
在这里插入图片描述C源文件如下:

 1 #include <gtk/gtk.h>
 2 #include "tfealert.h"
 3 
 4 struct _TfeAlert
 5 {
 6   GtkDialog parent;
 7   GtkLabel *lb_alert;
 8   GtkButton *btn_accept;
 9 };
10 
11 G_DEFINE_TYPE (TfeAlert, tfe_alert, GTK_TYPE_DIALOG);
12 
13 void
14 tfe_alert_set_message (TfeAlert *alert, const char *message) {
15   gtk_label_set_text (alert->lb_alert, message);
16 }
17 
18 void
19 tfe_alert_set_button_label (TfeAlert *alert, const char *label) {
20   gtk_button_set_label (alert->btn_accept, label);
21 }
22 
23 static void
24 tfe_alert_init (TfeAlert *alert) {
25   gtk_widget_init_template (GTK_WIDGET (alert));
26 }
27 
28 static void
29 tfe_alert_class_init (TfeAlertClass *class) {
30   gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (class), "/com/github/ToshioCP/tfe/tfealert.ui");
31   gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), TfeAlert, lb_alert);
32   gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), TfeAlert, btn_accept);
33 }
34 
35 GtkWidget *
36 tfe_alert_new (void) {
37   return GTK_WIDGET (g_object_new (TFE_TYPE_ALERT, NULL));
38 }
39 

该程序几乎与tfeprefer .c相同。

alert对象的用法如下。

  • 1.编写“response”信号处理程序。
  • 2.创建一个TfeAlert对象。
  • 3.将“response”信号连接到处理程序
  • 4.显示对话框
  • 5.在信号处理程序中,对response-id做一些处理并销毁对话框。

Top-level window

TfeWindow是GtkApplicationWindow的一个子类。

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <interface>
 3   <template class="TfeWindow" parent="GtkApplicationWindow">
 4     <property name="title">file editor</property>
 5     <property name="default-width">600</property>
 6     <property name="default-height">400</property>
 7     <child>
 8       <object class="GtkBox" id="boxv">
 9         <property name="orientation">GTK_ORIENTATION_VERTICAL</property>
10         <child>
11           <object class="GtkBox" id="boxh">
12             <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
13             <child>
14               <object class="GtkLabel" id="dmy1">
15                 <property name="width-chars">10</property>
16               </object>
17             </child>
18             <child>
19               <object class="GtkButton" id="btno">
20                 <property name="label">Open</property>
21                 <property name="action-name">win.open</property>
22               </object>
23             </child>
24             <child>
25               <object class="GtkButton" id="btns">
26                 <property name="label">Save</property>
27                 <property name="action-name">win.save</property>
28               </object>
29             </child>
30             <child>
31               <object class="GtkLabel" id="dmy2">
32                 <property name="hexpand">TRUE</property>
33               </object>
34             </child>
35             <child>
36               <object class="GtkButton" id="btnc">
37                 <property name="label">Close</property>
38                 <property name="action-name">win.close</property>
39               </object>
40             </child>
41             <child>
42               <object class="GtkMenuButton" id="btnm">
43                 <property name="direction">down</property>
44                 <property name="halign">start</property>
45                 <property name="icon-name">open-menu-symbolic</property>
46               </object>
47             </child>
48             <child>
49               <object class="GtkLabel" id="dmy3">
50                 <property name="width-chars">10</property>
51               </object>
52             </child>
53           </object>
54         </child>
55         <child>
56           <object class="GtkNotebook" id="nb">
57             <property name="scrollable">TRUE</property>
58             <property name="hexpand">TRUE</property>
59             <property name="vexpand">TRUE</property>
60           </object>
61         </child>
62       </object>
63     </child>
64   </template>
65 </interface>
66 

除了模板tag和按钮中的“action-name”属性之外,这个XML文件几乎与之前相同。

GtkButton实现了GtkActionable接口,该接口具有action-name属性。如果设置了这个属性,则GtkButton在单击时激活操作。例如,如果点击打开按钮,“win.open”操作将被激活,open_activated处理程序将被调用。

“<Control>o”加速器也使用此操作(参见tfeapplication.c)。如果对按钮使用"clicked"信号,则需要它的信号处理程序。这样,就会有两个处理程序:

  • 按钮上“clicked”信号的处理程序
  • "win.open" action上的“activate”信号处理程序。“<Control>o”加速器连接到该动作

这两个处理程序几乎相同。这是低效的。将按钮连接到动作是减少不必要代码的好方法。

 1 #ifndef __TFE_WINDOW_H__
 2 #define __TFE_WINDOW_H__
 3 
 4 #include <gtk/gtk.h>
 5 
 6 #define TFE_TYPE_WINDOW tfe_window_get_type ()
 7 G_DECLARE_FINAL_TYPE (TfeWindow, tfe_window, TFE, WINDOW, GtkApplicationWindow)
 8 
 9 void
10 tfe_window_notebook_page_new (TfeWindow *win);
11 
12 void
13 tfe_window_notebook_page_new_with_files (TfeWindow *win, GFile **files, int n_files);
14 
15 GtkWidget *
16 tfe_window_new (GtkApplication *app);
17 
18 #endif /* __TFE_WINDOW_H__ */
19 

有三个公共功能。函数tfe_window_notebook_page_new创建一个新的notebook页。这是notebook_page_new的一个包装器函数。它由TfeApplication对象调用。函数tfe_window_notebook_page_new_with_files从给定文件中读取内容,创建notebook页。函数tfe_window_new创建了一个TfeWindow实例。

  1 #include <gtk/gtk.h>
  2 #include "tfewindow.h"
  3 #include "tfenotebook.h"
  4 #include "tfepref.h"
  5 #include "tfealert.h"
  6 
  7 struct _TfeWindow {
  8   GtkApplicationWindow parent;
  9   GtkMenuButton *btnm;
 10   GtkNotebook *nb;
 11   gboolean is_quit;
 12 };
 13 
 14 G_DEFINE_TYPE (TfeWindow, tfe_window, GTK_TYPE_APPLICATION_WINDOW);
 15 
 16 /* alert response signal handler */
 17 static void
 18 alert_response_cb (GtkDialog *alert, int response_id, gpointer user_data) {
 19   TfeWindow *win = TFE_WINDOW (user_data);
 20 
 21   gtk_window_destroy (GTK_WINDOW (alert));
 22   if (response_id == GTK_RESPONSE_ACCEPT) {
 23     if (win->is_quit)
 24       gtk_window_destroy(GTK_WINDOW (win));
 25     else
 26       notebook_page_close (win->nb);
 27   }
 28 }
 29 
 30 static gboolean
 31 close_request_cb (TfeWindow *win) {
 32   TfeAlert *alert;
 33 
 34   if (has_saved_all (GTK_NOTEBOOK (win->nb)))
 35     return false;
 36   else {
 37     win->is_quit = true;
 38     alert = TFE_ALERT (tfe_alert_new ());
 39     gtk_window_set_transient_for (GTK_WINDOW (alert), GTK_WINDOW (win));
 40     tfe_alert_set_message (alert, "Contents aren't saved yet.\nAre you sure to quit?");
 41     tfe_alert_set_button_label (alert, "Quit");
 42     g_signal_connect (GTK_DIALOG (alert), "response", G_CALLBACK (alert_response_cb), win);
 43     gtk_window_present (GTK_WINDOW (alert));
 44     return true;
 45   }
 46 }
 47 
 48 /* ----- action activated handlers ----- */
 49 static void
 50 open_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) {
 51   TfeWindow *win = TFE_WINDOW (user_data);
 52 
 53   notebook_page_open (GTK_NOTEBOOK (win->nb));
 54 }
 55 
 56 static void
 57 save_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) {
 58   TfeWindow *win = TFE_WINDOW (user_data);
 59 
 60   notebook_page_save (GTK_NOTEBOOK (win->nb));
 61 }
 62 
 63 static void
 64 close_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) {
 65   TfeWindow *win = TFE_WINDOW (user_data);
 66   TfeAlert *alert;
 67 
 68   if (has_saved (win->nb))
 69     notebook_page_close (win->nb);
 70   else {
 71     win->is_quit = false;
 72     alert = TFE_ALERT (tfe_alert_new ());
 73     gtk_window_set_transient_for (GTK_WINDOW (alert), GTK_WINDOW (win));
 74     tfe_alert_set_message (alert, "Contents aren't saved yet.\nAre you sure to close?");
 75     tfe_alert_set_button_label (alert, "Close");
 76     g_signal_connect (GTK_DIALOG (alert), "response", G_CALLBACK (alert_response_cb), win);
 77     gtk_widget_show (GTK_WIDGET (alert));
 78   }
 79 }
 80 
 81 static void
 82 new_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) {
 83   TfeWindow *win = TFE_WINDOW (user_data);
 84 
 85   notebook_page_new (GTK_NOTEBOOK (win->nb));
 86 }
 87 
 88 static void
 89 saveas_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) {
 90   TfeWindow *win = TFE_WINDOW (user_data);
 91 
 92   notebook_page_saveas (GTK_NOTEBOOK (win->nb));
 93 }
 94 
 95 static void
 96 pref_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) {
 97   TfeWindow *win = TFE_WINDOW (user_data);
 98   GtkWidget *pref;
 99 
100   pref = tfe_pref_new ();
101   gtk_window_set_transient_for (GTK_WINDOW (pref), GTK_WINDOW (win));
102   gtk_window_present (GTK_WINDOW (pref));
103 }
104 
105 static void
106 close_all_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) {
107   TfeWindow *win = TFE_WINDOW (user_data);
108 
109   if (close_request_cb (win) == false)
110     gtk_window_destroy (GTK_WINDOW (win));
111 }
112 
113 /* --- public functions --- */
114 
115 void
116 tfe_window_notebook_page_new (TfeWindow *win) {
117   notebook_page_new (win->nb);
118 }
119 
120 void
121 tfe_window_notebook_page_new_with_files (TfeWindow *win, GFile **files, int n_files) {
122   int i;
123 
124   for (i = 0; i < n_files; i++)
125     notebook_page_new_with_file (win->nb, files[i]);
126   if (gtk_notebook_get_n_pages (win->nb) == 0)
127     notebook_page_new (win->nb);
128 }
129 
130 static void
131 tfe_window_init (TfeWindow *win) {
132   GtkBuilder *build;
133   GMenuModel *menu;
134 
135   gtk_widget_init_template (GTK_WIDGET (win));
136 
137   build = gtk_builder_new_from_resource ("/com/github/ToshioCP/tfe/menu.ui");
138   menu = G_MENU_MODEL (gtk_builder_get_object (build, "menu"));
139   gtk_menu_button_set_menu_model (win->btnm, menu);
140   g_object_unref(build);
141 
142 /* ----- action ----- */
143   const GActionEntry win_entries[] = {
144     { "open", open_activated, NULL, NULL, NULL },
145     { "save", save_activated, NULL, NULL, NULL },
146     { "close", close_activated, NULL, NULL, NULL },
147     { "new", new_activated, NULL, NULL, NULL },
148     { "saveas", saveas_activated, NULL, NULL, NULL },
149     { "pref", pref_activated, NULL, NULL, NULL },
150     { "close-all", close_all_activated, NULL, NULL, NULL }
151   };
152   g_action_map_add_action_entries (G_ACTION_MAP (win), win_entries, G_N_ELEMENTS (win_entries), win);
153 
154   g_signal_connect (GTK_WINDOW (win), "close-request", G_CALLBACK (close_request_cb), NULL);
155 }
156 
157 static void
158 tfe_window_class_init (TfeWindowClass *class) {
159   gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (class), "/com/github/ToshioCP/tfe/tfewindow.ui");
160   gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), TfeWindow, btnm);
161   gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), TfeWindow, nb);
162 }
163 
164 GtkWidget *
165 tfe_window_new (GtkApplication *app) {
166   return GTK_WIDGET (g_object_new (TFE_TYPE_WINDOW, "application", app, NULL));
167 }
  • 7-12: _TfeWindow结构体。TfeWindow实例指向该结构。
    14: G_DEFINE_TYPE宏。
    17-28: alert_response_cb是TfeAlert对话框的“响应”信号的回调函数。
    21:销毁警告对话框。
    22-27:如果用户点击了accept按钮,它会销毁主窗口或关闭当前的notebook页面。
    30-46: TfeWindow上的一个“关闭请求”信号处理程序。当用户单击关闭按钮(右上角的x形按钮)时,会在窗口关闭前调用处理程序。如果处理程序返回true,则不会调用默认的处理程序,窗口也不会关闭。如果处理程序返回false,则调用默认的处理程序并关闭窗口。
    34:如果has_saved_all返回true,处理程序返回false,窗口将关闭。否则,它会显示一个警告对话框。
    48-111:动作激活信号处理程序。user_data是一个指向TfeWindow实例的指针。
    115-128:公共函数。
    130-155:实例初始化功能。
    135:函数gtk_widget_init_template创建一个子部件并初始化它们。
    137-140:构建和插入菜单。它被插入到菜单按钮中。
    143-152:创建操作并将它们插入窗口。行动的范围是“赢”。
    154:连接“关闭请求”信号和处理程序。
    157-162:类初始化函数。
    159:设置复合小部件模板
    160-161:在子类模板中绑定私有变量。
    164 - 167: tfe_window_new。这个函数创建了一个TfeWindow实例。

TfeApplication

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值