<?xml version="1.0" encoding="utf-8"?>
<page xmlns="http://projectmallard.org/1.0/" type="topic" id="image-viewer.py" xml:lang="el">

  <info>

    <link type="guide" xref="index#py"/>

    <desc>Μια λίγο περισσότερο από απλή εφαρμογή σαν το "Γεια σου κόσμο! - Hello, world!" - γράψτε μια εφαρμογή για προβολή εικόνων σε GTK.</desc>

    <revision pkgversion="0.1" version="0.1" date="2011-03-19" status="review"/>
    <credit type="author">
      <name>Jonh Wendell</name>
      <email>jwendell@gnome.org</email>
    </credit>
    <credit type="author">
      <name>Johannes Schmid</name>
      <email>jhs@gnome.org</email>
    </credit>

  </info>

<title>1 Image Viewer</title>

<synopsis>
  <p>Σε αυτόν τον οδηγό θα φτιάξουμε μια πολύ απλή εφαρμογή σε GTK η οποία φορτώνει και εμφανίζει μια εικόνα. Θα μάθετε πως:</p>
  <list>
    <item><p>Γράψε μια απλή διεπαφή χρήστη GTK σε Python</p></item>
    <item><p>Να αντιμετωπίζετε γεγονότα συνδέοντας σήματα με το χειριστή σημάτων (signals handlers)</p></item>
    <item><p>Σχεδιασμό διαπεφών χρήστη GTK χρησιμοποιώντας υποδοχείς (containers)</p></item>
    <item><p>Φόρτωση και εμφάνιση εικόνων</p></item>
  </list>
  <p>Θα χρειαστείτε τα παρακάτω για να μπορέσετε να ακολουθήσετε αυτόν τον οδηγό:</p>
  <list>
    <item><p>Ένα εγκατεστημένο αντίγραφο του </p></item>
    <item><p>Βασική γνώση της γλώσσας προγραμματισμού Python</p></item>
  </list>
</synopsis>

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

<section id="anjuta">
  <title>Δημιουργήστε ένα έργο με το Anjuta</title>
  <p>Πριν ξεκινήσετε να προγραμματίζετε, πρέπει να δημιουργήσετε ένα καινούργιο έργο στο Anjuta. Έτσι θα δημιουργηθούν όλα τα απαραίτητα αρχεία που χρειάζονται για την εκτέλεση του κώδικα αργότερα. Επίσης θα ήταν χρήσιμο να τα κρατάτε όλα μαζί.</p>
  <steps>
    <item>
    <p>Ξεκινήστε το Anjuta και πατήστε <guiseq><gui>Αρχείο</gui><gui>Νέο</gui><gui>Έργο</gui></guiseq> για να ανοίξετε το μάγο του έργου (project wizard).</p>
    </item>
    <item>
    <p>Choose <gui>PyGTK (automake)</gui> from the <gui>Python</gui> tab, click <gui>Continue</gui>, and fill out your details on the next few pages. Use <file>image-viewer</file> as project name and directory.</p>
   	</item>
   	<item>
   	<p>Σιγουρευτείτε ότι απενεργοποιήσατε το <gui>Χρήση του GtkBuilder για διεπαφή χρήση</gui> επειδή θα φτιάξουμε τη διεπαφή χρήστη οι ίδιοι σε αυτό το παράδειγμα. Για ένα παράδειγμα χρήσης του σχεδιαστή διεπαφής, δείτε το <link xref="guitar-tuner.py">Επίδειξη ρυθμιστή κιθάρας</link>.</p>
    </item>
    <item>
    <p>Πατήστε <gui>Εφαρμογή</gui> και το έργο θα δημιουργηθεί για εσάς. Ανοίξτε το <file>src/image_viewer.py</file> από τις καρτέλες <gui>Έργο</gui> ή <gui>Αρχείο</gui>. Περιλαμβάνει πολύ απλό παράδειγμα κώδικα.</p>
    </item>
  </steps>
</section>

<section id="first">
  <title>Μια πρώτη εφαρμογή σε Gtk</title>
  <p>Για να δούμε πως φαίνεται μια πολύ απλή εφαρμογή Gtk σε Python:</p>
  <code mime="text/python" style="numbered"><![CDATA[
from gi.repository import Gtk, GdkPixbuf, Gdk
import os, sys

class GUI:
	def __init__(self):
		window = Gtk.Window()
		window.set_title ("Hello World")
		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>Ας ρίξουμε μια ματιά στο τι γίνεται:</p>
  <list>
    <item>
    <p>Η πρώτη γραμμή εισάγει το Gtk namespace (αυτό είναι που περιέχει την βιβλιοθήκη του Gtk). Οι βιβλιοθήκες παρέχονται από το GObject Introspection (gi), που παρέχει γλωσσικές επεκτάσεις για πολλές βιβλιοθήκες του GNOME.</p>
    </item>
    <item>
    <p>Στη μέθοδο <code>__init__</code> της κλάσης <code>GUI</code> δημιουργείστε ένα (άδειο) <code>Gtk.Window</code>, ορίστε τον τίτλο του και μετά συνδέστε ένα σήμα να τερματίζει την εφαρμογή όταν το παράθυρο κλείσει. Είναι πολύ απλό, περισσότερα για τα σήματα αργότερα.</p>
    </item>
    <item>
    <p>Μετά, ορίζουμε το <code>destroy</code> το οποίο απλά θα τερματίσει την εφαρμογή. Καλείται από το σήμα <code>destroy</code> που συνδέσατε παραπάνω.</p>
    </item>
    <item>
    <p>Το υπόλοιπο του αρχείου κάνει αρχικοποίηση για το Gtk και εμφανίζει το GUI.</p>
    </item>
  </list>

  <p>Ο κώδικας είναι έτοιμος να εκτελεστεί, οπότε δοκιμάστε το πατώντας <guiseq><gui>Εκτέλεση</gui><gui>Εκτέλεση</gui></guiseq>. Θα πρέπει να εμφανίσει ένα άδειο παράθυρο.</p>
</section>

<section id="signals">
  <title>Σήματα</title>
  <p>Τα σήματα είναι μια από τις έννοιες κλειδιά για τον προγραμματισμό σε Gtk. Όποτε κάτι συμβαίνει σε ένα αντικείμενο, εκπέμπει ένα σήμα! Για παράδειγμα, όταν πατιέται ένα κουμπί εκπέμπει το σήμα <code>clicked</code>. Άμα θέλετε το πρόγραμμα να κάνει κάτι όταν αυτό συμβαίνει, πρέπει να συνδέσετε μια συνάρτηση (ένα χειριστή σημάτων-signal handler) σε αυτό το σήμα. Ορίστε ένα παράδειγμα:</p>
  <code mime="text/python" style="numbered"><![CDATA[
def button_clicked () :
  print "you clicked me!"

b = new Gtk.Button ("Click me")
b.connect_after ('clicked', button_clicked)]]></code>
  <p>Οι τελευταίες δύο γραμμές δημιουργούν ένα <code>Gtk.Button</code> που ονομάζεται <code>b</code> και συνδέει το σήμα <code>clicked</code> στην συνάρτηση <code>button_clicked</code>, η οποία ορίζεται πιο πάνω. Κάθε φορά που πατιέται ένα κουμπί, ο κώδικας στη συνάρτηση <code>button_clicked</code> θα εκτελείται. Εδώ απλά τυπώνει ένα μήνυμα.</p>
</section>

<section id="containers">
  <title>Υποδοχείς (containers): Σχεδίαση διεπαφής χρήστης</title>
  <p>Γραφικά συστατικά (widgets) (όπως τα κουμπιά και οι ετικέτες) μπορούν να τοποθετηθούν στο παράθυρο κάνοντας χρήση των <em>υποδοχέων (containers)</em>. Μπορείτε να οργανώσετε την διάταξη συνδυάζοντας διαφορετικών ειδών υποδοχείς (containers), όπως κουτιά και πλέγματα.</p>
  <p>Ένα <code>Gtk.Window</code> είναι από μόνο του ένα είδος υποδοχέα (container), αλλά μπορείτε να τοποθετήσετε μόνο ένα γραφικό συστατικό (widget) άμεσα πάνω του. Θα θέλαμε να είχαμε δύο γραφικά συστατικά (widgets), μια εικόνα και ένα κουμπί, άρα θα πρέπει να τοποθετήσουμε έναν υποδοχέα (container) «υψηλής ποσότητας» μέσα στο παράθυρο για να κρατάει τα άλλα widget. Ένας αριθμός από <link href="http://library.gnome.org/devel/gtk/stable/GtkContainer.html">τύπους container</link> είναι διαθέσιμοι, αλλά θα πρέπει εδώ να χρησιμοποιήσουμε ένα <code>Gtk.Box</code>. Ένα <code>Gtk.Box</code> μπορεί να κρατήσει πολλά γραφικά συστατικά (widgets), τοποθετημένα οριζόντια ή κάθετα. Μπορείτε να κάνετε και πιο περίπλοκες διατάξεις χρησιμοποιώντας πολλά κουτιά το ένα μέσα στο άλλο.</p>
  <note>
  <p>Υπάρχει ένας σχεδιαστής γραφικού περιβάλλοντος με όνομα <app>Glade</app> ενσωματωμένο στο <app>Anjuta</app> το οποίο κάνει τη σχεδίαση γραφικού περιβάλλοντος πολύ εύκολη. Για αυτό το απλό παράδειγμα, όμως, θα γράψουμε τα πάντα σε κώδικα.</p>
  </note>
  <p>Ας προσθέσουμε ένα κουτί και γραφικά συστατικά στο παράθυρο. Προσθέστε τον παρακάτω κώδικα στη μέθοδο <code>__init__</code> αμέσως μετά τη γραμμή <code>window.connect_after</code>:</p>
<code mime="text/python" style="numbered"><![CDATA[
box = Gtk.Box()
box.set_spacing (5)
box.set_orientation (Gtk.Orientation.VERTICAL)
window.add (box)
]]>
</code>
  <p>Η πρώτη γραμμή δημιουργεί ένα <code>Gtk.Box</code> που ονομάζεται <code>box</code> και οι επόμενες γραμμές ρυθμίζουν δύο από τις ιδιότητες του: το <code>orientation</code> (προσανατολισμός) ρυθμίζεται να είναι κάθετο (οπότε τα γραφικά συστατικά-widgets τοποθετούνται σε στήλες), και το <code>spacing</code> (ενδοδιάστημα) ανάμεσα στα γραφικά συστατικά (widgets) έχει ρυθμιστεί στα 5 εικονοστοιχεία (pixels). Η επόμενη γραμμή προσθέτει στο παράθυρο το <code>Gtk.Box</code>.</p>
  <p>Μέχρι στιγμής το παράθυρο περιέχει μόνο ένα άδειο <code>Gtk.Box</code> και άμα εκτελέσετε το πρόγραμμα δε θα δείτε καμία απολύτως αλλαγή (το <code>Gtk.Box</code> είναι ένας διαφανής υποδοχέας-container, οπότε δεν μπορείτε να το δείτε).</p>
</section>

<section id="packing">
  <title>Συσκευασία: Πρόσθεση γραφικών συστατικών (widgets) στους υποδοχείς (container)</title>
  <p>Για να προσθέσετε κάποια γραφικά συστατικά (widgets) στο <code>Gtk.Box</code>, εισάγετε τον ακόλουθο κώδικα ακριβώς κάτω από την γραμμή <code>window.add (box)</code>:</p>
  <code mime="text/python" style="numbered"><![CDATA[
self.image = Gtk.Image()
box.pack_start (self.image, False, False, 0)]]></code>
  <p>Η πρώτη γραμμή δημιουργεί ένα καινούργιο <code>Gtk.Image</code> που ονομάζεται <code>image</code>, το οποίο θα εμφανίζει την εικόνα. Καθώς το χρειαζόμαστε αργότερα στο χειριστή σημάτων (signal handler), θα το ορίσουμε ως class-wide μεταβλητή (μεταβλητή με ορατότητα σε όλη την κλάση). Πρέπει να προσθέσετε το <code>image = 0</code> στην αρχή της κλάσης <code>GUI</code>. Έπειτα, το γραφικό συστατικό (widget) της εικόνας προστίθεται (<em>packed</em>) μέσα στον υποδοχέα (container) <code>box</code> χρησιμοποιώντας τη μέθοδο <link href="http://library.gnome.org/devel/gtk/stable/GtkBox.html#gtk-box-pack-start"><code>pack_start</code></link> του GtkBox.</p>
  <p>Το <code>pack_start</code> παίρνει 4 παράμετρους: το γραφικό συστατικό (widget) που θα προσθέσουμε στο GtkBox (<code>child</code>)· αν το <code>Gtk.Box</code> πρέπει να μεγαλώσει όταν προστεθεί νέο γραφικό συστατικό (<code>expand</code>)· αν το νέο γραφικό συστατικό θα έπρεπε να καλύψει όλο τον διαθέσιμο χώρο αν μεγαλώσει το <code>Gtk.Box</code> (<code>fill</code>)· και πόσος χώρος πρέπει να υπάρχει, σε εικονοστοιχεία, ανάμεσα στο γραφικό συστατικό και στους γείτονές του μέσα στο <code>Gtk.Box</code>, (<code>padding</code>).</p>
  <p>Οι υποδοχείς (containers) (και τα γραφικά συστατικά-widgets) του Gtk επεκτείνονται δυναμικά για να καλύπτουν τον διαθέσιμο χώρο, αν το επιλέξετε. Δεν τοποθετείτε τα γραφικά συστατικά (widgets) δίνοντας τους ακριβής, x, y-συντεταγμένες στο παράθυρο! Αλλά, τοποθετούνται σε σχέση το ένα με το άλλο. Αυτό κάνει την διαχείριση της αλλαγής του μεγέθους του παραθύρου πιο εύκολη, και τα γραφικά συστατικά (widgets) στις περισσότερες περιπτώσεις θα πάρουν από μόνα τους ένα λογικό μέγεθος.</p>
  <p>Επίσης σημειώστε πως τα widgets τοποθετούνται με μια ιεραρχία. Μόλις τοποθετήθηκε μέσα στο <code>Gtk.Box</code>, το <code>Gtk.Image</code> θεωρείται <em>θυγατρικό</em> του <code>Gtk.Box</code>. Αυτό μας δίνει την δυνατότητα να συμπεριφερθούμε σε όλα τα θυγατρικά ενός γραφικού στοιχείου (widget) ως μια ομάδα! Για παράδειγμα, μπορείτε να κρύψετε το <code>Gtk.Box</code>, το οποίο θα κρύψει όλες τα θυγατρικά του την ίδια στιγμή.</p>
  <p>Τώρα προσθέστε αυτές τις δύο γραμμές, κάτω από αυτές που μόλις προσθέσατε:</p>
  <code mime="text/python" style="numbered"><![CDATA[
button = Gtk.Button ("Open a picture...")
box.pack_start (button, False, False, 0)
]]></code>
  <p>Αυτές οι γραμμές είναι παρόμοιες με τις δυο πρώτες, αλλά αυτή τη φορά δημιουργούν ένα <code>Gtk.Button</code> και το προσθέτουν στο <code>box</code>. Σημειώστε ότι ορίζουμε την (δεύτερη) παράμετρο, την <code>expand</code> σε <code>False</code>, ενώ είχε οριστεί <code>True</code> για το <code>Gtk.Image</code>. Έτσι η εικόνα θα πάρει όλο το διαθέσιμο χώρο και τα κουμπιά μόνο όσο χρειάζονται. Όταν μεγιστοποιήσεις το παράθυρο, το μέγεθος των κουμπιών θα παραμείνει το ίδιο, ενώ της εικόνας θα αυξηθεί, χρησιμοποιώντας όλο το υπόλοιπο παράθυρο.</p>
</section>

<section id="loading">
  <title>Φόρτωση της εικόνας: Σύνδεση στο σήμα του κουμπιού <code>clicked</code></title>
  <p>Όταν ο χρήστης πατήσει πάνω στο κουμπί <gui>Άνοιγμα εικόνας…</gui>, ένας διάλογος θα εμφανιστεί ώστε ο χρήστης να διαλέξει μια εικόνα. Μόλις διαλέξει, η εικόνα θα φορτωθεί και θα εμφανιστεί στο αντίστοιχο γραφικό συστατικό.</p>
  <p>Το πρώτο βήμα είναι να συνδέσουμε το σήμα <code>clicked</code> του κουμπιού με μια συνάρτηση χειριστή σημάτων (signals handler), την οποία ονομάζουμε <code>on_open_clicked</code>. Βάλτε αυτόν τον κώδικα αμέσως μετά την γραμμή που το κουμπί δημιουργείται <code>button = Gtk.Button()</code>:</p>
  <code mime="text/python"><![CDATA[
button.connect_after('clicked', self.on_open_clicked)
]]></code>
  <p>Αυτό θα συνδέσει το σήμα <code>clicked</code> στη μέθοδο <code>on_open_clicked</code> η οποία θα οριστεί παρακάτω.</p>
</section>

<section id="loading2">
  <title>Φόρτωση της εικόνας: Γράφοντας την επανάκληση (callback) του σήματος</title>
  <p>Τώρα μπορούμε να δημιουργήσουμε τη μέθοδο <code>on_open_clicked</code>. Εισάγετε τα ακόλουθα στη κλάση <code>GUI</code>, μετά τη μέθοδο <code>__init__</code>:</p>
    <code mime="text/javascript" style="numbered"><![CDATA[
def on_open_clicked (self, button):
	dialog = Gtk.FileChooserDialog ("Open Image", 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>Αυτό είναι λίγο πιο περίπλοκο από όσα έχουμε κάνει μέχρι τώρα, για αυτό θα το χωρίσουμε σε κομμάτια:</p>
  <list>
    <item>
      <p>Η γραμμή που ξεκινάει με <code>dialog</code> δημιουργεί έναν διάλογο <gui>Open</gui>, τον οποίο ο χρήστης χρησιμοποιεί για να διαλέξει αρχεία. Ορίζουμε τρεις ιδιότητες: τον τίτλο του διαλόγου, την ενέργεια (type) του διαλόγου (είναι διάλογος «open», αλλά θα μπορούσαμε να χρησιμοποιήσουμε <code>SAVE</code> αν θέλαμε να αποθηκεύσουμε ένα αρχείο)· και <code>transient_for</code>, όπου ορίζει το γονικό παράθυρο του διαλόγου.</p>
    </item>
    <item>
    <p>Οι επόμενες δύο γραμμές προσθέτουν τα κουμπιά <gui>Cancel</gui> και <gui>Open</gui> στο διάλογο. Η δεύτερη παράμετρος (argument) της μεθόδου του <code>add_button</code> είναι η (ακέραιη) τιμή που επιστρέφει όταν πατιέται το κουμπί: 0 για το <gui>Ακύρωση</gui> και 1 για το <gui>Άνοιγμα</gui>.</p>
    <p>Σημειώστε όταν χρησιμοποιούμε τα <em>προκαθορισμένα</em> ονόματα κουμπιών που υπάρχουν στο Gtk, αντί να γράψουμε οι ίδιοι «Ακύρωση» ή «Άνοιγμα». Το πλεονέκτημα στη χρήση των προκαθορισμένων ονομάτων είναι ότι οι ετικέτες των κουμπιών θα έχουν ήδη μεταφραστεί στη γλώσσα του χρήστη.</p>
    </item>
    <item>
    <p>To <code>set_default_response</code> καθορίζει ποιο κουμπί θα ενεργοποιηθεί όταν ο χρήστης επιλέξει ένα αρχείο με διπλό κλικ ή πατήσει <key>Enter</key>. Στην περίπτωση μας, χρησιμοποιούμε το κουμπί <gui>Άνοιγμα</gui> σαν προεπιλεγμένο (το οποίο έχει τιμή 1).</p>
    </item>
    <item>
    <p>Οι επόμενες τρεις γραμμές περιορίζουν το διάλογο <gui>Άνοιγμα</gui> να εμφανίζει μόνο αρχεία που μπορούν να ανοιχθούν από το <code>Gtk.Image</code>. Δημιουργούμε πρώτα ένα αντικείμενο φίλτρου· προσθέτουμε στο φίλτρο όλων των ειδών αρχεία που υποστηρίζονται από το <code>Gdk.Pixbuf</code> (το οποίο περιέχει τα περισσότερα είδη εικόνων όπως PNG και JPEG). Τέλος, καθορίζουμε το φίλτρο να είναι το φίλτρο του διαλόγου <gui>Άνοιγμα</gui>.</p>
    </item>
    <item>
    <p>Το <code>dialog.run</code> εμφανίζει το διάλογο <gui>Άνοιγμα</gui>. Ο διάλογος θα περιμένει τον χρήστη να διαλέξει μια εικόνα· όταν διαλέξει, το <code>dialog.run</code> θα επιστρέψει την τιμή <output>1</output> (θα επιστρέψει <output>0</output> αν ο χρήστης πατήσει <gui>Ακύρωση</gui>). Ο έλεγχος ροής <code>if</code> ελέγχει για αυτό.</p>
    </item>
    <item><p>Αν υποθέσουμε ότι ο χρήστης πάτησε το <gui>Άνοιγμα</gui>, η επόμενη γραμμή ορίζει την ιδιότητα <code>file</code> του <code>Gtk.Image</code> στο όνομα του αρχείου εικόνας που επέλεξε ο χρήστης. Το <code>Gtk.Image</code> θα φορτώσει και θα εμφανίσει την επιλεγμένη εικόνα.</p>
    </item>
    <item>
    <p>Στην τελευταία γραμμή αυτής της μεθόδου, καταστρέφουμε τον διάλογο <gui>Open</gui> γιατί δεν τον χρειαζόμαστε πια.</p>
    </item>
  </list>

  </section>

<section id="run">
  <title>Τρέξτε την εφαρμογή</title>
  <p>Θα πρέπει να έχετε όλον τον κώδικα που χρειάζεστε, οπότε δοκιμάστε να τον εκτελέσετε. Εδώ είναι το τέλος· ένα πλήρες και λειτουργικό πρόγραμμα προβολής εικόνων (και ένας μικρός οδηγός σε Python και Gtk) σε ελάχιστο χρόνο!</p>
</section>

<section id="impl">
 <title>Υλοποίηση αναφοράς</title>
 <p>Αν αντιμετωπίσετε πρόβλημα με τον οδηγό, συγκρίνετε τον κώδικά σας με αυτόν <link href="image-viewer/image-viewer.py">τον κώδικα αναφοράς</link>.</p>
</section>

<section id="next">
  <title>Επόμενα βήματα</title>
  <p>Εδώ είναι κάποιες ιδέες για το πως μπορείτε να επεκτείνετε αυτή την απλή επίδειξη:</p>
  <list>
   <item>
   <p>Βάλτε τον χρήστη να επιλέξει ένα φάκελο αντί για αρχείο, και παρέχετε του τη δυνατότητα να περιηγηθεί σε όλες τις εικόνες σε ένα φάκελο.</p>
   </item>
   <item>
   <p>Εφαρμόστε τυχαία φίλτρα και εφέ στην εικόνα όταν αυτή φορτωθεί και δώστε την δυνατότητα στον χρήστη να αποθηκεύσει την επεξεργασμένη εικόνα.</p>
   <p>Το <link href="http://www.gegl.org/api.html">GEGL</link> παρέχει ισχυρές δυνατότητες επεξεργασίας εικόνας.</p>
   </item>
   <item>
   <p>Επιτρέψτε στον χρήστη να φορτώνει εικόνες από υπηρεσίες διαμοιρασμού αρχείων του Διαδικτύου, από σαρωτές και από άλλες περίπλοκες πηγές.</p>
   <p>Μπορείτε να χρησιμοποιήσετε το <link href="http://library.gnome.org/devel/gio/unstable/">GIO</link> για να ελέγχετε μεταφορές αρχείων και παρόμοια, και τη <link href="http://library.gnome.org/devel/gnome-scan/unstable/">Σάρωση του GNOME</link> για τους σαρωτές.</p>
   </item>
  </list>
</section>

</page>
