Gtk4-tutorial/docs/sec12.html

302 lines
22 KiB
HTML
Raw Normal View History

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<title>GTK 4 tutorial</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
pre{overflow: visible;}
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::after
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
color: #aaaaaa;
}
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
div.sourceCode
{ }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
code span.at { color: #7d9029; } /* Attribute */
code span.bn { color: #40a070; } /* BaseN */
code span.bu { } /* BuiltIn */
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
code span.ch { color: #4070a0; } /* Char */
code span.cn { color: #880000; } /* Constant */
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
code span.dt { color: #902000; } /* DataType */
code span.dv { color: #40a070; } /* DecVal */
code span.er { color: #ff0000; font-weight: bold; } /* Error */
code span.ex { } /* Extension */
code span.fl { color: #40a070; } /* Float */
code span.fu { color: #06287e; } /* Function */
code span.im { } /* Import */
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
code span.op { color: #666666; } /* Operator */
code span.ot { color: #007020; } /* Other */
code span.pp { color: #bc7a00; } /* Preprocessor */
code span.sc { color: #4070a0; } /* SpecialChar */
code span.ss { color: #bb6688; } /* SpecialString */
code span.st { color: #4070a0; } /* String */
code span.va { color: #19177c; } /* Variable */
code span.vs { color: #4070a0; } /* VerbatimString */
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
div.sourceCode { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
pre:not(.sourceCode) { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
table {margin-left: auto; margin-right: auto; border-collapse: collapse; border: 1px solid;}
th {padding: 2px 6px; border: 1px solid; background-color: ghostwhite;}
td {padding: 2px 6px; border: 1px solid;}
img {display: block; margin-left: auto; margin-right: auto;}
figcaption {text-align: center;}
</style>
</head>
<body style="padding-top: 70px;">
<div class="container">
<nav class="navbar fixed-top navbar-expand-lg navbar-dark bg-primary">
<div class="container-fluid">
<span class="navbar-brand">Gtk4 tutorial</span>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link" href="index.html">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="sec11.html">Prev: section11</a>
</li>
<li class="nav-item">
<a class="nav-link" href="sec13.html">Next: section13</a>
</li>
</ul>
</div>
</div>
</nav>
<h1 id="signals">Signals</h1>
<h2 id="signals-1">Signals</h2>
<p>Each object is encapsulated in Gtk programming. And it is not
recommended to use global variables because they are prone to make the
program complicated. So, we need something to communicate between
objects. There are two ways to do so.</p>
<ul>
<li>Instance methods: Instance methods are functions on instances. For
example, <code>tb = gtk_text_view_get_buffer (tv)</code> is an instance
method on the instance <code>tv</code>. The caller requests
<code>tv</code> to give <code>tb</code>, which is a GtkTextBuffer
instance connected to <code>tv</code>.</li>
<li>Signals: For example, <code>activate</code> signal on GApplication
object. When the application is activated, the signal is emitted. Then
the handler, which has been connected to the signal, is invoked.</li>
</ul>
<p>The caller of methods or signals are usually out of the object. One
of the difference between these two is that the object is active or
passive. In methods, objects passively respond to the caller. In
signals, objects actively send signals to handlers.</p>
<p>GObject signals are registered, connected and emitted.</p>
<ol type="1">
<li>Signals are registered in the class. The registration is done
usually when the class is initialized. Signals can have a default
handler, which is sometimes called “object method handler”. It is not a
user handler connected by <code>g_signal_connect</code> family
functions. A default handler is always called on any instance of the
class.</li>
<li>Signals are connected to handlers by the macro
<code>g_signal_connect</code> or its family functions. The connection is
usually done out of the object. One important thing is that signals are
connected on a certain instance. Suppose there exist two GtkButton
instances A, B and a function C. Even if you connected the “clicked”
signal on A to C, B and C are <em>not</em> connected.</li>
<li>When Signals are emitted, the connected handlers are invoked.
Signals are emitted on the instance of the class.</li>
</ol>
<h2 id="signal-registration">Signal registration</h2>
<p>In TfeTextView, two signals are registered.</p>
<ul>
<li>“change-file” signal. This signal is emitted when
<code>tv-&gt;file</code> is changed.</li>
<li>“open-response” signal. The function <code>tfe_text_view_open</code>
doesnt return the status because it cant get the status from the file
chooser dialog. (Instead, the call back function gets the status.) This
signal is emitted instead of the return value of the function.</li>
</ul>
<p>A static variable or array is used to store signal ID.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">enum</span> <span class="op">{</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a> CHANGE_FILE<span class="op">,</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a> OPEN_RESPONSE<span class="op">,</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a> NUMBER_OF_SIGNALS</span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="op">};</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> guint tfe_text_view_signals<span class="op">[</span>NUMBER_OF_SIGNALS<span class="op">];</span></span></code></pre></div>
<p>Signals are registered in the class initialization function.</p>
<div class="sourceCode" id="cb2"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb2-1"><a href="#cb2-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb2-2"><a href="#cb2-2"></a>tfe_text_view_class_init <span class="op">(</span>TfeTextViewClass <span class="op">*</span>class<span class="op">)</span> <span class="op">{</span></span>
<span id="cb2-3"><a href="#cb2-3"></a> GObjectClass <span class="op">*</span>object_class <span class="op">=</span> G_OBJECT_CLASS <span class="op">(</span>class<span class="op">);</span></span>
<span id="cb2-4"><a href="#cb2-4"></a></span>
<span id="cb2-5"><a href="#cb2-5"></a> object_class<span class="op">-&gt;</span>dispose <span class="op">=</span> tfe_text_view_dispose<span class="op">;</span></span>
<span id="cb2-6"><a href="#cb2-6"></a> tfe_text_view_signals<span class="op">[</span>CHANGE_FILE<span class="op">]</span> <span class="op">=</span> g_signal_new <span class="op">(</span><span class="st">&quot;change-file&quot;</span><span class="op">,</span></span>
<span id="cb2-7"><a href="#cb2-7"></a> G_TYPE_FROM_CLASS <span class="op">(</span>class<span class="op">),</span></span>
<span id="cb2-8"><a href="#cb2-8"></a> G_SIGNAL_RUN_LAST <span class="op">|</span> G_SIGNAL_NO_RECURSE <span class="op">|</span> G_SIGNAL_NO_HOOKS<span class="op">,</span></span>
<span id="cb2-9"><a href="#cb2-9"></a> <span class="dv">0</span> <span class="co">/* class offset */</span><span class="op">,</span></span>
<span id="cb2-10"><a href="#cb2-10"></a> NULL <span class="co">/* accumulator */</span><span class="op">,</span></span>
<span id="cb2-11"><a href="#cb2-11"></a> NULL <span class="co">/* accumulator data */</span><span class="op">,</span></span>
<span id="cb2-12"><a href="#cb2-12"></a> NULL <span class="co">/* C marshaller */</span><span class="op">,</span></span>
<span id="cb2-13"><a href="#cb2-13"></a> G_TYPE_NONE <span class="co">/* return_type */</span><span class="op">,</span></span>
<span id="cb2-14"><a href="#cb2-14"></a> <span class="dv">0</span> <span class="co">/* n_params */</span></span>
<span id="cb2-15"><a href="#cb2-15"></a> <span class="op">);</span></span>
<span id="cb2-16"><a href="#cb2-16"></a> tfe_text_view_signals<span class="op">[</span>OPEN_RESPONSE<span class="op">]</span> <span class="op">=</span> g_signal_new <span class="op">(</span><span class="st">&quot;open-response&quot;</span><span class="op">,</span></span>
<span id="cb2-17"><a href="#cb2-17"></a> G_TYPE_FROM_CLASS <span class="op">(</span>class<span class="op">),</span></span>
<span id="cb2-18"><a href="#cb2-18"></a> G_SIGNAL_RUN_LAST <span class="op">|</span> G_SIGNAL_NO_RECURSE <span class="op">|</span> G_SIGNAL_NO_HOOKS<span class="op">,</span></span>
<span id="cb2-19"><a href="#cb2-19"></a> <span class="dv">0</span> <span class="co">/* class offset */</span><span class="op">,</span></span>
<span id="cb2-20"><a href="#cb2-20"></a> NULL <span class="co">/* accumulator */</span><span class="op">,</span></span>
<span id="cb2-21"><a href="#cb2-21"></a> NULL <span class="co">/* accumulator data */</span><span class="op">,</span></span>
<span id="cb2-22"><a href="#cb2-22"></a> NULL <span class="co">/* C marshaller */</span><span class="op">,</span></span>
<span id="cb2-23"><a href="#cb2-23"></a> G_TYPE_NONE <span class="co">/* return_type */</span><span class="op">,</span></span>
<span id="cb2-24"><a href="#cb2-24"></a> <span class="dv">1</span> <span class="co">/* n_params */</span><span class="op">,</span></span>
<span id="cb2-25"><a href="#cb2-25"></a> G_TYPE_INT</span>
<span id="cb2-26"><a href="#cb2-26"></a> <span class="op">);</span></span>
<span id="cb2-27"><a href="#cb2-27"></a><span class="op">}</span></span></code></pre></div>
<ul>
<li>6-15: Registers “change-file” signal. <code>g_signal_new</code>
function is used. The signal “change-file” has no default handler
(object method handler) so the offset (the line 9) is set to zero. You
usually dont need a default handler. If you need it, use
<code>g_signal_new_class_handler</code> function instead of
<code>g_signal_new</code>. See <a
href="https://docs.gtk.org/gobject/func.signal_new_class_handler.html">GObject
API Reference</a> for further information.</li>
<li>The return value of <code>g_signal_new</code> is the signal id. The
type of signal id is guint, which is the same as unsigned int. It is
used in the function <code>g_signal_emit</code>.</li>
<li>16-26: Registers “open-response” signal. This signal has a
parameter.</li>
<li>24: Number of the parameters. “open-response” signal has one
parameter.</li>
<li>25: The type of the parameter. <code>G_TYPE_INT</code> is a type of
integer. Such fundamental types are described in <a
href="https://developer-old.gnome.org/gobject/stable/gobject-Type-Information.html">GObject
reference manual</a>.</li>
</ul>
<p>The handlers are declared as follows.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="co">/* &quot;change-file&quot; signal handler */</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>user_function <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">,</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a> gpointer user_data<span class="op">)</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a><span class="co">/* &quot;open-response&quot; signal handler */</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a>user_function <span class="op">(</span>TfeTextView <span class="op">*</span>tv<span class="op">,</span></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a> TfeTextViewOpenResponseType response<span class="op">-</span>id<span class="op">,</span></span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a> gpointer user_data<span class="op">)</span></span></code></pre></div>
<ul>
<li>The signal “change-file” doesnt have parameter, so the handlers
arguments are a TfeTextView instance and a user data.</li>
<li>The signal “open-response” signal has one parameter and its
arguments are a TfeTextView instance, the signals parameter and user
data.</li>
<li>The variable <code>tv</code> points the instance on which the signal
is emitted.</li>
<li>The last argument <code>user_data</code> comes from the fourth
argument of <code>g_signal_connect</code>.</li>
<li>The <code>parameter</code> (<code>response-id</code>) comes from the
fourth argument of <code>g_signal_emit</code>.</li>
</ul>
<p>The values of the type <code>TfeTextViewOpenResponseType</code> are
defined in <code>tfetextview.h</code>.</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="co">/* &quot;open-response&quot; signal response */</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="kw">enum</span> TfeTextViewOpenResponseType</span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a><span class="op">{</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a> TFE_OPEN_RESPONSE_SUCCESS<span class="op">,</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a> TFE_OPEN_RESPONSE_CANCEL<span class="op">,</span></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a> TFE_OPEN_RESPONSE_ERROR</span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a><span class="op">};</span></span></code></pre></div>
<ul>
<li>The parameter is set to <code>TFE_OPEN_RESPONSE_SUCCESS</code> when
<code>tfe_text_view_open</code> has successfully opened a file and read
it.</li>
<li>The parameter is set to <code>TFE_OPEN_RESPONSE_CANCEL</code> when
the user has canceled.</li>
<li>The parameter is set to <code>TFE_OPEN_RESPONSE_ERROR</code> when an
error has occurred.</li>
</ul>
<h2 id="signal-connection">Signal connection</h2>
<p>A signal and a handler are connected by the function macro
<code>g_signal_connect</code>. There are some similar function macros
like <code>g_signal_connect_after</code>,
<code>g_signal_connect_swapped</code> and so on. However,
<code>g_signal_connect</code> is used most often. The signals
“change-file” and “open-response” are connected to their callback
functions out of the TfeTextView object. Those callback functions are
defined by users.</p>
<p>For example, callback functions are defined in
<code>src/tfe6/tfewindow.c</code> and their names are
<code>file_changed_cb</code> and <code>open_response_cb</code>. They
will be explained later.</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a>g_signal_connect <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> <span class="st">&quot;change-file&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>file_changed_cb<span class="op">),</span> nb<span class="op">);</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a>g_signal_connect <span class="op">(</span>TFE_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> <span class="st">&quot;open-response&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>open_response_cb<span class="op">),</span> nb<span class="op">);</span></span></code></pre></div>
<h2 id="signal-emission">Signal emission</h2>
<p>A signal is emitted on the instance. A function
<code>g_signal_emit</code> is used to emit the signal. The following
lines are extracted from <code>src/tfetextview/tfetextview.c</code>.
Each line comes from a different line.</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a>g_signal_emit <span class="op">(</span>tv<span class="op">,</span> tfe_text_view_signals<span class="op">[</span>CHANGE_FILE<span class="op">],</span> <span class="dv">0</span><span class="op">);</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a>g_signal_emit <span class="op">(</span>tv<span class="op">,</span> tfe_text_view_signals<span class="op">[</span>OPEN_RESPONSE<span class="op">],</span> <span class="dv">0</span><span class="op">,</span> TFE_OPEN_RESPONSE_SUCCESS<span class="op">);</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a>g_signal_emit <span class="op">(</span>tv<span class="op">,</span> tfe_text_view_signals<span class="op">[</span>OPEN_RESPONSE<span class="op">],</span> <span class="dv">0</span><span class="op">,</span> TFE_OPEN_RESPONSE_CANCEL<span class="op">);</span></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a>g_signal_emit <span class="op">(</span>tv<span class="op">,</span> tfe_text_view_signals<span class="op">[</span>OPEN_RESPONSE<span class="op">],</span> <span class="dv">0</span><span class="op">,</span> TFE_OPEN_RESPONSE_ERROR<span class="op">);</span></span></code></pre></div>
<ul>
<li>The first argument is the instance on which the signal is
emitted.</li>
<li>The second argument is the signal id.</li>
<li>The third argument is the detail of the signal. The signals
“change-file” and “open-response” dont have details and the arguments
are zero.</li>
<li>The signal “change-file” doesnt have parameters, so theres no
fourth argument.</li>
<li>The signal “open-response” has one parameter. The fourth argument is
the parameter.</li>
</ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
</body>
</html>