<?xml version="1.0" encoding="utf-8"?>
<page xmlns="http://projectmallard.org/1.0/" xmlns:its="http://www.w3.org/2005/11/its" type="topic" id="image-viewer.py" lang="sv">

  <info>
    <title type="text">Bildvisare (Python)</title>
    <link type="guide" xref="py#examples"/>

    <desc>Lite mer än ett enkelt ”Hej världen”-program - skriv en bildvisare i GTK.</desc>

    <revision pkgversion="0.1" version="0.1" date="2011-03-19" status="review"/>
    <credit type="author">
      <name>Jonh Wendell</name>
      <email its:translate="no">jwendell@gnome.org</email>
    </credit>
    <credit type="author">
      <name>Johannes Schmid</name>
      <email its:translate="no">jhs@gnome.org</email>
    </credit>
    <credit type="editor">
      <name>Marta Maria Casetti</name>
      <email its:translate="no">mmcasetti@gmail.com</email>
      <years>2013</years>
    </credit>
  <credit type="translator copyright"><name>Sebastian Rasmussen</name><email>sebras@gmail.com</email><years>2019</years></credit><credit type="translator copyright"><name>Anders Jonsson</name><email>anders.jonsson@norsjovallen.se</email><years>2021</years></credit></info>

<title>Bildvisare</title>

<synopsis>
  <p>I denna handledning kommer vi skriva ett väldigt enkelt GTK-program som läser in och visar en bildfil. Du kommer lära dig hur du:</p>
  <list>
    <item><p>Skriver ett enkelt GTK-användargränssnitt i Python</p></item>
    <item><p>Hanterar händelser genom att ansluta signaler till signalhanterare</p></item>
    <item><p>Gör layouter för GTK-användargränssnitt genom att använda behållare</p></item>
    <item><p>Läser in och visar bildfiler</p></item>
  </list>
  <p>Du behöver följande för att kunna följa denna handledning:</p>
  <list>
    <item><p>En installerad kopia av den <link xref="getting-ready">integrerade utvecklingsmiljön Anjuta</link></p></item>
    <item><p>Grundläggande kunskap i programmeringsspråket Python</p></item>
  </list>
</synopsis>

<media type="image" mime="image/png" src="media/image-viewer.png"/>

<section id="anjuta">
  <title>Skapa ett projekt i Anjuta</title>
  <p>Innan du börjar koda kommer du behöva konfigurera ett nytt projekt i Anjuta. Detta kommer skapa alla filer som du behöver för att bygga och köra koden senare. Det är också användbart för att hålla allting samlat.</p>
  <steps>
    <item>
    <p>Starta Anjuta och klicka på <guiseq><gui>Arkiv</gui><gui>Ny</gui><gui>Projekt</gui></guiseq> för att öppna projektguiden.</p>
    </item>
    <item>
    <p>Välj <gui>PyGTK (automake)</gui> från fliken <gui>Python</gui>, klicka på <gui>Fortsätt</gui>, och fyll i dina detaljer på de nästkommande sidorna. Använd <file>guitar-tuner</file> som projektnamn och katalog.</p>
   	</item>
   	<item>
   	<p>Se till att inaktivera <gui>Använd GtkBuilder för användargränssnitt</gui> då vi kommer bygga användargränssnittet manuellt i detta exempel. Se <link xref="guitar-tuner.py">gitarrstämmardemonstrationen</link> för ett exempel på hur du använder gränssnittsbyggaren.</p>
    </item>
    <item>
    <p>Klicka på <gui>Verkställ</gui> så kommer projektet skapas åt dig. Öppna <file>src/image_viewer.py</file> från flikarna <gui>Projekt</gui> eller <gui>Filer</gui>. Den innehåller grundläggande exempelkod.</p>
    </item>
  </steps>
</section>

<section id="first">
  <title>Ett första Gtk-program</title>
  <p>Låt oss se hur ett väldigt enkelt Gtk-program ser ut i Python:</p>
  <code mime="text/python" style="numbered">
from gi.repository import Gtk, GdkPixbuf, Gdk
import os, sys

class GUI:
	def __init__(self):
		window = Gtk.Window()
		window.set_title ("Hej världen")
		window.connect_after('destroy', self.destroy)

		window.show_all()

	def destroy(window, self):
		Gtk.main_quit()

def main():
	app = GUI()
	Gtk.main()

if __name__ == "__main__":
    sys.exit(main())

  </code>
  <p>Låt oss ta en titt på vad som händer:</p>
  <list>
    <item>
    <p>Den första raden importerar Gtk-namnrymden (d.v.s. inkluderar Gtk-biblioteket). Biblioteken tillhandahålls av GObject Introspection (gi), vilket tillhandahåller språkbindningar för många GNOME-bibliotek.</p>
    </item>
    <item>
    <p>In the <code>__init__</code> method of the <code>GUI</code> class creates an
    (empty) <code>Gtk.Window</code>, sets its title and then connects a signal to quit the application
    once the window is closed. That's pretty simple overall, more on signals later.</p>
    </item>
    <item>
    <p>Next, <code>destroy</code> is defined which just quits the application. It is called
    by the <code>destroy</code> signal connected above.</p>
    </item>
    <item>
    <p>Resten av filen gör initiering för Gtk och visar det grafiska användargränssnittet.</p>
    </item>
  </list>

  <p>Koden är redo att köras, så pröva den med <guiseq><gui>Kör</gui><gui>Kör</gui></guiseq>. Den kommer visa ett tomt fönster.</p>
</section>

<section id="signals">
  <title>Signaler</title>
  <p>Signals are one of the key concepts in Gtk programming. Whenever something happens to an object, it emits a signal; for example, when a button is clicked it gives off the <code>clicked</code> signal. If you want your program to do something when that event occurs, you must connect a function (a "signal handler") to that signal. Here's an example:</p>
  <code mime="text/python" style="numbered">
def button_clicked () :
  print "Du klickade på mig!"

b = new Gtk.Button ("Klicka på mig")
b.connect_after ('clicked', button_clicked)</code>
  <p>The last two lines create a <code>Gtk.Button</code> called <code>b</code> and connect its <code>clicked</code> signal to the <code>button_clicked</code> function, which is defined above. Every time the button is clicked, the code in the <code>button_clicked</code> function will be executed. It just prints a message here.</p>
</section>

<section id="containers">
  <title>Behållare: göra en layout för användargränssnittet</title>
  <p>Widgets (controls, such as buttons and labels) can be arranged in the window by making use of <em>containers</em>. You can organize the layout by mixing different types of containers, like boxes and grids.</p>
  <p>A <code>Gtk.Window</code> is itself a type of container, but you can only put one widget directly into it. We would like to have two widgets, an image and a button, so we must put a "higher-capacity" container inside the window to hold the other widgets. A number of <link href="http://library.gnome.org/devel/gtk/stable/GtkContainer.html">container types</link> are available, but we will use a <code>Gtk.Box</code> here. A <code>Gtk.Box</code> can hold several widgets, organized horizontally or vertically. You can do more complicated layouts by putting several boxes inside another box and so on.</p>
  <note>
  <p>There is a graphical user interface designer called <app>Glade</app> integrated in <app>Anjuta</app> which makes UI design really easy. For this simple example, however, we will code everything manually.</p>
  </note>
  <p>Let's add the box and widgets to the window. Insert the following code into the <code>__init__</code> method, immediately after the <code>window.connect_after</code> line:</p>
<code mime="text/python" style="numbered">
box = Gtk.Box()
box.set_spacing (5)
box.set_orientation (Gtk.Orientation.VERTICAL)
window.add (box)

</code>
  <p>The first line creates a <code>Gtk.Box</code> called <code>box</code> and the following lines set two of its properties: the <code>orientation</code> is set to vertical (so the widgets are arranged in a column), and the <code>spacing</code> between the widgets is set to 5 pixels. The next line then adds the newly-created <code>Gtk.Box</code> to the window.</p>
  <p>So far the window only contains an empty <code>Gtk.Box</code>, and if you run the program now you will see no changes at all (the <code>Gtk.Box</code> is a transparent container, so you can't see that it's there).</p>
</section>

<section id="packing">
  <title>Packning: Lägga till komponenter till behållaren</title>
  <p>To add some widgets to the <code>Gtk.Box</code>, insert the following code directly below the <code>window.add (box)</code> line:</p>
  <code mime="text/python" style="numbered">
self.image = Gtk.Image()
box.pack_start (self.image, False, False, 0)</code>
  <p>The first line creates a new <code>Gtk.Image</code> called <code>image</code>, which will be used to display an image file.
As we need that later on in the signal handler, we will define it as a class-wide variable. You need to add <code>image = 0</code> to the beginning of the <code>GUI</code> class. Then, the image widget is added (<em>packed</em>) into the <code>box</code> container using GtkBox's <link href="http://library.gnome.org/devel/gtk/stable/GtkBox.html#gtk-box-pack-start"><code>pack_start</code></link> method.</p>
  <p><code>pack_start</code> takes 4 arguments: the widget that is to be added to the GtkBox (<code>child</code>); whether the <code>Gtk.Box</code> should grow larger when the new widget is added (<code>expand</code>); whether the new widget should take up all of the extra space created if the <code>Gtk.Box</code> gets bigger (<code>fill</code>); and how much space there should be, in pixels, between the widget and its neighbors inside the <code>Gtk.Box</code> (<code>padding</code>).</p>
  <p>Gtk containers (and widgets) dynamically expand to fill the available space, if you let them. You don't position widgets by giving them a precise x,y-coordinate location in the window; rather, they are positioned relative to one another. This makes handling window resizing much easier, and widgets should automatically take a sensible size in most situations.</p>
  <p>Also note how the widgets are organized in a hierarchy. Once packed in the <code>Gtk.Box</code>, the <code>Gtk.Image</code> is considered a <em>child</em> of the <code>Gtk.Box</code>. This allows you to treat all of the children of a widget as a group; for example, you could hide the <code>Gtk.Box</code>, which would also hide all of its children at the same time.</p>
  <p>Infoga nu dessa två rader under de två du just lade till:</p>
  <code mime="text/python" style="numbered">
button = Gtk.Button ("Öppna en bild…")
box.pack_start (button, False, False, 0)
</code>
  <p>These lines are similar to the first two, but this time they create a <code>Gtk.Button</code> and add it to <code>box</code>. Notice that we are setting the <code>expand</code> argument (the second one) to <code>False</code> here, whereas it was set to <code>True</code> for the <code>Gtk.Image</code>. This will cause the image to take up all available space and the button to take only the space it needs. When you maximize the window, the button size will remain the same, but the image size will increase, taking up all of the rest of the window.</p>
</section>

<section id="loading">
  <title>Läsa in bilden: ansluta till knappens <code>clicked</code>-signal</title>
  <p>When the user clicks on the <gui>Open Image...</gui> button, a dialog should appear so that the user can choose a picture. Once chosen, the picture should be loaded and shown in the image widget.</p>
  <p>The first step is to connect the <code>clicked</code> signal of the button to a signal handler function, which we call <code>on_open_clicked</code>. Put this code immediately after the <code>button = Gtk.Button()</code> line where the button was created:</p>
  <code mime="text/python">
button.connect_after('clicked', self.on_open_clicked)
</code>
  <p>Detta kommer ansluta <code>clicked</code>-signalen till metoden <code>on_open_clicked</code> som vi kommer definiera nedan.</p>
</section>

<section id="loading2">
  <title>Läsa in bilden: skriva signalens återanrop</title>
  <p>Now we can create the <code>on_open_clicked</code> method. Insert the following into the <code>GUI</code> class code block, after the <code>__init__</code> method:</p>
    <code mime="text/javascript" style="numbered">
def on_open_clicked (self, button):
	dialog = Gtk.FileChooserDialog ("Öppna bild", button.get_toplevel(), Gtk.FileChooserAction.OPEN);
	dialog.add_button (Gtk.STOCK_CANCEL, 0)
	dialog.add_button (Gtk.STOCK_OK, 1)
	dialog.set_default_response(1)

	filefilter = Gtk.FileFilter ()
	filefilter.add_pixbuf_formats ()
	dialog.set_filter(filefilter)

	if dialog.run() == 1:
		self.image.set_from_file(dialog.get_filename())

	dialog.destroy()</code>
  <p>Det här är lite mer komplicerat än något som vi försökt så här långt, så låt oss dela upp det:</p>
  <list>
    <item>
      <p>The line beginning with <code>dialog</code> creates an <gui>Open</gui> dialog, which the user can use to choose files. We set three properties: the title of the dialog; the action (type) of the dialog (it's an "open" dialog, but we could have used <code>SAVE</code> if the intention was to save a file; and <code>transient_for</code>, which sets the parent window of the dialog.</p>
    </item>
    <item>
    <p>The next two lines add <gui>Cancel</gui> and <gui>Open</gui> buttons to the dialog. The second argument of the <code>add_button</code> method is the (integer) value that is returned when the button is pressed: 0 for <gui>Cancel</gui> and 1 for <gui>Open</gui>.</p>
    <p>Observera att vi använder <em>standard</em>knappnamn från Gtk, istället för att manuellt skriva in ”Avbryt” eller ”Öppna”. Fördelen med att använda standardnamn är att knappetiketterna redan kommer vara översatta till användarens språk.</p>
    </item>
    <item>
    <p><code>set_default_response</code> determines the button that will be activated if the user double-clicks a file or presses <key>Enter</key>. In our case, we are using the <gui>Open</gui> button as default (which has the value 1).</p>
    </item>
    <item>
    <p>The next three lines restrict the <gui>Open</gui> dialog to only display files which can be opened by <code>Gtk.Image</code>. A filter object is created first; we then add all kinds of files supported by <code>Gdk.Pixbuf</code> (which includes most image formats like PNG and JPEG) to the filter. Finally, we set this filter to be the <gui>Open</gui> dialog's filter.</p>
    </item>
    <item>
    <p><code>dialog.run</code> displays the <gui>Open</gui> dialog. The dialog will wait for the user to choose an image; when they do, <code>dialog.run</code> will return the value <output>1</output> (it would return <output>0</output> if the user clicked <gui>Cancel</gui>). The <code>if</code> statement tests for this.</p>
    </item>
    <item><p>Assuming that the user did click <gui>Open</gui>, the next line sets the <code>file</code> property of the <code>Gtk.Image</code> to the filename of the image selected by the user. The <code>Gtk.Image</code> will then load and display the chosen image.</p>
    </item>
    <item>
    <p>In the final line of this method, we destroy the <gui>Open</gui> dialog because we don't need it any more.</p>
    </item>
  </list>

  </section>

<section id="run">
  <title>Kör programmet</title>
  <p>All kod som du behöver bör nu vara på plats, så testa att köra koden. Det var det, en fullt fungerande bildvisare (och en snabb rundtur av Python och Gtk) på inte mycket tid alls!</p>
</section>

<section id="impl">
 <title>Referensimplementation</title>
 <p>Om du stöter på problem med handledningen kan du jämföra din kod med denna <link href="image-viewer/image-viewer.py">referenskod</link>.</p>
</section>

<section id="next">
  <title>Nästa steg</title>
  <p>Här är några idéer på hur du kan utöka denna enkla demonstration:</p>
  <list>
   <item>
   <p>Låt användaren välja en katalog snarare än en fil, och tillhandahåll kontroller för att gå igenom alla bilderna i en katalog.</p>
   </item>
   <item>
   <p>Tillämpa slumpmässiga filter och effekter till bilden då den läses in och låt användaren spara den ändrade bilden.</p>
   <p><link href="http://www.gegl.org/api.html">GEGL</link> har kraftfulla bildmanipuleringsförmågor.</p>
   </item>
   <item>
   <p>Allow the user to load images from network shares, scanners, and other more complicated sources.</p>
   <p>You can use <link href="http://library.gnome.org/devel/gio/unstable/">GIO</link> to handle network file transfers and the like, and <link href="http://library.gnome.org/devel/gnome-scan/unstable/">GNOME Scan</link> to handle scanning.</p>
   </item>
  </list>
</section>

</page>
