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 frominit
construct()
is called when the object is created, in the example the linenew RotatingButtonWindow( "Hello World!" )
instantiates the object. If you repeat the line you will have two separate windows, that is two instances of theRotatingButtonWindow
data type- You will notice that the
RotatingButtonWindow
type is also defined as aWindow
type. This means it is adding more detail to theGtk.Window
class. This is whytitle
andset_border_width()
can be used within the new class. They have been "inherited" from the parentGtk.Window
class - By using the Gtk namespace with
uses Gtk
we don't need to prefix everything withGtk
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 betrue
orfalse
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 changeddb:Database
to show thisProcess.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 changedProcess.exit( -1 )
tobreak
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
Kernel Preemption While Holding Spinlock
How to Overwrite Linux System Files into The Yocto Filesystem
Cannot Kill Redis-Server on Linux
Linux History of All Commands Executed During Whole Day, Everyday
_Ldg Causes Slower Execution Time in Certain Situation
Restart Service from Cgi Script
Brother Ql-720Nw Specifying Media Size Seems Ignored
Ssh - Help Understanding Proxy Command
Why Questionmark Comes in The End of Filename When I Create .Txt File Through Shell Script
How to Source a Simple Bash Script
Suppressing Compile Time Linkage of Shared Libraries
Tcp Keepalive - Protocol Not Available
How to Boot with My Latest Rpi-3.18.0 Kernel and Enabling The Device Tree
How to Enable Spell Checker in Google Colab (Colab Operates on Linux Os)
Fortran: How to Get The Node Name of a Cluster
Search Ip from a Text File in .Csv Log File, If Found Add New Column Next to It