Ejemplos de código

En esta sección se exponen extractos de código de distinta complejidad a modo de ejemplo y aplicaciones desarrolladas con Genie.

Siempre viene bien ver cómo lo hacen otros.

Stats Calc

Calculadora estadística. Se trata de un proyecto en una fase inicial de desarrollo, en el que el autor de esta Wiki pone en práctica algunos de los contenidos aquí expuestos, y en particular la interfaz gráfica Gtk y las colecciones, entre otros. Ocasionalmente también se utilizan otros recursos que (todavía) no han sido tratados en Genie Doc.

Código disponible en el repositorio de GitHub StatsCalc. También disponible en paquete deb listo para instalar.

Capturas de pantalla:

AgenDie

 AgenDie, icono de aplicación

AgenDie es una aplicación que recrea una agenda de contactos. Como tal, dispone de las funciones básicas de una libreta de direcciones o directorio para organizar contactos: añadir, eliminar, editar y buscar contactos.

Se trata de un proyecto en una fase inicial de desarrollo, en el que el autor de esta Wiki pone en práctica algunos de los contenidos aquí expuestos, y en particular la interfaz gráfica Gtk, la base de datos SQLite y las listas, entre otros. Ocasionalmente también se utilizan otros recursos que (todavía) no han sido tratados en Genie Doc.

Desde la web del proyecto, publicado en Launchpad, se puede obtener el código y descargar un paquete deb para instalar. También publicado en un repositorio de GitHub.

Desde allí se pueden seguir los progresos de la aplicación y aportar sugerencias e ideas para mejorarlo.

Game Classic: Number Guessing

Código tomado de Projects Genie y traducido para esta Wiki. Juego para adivinar un número.

NumberGuessing.gs
class NumberGuessing
 
	prop min : int
	prop max : int
 
	construct ( m : int, n : int)
		self.min = m
		self.max = n
 
	def start()
		try_count : int = 0
		number : int = Random.int_range (min, max)
 
		stdout.printf ("Bienvenido a ¡Adivina un número!\n\n")
		stdout.printf ("Yo estoy pensando un número entre %d y %d\n", min, max)
		stdout.printf ("que tienes que adivinar. Pero no te preocupes, te\n")
		stdout.printf ("daré algunas pistas.\n\n")
 
		while true
			stdout.printf ("Intento #%d\n", ++try_count)
			stdout.printf ("Por favor, introduce un número entre %d y %d: ", min, max)
			input : int = int.parse (stdin.read_line ())
			if number is input
				stdout.printf ("¡Felicidades! Lo has adivinado.\n")
				break
			else
				stdout.printf ("Incorrecto. El número que buscas es %s que %d.\n",
				number > input ? "mayor" : "menor", input)
init 
		var game = new NumberGuessing(1, 100)
		game.start()

A for loop and a password routine

Código enviado a Stack Overflow en respuesta a una consulta sobre un código de verificación de contraseña (solución sin necesidad de la librería libgee).

password.gs
init
	users: array of string = {"Fred","John","Steve","Ann","Mary"}
	passwords: array of string = {"access","dog","12345","kids","qwerty"}
 
	print "Enter user name"
	usrname:string = stdin.read_line()
	print "Enter password"
	pwd:string = stdin.read_line()
 
	error:int = 0
	cont:int = 0
	for var user in users
		if (user!=usrname)
			error++
			if error == (users.length)
				print "No reconocido. Acceso denegado."
		if (user==usrname)
			position:int = cont                 
			if pwd == passwords[position]               
				print "OK: Acceso Concedido."
			else                
				print "Password incorrecta."
		cont++

Simple Text Viewer

Como su nombre indica, un sencillo visor de texto que ha sido programado con Genie y que utiliza la interfaz gráfica Gtk. Tomado del foro Vala and Genie programming y corregido para esta Wiki.

SimpleTextViewer.gs
// compilar con valac --pkg gtk+-3.0 nombre_archivo.gs
uses
	Gtk
 
init
	Gtk.init (ref args)
	var test = new TextFileViewer ()
	test.show_all ()
	Gtk.main ();
 
class TextFileViewer : Window
	text_view : TextView
	init
		title = "Simple Text Viewer"
		default_height = 350
		default_width = 500
		window_position = WindowPosition.CENTER
		destroy += Gtk.main_quit
		var toolbar = new Toolbar ()
		var open_button = new ToolButton.from_stock (STOCK_OPEN)
		var quit_button = new ToolButton.from_stock (STOCK_QUIT)
		toolbar.add (open_button)
		toolbar.add (quit_button)
		open_button.clicked += on_open_clicked
		quit_button.clicked += Gtk.main_quit
		this.text_view = new TextView ()
		this.text_view.editable = false
		this.text_view.cursor_visible = true
		var scroll = new ScrolledWindow (null, null)
		scroll.set_policy (PolicyType.AUTOMATIC, PolicyType.AUTOMATIC)
		scroll.add (this.text_view)
		var vbox = new VBox (false, 0)
		vbox.pack_start (toolbar, false, true, 0)
		vbox.pack_start (scroll, true, true, 0)
		add (vbox)
 
	def private on_open_clicked ()
		var file_chooser = new FileChooserDialog ("Open File", this, FileChooserAction.OPEN, STOCK_CANCEL, ResponseType.CANCEL, STOCK_OPEN, ResponseType.ACCEPT, null)
		if (file_chooser.run () == ResponseType.ACCEPT)
			open_file (file_chooser.get_filename ())
		file_chooser.destroy ()
 
	def private open_file (filename : string)
			try
				text : string
				len : ulong
				FileUtils.get_contents (filename, out text, out len)
				leni:int = 0
				leni = (int) len
				this.text_view.buffer.set_text (text, leni)
			except e:OptionError
				stdout.printf ("%s\n", e.message) 

Writing a gedit Plugin with Genie

Tutorial sobre cómo programar con Genie un plugin para Gedit.

TreeView List Example

Código tomado de srackham | blog. La interfaz de usuario se construyó utilizando Glade.

treeview-list.gs
/*
	treeview-list.gs - Genie TreeView list example
 
	Build with:
	valac --pkg gtk+-2.0 --pkg posix treeview-list.gs
*/
 
 
init
	Gtk.init(ref args)
	var app = new App ()
	app.start ()
	Gtk.main ()
 
class App: Object
 
	window: Gtk.Window
	treeview: Gtk.TreeView
	liststore: Gtk.ListStore
	msg_label: Gtk.Label
 
	struct Person
		id: int
		name: string
		city: string
		country: string
 
		def to_string (): string
			return @"$(id), $(name), $(city), $(country)"
 
	data: array of Person = {
		Person() {id=1, name="Joe Bloggs", city="London", country="England"},
		Person() {id=2, name="Bill Smith", city="Auckland", country="New Zealand"},
		Person() {id=3, name="Joan Miller", city="Boston", country="USA"}
	}
 
	init
		var builder = new Gtk.Builder()
		try
			builder.add_from_file("treeview-list.ui")
		except e:Error
			stderr.printf(@"$(e.message)\n")
			Posix.exit(1)
		builder.connect_signals(this)
		this.window = builder.get_object("window") as Gtk.Window
		this.msg_label = builder.get_object("msg-label") as Gtk.Label
		this.treeview = builder.get_object ("treeview") as Gtk.TreeView
		// Load list data.
		this.liststore = builder.get_object ("liststore") as Gtk.ListStore
		this.liststore.clear ()
		for p: Person in this.data
			iter: Gtk.TreeIter
			this.liststore.append (out iter)
			this.liststore.set (iter, 0, p.id, 1, p.name, 2, p.city, 3, p.country)
		// Monitor list double-clicks.
		this.treeview.row_activated.connect (on_row_activated)
		// Monitor list selection changes.
		this.treeview.get_selection().changed.connect (on_selection)
		this.window.destroy.connect (Gtk.main_quit)
 
	def start ()
		this.window.show_all ();
 
	def static get_selection (model: Gtk.TreeModel, iter: Gtk.TreeIter): Person
		var p = Person()
		model.get (iter, 0, out p.id, 1, out p.name, 2, out p.city, 3, out p.country)
		return p
 
	/* List item double-click handler. */
	def on_row_activated (treeview: Gtk.TreeView, path: Gtk.TreePath, column: Gtk.TreeViewColumn)
		iter: Gtk.TreeIter
		if treeview.model.get_iter (out iter, path)
			p: Person = get_selection (treeview.model, iter)
			this.msg_label.label = @"Double-clicked: $(p)"
 
	/* List item selection handler. */
	def on_selection (selection: Gtk.TreeSelection)
		model: Gtk.TreeModel
		iter: Gtk.TreeIter
		if selection.get_selected (out model, out iter)
			p: Person = get_selection (model, iter)
			this.msg_label.label = @"Selected: $(p)"
treeview-list.ui
<?xml version="1.0"?>
<interface>
  <requires lib="gtk+" version="2.16"/>
  <!-- interface-naming-policy project-wide -->
  <object class="GtkListStore" id="liststore">
    <columns>
      <!-- column-name id -->
      <column type="gint"/>
      <!-- column-name name -->
      <column type="gchararray"/>
      <!-- column-name city -->
      <column type="gchararray"/>
      <!-- column-name country -->
      <column type="gchararray"/>
    </columns>
  </object>
  <object class="GtkWindow" id="window">
    <property name="title" translatable="yes">TreeView List Example</property>
    <property name="window_position">center</property>
    <property name="default_width">440</property>
    <property name="default_height">250</property>
    <child>
      <object class="GtkVBox" id="vbox1">
        <property name="visible">True</property>
        <child>
          <object class="GtkScrolledWindow" id="scrolledwindow1">
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="hscrollbar_policy">automatic</property>
            <property name="vscrollbar_policy">automatic</property>
            <child>
              <object class="GtkTreeView" id="treeview">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="model">liststore</property>
                <child>
                  <object class="GtkTreeViewColumn" id="treeviewcolumn1">
                    <property name="title">Name</property>
                    <child>
                      <object class="GtkCellRendererText" id="cellrenderertext1"/>
                      <attributes>
                        <attribute name="text">1</attribute>
                      </attributes>
                    </child>
                  </object>
                </child>
                <child>
                  <object class="GtkTreeViewColumn" id="treeviewcolumn2">
                    <property name="title">City</property>
                    <child>
                      <object class="GtkCellRendererText" id="cellrenderertext2"/>
                      <attributes>
                        <attribute name="text">2</attribute>
                      </attributes>
                    </child>
                  </object>
                </child>
                <child>
                  <object class="GtkTreeViewColumn" id="treeviewcolumn3">
                    <property name="title">Country</property>
                    <child>
                      <object class="GtkCellRendererText" id="cellrenderertext3"/>
                      <attributes>
                        <attribute name="text">3</attribute>
                      </attributes>
                    </child>
                  </object>
                </child>
              </object>
            </child>
          </object>
          <packing>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkLabel" id="msg-label">
            <property name="visible">True</property>
            <property name="xalign">0</property>
            <attributes>
              <attribute name="weight" value="bold"/>
              <attribute name="foreground" value="#30af5549c84f"/>
            </attributes>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="padding">2</property>
            <property name="position">1</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
</interface>

Tenis: Genie + SDL

Para ver el proceso de desarrollo y más detalles visita el apartado sobre SDL en esta Wiki. También desde allí puedes descargar el archivo compilado y los archivos de sonido necesarios.

tenis.gs
// compila con valac --pkg sdl --pkg sdl-gfx -X -lSDL_gfx --pkg sdl-mixer -X -lSDL_mixer nombre_archivo.gs
uses SDL
uses SDLGraphics
uses SDLMixer
 
// sonido
archivo1: SDL.RWops
archivo2: SDL.RWops
sonido1: SDLMixer.Chunk
sonido2: SDLMixer.Chunk
canal1: SDLMixer.Channel
canal2: SDLMixer.Channel
 
init	
	titulo:string = "Genie SDL Demo"
	ancho:int16 = 640
	alto:int16 = 480
	bpp:int = 16
	flags:uint32 = SurfaceFlag.FULLSCREEN | SurfaceFlag.DOUBLEBUF | SurfaceFlag.HWACCEL | SurfaceFlag.HWSURFACE | SurfaceFlag.ANYFORMAT
	screen: unowned SDL.Screen
 
	tenis:bool = false
	juego: SDL.Event	
	intro:bool = false
	inicio: SDL.Event	
	gameover:bool = true
	evento: SDL.Event	
	pausa:bool = false
	saque: bool = true
	fin:bool = false
 
	var x = 100
	var y = 240
	var new_x = 0
	var new_y = 0
 
	var pos1 = 200
	var pos2 = 200
	var pos1_new = 0
	var pos2_new = 0
 
	mar1:int = 0
	mar2:int = 0
	marca1:string
	marca2: string
 
	SDL.init(SDL.InitFlag.VIDEO) 
	SDL.WindowManager.set_caption (titulo, "") 
	screen = SDL.Screen.set_video_mode (ancho, alto, bpp, flags)
 
	//oculta cursor
	SDL.Cursor.show(0)
 
	// sonidos
	SDLMixer.open(44100,SDL.AudioFormat.S16LSB,2,4096)
	archivo1 = new SDL.RWops.from_file ("/home/jcv/Programacion/Genie/SDL/Boing.ogg","rb")
	sonido1 = new SDLMixer.Chunk.WAV (archivo1,-1)
	archivo2 = new SDL.RWops.from_file ("/home/jcv/Programacion/Genie/SDL/error.ogg","rb")
	sonido2 = new SDLMixer.Chunk.WAV (archivo2,-1)
 
	while (!tenis)
		while (Event.poll (out juego)) == 1
 
			// pantalla de inicio
			while (!intro)
				while (Event.poll (out inicio)) == 1
					case inicio.type
						when SDL.EventType.QUIT
							intro = true
							tenis = true
							break
						when EventType.KEYDOWN
							case inicio.key.keysym.sym
								when KeySymbol.ESCAPE
									intro = true
									tenis = true
									break
								when KeySymbol.i
									intro = true
									gameover = false
									break
 
				Rectangle.fill_rgba(screen, 0, 0, ancho, alto, 0, 0, 0, 200)
 
				Text.rgba(screen, 260, 100, "JUEGO DE TENIS RETRO", 255, 255, 255, 200)
				Text.rgba(screen, 300, 180, "CONTROLES", 255, 255, 255, 200)
				Text.rgba(screen, 160, 200, "Inicio: I     Salir : ESC   Pausa: P", 255, 255, 255, 200)
				Text.rgba(screen, 160, 240, "              Jugador 1     Jugador 2", 255, 255, 255, 200)
				Text.rgba(screen, 160, 260, "Subir:           A             Up", 255, 255, 255, 200)
				Text.rgba(screen, 160, 280, "Bajar:           Z            Down", 255, 255, 255, 200)
				Text.rgba(screen, 160, 300, "Sacar:           Q", 255, 255, 255, 200)
 
				screen.flip()
 
			// pantalla de juego
			while (!gameover)
				while (Event.poll (out evento)) == 1
					case evento.type
						when SDL.EventType.QUIT
							gameover = true
							intro = false
							break
						when EventType.KEYDOWN
							case evento.key.keysym.sym
								when KeySymbol.ESCAPE
									// reiniciar variables
									x = 100
									y = 240
									new_x = 0
									new_y = 0
									pos1 = 200
									pos2 = 200
									pos1_new = 0
									pos2_new = 0
									mar1 = 0
									mar2 = 0
									fin = false
									gameover = true
									saque = true
									intro = false
									break
								when KeySymbol.q
									if saque == true
										new_x = 2
										var saque_y = Random.int_range (1, 3)
										if saque_y == 1
											new_y = -2
										else
											new_y = 2
										canal1.play(sonido1,0)  // sonido de saque
									saque = false
								when KeySymbol.p  //pausa
									if pausa == false
										pausa = true
									else if pausa == true
										pausa = false
									break						
								when KeySymbol.a
									pos1_new = -1
									break
								when KeySymbol.z
									pos1_new = +1
									break
								when KeySymbol.UP
									pos2_new = -1
									break
								when KeySymbol.DOWN
									pos2_new = +1
									break
						when EventType.KEYUP
							case evento.key.keysym.sym
								when KeySymbol.a
									pos1_new = 0
									break
							case evento.key.keysym.sym
								when KeySymbol.z
									pos1_new = 0
									break
							case evento.key.keysym.sym
								when KeySymbol.UP
									pos2_new = 0
									break
							case evento.key.keysym.sym
								when KeySymbol.DOWN
									pos2_new = 0
									break
 
				// pausa		
				while (pausa == true)
					SDL.Event.wait (out evento)
					Text.rgba(screen, 260, 240, "JUEGO EN PAUSA (P)", 255, 255, 255, 200)
					screen.flip()
					if evento.type == EventType.KEYDOWN
						if evento.key.keysym.sym == KeySymbol.p
							pausa = false
							break
 
				// actualiza la posición de los jugadores
				pos1 += pos1_new
				pos2 += pos2_new
 
				// evita que los jugadores se salgan de la pantalla
				if pos1 < 0 do pos1 = -1
				if pos1 > 400 do pos1 = 401
				if pos2 < 0 do pos2 = -1
				if pos2 > 400 do pos2 = 401
 
				// rebota en las paredes
				if x > ancho or x < 0
					canal2.play(sonido2,0)
					if x < 0 
						mar2 += 1
					if x > ancho
						mar1 += 1
					new_x = -new_x
				if y > alto or y < 0
					new_y = -new_y
 
				// rebota en los rectángulos de los jugadores
				// jugador 1
				if (x > 60 and x < 65) and (y >= pos1 and y <= pos1+80)
					if y == pos1 or y == pos1+80
						new_y = -new_y
					else
						new_x = -new_x
					canal1.play(sonido1,0)
				// jugador 2
				if (x > 575 and x < 580) and (y >= pos2 and y <= pos2+80)
					if y == pos2 or y == pos2+80
						new_y = -new_y
					else
						new_x = -new_x
					canal1.play(sonido1,0)
				x = x + new_x
				y = y + new_y
 
				// fondo y linea central
				Line.rgba(screen, 320, 40, 320, 440, 255, 255, 255, 128) 
				Rectangle.fill_rgba(screen, 0, 0, ancho, alto, 0, 128, 255, 128)
				// jugadores
				Rectangle.fill_rgba(screen, 60, pos1, 65, pos1+80, 255, 255, 255, 200)
				Rectangle.fill_rgba(screen, 575, pos2, 580, pos2+80, 255, 255, 255, 200)
				// pelota
				Circle.fill_rgba (screen, x, y, 10, 255, 255, 0, 200)
				Circle.outline_rgba_aa (screen, x, y, 10, 255, 255, 0, 200)
				// marcador
				marca1 = mar1.to_string()
				marca2 = mar2.to_string()
				Text.rgba(screen, 280, 10, marca1, 255, 255, 255, 200)
				Text.rgba(screen, 340, 10, marca2, 255, 255, 255, 200)
 
				if marca1 == "21" or marca2 == "21"						
					fin = true
 
				while (fin == true)
					Text.rgba(screen, 260, 220, "JUEGO TERMINADO", 255, 255, 255, 200)
					Text.rgba(screen, 260, 260, "Inicio: ESC / Otro Juego: J", 255, 255, 255, 200)
					screen.flip()
					SDL.Event.wait (out evento)									
					if evento.type == EventType.KEYDOWN
						if evento.key.keysym.sym == KeySymbol.ESCAPE
							// reiniciar variables
							x = 100
							y = 240
							new_x = 0
							new_y = 0
							pos1 = 200
							pos2 = 200
							pos1_new = 0
							pos2_new = 0
							mar1 = 0
							mar2 = 0
							fin = false
							gameover = true
							saque = true
							intro = false
							break		
 
						if evento.key.keysym.sym == KeySymbol.j
							// reiniciar variables
							x = 100
							y = 240
							new_x = 0
							new_y = 0
							pos1 = 200
							pos2 = 200
							pos1_new = 0
							pos2_new = 0
							mar1 = 0
							mar2 = 0
							fin = false
							saque = true
							break
 
				screen.flip()
 
		screen.flip()
	SDL.quit ()

Kataluga

Kataluga es un programa en euskera y castellano creado para tratar la dislexia y otros problemas de adquisición de la lectura y la escritura. Desarrollado para Linux y Windows por Gontzal Uriarte, el código usa y combina distintas librerías, como Gee, Gtk, Pango, Cairo y SDL, y está disponible en GitLab. Además Gontzal es autor del blog Tutorial de Genie / Vala que ha sido una de las referencias importantes de esta Wiki.

Genie progress bar example

Ejemplo para mostrar cómo se podría usar Gtk.ProgressBar mientras se realiza una tarea. Autor: José Fonte (phastmike). Fuente: https://github.com/phastmike/genie-progress-bar-example

genie-progress-bar.gs
/* -*- Mode: Genie; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*- */
/* vim: set tabstop=4 softtabstop=4 shiftwidth=4 expandtab : */ 
/* 
 * genie-progress-bar.gs 
 *
 * Genie progress bar example - activity mode
 */
 
uses Gtk
 
init
	Gtk.init (ref args)
	var AppWindow = new MainAppWindow ()
	AppWindow.show_all ()
	Gtk.main ()
 
class MainAppWindow : Window
	progress_bar:Gtk.ProgressBar
 
	init
		title = "Genie Progress Bar example"
		default_height = 40
		default_width = 350
		window_position = WindowPosition.CENTER
 
		var grid = new Gtk.Grid ()
		progress_bar = new Gtk.ProgressBar ()
		progress_bar.expand = true
		grid.attach (new Gtk.Label ("Genie, ProgressBar Example"), 0, 0, 1, 1) 
		grid.attach (progress_bar, 0, 1, 1, 1)
 
		add (grid)
		destroy.connect (Gtk.main_quit)
		Timeout.add (50, pb_pulse);
 
		var parser = new Parser ()
		parser.parse_ended.connect (on_parse_ended)
		parser.parse ()
 
	def pb_pulse () : bool
		progress_bar.pulse ()
		return true
 
	def on_parse_ended ()
		Gtk.main_quit ()
 
class Parser : Object
	counter:int
	event parse_ended ()
 
	def parse ()
		counter = 0
		print ("Start parsing..")
		Timeout.add_seconds (1, work_counter)
 
	def work_counter () : bool
		counter += 1
		print ("Counter %d", counter)
		if counter < 10
			return true
		else
			print ("Ended parsing..")
			parse_ended ()
			return false
🔝