The Button.Connect Syntax in Genie

The Button.connect syntax in Genie

The problem is to do with where identifiers are valid and is known as "scope".

The Vala example makes use of an anonymous function, also called a lambda expression in Vala. An anonymous function can be a "closure", when the variables in the scope that defines the anonymous function are also available within the anonymous function. This is useful because the callback occurs after the original block of code has been run, but the variables are still available within the callback. So in the Vala example, where both the button and label are defined in the enclosing scope, the button and label are also available in the callback anonymous function.

Unfortunately Genie isn't able to parse anonymous functions as function arguments, in this case within the connect() call. Although some work has been done on this in 2015. So you have rightly used a function name instead. The problem is the callback only passes the button as an argument and not the adjacent label. So to make the label available within the callback function we could use a class:

/* ANOTHER GTK EXPERIMENT WITH GENIE BASED ON ELEMENTARY INTRODUCTORY PAGE
** compile with valac --pkg gtk+-3.0 layoutgtkexample.gs */

[indent=4]
uses Gtk

init
Gtk.init (ref args)
new RotatingButtonWindow( "Hello World!" )
Gtk.main ()

class RotatingButtonWindow:Window
_hello_label:Label
_rotate_label:Label

construct( window_title:string )
title = window_title
set_border_width(12)

var layout = new Grid ()
layout.column_spacing = 6
layout.row_spacing = 6

// add 'hello' row of widgets
var hello_button = new Button.with_label("Say Hello")
_hello_label = new Label("Hello")
layout.attach (hello_button, 0, 0, 1,1)
layout.attach_next_to (_hello_label, hello_button, PositionType.RIGHT, 1, 1)

// add 'rotate' row of widgets
var rotate_button = new Button.with_label ("Rotate")
_rotate_label = new Label("Horizontal")
layout.attach(rotate_button, 0,1,1,1)
layout.attach_next_to(_rotate_label, rotate_button, PositionType.RIGHT, 1, 1)

add(layout)

hello_button.clicked.connect(hello_pushed)
rotate_button.clicked.connect(rotate_pushed)

destroy.connect(Gtk.main_quit)
show_all ()

def hello_pushed (btn:Button)
_hello_label.label = "Hello World!"
btn.sensitive = false

def rotate_pushed (btn:Button)
_rotate_label.label = "Vertical"
_rotate_label.angle = 90
btn.sensitive = false

A few notes:

  • By placing the definitions of the _hello_label and _rotate_label within the scope of the class they become available to all the functions defined in the class. Definitions like this are often called "fields". The underscore means they are not available outside the class, so in the example you cannot access them from init
  • construct() is called when the object is created, in the example the line new RotatingButtonWindow( "Hello World!" ) instantiates the object. If you repeat the line you will have two separate windows, that is two instances of the RotatingButtonWindow data type
  • You will notice that the RotatingButtonWindow type is also defined as a Window type. This means it is adding more detail to the Gtk.Window class. This is why title and set_border_width() can be used within the new class. They have been "inherited" from the parent Gtk.Window class
  • By using the Gtk namespace with uses Gtk we don't need to prefix everything with Gtk

As your Gtk application gets more complex you probably want to look at GtkBuilder. That allows windows and widgets to be laid out in an external file. Then use GResource to build the file into the binary of your application so there is no need to distribute the UI file separately.

How to pack a button in a HeaderBar using Genie?

You are trying to call pack_start on the button, not on the headerbar:

// Add everything to the toolbar
open_button.pack_start ()

The correct code is:

headerbar.pack_start (open_button)

Better syntax highlighting for Genie

GEANY is an open-source, lightweight and fast text editor, providing the main features of an IDE, including syntax-highlighting, with support for Genie.

The following article has installation instructions and also mentions that Geany should work fine with elementaryOS.

http://linuxg.net/install-geany-on-ubuntu/

Alternatives to vala lambdas in Genie

uses Gtk
class TestWindow : Window
notebook:Gtk.Notebook
init
// General characteristics of the window
title = "Gtk Containers"
default_height = 250
default_width = 250
window_position = WindowPosition.CENTER
destroy.connect(Gtk.main_quit)

// Now building the notebook
notebook = new Gtk.Notebook()
var label1 = new Gtk.Label("Page one")
var label2 = new Gtk.Label("Page two")
var label3 = new Gtk.Label("Page three")
var label4 = new Gtk.Label("Page four")

var child1 = new Button.with_label ("Go to next page")
child1.clicked.connect (childclicked1)
var child2 = new Button.with_label ("Go to next page")
child2.clicked.connect (childclicked2)
var child3 = new Button.with_label ("Go to next page")
child3.clicked.connect (childclicked3)
var child4 = new Button.with_label ("Go to first page")
child4.clicked.connect (childclicked4)

notebook.append_page(child1, label1)
notebook.append_page(child2, label2)
notebook.append_page(child3, label3)
notebook.append_page(child4, label4)

// Now building the grid
var grid = new Grid()
var button1 = new Gtk.Button.with_mnemonic("Button_1")
var button2 = new Button.with_mnemonic("Button 2")

// Attaching all elements into the grid
grid.attach(notebook, 0,0,2,1)
grid.attach(button1, 0,1,1,1)
grid.attach(button2, 1,1,1,1)
add(grid)

def childclicked1()
notebook.set_current_page(1)

def childclicked2()
notebook.set_current_page(2)

def childclicked3()
notebook.set_current_page(3)

def childclicked4()
notebook.set_current_page(0)

init
Gtk.init (ref args)
var test = new TestWindow ()
test.show_all ()
Gtk.main ()

I think the only alternative is this. Is not supported.

How to use the ToolButton clicked signal (in a HeaderBar)?

You didn't include the click handler in your code, using this example stub it works just fine:

def openfile ()
warning ("Button clicked")

So I guess that the type signature of your click handler is wrong and that's why the compiler is complaining here.

Gtk.Application in Genie

You're example looks like a pretty good start to me, but I think you should add an application ID and some application flags.

Three good resources are the GTK+3 Reference Manual's documentation for GtkApplication, the GNOME Wiki "HowDoI" section's page called "Using GtkApplication" and the GIO Reference Manual's documentation for GApplication. GApplication, or GLib.Application in the Vala binding, is the parent class for GtkApplication.

The "HowDoI" page advises:

GtkApplication does not implement main() for you. You must do so yourself. Your main() function should be as small as possible and do almost nothing except creating your GtkApplication and running it. The "real work" should always be done in response to the signals fired by GtkApplication.

Your main() function in Genie is:

init
new MyApplication().run( args )

and that's about as simple as you can get.

The "HowDoI" page also advises:

When your application starts, the startup signal will be fired. This gives you a chance to perform initialisation tasks that are not directly related to showing a new window. After this, depending on how the application is started, either activate or open will be called next.

You're not doing any start up tasks with your example, which is fine. So there is no need to use the startup signal, but you are using the activate signal by overriding a virtual function with def override activate (). activate is effectively the default signal when the Gtk.Application runs, but alternative signals can be emitted when the appropriate ApplicatonFlags are set. For example if the HANDLES_OPEN flag is set then the open signal will be sent if there are unparsed command line arguments. The unparsed arguments are taken to be filenames or URIs. The default flags are FLAGS_NONE and that will be made explicit in the example code later.

The GTK+3 Reference Manual's section on GtkApplication states:

Currently, GtkApplication handles GTK+ initialization, application uniqueness, session management, provides some basic scriptability and desktop shell integration by exporting actions and menus and manages a list of toplevel windows whose life-cycle is automatically tied to the life-cycle of your application...If no application ID is given then some features (most notably application uniqueness) will be disabled. A null application ID is only allowed with GTK+ 3.6 or later.

The application ID should be made up of at least two names separated by a dot. If the application is run a second time then the second instance's window becomes part of the first application, but the second application instance is then closed. This is the application uniqueness feature and can be disabled using ApplicationFlags.NON_UNIQUE. The application is registered on the session bus using the application ID. If you are using Linux you can use a tool like D-Feet to see the application appear on the session bus and also what happens when you run the application again (you need to refresh the view).

Time for some code:

// compila con valac --pkg gtk+-3.0 nombre_archivo.gs
[indent=4]
uses Gtk

init
new MyApplication( "org.genie.Example.SimpleGtkApplication",
ApplicationFlags.FLAGS_NONE
).run( args )

class MyApplication:Gtk.Application
construct( application_id:string, flags:ApplicationFlags )
if !id_is_valid( application_id )
error( "application id %s is not valid", application_id )
this.application_id = application_id
this.flags = flags

def override activate ()
var window = new Gtk.ApplicationWindow( this )
window.title = "Welcome to GNOME"
window.set_default_size( 400, 400 )
window.show_all()

This adds an application ID and makes the ApplicationFlags explicit.

Vala GtkButton.Clicked.Connect not calling function

You need [GtkChild] on every field if they are in the template. Right now, menurefresh contains null and won't be connected to anything. label1 is also null, so changing its label won't do anything.

The correct code is:

namespace Zeiterfassunggtk {
[GtkTemplate (ui = "/org/gnome/Zeiterfassunggtk/window.ui")]
public class Window : Gtk.ApplicationWindow {
[GtkChild]
Gtk.Button refreshbutton;
[GtkChild]
Gtk.Button menubuttonrefresh;
[GtkChild]
Gtk.Label label1;

void refresh () {
label1.label = "Clicked";
}

public Window (Gtk.Application app) {
Object (application: app);

refreshbutton.clicked.connect (this.refresh);
menubuttonrefresh.clicked.connect (this.refresh);

this.show_all ();
}
}
}

Printing the results from a select query with Genie

Yes, you need to assign a prepared statement, not null, to stmt. For example:

// Trying to do a cookbook program
// raw_input for Genie included, compile with
// valac --pkg sqlite3 --pkg gee-0.8 cookbook.gs
[indent=4]
uses Sqlite

init
db:Database
if (Database.open ("cookbook.db3", out db) != OK)
stderr.printf ("Error: %d: %s \n", db.errcode (), db.errmsg ())
Process.exit (-1)

while true
response:string = UserInterface.get_input_from_menu()
if response is "1" // Show All Recipes
PrintAllRecipes( db )
else if response is "2" // Search for a recipe
pass
else if response is "3" //Show a Recipe
pass
else if response is "4"//Delete a recipe
pass
else if response is "5" //Add a recipe
pass
else if response is "6" //Print a recipe
pass
else if response is "0" //Exit
print "Goodbye"
break
else
print "Unrecognized command. Try again."

namespace UserInterface
def get_input_from_menu():string
show_menu()
return raw_input("Enter a selection -> ")

def raw_input (query:string = ""):string
stdout.printf ("%s", query)
return stdin.read_line ()

def show_menu()
print """===================================================
RECIPE DATABASE
1 - Show All Recipes
2 - Search for a recipe
3 - Show a Recipe
4 - Delete a recipe
5 - Add a recipe
6 - Print a recipe
0 - Exit
==================================================="""

namespace PreparedStatements
def select_all( db:Database ):Statement
statement:Statement
db.prepare_v2( """
select name, servings as serves, source from Recipes
""", -1, out statement )
return statement

def PrintAllRecipes ( db:Database )
print "%-5s%-30s%-20s%-30s", "Item", "Name", "Serves", "Source"
print "--------------------------------------------------------------------------------------"
stmt:Statement = PreparedStatements.select_all( db )
cols:int = stmt.column_count ()
var row = new dict of string, string
item:int = 1
while stmt.step() == ROW
for i:int = 0 to (cols - 1)
row[ stmt.column_name( i ) ] = stmt.column_text( i )
stdout.printf( "%-5s", item.to_string( "%03i" ))
stdout.printf( "%-30s", row[ "name" ])
stdout.printf( "%-20s", row[ "serves" ])
stdout.printf( "%-30s\n", row[ "source" ])
item++

A few pointers

  • Generally you want to avoid assigning null. null is no value. For example a boolean can either be true or false and nothing else, but a variable that can have no value makes things more complicated.

    a:bool? = null
    if a == null
    print "I'm a boolean variable, but I am neither true nor false???"

    If you are looking to declare a variable in Genie before assigning a value, for example when calling a function with an out parameter, don't assign anything. I have changed db:Database to show this

  • Process.exit( -1 ) should probably be used sparingly and really only for error conditions that you want to signal to a calling command line script. I don't think a user selected exit from the program is such an error condition, so I have changed Process.exit( -1 ) to break for that
  • The definition of functions doesn't matter whether it is before or after init, I prefer to put them after so the first function that is called, i.e. init, is at the top and easy to read
  • A class is a data type and yes, it can have functions, but usually you need some data defined in the class and the function is written to act on that data. A function in a class is often called a 'method' and in the past with object oriented programming classes were defined to group methods together. These methods had no data to act on and are defined as 'static' methods. The modern practise is to mainly use static methods for creating more complex object constructors, look up 'factory' methods and creational design patterns. Instead to group functions, and other syntax, we use namespaces. I have used a couple of namespaces in the example. Usually a namespace is given its own file or files. If you are thinking of splitting your Genie project into more source files then take a look at https://wiki.gnome.org/Projects/Genie#A_Simple_Build_Script
  • A primary key should be internal to the database and would not be presented to a user, only a database administrator would be interested in such things. So I have changed 'item' in the output to be a count of the number of entries displayed
  • Genie and Vala bind the SQLite C interface. If you need more details on a particular function take a look at C-language Interface Specification for SQLite

genie' programming language usage outside Puppy Linux

It looks like the sources are all in the valagenie{parser,scanner,tokentype}.vala files at Gnome's git repo.

It makes sense that Genie's sources are in the vala tree because you have to use valac to compile Genie scripts. Without actually examining the source, I'm presuming that Genie just gets translated to Vala.



Related Topics



Leave a reply



Submit