Gtk4-tutorial/docs/sec12.html

233 lines
19 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>Gtk4 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 */
body {width: 1080px; margin: 0 auto; font-size: large;}
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>In Gtk programming, each object is encapsulated. And it is not recommended to use global variables because they tend to make the program complicated. So, we need something to communicate between objects. There are two ways to do so.</p>
<ul>
<li>Functions. For example, <code>tb = gtk_text_view_get_buffer (tv)</code>. The caller requests <code>tv</code> to give <code>tb</code>, which is a GtkTextBuffer instance connected to <code>tv</code> to the caller.</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 the function or the handler connected to the signal is usually out of the object. One of the difference between these two is that the object is active or passive. In functions the object passively responds to the caller. In signals the object actively sends a signal to the handler.</p>
<p>GObject signals are registered, connected and emitted.</p>
<ol type="1">
<li>Signals are registered with the object type on which they are emitted. The registration is done usually when the object class is initialized.</li>
<li>Signals are connected to handlers by <code>g_connect_signal</code> or its family functions. The connection is usually done out of the object.</li>
<li>When Signals are emitted, the connected handlers are invoked. Signal is emitted on the instance of the object.</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. <code>tfe_text_view_open</code> function is not able to return the status because it uses GtkFileChooserDialog. This signal is emitted instead of the return value of the function.</li>
</ul>
<p>A static variable or array is used to store the signal ID. A static array is used to register two or more signals.</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"></a><span class="kw">enum</span> {</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true"></a> CHANGE_FILE,</span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true"></a> OPEN_RESPONSE,</span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true"></a> NUMBER_OF_SIGNALS</span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true"></a>};</span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true"></a></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true"></a><span class="dt">static</span> guint tfe_text_view_signals[NUMBER_OF_SIGNALS];</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 (TfeTextViewClass *class) {</span>
<span id="cb2-3"><a href="#cb2-3"></a> GObjectClass *object_class = G_OBJECT_CLASS (class);</span>
<span id="cb2-4"><a href="#cb2-4"></a></span>
<span id="cb2-5"><a href="#cb2-5"></a> object_class-&gt;dispose = tfe_text_view_dispose;</span>
<span id="cb2-6"><a href="#cb2-6"></a> tfe_text_view_signals[CHANGE_FILE] = g_signal_new (<span class="st">&quot;change-file&quot;</span>,</span>
<span id="cb2-7"><a href="#cb2-7"></a> G_TYPE_FROM_CLASS (class),</span>
<span id="cb2-8"><a href="#cb2-8"></a> G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,</span>
<span id="cb2-9"><a href="#cb2-9"></a> <span class="dv">0</span> <span class="co">/* class offset */</span>,</span>
<span id="cb2-10"><a href="#cb2-10"></a> NULL <span class="co">/* accumulator */</span>,</span>
<span id="cb2-11"><a href="#cb2-11"></a> NULL <span class="co">/* accumulator data */</span>,</span>
<span id="cb2-12"><a href="#cb2-12"></a> NULL <span class="co">/* C marshaller */</span>,</span>
<span id="cb2-13"><a href="#cb2-13"></a> G_TYPE_NONE <span class="co">/* return_type */</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>
<span id="cb2-16"><a href="#cb2-16"></a> tfe_text_view_signals[OPEN_RESPONSE] = g_signal_new (<span class="st">&quot;open-response&quot;</span>,</span>
<span id="cb2-17"><a href="#cb2-17"></a> G_TYPE_FROM_CLASS (class),</span>
<span id="cb2-18"><a href="#cb2-18"></a> G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,</span>
<span id="cb2-19"><a href="#cb2-19"></a> <span class="dv">0</span> <span class="co">/* class offset */</span>,</span>
<span id="cb2-20"><a href="#cb2-20"></a> NULL <span class="co">/* accumulator */</span>,</span>
<span id="cb2-21"><a href="#cb2-21"></a> NULL <span class="co">/* accumulator data */</span>,</span>
<span id="cb2-22"><a href="#cb2-22"></a> NULL <span class="co">/* C marshaller */</span>,</span>
<span id="cb2-23"><a href="#cb2-23"></a> G_TYPE_NONE <span class="co">/* return_type */</span>,</span>
<span id="cb2-24"><a href="#cb2-24"></a> <span class="dv">1</span> <span class="co">/* n_params */</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>
<span id="cb2-27"><a href="#cb2-27"></a>}</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). You usually dont need to set a default handler. If you need it, use <code>g_signal_new_class_handler</code> function. See <a href="https://docs.gtk.org/gobject/func.signal_new_class_handler.html">GObject API Reference, g_signal_new_class_handler</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"></a><span class="co">/* &quot;change-file&quot; signal handler */</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true"></a><span class="dt">void</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true"></a>user_function (TfeTextView *tv,</span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true"></a> gpointer user_data)</span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true"></a></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true"></a><span class="co">/* &quot;open-response&quot; signal handler */</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true"></a><span class="dt">void</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true"></a>user_function (TfeTextView *tv,</span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true"></a> TfeTextViewOpenResponseType response-id,</span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true"></a> gpointer user_data)</span></code></pre></div>
<ul>
<li>Because “change-file” signal doesnt have parameter, the handlers parameters are a TfeTextView instance and user data.</li>
<li>Because “open-response” signal has one parameter, the handlers parameters are a TfeTextView instance, the signals parameter and user data.</li>
<li><code>tv</code> is the object instance on which the signal is emitted.</li>
<li><code>user_data</code> comes from the fourth argument of <code>g_signal_connect</code>.</li>
<li><code>parameter</code> comes from the fourth argument of <code>g_signal_emit</code>.</li>
</ul>
<p>The values of the parameter is defined in <code>tfetextview.h</code> because they are public.</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"></a><span class="co">/* &quot;open-response&quot; signal response */</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true"></a><span class="kw">enum</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true"></a>{</span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true"></a> TFE_OPEN_RESPONSE_SUCCESS,</span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true"></a> TFE_OPEN_RESPONSE_CANCEL,</span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true"></a> TFE_OPEN_RESPONSE_ERROR</span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true"></a>};</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 <code>g_signal_connect</code>. There are some similar functions like <code>g_signal_connect_after</code>, <code>g_signal_connect_swapped</code> and so on. However, <code>g_signal_connect</code> is the most common. The signals “change-file” is connected to a callback function out of the TfeTextView object. In the same way, the signals “open-response” is connected to a callback function out of the TfeTextView object. Those callback functions are defined by users.</p>
<p>In the program <code>tfe</code>, callback functions are defined in <code>tfenotebook.c</code>. And their names are <code>file_changed</code> and <code>open_response</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"></a>g_signal_connect (GTK_TEXT_VIEW (tv), <span class="st">&quot;change-file&quot;</span>, G_CALLBACK (file_changed), nb);</span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true"></a></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true"></a>g_signal_connect (TFE_TEXT_VIEW (tv), <span class="st">&quot;open-response&quot;</span>, G_CALLBACK (open_response), nb);</span></code></pre></div>
<h2 id="signal-emission">Signal emission</h2>
<p>Signals are emitted on an instance. The type of the instance is the second argument of <code>g_signal_new</code>. The relationship between the signal and object type is determined when the signal is registered.</p>
<p>A function <code>g_signal_emit</code> is used to emit the signal. The following lines are extracted from <code>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"></a>g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], <span class="dv">0</span>);</span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true"></a>g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], <span class="dv">0</span>, TFE_OPEN_RESPONSE_SUCCESS);</span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true"></a>g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], <span class="dv">0</span>, TFE_OPEN_RESPONSE_CANCEL);</span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true"></a>g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], <span class="dv">0</span>, TFE_OPEN_RESPONSE_ERROR);</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. “change-file” signal and “open-response” signal doesnt have details and the argument is zero when no details.</li>
<li>“change-file” signal doesnt have parameter, so theres no fourth parameter.</li>
<li>“open-response” signal has one parameter. The fourth parameter 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>