GTK-3 Treeview in liststore mode


tut09-treeview-listview.png

Figure 1: Treeview in list mode

The below example demonstrates using the treeview to display table style data, it generates a few rows and shows row selection from a mouse click.")

#!/usr/bin/env python
from gi.repository import Gtk, GLib, GObject


class application_gui:
    """Tutorial 09 treeview in list mode."""
    count = 0

    def __init__(self):
	#load in our glade interface
	xml = Gtk.Builder()
	xml.add_from_file('tut09.glade')

	#grab our widget using get_object this is the name of the widget from glade, window1 is the default name
	self.window = xml.get_object('window1')
	self.text = xml.get_object('entry1')

	#load our widgets from the glade file
	self.widgets = {}
	self.widgets['treeview'] = xml.get_object('treeview1')
	treeview(self.widgets['treeview'], self.text)

	#connect to events, in this instance just quit our application
	self.window.connect('delete_event', Gtk.main_quit)
	self.window.connect('destroy', lambda quit: Gtk.main_quit())

	#show the window else there is nothing to see :)
	self.window.show()


class treeview:
    treeview = None
    treemodel = None

    selected = 'workspace'

    def __init__(self, treeview, entry):
	self.entry = entry
	self.treeview = treeview

	self.treeview.connect('row-activated', self.selection)
	self.treeview.connect('button_press_event', self.mouse_click)

	#create a storage model in this case a treemodel
	self.treemodel = Gtk.ListStore(str, str, str)
	self.treeview.set_model(self.treemodel)

	#add columns usually only one in case of the treeview
	column1 = Gtk.TreeViewColumn("Column 01")
	self.treeview.append_column(column1)

	column2 = Gtk.TreeViewColumn("Column 02")
	self.treeview.append_column(column2)

	column3 = Gtk.TreeViewColumn("Column 03")
	self.treeview.append_column(column3)

	#add in a text renderer so we can see the items we add 
	cell = Gtk.CellRendererText()
	column1.pack_start(cell, False)
	column1.add_attribute(cell, "text", 0)

	cell = Gtk.CellRendererText()
	column2.pack_start(cell, False)
	column2.add_attribute(cell, "text", 0)

	cell = Gtk.CellRendererText()
	column3.pack_start(cell, False)
	column3.add_attribute(cell, "text", 0)

	self.populate()
	self.menu()

    def populate(self):
	self.treemodel.clear()
	#populate the treeview with some list items
	for item1 in range(0, 5):
	    iter_level_1 = self.append_tree('Item ' + str(item1))

    def append_tree(self, name, parent=None):
	"""
	    append to the treeview if parent is null append to root level.
	    if parent is a valid iter (possibly returned from previous append) then append under the parent
	"""
	myiter = self.treemodel.insert_after(parent, None)
	self.treemodel.set_value(myiter, 0, name)
	return myiter

    def menu(self):
	"""
	popover menu shown on right clicking a treeview item.
	"""
	self.treeview_menu = Gtk.Menu()
	for item in range(0, 5):
	    menu_item = Gtk.MenuItem("Menu " + str(item))
	    self.treeview_menu.append(menu_item)

    def mouse_click(self, tv, event):
	if event.button == 3:
	    # right mouse button pressed popup the menu
	    self.treeview_menu.show_all()
	    self.treeview_menu.popup(None, None, None, None, 1, 0)

    def selection(self, tv, treepath, tvcolumn):
	""" 
	    on double click get the value of the item we clicked
	"""
	model = tv.get_model()
	treeiter = model.get_iter(treepath)
	self.selected = model.get_value(treeiter, 0)
	self.entry.set_text(self.selected)



application = application_gui()
Gtk.main()

GTK-3 Calendars and menus


tut08-menu-calendar.png

Figure 1: Calendara & menus

Simple GTK application demonstrating the use of the date picker and application menus. The menu is hooked upto to show and hide the calendar and to set the text entry field to say hello world. Double clicking a day in the calendar widget will show the date in the text entry field.

#!/usr/bin/env python
from gi.repository import Gtk, GLib, GObject


class application_gui:
    """Tutorial 08 menu, calendar widget."""
    count = 0

    def __init__(self):
	#load in our glade interface
	xml = Gtk.Builder()
	xml.add_from_file('tut08.glade')

	#grab our widget using get_object this is the name of the widget from glade, window1 is the default name
	self.window = xml.get_object('window1')
	self.text = xml.get_object('entry1')

	#load our widgets from the glade file
	self.widgets = {}
	self.widgets['calendar'] = xml.get_object('calendar1')
	self.widgets['menushow'] = xml.get_object('menuitem6')
	self.widgets['menuhide'] = xml.get_object('menuitem7')
	self.widgets['menuhello'] = xml.get_object('menuitem8')

	#connect to events, in this instance just quit our application
	self.window.connect('delete_event', Gtk.main_quit)
	self.window.connect('destroy', lambda quit: Gtk.main_quit())
	self.widgets['menushow'].connect('activate', self.showcalendar)
	self.widgets['menuhide'].connect('activate', self.hidecalendar)
	self.widgets['menuhello'].connect('activate', self.hello)
	self.widgets['calendar'].connect('day-selected', self.date_selected)

	#show the window else there is nothing to see :)
	self.window.show()

    def hidecalendar(self, *args):
	self.widgets['calendar'].hide()

    def showcalendar(self, *args):
	self.widgets['calendar'].show()

    def hello(self, *args):
	self.text.set_text('hello world')

    def date_selected(self, *args):
	date = self.widgets['calendar'].get_date()
	self.text.set_text(str(date[2]) + '-'+str(date[1]) + '-' + str(date[0]))

application = application_gui()
Gtk.main()

App chooer and scale buttons


tut07-appchooser-scale.png

Figure 1: App chooser & scale widgets

This example program demonstrates the use of appchooser buttons when selecting an application from the drop down launch the application loading a test text file, this could be a video or mp3 or any file type.

you can change the application list by modify the content type value in glade this then shows all registered apps for that content type.

#!/usr/bin/env python
from gi.repository import Gtk, GLib, Gio


class application_gui:
    """Tutorial 04 text input, spin input, drop down options"""
    count = 0

    def __init__(self):
	#load in our glade interface
	xml = Gtk.Builder()
	xml.add_from_file('tut07.glade')

	#grab our widget using get_object this is the name of the widget from glade, window1 is the default name
	self.window = xml.get_object('window1')
	self.text = xml.get_object('entry1')

	#load our widgets from the glade file
	self.widgets = {}
	self.widgets['scale1'] = xml.get_object('scalebutton1')
	self.widgets['scale2'] = xml.get_object('scalebutton2')
	self.widgets['appchooseraudio'] = xml.get_object('appchooserbutton1')
	self.widgets['appchoosertext'] = xml.get_object('appchooserbutton2')

	self.widgets['appchooseraudio'].connect('changed', self.app_chooser)
	self.widgets['appchoosertext'].connect('changed', self.app_chooser)
	self.widgets['scale1'].connect('value-changed', self.scale)
	self.widgets['scale2'].connect('value-changed', self.scale)

	#connect to events, in this instance just quit our application
	self.window.connect('delete_event', Gtk.main_quit)
	self.window.connect('destroy', lambda quit: Gtk.main_quit())

	#show the window else there is nothing to see :)
	self.window.show()

    def app_chooser(self, widget):
	list_view_model = widget.get_model()
	active_iter_index = widget.get_active()
	row_iter = list_view_model.get_iter(active_iter_index)
	app_info = list_view_model.get_value(row_iter, 0)

	gio_file = Gio.File.new_for_path('/tmp/tut07-appchooser-test.txt')
	app_info.launch((gio_file,), None)
	self.text.set_text(widget.get_name() + ' ' + app_info.get_name())

    def scale(self, widget, value):
	self.text.set_text(widget.get_name() + ' ' + str(value))


application = application_gui()
Gtk.main()

Displaying dialog boxes


tut06-dialogs.png

Figure 1: Dialog Boxes

This example program allows you to open the file selector dialogs and displays the selected files, it also demonstrates selecting colour and font from the built in selector dialogs. once a file font or colour has been selected we grab the resulting value and display it as an example of retrieving the value.

#!/usr/bin/env python
from gi.repository import Gtk


class application_gui:
    """Tutorial 06 demo of file, colour and font dialogs."""

    def __init__(self):
	#load in our glade interface
	xml = Gtk.Builder()
	xml.add_from_file('tut06.glade')

	#grab our widget using get_object this is the name of the widget from glade, window1 is the default name
	self.window = xml.get_object('window1')
	self.text = xml.get_object('entry1')

	#load our widgets from the glade file
	self.widgets = {}
	self.widgets['entry'] = xml.get_object('entry2')
	self.widgets['color_button'] = xml.get_object('btnColour')
	self.widgets['font_button'] = xml.get_object('btnFont')
	self.widgets['open_button'] = xml.get_object('btnLoad')
	self.widgets['save_button'] = xml.get_object('btnSave')

	self.widgets['color_button'].connect('clicked', self.colour_selector)
	self.widgets['font_button'].connect('clicked', self.font_selector)
	self.widgets['save_button'].connect('clicked', self.save_file_selector)
	self.widgets['open_button'].connect('clicked', self.open_file_selector)

	self.widgets['open_dialog'] = xml.get_object('fileChooserLoad')
	self.widgets['open_dialog'].add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CLOSE)
	self.widgets['open_dialog'].add_button(Gtk.STOCK_OPEN, Gtk.ResponseType.OK)

	self.widgets['save_dialog'] = xml.get_object('fileChooserSave')
	self.widgets['save_dialog'].add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CLOSE)
	self.widgets['save_dialog'].add_button(Gtk.STOCK_SAVE, Gtk.ResponseType.OK)

	self.widgets['color_dialog'] = xml.get_object('colorSelect')

	self.widgets['font_dialog'] = xml.get_object('fontSelect')

	#connect to events, in this instance just quit our application
	self.window.connect('delete_event', Gtk.main_quit)
	self.window.connect('destroy', lambda quit: Gtk.main_quit())

	#show the window else there is nothing to see :)
	self.window.show()

    def colour_selector(self, widget):
	self.widgets['color_dialog'].run()
	self.colour = self.widgets['color_dialog'].get_color_selection().get_current_color()

	#accessing the individual values as integers, converting to floats here
	colour = [self.colour.red / 65535.0,
		  self.colour.green / 65535.0,
		  self.colour.blue / 65535.0]

	self.widgets['color_dialog'].hide()
	self.text.set_text(self.widgets['color_dialog'].get_name() + ' ' + self.colour.to_string())

    def font_selector(self, widget):
	self.widgets['font_dialog'].run()
	self.font = self.widgets['font_dialog'].get_font_selection()
	self.text.set_text(self.widgets['font_dialog'].get_name() + ' ' + self.font.get_font_name() + ' ' + str(self.font.get_size()))
	self.widgets['font_dialog'].hide()

    def open_file_selector(self, widget):
	response = self.widgets['open_dialog'].run()
	if self.widgets['open_dialog'].get_filenames():
	    self.text.set_text(self.widgets['open_dialog'].get_name() + ' '+ str(self.widgets['open_dialog'].get_filenames()))
	else:
	    if self.widgets['open_dialog'].get_filename():
		self.text.set_text(self.widgets['open_dialog'].get_name() + ' '+ self.widgets['open_dialog'].get_filename())
	self.widgets['open_dialog'].hide()

    def save_file_selector(self, widget):
	response = self.widgets['save_dialog'].run()
	self.text.set_text(self.widgets['save_dialog'].get_name() + ' '+ self.widgets['save_dialog'].get_filename())
	self.widgets['save_dialog'].hide()


application = application_gui()
Gtk.main()

Adding progress bars & spinners


tut05-progress-spinners.png

Figure 1: Progress bars and spinners

This sample demonstrates two progress bar styles and how to update them too show progress, it also demonstrates starting and stopping a spinning graphic.

#!/usr/bin/env python
from gi.repository import Gtk, GLib


class application_gui:
    """Tutorial 05 demo progress bars and the spinner graphic."""
    count = 0

    def __init__(self):
	#load in our glade interface
	xml = Gtk.Builder()
	xml.add_from_file('tut05.glade')

	#grab our widget using get_object this is the name of the widget from glade, window1 is the default name
	self.window = xml.get_object('window1')
	self.text = xml.get_object('entry1')

	#load our widgets from the glade file
	self.widgets = {}
	self.widgets['entry'] = xml.get_object('entry2')

	self.widgets['spinner1'] = xml.get_object('spinner1')
	self.widgets['spinnerbutton'] = xml.get_object('togglebutton1')
	self.widgets['progress1'] = xml.get_object('progressbar1')
	self.widgets['progress2'] = xml.get_object('progressbar2')

	self.widgets['spinnerbutton'].connect('clicked', self.toggle_spinner)

	self.widgets['progress1'].set_text('active mode')
	self.widgets['progress2'].set_text('percentage mode')

	#connect to events, in this instance just quit our application
	self.window.connect('delete_event', Gtk.main_quit)
	self.window.connect('destroy', lambda quit: Gtk.main_quit())

	#show the window else there is nothing to see :)
	self.window.show()
	self.widgets['spinner1'].start()

	GLib.timeout_add_seconds(1, self.update_active_progress_bar)
	GLib.timeout_add_seconds(1, self.update_percentage_progress_bar)

    def update_active_progress_bar(self):
	# nudge the progress bar to show something is still happening, just updating every second in the example
	# long running processes which have an unknow finish time would use this version
	self.widgets['progress1'].pulse()
	return True

    def update_percentage_progress_bar(self):
	self.count +=0.1
	self.widgets['progress2'].set_fraction(self.count)
	if self.count<1:
	    return True
	else:
	    return False

    def toggle_spinner(self, widget):
	if widget.get_active():
	    self.widgets['spinner1'].start()
	else:
	    self.widgets['spinner1'].stop()


application = application_gui()
Gtk.main()

Adding Dropdowns and spin buttons


tut04-text-dropdown-spin.png

Figure 1: Adding dropdown & spin widgets

This example demonstrates using some drop down boxes and adding new items and retrieving the selected values. This snippet also demonstrates the use of spin buttons for selecting a value from a range.

When creating a combobbox in glade make sure you add a liststore to the widget, and also edit the combobox properties in a seperate window so you can access and the hierarchy menu and assign a cell render to the column in your listview.

add-list-store.png

Figure 2: Adding a list store

attach-cell-renderer-set-text-column.png

Figure 3: Adding a cell renderer

attach-list-store-model.png

Figure 4: Adding a list store

edit-combobox-liststore.png

Figure 5: Adding a combobox list store

#!/usr/bin/env python
from gi.repository import Gtk


class application_gui:
    """Tutorial 04 text input, spin input, drop down options"""

    def __init__(self):
	#load in our glade interface
	xml = Gtk.Builder()
	xml.add_from_file('tut04.glade')

	#grab our widget using get_object this is the name of the widget from glade, window1 is the default name
	self.window = xml.get_object('window1')
	self.text = xml.get_object('entry1')

	#load our widgets from the glade file
	self.widgets = {}
	self.widgets['spin1'] = xml.get_object('spinbutton1')
	self.widgets['spin2'] = xml.get_object('spinbutton2')

	#simple dropdown just text
	self.widgets['comboboxtext'] = xml.get_object('comboboxtext1')

	#more complex multi row multi data types in dropdown using renderers
	self.widgets['combobox'] = xml.get_object('combobox1')
	self.widgets['combobox_liststore']= xml.get_object('liststore1')

	#add some items to the drop down 
	self.widgets['combobox_liststore'].append(['new row test'])
	self.widgets['combobox_liststore'].append(['second row test'])

	#adding new rows to the dropdown
	self.widgets['comboboxtext'].append_text('row4')
	self.widgets['comboboxtext'].append_text('row5')

	#connect to events
	self.widgets['comboboxtext'].connect('changed', self.dropdown_event_text)
	self.widgets['spin1'].connect('value-changed', self.spin_button_event)
	self.widgets['spin2'].connect('value-changed', self.spin_button_event)
	self.widgets['combobox'].connect('changed', self.dropdown_event)
	self.window.connect('delete_event', Gtk.main_quit)
	self.window.connect('destroy', lambda quit: Gtk.main_quit())

	#show the window else there is nothing to see :)
	self.window.show_all()

    def dropdown_event_text(self, widget):
	self.text.set_text(widget.get_active_text())

    def dropdown_event(self, widget):
	list_view_model = widget.get_model()
	active_iter_index = widget.get_active()
	row_iter = list_view_model.get_iter(active_iter_index)
	self.text.set_text(widget.get_name() + ' ' +list_view_model.get_value(row_iter, 0 ))

    def spin_button_event(self, widget):
	self.text.set_text(widget.get_name() + ' ' + ' ' + str(widget.get_value()))


application = application_gui()
Gtk.main()

Adding switch and radio widgets


tut03-switch-radio.png

Figure 1: Adding radio buttons & switches

The below sample shows loading and retrieving state values for radio buttons and switches.

#!/usr/bin/python
from gi.repository import Gtk


class application_gui:
    """Tutorial 03 Radio Buttons and switches"""

    def __init__(self):
	#load in our glade interface
	xml = Gtk.Builder()
	xml.add_from_file('tut03.glade')

	#grab our widget using get_object this is the name of the widget from glade, window1 is the default name
	self.window = xml.get_object('window1')
	self.text = xml.get_object('entry1')

	self.buttons = {}
	self.buttons['radio1'] = xml.get_object('radiobutton1')
	self.buttons['radio2'] = xml.get_object('radiobutton2')
	self.buttons['switch1'] = xml.get_object('switch1')
	self.buttons['switch2'] = xml.get_object('switch2')

	#set a name of the switches because they dont have a label so we can use this to show selected button
	self.buttons['switch1'].set_name('switch1')
	self.buttons['switch2'].set_name('switch2')

	#connect the interface events to functions
	self.buttons['switch1'].connect('button-press-event', self.switch_button_events)
	self.buttons['switch2'].connect('button-press-event', self.switch_button_events)
	self.buttons['radio1'].connect('toggled', self.radio_button_events)
	self.buttons['radio2'].connect('toggled', self.radio_button_events)

	#add the second radio to the first radio button making it part of the group
	self.buttons['radio2'].join_group(self.buttons['radio1'])

	#connect to events, in this instance just quit our application
	self.window.connect('delete_event', Gtk.main_quit)
	self.window.connect('destroy', lambda quit: Gtk.main_quit())

	#show the window else there is nothing to see :)
	self.window.show()

    def switch_button_events(self, widget, params):
	toggle_value = str(widget.get_active())
	self.text.set_text(widget.get_name() + ' ' + toggle_value)

    def radio_button_events(self, widget):
	toggle_value = str(widget.get_active())
	self.text.set_text(widget.get_name() + ' ' +widget.get_label()+ ' ' + toggle_value)


application = application_gui()
Gtk.main()

Adding GTK-3 buttons and switches


tut02-buttons.png

Figure 1: Display GTK-3 Buttons and switches

The example below loads 4 buttons from a glade 2 standard and 2 toggle buttons, it then connects event handles to show some text when clicked or to show the toggle state if a toggle button was clicked.

#!/usr/bin/python
from gi.repository import Gtk


class application_gui:
    """Tutorial 02 buttons"""

    def __init__(self):
	#load in our glade interface
	xml = Gtk.Builder()
	xml.add_from_file('tut02-buttons.glade')

	#grab our widget using get_object this is the name of the widget from glade, window1 is the default name
	self.window = xml.get_object('window1')
	self.text = xml.get_object('entry1')

	self.buttons = {}
	self.buttons['but1'] = xml.get_object('button1')
	self.buttons['but2'] = xml.get_object('button2')
	self.buttons['but3'] = xml.get_object('togglebutton1')
	self.buttons['but4'] = xml.get_object('togglebutton2')

	self.buttons['but1'].connect('clicked', self.button_events)
	self.buttons['but2'].connect('clicked', self.button_events)
	self.buttons['but3'].connect('clicked', self.button_events)
	self.buttons['but4'].connect('clicked', self.button_events)

	#connect to events, in this instance just quit our application
	self.window.connect('delete_event', Gtk.main_quit)
	self.window.connect('destroy', lambda quit: Gtk.main_quit())

	#show the window else there is nothing to see :)
	self.window.show()

    def button_events(self, widget):
	toggle_value = ''
	if widget.get_name() == 'GtkToggleButton':
	    toggle_value = str(widget.get_active())
	self.text.set_text(widget.get_name() + ' ' +widget.get_label()+ ' ' + toggle_value)


application = application_gui()
Gtk.main()

Display a GTK window


tut01-windows.png

Figure 1: Display GTK-3 window

Simple GTK application load a window from a glade file and exit on hitting the window close button, we use the getobject method to get the window by name then use connect to attach to the close and destroy events.

There are lots of event we can connect to like mouse click key press and window minimise maximise check the gtk docs or glade for a list of possible event for each widget.

#!/usr/bin/python
from gi.repository import Gtk


class application_gui:
    """Tutorial 01 Create and destroy a window"""

    def __init__(self):
	#load in our glade interface
	xml = Gtk.Builder()
	xml.add_from_file('tut01.glade')

	#grab our widget using get_object this is the name of the widget from glade, window1 is the default name
	self.window = xml.get_object('window1')

	#connect to events, in this instance just quit our application
	self.window.connect('delete_event', Gtk.main_quit)
	self.window.connect('destroy', lambda quit: Gtk.main_quit())

	#show the window else there is nothing to see :)
	self.window.show()

application = application_gui()
Gtk.main()

Python CAD Tutorial - Developing a cad application Index


Linux has been mising a good 3d CAD program for many years, current ones have poor interfaces are missing features or are just to unstable and slow. To help rectify this situation i thought i would write my own but realising i often loose intrest in projects or hit road blocks i decided to write the software as a series of tutorials. The Aim of these tutorials is to inspire other people to work on cad software using this as a base or just enhancing this software into a functional form, this could also be the base of 3d printer / CNC or laser cutter machines. We will skip the basics of gtk and not go into the maths in a high level of detail, but will aim to have clean well documented code that will be be easy to modify. If any graphics designers want to mock up a simple but functional interface that looks good please get in touch, or send some designs my way currently its lacks any real design of the interface. Here are some other suggestions for applications you could write using this code as a base, circuit design application, ui design tool, uml diagrams.

  • Initial Application
  • Draw Points
  • Setup Camera
  • Mouse Cursor
  • Draw Lines
  • Draw a plane
  • Draw a grid
  • Create shape / polygon object
  • Create a workspace / scene object
  • hook up some gui elements