二十、Gtk4-GtkMenuButton, accelerators, font, pango and gsettings

news2024/11/26 2:04:20

本节将重新构造Tfe文本编辑器。

  • 在工具栏上放置了打开、保存和关闭按钮。此外,GtkMenuButton被添加到工具栏中。当点击这个按钮时会显示一个弹出式菜单。在这里,弹出式的含义很广泛,包括下拉式菜单。
  • 新建、另存为、偏好和退出项目被放入菜单中。
    这将最常用的操作绑定到工具栏按钮上。其他的则存储在菜单后面。因此,它更实用。

此外,还增加了以下特性。

  • 加速器。例如,Ctrl-O读取文件并创建一个新页面。
  • 字体选择的首选对话框。
  • 警告对话框,确认关闭或退出,不保存内容。
  • GSettings保留字体选择。
    在这里插入图片描述

Static variables shared by functions in tfeapplication.c

tfe的下一个版本在tfeapplication.c中有静态变量。静态变量很方便,但不利于维护。因此,最终版本将删除它们,并采用另一种方式来覆盖静态变量。

无论如何,下面是关于静态变量的代码。

static GtkDialog *pref; // preference dialog
static GtkFontButton *fontbtn; // font button
static GSettings *settings; // GSetting
static GtkDialog *alert; // alert dialog
static GtkLabel *lb_alert;  // label in the alert dialog
static GtkButton *btn_accept; // accept button in the alert dialog
static GtkCssProvider *provider0; //CSS provider for textview padding
static GtkCssProvider *provider; // CSS provider for fonts

static gulong pref_close_request_handler_id = 0;
static gulong alert_close_request_handler_id = 0;
static gboolean is_quit; // flag whether to quit or close

文件中的任何函数都可以使用这些变量。

Signal tags in ui files

这四个按钮包含在ui文件tfe.ui中。与前面章节不同的是信号标签。以下内容是从tfe.ui中提取的,它描述了打开按钮。

<object class="GtkButton" id="btno">
  <property name="label">Open</property>
  <signal name="clicked" handler="open_cb" swapped="TRUE" object="nb"></signal>
</object>

Signal tag指定了信号、处理程序和user_data对象的名称。

  • 信号名称为“clicked”。
  • 处理程序是“open_cb”。
  • 用户数据对象是“nb”(GtkNoteBook实例)。

swapped属性与g_signal_connect_swap函数具有相同的效果。所以,上面的信号标签的工作原理是一样的:

g_signal_connect_swapped (btno, "clicked", G_CALLBACK (open_cb), nb);

这个函数在处理程序中交换了按钮和第四个参数(btno和nb)。如果使用g_signal_connect,处理程序如下所示:

/* The parameter user_data is assigned with nb */
static void
open_cb (GtkButton *btno, gpointer user_data) { ... ... }

如果使用g_signal_connect_swapped,则交换按钮和用户数据。

/* btno and user_data (nb) are exchanged */
static void
open_cb (GtkNoteBook *nb) { ... ... }

如果button实例在处理程序中无用,那就好了。

当你在ui文件中使用signal标签时,你需要"-WI, --export-dynamic" 选项来编译。你可以通过在meson.build的可执行函数中添加“export_dynamic: true”参数来实现这一点。并从处理程序中删除static类。

void
open_cb (GtkNotebook *nb) {
  notebook_page_open (nb);
}

如果你添加了static,函数就在文件的作用域中,从外部是看不到的。信号tag无法找到函数。

Menu and GkMenuButton

传统的菜单结构是好的。然而,我们并不经常使用所有的菜单或按钮。有些可能根本不会被点击。因此,将一些常用的按钮放在工具栏上,将其他按钮放在菜单中是一个好主意。这样的菜单通常连接到GtkMenuButton。

菜单被描述在menu.ui文件。

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <interface>
 3   <menu id="menu">
 4     <section>
 5       <item>
 6         <attribute name="label">New</attribute>
 7         <attribute name="action">win.new</attribute>
 8       </item>
 9       <item>
10         <attribute name="label">Save As…</attribute>
11         <attribute name="action">win.saveas</attribute>
12       </item>
13     </section>
14     <section>
15       <item>
16         <attribute name="label">Preference</attribute>
17         <attribute name="action">win.pref</attribute>
18       </item>
19     </section>
20     <section>
21       <item>
22         <attribute name="label">Quit</attribute>
23         <attribute name="action">win.close-all</attribute>
24       </item>
25     </section>
26   </menu>
27 </interface>

这里有4个元素,“New”、“Saveas”、“Preference"和"Quit”。

  • “新建”菜单创建一个新的空白页面。
  • “Saveas”菜单将当前页面保存为与原始页面不同的文件名。
  • “偏好”菜单设置偏好项。这个版本的tfe只有字体首选项。
  • “Quit”菜单退出应用程序。

这四个菜单不太常用。这就是为什么它们被放在菜单按钮后面的菜单中。

以上所有操作都具有“win”范围。即使运行第二个应用程序,Tfe也只有一个窗口。因此,在这个应用程序中,作用域“app”和“win”差别很小。

菜单和菜单按钮连接到gtk_menu_button_set_menu_model函数。下面的变量btnm指向一个GtkMenuButton对象。

  build = gtk_builder_new_from_resource ("/com/github/ToshioCP/tfe/menu.ui");
  menu = G_MENU_MODEL (gtk_builder_get_object (build, "menu"));
  gtk_menu_button_set_menu_model (btnm, menu);

Actions and Accelerators

菜单与操作相连接。动作由一个数组和g_action_map_add_action_entries函数定义。

  const GActionEntry win_entries[] = {
    { "open", open_activated, NULL, NULL, NULL },
    { "save", save_activated, NULL, NULL, NULL },
    { "close", close_activated, NULL, NULL, NULL },
    { "new", new_activated, NULL, NULL, NULL },
    { "saveas", saveas_activated, NULL, NULL, NULL },
    { "pref", pref_activated, NULL, NULL, NULL },
    { "close-all", close_all_activated, NULL, NULL, NULL }
  };
  g_action_map_add_action_entries (G_ACTION_MAP (win), win_entries, G_N_ELEMENTS (win_entries), nb);

有7个操作:打开、保存、关闭、新建、保存、优先和关闭所有。但是只有四份菜单。New、saveas、pref和close-all操作分别对应New、saveas、preference和quit菜单。“打开”、“保存”和“关闭”三个操作没有对应的菜单。它们是必要的吗?是的,因为它们对应于加速器。

加速器是一种快捷键功能。它们由数组和gtk_application_set_accs_for_action函数定义。

  struct {
    const char *action;
    const char *accels[2];
  } action_accels[] = {
    { "win.open", { "<Control>o", NULL } },
    { "win.save", { "<Control>s", NULL } },
    { "win.close", { "<Control>w", NULL } },
    { "win.new", { "<Control>n", NULL } },
    { "win.saveas", { "<Shift><Control>s", NULL } },
    { "win.close-all", { "<Control>q", NULL } },
  };

  for (i = 0; i < G_N_ELEMENTS(action_accels); i++)
    gtk_application_set_accels_for_action(GTK_APPLICATION(app), action_accels[i].action, action_accels[i].accels);

这段代码有点复杂。数组action-accels[]是一个结构体数组。其结构如下:

  struct {
    const char *action;
    const char *accels[2];
  }

成员操作是一个字符串。成员accels是一个包含两个字符串的数组。例如,

{ "win.open", { "<Control>o", NULL } },

这是数组action_accels的第一个元素。

  • 成员行为是"win.open"。这指定了"open"操作属于window对象。
  • 成员accels是一个由两个字符串组成的数组,"<Control>o"和NULL。第一个字符串指定了一个键组合。控制键和“o”。如果你一直按control键并按o键,就会激活win.open动作。第二个字符串NULL(或零)表示列表(数组)结束。你可以定义多个加速键,列表必须以NULL(零)结尾。如果你想这样做,数组长度必须大于等于3。解析器识别"<control>o" "<Shift><Alt>F2", "<Ctrl>minus"等等。如果你想使用类似“-”的符号键,请使用“-”。小写字母和符号(字符代码)之间的这种关系在GTK 4源代码中的gdkkeysyms.h中指定。

Open, save and close handlers

有两个打开的处理程序。一个是按钮上单击信号的处理程序。另一个是动作上的激活信号。

Open button (clicked)> open.cb handler
Ctrl-o key (accerelator) (key down)> open action activated ==> open_activated handler

但这两个处理程序的行为是相同的。open_activate调用open_cb。

 1 void
 2 open_cb (GtkNotebook *nb) {
 3   notebook_page_open (nb);
 4 }
 5 
 6 static void
 7 open_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) {
 8   GtkNotebook *nb = GTK_NOTEBOOK (user_data);
 9   open_cb (nb);
10 }

保存和关闭处理程序也是如此。

Saveas handler

TfeTextView有一个saveas函数。因此,我们只需在tfenotebook.c中编写一个包装器函数。

 1 static TfeTextView *
 2 get_current_textview (GtkNotebook *nb) {
 3   int i;
 4   GtkWidget *scr;
 5   GtkWidget *tv;
 6 
 7   i = gtk_notebook_get_current_page (nb);
 8   scr = gtk_notebook_get_nth_page (nb, i);
 9   tv = gtk_scrolled_window_get_child (GTK_SCROLLED_WINDOW (scr));
10   return TFE_TEXT_VIEW (tv);
11 }
12 
13 void
14 notebook_page_saveas (GtkNotebook *nb) {
15   g_return_if_fail(GTK_IS_NOTEBOOK (nb));
16 
17   TfeTextView *tv;
18 
19   tv = get_current_textview (nb);
20   tfe_text_view_saveas (TFE_TEXT_VIEW (tv));
21 }

函数get_current_textview和之前一样。函数notebook_page_saveas只是调用了tfe_text_view_saveas。

在tfeapplication.c中,保存处理程序只调用notebook_page_saveas。

1 static void
2 saveas_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) {
3   GtkNotebook *nb = GTK_NOTEBOOK (user_data);
4   notebook_page_saveas (nb);
5 }

Preference and alert dialog

Preference dialog

首选项对话框xml定义被添加到tfe.ui中。

<object class="GtkDialog" id="pref">
  <property name="title">Preferences</property>
  <property name="resizable">FALSE</property>
  <property name="modal">TRUE</property>
  <property name="transient-for">win</property>
  <child internal-child="content_area">
    <object class="GtkBox" id="content_area">
      <child>
        <object class="GtkBox" id="pref_boxh">
          <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
          <property name="spacing">12</property>
          <property name="margin-start">12</property>
          <property name="margin-end">12</property>
          <property name="margin-top">12</property>
          <property name="margin-bottom">12</property>
          <child>
            <object class="GtkLabel" id="fontlabel">
              <property name="label">Font:</property>
              <property name="xalign">1</property>
            </object>
          </child>
          <child>
            <object class="GtkFontButton" id="fontbtn">
            </object>
          </child>
        </object>
      </child>
    </object>
  </child>
</object>
  • 偏好对话框是独立的对话框。它不是顶级GtkApplicationwindow的后代部件。因此,对话框对象周围没有子标签。
  • 这个对话框有四个属性。GtkDialog是GtkWindow的一个子对象(不是子构件),因此它继承了GtkWindow的所有属性。Title、resizable、modal和transient-for属性继承自GtkWindow。Transient-for指定一个临时的父窗口,对话框的位置就是基于这个窗口。
  • 标签<child internal-child="content_area">放在对话框内容的顶部。你需要用content_area id指定一个GtkBox对象标签。这个对象在gtkdialog.ui((复合部件))中定义。但您需要在子标签中再次定义它。复合小部件将在下一节中解释。有关GtkDialog ui标签的更多信息,请参阅:
    • GTK 4 API reference – GtkBuilder
    • GTK 4 API reference – GtkDialog
    • GtkDialog ui file
  • 在内容区域中有一个水平的GtkBox。
  • GtkLabel和GtkFontButton在GtkBox中。

我希望首选项对话框在应用程序的生命周期内保持活跃。因此,有必要从对话框中捕获“close-request”信号并停止该信号的传播。(当点击窗口右上角的x按钮关闭按钮时,会发出这个信号。)这是通过信号处理程序返回TRUE来完成的。

static gboolean
dialog_close_cb (GtkDialog *dialog) {
  gtk_widget_set_visible (GTK_WIDGET (dialog), false);
  return TRUE;
}
... ...
( in app_startup function )
pref_close_request_handler_id = g_signal_connect (GTK_DIALOG (pref), "close-request", G_CALLBACK (dialog_close_cb), NULL);
... ...

信号发射一般分为五个阶段。

  • 如果信号的标志是G_SIGNAL_RUN_FIRST,则调用默认处理程序。在注册信号时设置默认处理程序。它不同于用户信号处理程序,简称为信号处理程序,由g_signal_connectseries函数连接。默认处理程序可以在阶段1、3或5中调用。大多数默认处理程序是G_SIGNAL_RUN_FIRST或G_SIGNAL_RUN_LAST。
  • 调用信号处理程序,除非通过g_signal_connect_after连接。
  • 如果信号的标志是G_SIGNAL_RUN_LAST,则调用默认处理程序。
  • 如果通过g_signal_connect_after连接,则调用信号处理程序。
  • 如果信号的标志是G_SIGNAL_RUN_CLEANUP,则调用默认处理程序。

“close-request”信号是G_SIGNAL_RUN_LAST。因此,调用的顺序是:

  1. 信号处理程序dialog_close_cb
  2. 默认的处理程序

如果用户信号处理程序返回TRUE,那么将停止调用其他处理程序。因此,上面的程序阻止了对默认处理程序的调用,并停止了对话框的关闭过程。

下列代码是从tfeapplication.c中提取的。

static gulong pref_close_request_handler_id = 0;
static gulong alert_close_request_handler_id = 0;
... ...
static gboolean
dialog_close_cb (GtkDialog *dialog, gpointer user_data) {
  gtk_widget_set_visible (GTK_WIDGET (dialog), false);
  return TRUE;
}
... ...
static void
pref_activated (GSimpleAction *action, GVariant *parameter, gpointer nb) {
  gtk_window_present (GTK_WINDOW (pref));
}
... ...
void
app_shutdown (GApplication *application) {
   ... ... ...
  if (pref_close_request_handler_id > 0)
    g_signal_handler_disconnect (pref, pref_close_request_handler_id);
  gtk_window_destroy (GTK_WINDOW (pref));
   ... ... ...
}
... ...
static void
tfe_startup (GApplication *application) {
  ... ...
  pref = GTK_DIALOG (gtk_builder_get_object (build, "pref"));
  pref_close_request_handler_id = g_signal_connect (GTK_DIALOG (pref), "close-request", G_CALLBACK (dialog_close_cb), NULL);
  ... ... 
}
  • 首选项对话框上的close- request信号连接到处理程序dialog_close_cb。它改变了对话框的close行为。当信号发出时,可见性被设置为false,默认的处理程序被取消。因此,对话框消失但存在。
  • 处理程序pref_activate显示首选项对话框。
  • 关闭处理程序app_shutdown将处理程序与“close-request”信号断开连接,并销毁pref window。

Alert dialog

如果用户没有保存就关闭了一个页面,建议显示一个警告,让用户确认。警报对话框就是在这种情况下使用的。

  <object class="GtkDialog" id="alert">
    <property name="title">Are you sure?</property>
    <property name="resizable">FALSE</property>
    <property name="modal">TRUE</property>
    <property name="transient-for">win</property>
    <child internal-child="content_area">
      <object class="GtkBox">
        <child>
          <object class="GtkBox">
            <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
            <property name="spacing">12</property>
            <property name="margin-start">12</property>
            <property name="margin-end">12</property>
            <property name="margin-top">12</property>
            <property name="margin-bottom">12</property>
            <child>
              <object class="GtkImage">
                <property name="icon-name">dialog-warning</property>
                <property name="icon-size">GTK_ICON_SIZE_LARGE</property>
              </object>
            </child>
            <child>
              <object class="GtkLabel" id="lb_alert">
              </object>
            </child>
          </object>
        </child>
      </object>
    </child>
    <child type="action">
      <object class="GtkButton" id="btn_cancel">
        <property name="label">Cancel</property>
      </object>
    </child>
    <child type="action">
      <object class="GtkButton" id="btn_accept">
        <property name="label">Close</property>
      </object>
    </child>
    <action-widgets>
      <action-widget response="cancel" default="true">btn_cancel</action-widget>
      <action-widget response="accept">btn_accept</action-widget>
    </action-widgets>
    <signal name="response" handler="alert_response_cb" swapped="NO" object="nb"></signal>
  </object>

这个ui文件描述了警告对话框。有些部分与选项对话框相同。在内容区域中有两个对象,GtkImage和GtkLabel。

GtkImage显示一个图像。图片可以来自文件、资源、图标主题等。上图显示了当前图标主题中的一个图标。您可以通过gtk4-icon-browser查看主题中的图标。

$ gtk4-icon-browser

“对话框警告”图标类似于下面这样。
在这里插入图片描述这些是我亲手做的。警告对话框中的真实图像更漂亮。

-GtkLabel lb_alert还没有文本。一个警告消息将被插入到程序中。

有两个子标签具有“action”类型。它们是位于操作区域的按钮对象。Action-widgets标签描述按钮的操作。如果单击按钮btn_cancel,则发出带有cancel响应(GTK_RESPONSE_CANCEL)的响应信号。如果单击按钮btn_accept,则用accept响应(GTK_RESPONSE_ACCEPT)发出响应信号。响应信号连接到alert_response_cb处理程序。

在应用程序存活期间,警告对话框保持活跃。“close-request”信号被处理程序停止

Alert dialog and close handlers

如果用户关闭页面或退出应用程序而没有保存内容,则会出现警告对话框。有4个处理程序,close_cb, close_activated, win_close_request_cb和close_all_activated。前两个函数在notebook页面关闭时调用。其他的在主窗口关闭时调用——因此,所有的notebook都关闭了。

  • close button => close_cb (=> alert dialog)
  • Ctrl-W => close_activated => close_cb (=> alert dialog)
  • Close button (x button at the right top of the main window) => win_close_request_cb (=> alert dialog)
  • Quit menu or Ctrl-Q => close_all_activated => win_close_request_cb (=> alert dialog)
static gboolean is_quit;
... ...
static gboolean
win_close_request_cb (GtkWindow *win, GtkNotebook *nb) {
  is_quit = true;
  if (has_saved_all (nb))
    return false;
  else {
    gtk_label_set_text (lb_alert, "Contents aren't saved yet.\nAre you sure to quit?");
    gtk_button_set_label (btn_accept, "Quit");
    gtk_window_present (GTK_WINDOW (alert));
    return true;
  }
}
... ...
void
close_cb (GtkNotebook *nb) {
  is_quit = false;
  if (has_saved (GTK_NOTEBOOK (nb)))
    notebook_page_close (GTK_NOTEBOOK (nb));
  else {
    gtk_label_set_text (lb_alert, "Contents aren't saved yet.\nAre you sure to close?");
    gtk_button_set_label (btn_accept, "Close");
    gtk_window_present (GTK_WINDOW (alert));
  }
}
... ...
static void
close_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) {
  GtkNotebook *nb = GTK_NOTEBOOK (user_data);
  close_cb (nb);
}
... ...
static void
close_all_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) {
  GtkNotebook *nb = GTK_NOTEBOOK (user_data);
  GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (nb), GTK_TYPE_WINDOW);

  if (! win_close_request_cb (GTK_WINDOW (win), nb)) // checks whether contents are saved
    gtk_window_destroy (GTK_WINDOW (win));
}
... ...
void
alert_response_cb (GtkDialog *alert, int response_id, gpointer user_data) {
  GtkNotebook *nb = GTK_NOTEBOOK (user_data);
  GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (nb), GTK_TYPE_WINDOW);

  gtk_widget_set_visible (GTK_WIDGET (alert), false);
  if (response_id == GTK_RESPONSE_ACCEPT) {
    if (is_quit)
      gtk_window_destroy (GTK_WINDOW (win));
    else
      notebook_page_close (nb);
  }
}
... ...
static void
app_startup (GApplication *application) {
  ... ...
  build = gtk_builder_new_from_resource ("/com/github/ToshioCP/tfe/tfe.ui");
  win = GTK_APPLICATION_WINDOW (gtk_builder_get_object (build, "win"));
  ... ...
  g_signal_connect (GTK_WINDOW (win), "close-request", G_CALLBACK (win_close_request_cb), nb);
  ... ...
}

当用户试图退出应用程序时,静态变量is_quit为true,否则为false。

  • 当用户单击关闭按钮时,close_cb处理程序将被调用。处理程序将is_quit设置为false。如果当前页已经保存,函数has_saved返回true。如果为true,它调用notebook_page_close关闭当前页。否则,显示警告对话框。对话框的响应信号连接到处理程序alert_response_cb。它首先隐藏对话框。然后检查response_id。如果是GTK_RESPONSE_ACCEPT,表示用户单击了关闭按钮,则关闭当前页面。否则它什么都不做。
  • 当用户按下"Ctrl-w"时,close_activated处理程序会被调用。它只是调用close_cb。
  • 当用户点击主窗口的关闭按钮时,窗口会发出“关闭请求”信号。该信号事先已经连接到win_close_request_cb处理程序。连接是在应用程序上的启动处理程序中完成的。win_close_request_cb处理程序将is_quit设置为true。如果has_save_all返回true,它就返回false,这意味着信号移动到默认处理程序,主窗口将关闭。否则,显示警告对话框并返回true。因此,信号停止,默认处理程序不会被调用。但是如果用户点击了警告对话框中的accept按钮,响应处理程序alert_response_cb会调用gtk_window_destroy,主窗口将被关闭。
  • 当用户单击quit菜单或按下"Ctrl-q"时,则调用close_all_activated处理程序。它调用了win_close_request_cb。如果返回值为false,它会销毁主窗口。否则它什么也不做,但是win_close_request_cb显示了警告对话框。

Has_saved and has_saved_all functions

这两个函数定义在文件tfenotebook.c中。它们是公共函数。

 1 gboolean
 2 has_saved (GtkNotebook *nb) {
 3   g_return_val_if_fail (GTK_IS_NOTEBOOK (nb), false);
 4 
 5   TfeTextView *tv;
 6   GtkTextBuffer *tb;
 7 
 8   tv = get_current_textview (nb);
 9   tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
10   if (gtk_text_buffer_get_modified (tb))
11     return false;
12   else
13     return true;
14 }
15 
16 gboolean
17 has_saved_all (GtkNotebook *nb) {
18   g_return_val_if_fail (GTK_IS_NOTEBOOK (nb), false);
19 
20   int i, n;
21   GtkWidget *scr;
22   GtkWidget *tv;
23   GtkTextBuffer *tb;
24 
25   n = gtk_notebook_get_n_pages (nb);
26   for (i = 0; i < n; ++i) {
27     scr = gtk_notebook_get_nth_page (nb, i);
28     tv = gtk_scrolled_window_get_child (GTK_SCROLLED_WINDOW (scr));
29     tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
30     if (gtk_text_buffer_get_modified (tb))
31       return false;
32   }
33   return true;
34 }
  • 1-14: has_saved函数。
  • 10:如果缓冲区的内容已经被修改,函数gtk_text_buffer_get_modified返回true,因为modified标志设置为false。在以下情况下,该标志设置为false:
    • 创建缓冲区。
    • 缓冲区的内容被替换
    • 缓冲区的内容保存到文件中。
  • 10-13:如果保存了当前页面的内容且没有对其进行任何修改,则此函数返回true。如果当前页面被修改过且没有保存,则返回false。
  • 16-34: has_saved_all函数。该函数类似于has_saved函数。如果所有的页都保存了,则返回true。如果在上次保存页之后,至少有一页被修改过,则返回false。

Notebook page tab

如果你有一些页面,并将它们编辑在一起,你可能会弄不清需要保存哪个文件。普通文件编辑器在修改内容时更改选项卡。GtkTextBuffer提供了"modified-changed"信号来通知修改。

static void
notebook_page_build (GtkNotebook *nb, GtkWidget *tv, char *filename) {
  ... ...
  g_signal_connect (GTK_TEXT_VIEW (tv), "change-file", G_CALLBACK (file_changed_cb), NULL);
  g_signal_connect (tb, "modified-changed", G_CALLBACK (modified_changed_cb), tv);
}

在建立页时,将“change-file”和“modified-changed”信号分别连接到file_changed_cb和modified_changed_cb处理程序。

 1 static void
 2 file_changed_cb (TfeTextView *tv) {
 3   GtkWidget *nb =  gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_NOTEBOOK);
 4   GtkWidget *scr;
 5   GtkWidget *label;
 6   GFile *file;
 7   char *filename;
 8 
 9   if (! GTK_IS_NOTEBOOK (nb)) /* tv not connected to nb yet */
10     return;
11   file = tfe_text_view_get_file (tv);
12   scr = gtk_widget_get_parent (GTK_WIDGET (tv));
13   if (G_IS_FILE (file)) {
14     filename = g_file_get_basename (file);
15     g_object_unref (file);
16   } else
17     filename = get_untitled ();
18   label = gtk_label_new (filename);
19   gtk_notebook_set_tab_label (GTK_NOTEBOOK (nb), scr, label);
20 }
21 
22 static void
23 modified_changed_cb (GtkTextBuffer *tb, gpointer user_data) {
24   TfeTextView *tv = TFE_TEXT_VIEW (user_data);
25   GtkWidget *scr = gtk_widget_get_parent (GTK_WIDGET (tv));
26   GtkWidget *nb =  gtk_widget_get_ancestor (GTK_WIDGET (tv), GTK_TYPE_NOTEBOOK);
27   GtkWidget *label;
28   const char *filename;
29   char *text;
30 
31   if (! GTK_IS_NOTEBOOK (nb)) /* tv not connected to nb yet */
32     return;
33   else if (gtk_text_buffer_get_modified (tb)) {
34     filename = gtk_notebook_get_tab_label_text (GTK_NOTEBOOK (nb), scr);
35     text = g_strdup_printf ("*%s", filename);
36     label = gtk_label_new (text);
37     g_free (text);
38     gtk_notebook_set_tab_label (GTK_NOTEBOOK (nb), scr, label);
39   } else
40     file_changed_cb (tv);
41 }

file_changed_cb处理程序为notebook的page标记提供了一个新文件名。modified_changed_cb处理程序在文件名的开头插入一个星号。这是一个标志,表明文件已经被修改,但还没有保存。

  • 1-20: file_changed_cb处理程序。
  • 9-10:如果信号是在页面构建过程中发出的,那么tv可能不是nb的后代。也就是说,没有对应于tv的页面。这样就不需要修改选项卡的名称,因为不存在选项卡。
  • 13-15:如果file是GFile,那么它获取文件名并释放对file的引用。
  • 16-17:否则,它将"Untitled"(+一个数字)赋值给filename
  • 18-19:用文件名创建GtkLabel,并用GtkLabel设置页面的选项卡。
  • 22-41: modified_changed_cb处理程序。
  • 31-32:如果tv不是nb的后代,那么什么都不需要做。
  • 33-35:如果内容被修改了,那么它获取选项卡的文本并在文本的开头添加星号。
  • 36-38:设置带有星号的文件名选项卡
  • 39-40:否则调用file_changed_cb并更新文件名(不带星号)。

Font

GtkFontButton and GtkFontChooser

GtkFontButton是一个按钮类,它显示当前字体,用户可以使用按钮更改字体。如果用户点击按钮,它会打开一个字体选择对话框。用户可以在对话框中改变字体(字体族、样式、粗细和大小)。然后按钮保持新的字体并显示它。

该按钮在应用程序启动过程中使用构建器设置。信号“font-set”连接到处理程序font_set_cb。当用户选择字体时,会发出信号“font-set”。

static void
font_set_cb (GtkFontButton *fontbtn) {
  PangoFontDescription *pango_font_desc;
  char *s, *css;

  pango_font_desc = gtk_font_chooser_get_font_desc (GTK_FONT_CHOOSER (fontbtn));
  s = pfd2css (pango_font_desc); // converts Pango Font Description into CSS style string
  css = g_strdup_printf ("textview {%s}", s);
  gtk_css_provider_load_from_data (provider, css, -1);
  g_free (s);
  g_free (css);
}
... ...
static void
app_startup (GApplication *application) {
  ... ...
  fontbtn = GTK_FONT_BUTTON (gtk_builder_get_object (build, "fontbtn"));
  ... ...
  g_signal_connect (fontbtn, "font-set", G_CALLBACK (font_set_cb), NULL);
  ... ...
}

GtkFontChooser是由GtkFontButton实现的接口。函数gtk_font_chooser_get_font_desc获取当前选择字体的PangoFontDescription。PangoFontDescription包含字体族、样式、粗细和大小。函数pfd2css将它们转换为CSS样式字符串。转换如下所示。

PangoFontDescription:
font-family: Monospace
font-style: normal
font-weight: normal
font-size: 12pt
=>
“font-family: Monospace; font-style: normal; font-weight: 400; font-size: 12pt;”

然后,font_set_cb创建一个CSS字符串并将其放入provider实例中。提供程序已提前添加到默认显示。因此,该处理程序立即影响textview内容的字体。

CSS and Pango

pfd2css.c中包含了从PangoFontDescription到CSS的转换器。文件名的意思是:

  • pfd => PangoFontDescripter
  • 2 => to
  • css => CSS (Cascade Style Sheet)
    文件中的所有公共函数都有“pdf2css”前缀。
 1 #include <pango/pango.h>
 2 #include "pfd2css.h"
 3 
 4 // Pango font description to CSS style string
 5 // Returned string is owned by caller. The caller should free it when it is useless.
 6 
 7 char*
 8 pfd2css (PangoFontDescription *pango_font_desc) {
 9   char *fontsize;
10 
11   fontsize = pfd2css_size (pango_font_desc);
12   return g_strdup_printf ("font-family: \"%s\"; font-style: %s; font-weight: %d; font-size: %s;",
13               pfd2css_family (pango_font_desc), pfd2css_style (pango_font_desc),
14               pfd2css_weight (pango_font_desc), fontsize);
15   g_free (fontsize); 
16 }
17 
18 // Each element (family, style, weight and size)
19 
20 const char*
21 pfd2css_family (PangoFontDescription *pango_font_desc) {
22   return pango_font_description_get_family (pango_font_desc);
23 }
24 
25 const char*
26 pfd2css_style (PangoFontDescription *pango_font_desc) {
27   PangoStyle pango_style = pango_font_description_get_style (pango_font_desc);
28   switch (pango_style) {
29   case PANGO_STYLE_NORMAL:
30     return "normal";
31   case PANGO_STYLE_ITALIC:
32     return "italic";
33   case PANGO_STYLE_OBLIQUE:
34     return "oblique";
35   default:
36     return "normal";
37   }
38 }
39 
40 int
41 pfd2css_weight (PangoFontDescription *pango_font_desc) {
42   PangoWeight pango_weight = pango_font_description_get_weight (pango_font_desc);
43   switch (pango_weight) {
44   case PANGO_WEIGHT_THIN:
45     return 100;
46   case PANGO_WEIGHT_ULTRALIGHT:
47     return 200;
48   case PANGO_WEIGHT_LIGHT:
49     return 300;
50   case PANGO_WEIGHT_SEMILIGHT:
51     return 350;
52   case PANGO_WEIGHT_BOOK:
53     return 380;
54   case PANGO_WEIGHT_NORMAL:
55     return 400; /* or "normal" */
56   case PANGO_WEIGHT_MEDIUM:
57     return 500;
58   case PANGO_WEIGHT_SEMIBOLD:
59     return 600;
60   case PANGO_WEIGHT_BOLD:
61     return 700; /* or "bold" */
62   case PANGO_WEIGHT_ULTRABOLD:
63     return 800;
64   case PANGO_WEIGHT_HEAVY:
65     return 900;
66   case PANGO_WEIGHT_ULTRAHEAVY:
67     return 900; /* In PangoWeight definition, the weight is 1000. But CSS allows the weight below 900. */
68   default:
69     return 400; /* "normal" */
70   }
71 }
72 
73 char *
74 pfd2css_size (PangoFontDescription *pango_font_desc) {
75   if (pango_font_description_get_size_is_absolute (pango_font_desc))
76     return g_strdup_printf ("%dpx", pango_font_description_get_size (pango_font_desc) / PANGO_SCALE);
77   else
78     return g_strdup_printf ("%dpt", pango_font_description_get_size (pango_font_desc) / PANGO_SCALE);
79 }
  • 1: Pango的公共函数、常量和结构定义在pango/pango.h中。
  • 2:包含pdf2css.h使得在pdf2css.c文件的任何地方调用公共函数成为可能。因为头文件包含了所有公共函数的声明。
  • 7-16: pdf2css功能。这个函数从作为参数的PangoFontDescription实例中获取字体族、样式、粗细和大小。它将它们构建成一根弦。返回的字符串归调用者所有。调用者应该在字符串无用时释放它。
  • 20-23: pfd2css_famili函数。这个函数从PangoFontDescription实例中获取font-family字符串。该字符串属于PFD实例,因此调用者不能修改或释放该字符串。
  • 25-38: pdf2css_style函数。这个函数从PangoFontDescription实例中获取font-style字符串。字符串是静态的,调用者不能修改或释放它。
  • 40-71: pfd2css_weight函数。这个函数从PangoFontDescription实例中获取font-weight整数值。取值范围为100 ~ 900。它定义在[CSS字体模块第3级](…/src/CSS字体模块(第三级)规范。
    • 100 -薄
    • 200 -超短(超短)
    • 300 -短
    • 400 -正常
    • 500 -中等
    • 600 -半加粗(半加粗)
    • 700 -粗体
    • 800 -加粗(超加粗)
    • 900 -黑色(重)
  • 73-79: pdf2css_size函数。这个函数从PangoFontDescription实例中获取字体大小字符串。字符串是由调用者拥有的,因此调用者应该在它无用时释放它。PangoFontDescription具有绝对大小或非绝对大小。
    • 如果是绝对的,则大小以设备为单位。
    • 如果它是非绝对的,则大小以点为单位。
  • 设备单位的定义依赖于输出设备。它通常是屏幕的像素,打印机的点。
  • Pango将尺寸作为自己的尺寸。常量PANGO_SCALE是用于Pango距离的尺寸和设备单位之间的比例。PANGO_SCALE当前的值是1024,但将来可能会改变。在设置字体大小时,设备单位总是被认为是点而不是像素。如果字体大小为12pt,则pango中的大小为12PANGO_SCALE=121024=12288。

有关更多信息,请参阅Pango API参考。

GSettings

我们希望在应用程序退出后保持字体数据。有一些方法可以实现它。

  • 制作配置文件。例如,一个文本文件“~/.config/tfe/font.cfg”保存字体信息。
  • 使用GSettings对象。GSettings的基本思想类似于configuration file。配置信息数据放入数据库文件中。

GSettings简单易用,但概念有点难以理解。这一小节首先描述概念,然后如何编程。

GSettings schema

GSettings schema描述了一组键、值类型和其他一些信息。GSettings对象使用这种模式,它将键的值写入/读取到数据库中的正确位置。

  • schema有id。id不能重复。我们经常使用与应用程序id相同的字符串,但是schema id和应用程序id是不同的。您可以使用不同于应用程序id的名称。schema id是由句点分隔的字符串。例如,com.github.ToshioCP.Tfe”是正确的schema id。
  • schema通常有一个路径path。路径是数据库中的一个位置。每个键都存储在该路径下。例如,如果在路径/com/github/ToshioCP/tfe/中定义了一个key = font,那么该key’s在数据库中的位置就是/com/github/ToshioCP/tfe/font。Path是一个以斜杠(/)开始和结束的字符串。它由斜线分隔。
  • GSettings将信息保存为键值(key-value)样式。Key是一个字符串,以小写字母开始,然后是小写字母、数字或破折号(-),以小写字母或数字结束。不允许连续的破折号。值可以是任何类型。GSettings将值存储为GVariant类型,可以是整型、双精度、布尔型、字符串,也可以是数组等复杂类型。值的类型需要在模式中定义。
  • 每个键都需要设置一个默认值。
  • 可以为每个键设置可选的摘要和描述。

schema以XML格式描述。例如,

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <schemalist>
 3   <schema path="/com/github/ToshioCP/tfe/" id="com.github.ToshioCP.tfe">
 4     <key name="font" type="s">
 5       <default>'Monospace 12'</default>
 6       <summary>Font</summary>
 7       <description>A font to be used for textview.</description>
 8     </key>
 9   </schema>
10 </schemalist>
  • 4: type属性为“s”。它是GVariant类型的字符串。对于GVariant类型字符串,请参阅GLib API参考——GVariant类型字符串。其他常见的类型有:
    • “b”:gboolean
    • “i”:gint32。
    • “d”:double。

更多资料请参阅:

  • GLib API Reference – GVariant Format Strings
  • GLib API Reference – GVariant Text Format
  • GLib API Reference – GVariant
  • GLib API Reference – VariantType

gsettings

首先,让我们尝试gsettings应用程序。它是GSettings的配置工具。

$ gsettings help
Usage:
  gsettings --version
  gsettings [--schemadir SCHEMADIR] COMMAND [ARGS?]

Commands:
  help                      Show this information
  list-schemas              List installed schemas
  list-relocatable-schemas  List relocatable schemas
  list-keys                 List keys in a schema
  list-children             List children of a schema
  list-recursively          List keys and values, recursively
  range                     Queries the range of a key
  describe                  Queries the description of a key
  get                       Get the value of a key
  set                       Set the value of a key
  reset                     Reset the value of a key
  reset-recursively         Reset all values in a given schema
  writable                  Check if a key is writable
  monitor                   Watch for changes

Use "gsettings help COMMAND" to get detailed help.

List schemas.

$ gsettings list-schemas
org.gnome.rhythmbox.podcast
ca.desrt.dconf-editor.Demo.Empty
org.gnome.gedit.preferences.ui
org.gnome.evolution-data-server.calendar
org.gnome.rhythmbox.plugins.generic-player

... ...

每一行都是一个schema id。每个schema都有一个key-value配置数据。你可以使用list- recur命令查看它们。让我们看一下org.gnome.calculator模式的键和值。

gsettings list-recursively org.gnome.calculator
org.gnome.calculator source-currency ''
org.gnome.calculator source-units 'degree'
org.gnome.calculator button-mode 'basic'
org.gnome.calculator target-currency ''
org.gnome.calculator base 10
org.gnome.calculator angle-units 'degrees'
org.gnome.calculator word-size 64
org.gnome.calculator accuracy 9
org.gnome.calculator show-thousands false
org.gnome.calculator window-position (122, 77)
org.gnome.calculator refresh-interval 604800
org.gnome.calculator target-units 'radian'
org.gnome.calculator precision 2000
org.gnome.calculator number-format 'automatic'
org.gnome.calculator show-zeroes false

GNOME计算器使用此schema。运行计算器并更改schema,然后再次检查schema。

$ gnome-calculator

修改为“高级模式”并退出。
在这里插入图片描述运行gsettings并检查button-mode的值。

gsettings list-recursively org.gnome.calculator

... ...

org.gnome.calculator button-mode 'advanced'

... ...

现在我们知道GNOME计算器使用了gsettings,它将button-mode键设置为“advanced”。即使计算器退出,value仍然存在。因此,当计算器再次运行时,它将显示为高级模式。

glib-compile-schemas

GSettings schema 使用XML格式指定。XML模式文件必须具有文件名扩展名 .gschema.xml。下面是应用程序tfe的XML schema文件。

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <schemalist>
 3   <schema path="/com/github/ToshioCP/tfe/" id="com.github.ToshioCP.tfe">
 4     <key name="font" type="s">
 5       <default>'Monospace 12'</default>
 6       <summary>Font</summary>
 7       <description>A font to be used for textview.</description>
 8     </key>
 9   </schema>
10 </schemalist>

文件名是"com.github.ToshioCP.tfe.gschema.xml"。Schema XML文件名通常是Schema id后面加上".gschema.xml "后缀。您可以使用与模式id不同的名称,但不建议这样做。

  • 2:顶层元素为<schemalist>
  • 3: schema标签具有path和id属性。路径确定settings存储在概念全局设置树中的位置。id标识模式。
  • 4: Key标签有两个属性。Name是键的名称。Type是键值的类型,它是一个GVariant格式字符串。
  • 5:默认值font = Monospace 12
    6:概述和描述元素用于描述key。它们是可选的,但建议将它们添加到XML文件中。

XML文件由glib-compile-schemas编译。编译时,glib-compile-schemas编译所有在给定的目录中具有“.gschema.xml”文件扩展名作为参数的XML文件。它将XML文件转换为二进制文件gschema .compiled。假设上面的XML文件在tfe6目录下。

$ glib-compile-schemas tfe6

然后,在tfe6下生成gschemas.compiled。测试应用时,设置环境变量GSETTINGS_SCHEMA_DIR,这样GSettings对象就能找到gschemas.compiled。

$ GSETTINGS_SCHEMA_DIR=(the directory gschemas.compiled is located):$GSETTINGS_SCHEMA_DIR (your application name)

GSettings对象通过以下步骤查找此文件。

  • 它搜索在环境变量XDG_DATA_DIRS中指定的所有目录的glib-2.0/schemas子目录。常见的目录是/usr/share/glib-2.0/schemas和/usr/local/share/glib-2.0/schemas。
  • 如果定义了GSETTINGS_SCHEMA_DIR环境变量,它将搜索该变量中指定的所有目录。GSETTINGS_SCHEMA_DIR可以指定多个以冒号(:)分隔的目录。

在上面的目录中,存储了所有的.gschema.xml文件。因此,在安装应用程序时,请按照下面的说明安装模式。

  • 1.创建.gschema.xml文件。
  • 2.将其复制到上面的目录之一。例如,/usr/local/share/glib-2.0/schemas。
  • 3.在上面的目录上运行glib-compile-schemas。你可能需要sudo。

GSettings object and g_settings_bind

现在,我们进入下一个主题——如何编写GSettings。

... ...
static GSettings *settings;
... ...
void
app_shutdown (GApplication *application) {
  ... ...
  g_clear_object (&settings);
  ... ...
}
... ...
static void
app_startup (GApplication *application) {
  ... ...
  settings = g_settings_new ("com.github.ToshioCP.tfe");
  g_settings_bind (settings, "font", fontbtn, "font", G_SETTINGS_BIND_DEFAULT);
  ... ...
}

静态变量settings保存了一个指向GSettings实例的指针。在应用程序退出之前,应用程序释放GSettings实例。函数g_clear_object减少GSettings实例的引用计数,并将NULL赋值给变量settings。

Startup handler创建模式id为“com.github.ToshioCP”的GSettings实例。并将指针赋值给settings。函数g_settings_bind连接settings键(key和value)和fontbtn的"font"属性。那么这两个值将始终相同。如果一个值改变了,那么另一个也会自动改变。

有关更多信息,请参阅GIO API参考——GSettings。

Build with Meson

Build and test

Meson提供了gnome.compile_schemas方法在构建目录中编译XML文件。这用于测试应用程序。写下面的meson.build文件。

gnome.compile_schemas(build_by_default: true, depend_files: 'com.github.ToshioCP.tfe.gschema.xml')
  • build_by_default:如果为true,将默认构建目标。
  • depend_files:要编译的XML文件。

在上面的例子中,这个方法运行glib-compile-schemas,从XML文件com.github.ToshioCP.tfe.gschema.xml生成gschemas.compiled。gschemas.compiled文件位于build目录下。如果你将meson运行为meson _build,将ninja运行为ninja -C _build,那么它就在_build目录下。

编译后,你可以像这样测试你的应用程序:

$ GSETTINGS_SCHEMA_DIR=_build:$GSETTINGS_SCHEMA_DIR _build/tfe

installation

将应用程序安装在 H O M E / b i n 或 HOME/bin或 HOME/binHOME/中是一个好主意。本地/ bin目录。它们是本地bin目录,工作方式类似于系统bin目录,如/bin、/usr/bin或usr/local/bin。你需要添加--prefix=$HOME or --prefix=$HOME/.local到meson。

$ meson --prefix=$HOME/.local _build

如果你想将应用程序安装到系统的bin目录中,例如/usr/local/bin,则不需要——prefix选项。

Meson识别这样的选项:
请添加图片描述函数executable需要install: true才能安装程序。

executable('tfe', sourcefiles, resources, dependencies: gtkdep, export_dynamic: true, install: true)

然而,你还需要做一件事。将XML文件复制到schema目录,并在该目录上执行glib-compile-schemas。

  • Install_data函数将文件复制到目标目录。
  • gnome.post_install函数在安装后以schema_dir作为参数执行glib-compile-schemas。该函数从Meson 0.57.0开始可用。如果版本早于此,则使用meson.add_install_script代替。
schema_dir = get_option('prefix') / get_option('datadir') / 'glib-2.0/schemas/'
install_data('com.github.ToshioCP.tfe.gschema.xml', install_dir: schema_dir)
gnome.post_install (glib_compile_schemas: true)

函数get_option返回构建选项的值。参见Meason参考手册。运算符//分隔符连接字符串。
请添加图片描述Meson.buid 的源代码如下。

 1 project('tfe', 'c')
 2 
 3 gtkdep = dependency('gtk4')
 4 
 5 gnome=import('gnome')
 6 resources = gnome.compile_resources('resources','tfe.gresource.xml')
 7 gnome.compile_schemas(build_by_default: true, depend_files: 'com.github.ToshioCP.tfe.gschema.xml')
 8 
 9 sourcefiles=files('tfeapplication.c', 'tfenotebook.c', 'pfd2css.c', '../tfetextview/tfetextview.c')
10 
11 executable('tfe', sourcefiles, resources, dependencies: gtkdep, export_dynamic: true, install: true)
12 
13 schema_dir = get_option('prefix') / get_option('datadir') / 'glib-2.0/schemas/'
14 install_data('com.github.ToshioCP.tfe.gschema.xml', install_dir: schema_dir)
15 gnome.post_install (glib_compile_schemas: true)

tfe的源文件在src/tfe6目录下。将它们复制到临时目录,然后编译并安装它。

$ meson --prefix=$HOME/.local _build
$ ninja -C _build
$ GSETTINGS_SCHEMA_DIR=_build:$GSETTINGS_SCHEMA_DIR _build/tfe # test
$ ninja -C _build install
$ ls $HOME/.local/bin
... ...
... tfe
... ...
$ ls $HOME/.local/share/glib-2.0/schemas
com.github.ToshioCP.tfe.gschema.xml
gschema.dtd
gschemas.compiled
... ...
$ tfe

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/181169.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java IO流之序列化流

序列化流/对象操作输出流 可以把Java中的对象写到本地文件中 序列化流的小细节 使用对象输出流将对象保存到文件时会出现NotSerializableException\color{#FF0000}{NotSerializableException}NotSerializableException 异常 解决方案&#xff1a;需要让JavaBean类实现Serializa…

【树】二叉树递归遍历和创建

二叉树的遍历二叉树的遍历是将二叉树的每个结点访问且访问一次遍历按根来说常用三种设访问根结点记作V&#xff0c;遍历根的左子树记作L&#xff0c;遍历根的右子树记作R&#xff1b;有&#xff1a;先序遍历VLR&#xff0c;中序遍历LVR&#xff0c;后序遍历LRV。这个遍历是递归…

设计模式 - 六大设计原则之ISP(接口隔离原则)

文章目录概述CaseBad ImplBetter Impl概述 接口隔离原则&#xff08;Interface Segregation Principle, ISP&#xff09;,要求尽量将臃肿庞大的接口拆分成更小的和更具体的接口&#xff0c;让接口中只包含相关的方法。 接口隔离是为了高内聚、低耦合。 在实际的开发中&#x…

Altova MobileTogether 8.1

Altova MobileTogether 8.1 使用RecordsManager进行无代码开发的主要更新 可视化配置复杂条件的主要脚本扩展。 分层用户组。 客户端之间的UI兼容性模式 当开发人员希望应用程序在所有平台上看起来都一样&#xff0c;而不是使用每个平台的原生外观时&#xff0c;现在可以使用新…

蓝桥杯刷题016——最大子矩阵(尺取法+单调队列)

题目来源&#xff1a;最大子矩阵 - 蓝桥云课 (lanqiao.cn) 问题描述 小明有一个大小为 NM 的矩阵, 可以理解为一个 N 行 M 列的二维数组。 我们定义一个矩阵 m 的稳定度 f(m) 为f(m)max(m)−min(m), 其中 max(m) 表示矩阵 m 中的最大值, min(m) 表示矩阵 m 中的最小值。 现在小…

朴素贝叶斯分类

一、朴素贝叶斯法原理 1.基本原理 朴素贝叶斯法(Naive Bayes)是一种基础分类算法&#xff0c;它的核心是贝叶斯定理条件独立性假设。贝叶斯定理描述的是两个条件概率之间的关系&#xff0c;对两个事件A和B&#xff0c;由乘法法则易知(A∩B)P(A)P(B│A)P(B)P(A│B)(A∩B)P(A)P(…

离散数学与组合数学-06特殊的图

文章目录离散数学与组合数学-06特殊的图6.1 欧拉图6.1.1 哥尼斯堡七桥问题6.1.2 欧拉图的定义6.1.3 无向欧拉图的判定6.1.4 有向欧拉图的判定6.1.5 一笔画问题6.1.6 求回路6.2 哈密顿图6.2.1 周游世界问题6.2.2 哈密顿图的定义6.2.3 哈密顿图的必要条件6.2.4 哈密顿图的充分条件…

Hadoop基础之《(5)—MapReduce概述》

一、什么是MapReduce MapReduce将计算过程分为两个阶段&#xff1a;Map和Reduce。 1、Map阶段并行处理输入数据。 2、Reduce阶段对map结果进行汇总。 二、结构图 三、HDFS、Yarn、MapReduce三者之间的调用关系 HDFS三台服务器&#xff0c;第一台上有DataNode和NameNode。第二…

如何像程序员一样思考

如何像程序员一样思考 全世界越来越多的人发现了软件开发的乐趣和兴奋。一个你构建东西的职业&#xff0c;去打破它们&#xff0c;然后再把它们构建得更好。您可以经常设定自己的工作时间并与来自全球各地的客户一起工作的职业。一个可以自己当老板的职业&#xff1f;编程在 2…

android12.0(S) Launcher3 导入 AndroidStudio 调试编译

验证环境 aosp 12.0 源码&#xff0c;分支 android-12.0.0_r3 可以参考之前写的 android12.0(S) Pixel 3XL (QCOM 845) 编译刷机 AndroidStudio 版本 Android Studio Arctic Fox | 2020.3.1 Patch 4 gradle 版本 gradle-7.0.2-bin.zip gradle:7.0.4 二手 Pixel 3 XL一台可直…

[WUSTCTF2020]颜值成绩查询(布尔注入)

目录 信息收集 脚本 reference 信息收集 输入1出现固定的回显&#xff0c;输入2-4出现乱码 Hi admin, your score is: 100 输入其他特殊字符无回显&#xff0c;也无报错回显 采用布尔注入进行判断 布尔注入原理 代码存在SQL注入漏洞&#xff0c;然而页面即不会回显数据&am…

【C++算法图解专栏】一篇文章带你掌握尺取法(双指针)

✍个人博客&#xff1a;https://blog.csdn.net/Newin2020?spm1011.2415.3001.5343 &#x1f4e3;专栏定位&#xff1a;为 0 基础刚入门数据结构与算法的小伙伴提供详细的讲解&#xff0c;也欢迎大佬们一起交流~ &#x1f4da;专栏地址&#xff1a;https://blog.csdn.net/Newin…

java spring IOC xml方式工厂Bean操作

spring中有两种Bean 第一种 普通 Bean 就是我们在配置文件中 定义的类对象 创建bean 并定义相关的id和声明类对象 普通bean的特点在于 在配置文件中定义的类型 也就是返回类型 就比如 你定义的是一个 book类的类型 那你这个bean返回的 也比如是个book类型的对象 第二种 是在工…

模拟实现vector/迭代器失效问题

对于STL&#xff0c;我们不仅学会使用STL,还要了解其底层原理&#xff0c;这样一来&#xff0c;我们就能知道什么时候用string好&#xff0c;什么时候用vector&#xff0c;什么时候用list&#xff0c;哪种方法效率高等等。其次了解了STL的底层原理&#xff0c;也助于我们的C功力…

RTF、RIR、Steering Vector傻傻分不清

RTF&#xff1a; Relative transfer function&#xff0c;相对传递函数RIR: Room impulse response&#xff0c;空间冲击响应Steering vector: 导向矢量场景问题定义&#xff1a;空间中存在I个麦克风和J个声源&#xff0c;麦克风采集到的信号其中&#xff0c;麦克i的信号其中表…

一起自学SLAM算法:9.1 ORB-SLAM2算法

连载文章&#xff0c;长期更新&#xff0c;欢迎关注&#xff1a; 下面将从原理分析、源码解读和安装与运行这3个方面展开讲解ORB-SLAM2算法。 9.1.1 ORB-SLAM2原理分析 前面已经说过&#xff0c;ORB-SLAM2算法是特征点法的典型代表。因此在下面的分析中&#xff0c;首先介绍一…

被删库勒索了,怎么使用docker进行MySQL容器的管理?

大家觉得写还可以&#xff0c;可以点赞、收藏、关注一下吧&#xff01; 也可以到我的个人博客参观一下&#xff0c;估计近几年都会一直更新&#xff01;和我做个朋友吧&#xff01;https://motongxue.cn 起因&#xff1a;云服务器MySQL密码设置的太简单了&#xff0c;导致到被入…

路由策略实验

1.先配置IP和环回 [Huawei]sysname R1 [R1]interface GigabitEthernet 0/0/0 [R1-GigabitEthernet0/0/0]ip add 12.1.1.1 24 [R1-GigabitEthernet0/0/0]int g 0/0/1 [R1-GigabitEthernet0/0/1]ip add 22.1.1.1 24 [R1-GigabitEthernet0/0/1]q [R1]int l 0 [R1-LoopBack0]ip ad…

ETHDenver 2023 的 Cartesi BUIDLathon 项目创意

希望你在了解Cartesi之前&#xff0c;谨慎对待自己的行为。一旦你开始研究并搜寻可以使用Cartesi Rollups构建的项目或者应用&#xff0c;你就会陷入一个令人兴奋的螺旋洞穴中&#xff0c;你会上瘾。如果你想在2023年中建造一些很具有意义的事情&#xff0c;那你就来对地方了。…

Python01概述 基础语法 判断

Python概述 第二章-Python基础语法 01-字面量 02-注释 03-变量 04-数据类型 05-数据类型转换 06-标识符 07-运算符 08-字符串的三种定义方式 09-字符串的拼接 10-字符串格式化 11-字符串格式化的精度控制 12-字符串格式化的方式-快速写法 13-对表达式进行格式化 14-字符串格…