Create  Edit  FrontPage  Index  Search  Changes  History  RSS  Login

tut-gtk2-mnstbs-tyu

Test Your Understanding

As always, at the end of a chapter I suggest you try to write the provided exercises on your own. If you have read and tried out all the examples and exercises so far you should be able to accomplish this with the help of the code you've worked on so far.

Exercise #1:

At the end of sessions in chapter (7) "The Text View Widget" you created a simple text editor using Gtk::TextView widget. In this exercise, expand on that application and provide a toolbar for actions instead of a vertical box filled with Gtk::Button widgets.

While manual toolbar creation is possible, in most applications you will want to utilize the Gtk::UIManager method of toolbar creation. Therefore, use it also in this exercise. Also make use of Built-in stock items.

toolbar2.ui

<ui>
  <toolbar name="Toolbar">
    <toolitem name="FileNew" action="New"/>
    <toolitem name="FileOpen" action="Open"/>
    <toolitem name="FileSave" action="Save"/>
    <separator/>
    <toolitem name="EditCut" action="Cut"/>
    <toolitem name="EditCopy" action="Copy"/>
    <toolitem name="EditPaste" action="Paste"/>
  </toolbar>
</ui>

mnstbs-tyu1.png


simtextedit-ui1.rb

#!/usr/bin/env ruby
require 'gtk2'

# Make sure "texteditor" will be part of the closures
class TextEditor
  attr_accessor :textview, :search
end
texeditor = TextEditor.new

# Verify that the user want to create a new document.
# If so, delete all of the text from the buffer.
new_clicked = Proc.new do 
  dialog = Gtk::MessageDialog.new(
      nil,
      Gtk::Dialog::MODAL,
      Gtk::MessageDialog::QUESTION,
      Gtk::MessageDialog::BUTTONS_YES_NO,
      "All changes will be lost. Do you want to continue?"
  )
  dialog.title = "Information"
  dialog.run do |r|
    texeditor.textview.buffer.text = "" if r == Gtk::Dialog::RESPONSE_YES
  end
  dialog.destroy
end

# Replace the content of the current buffer with the
# content of a file.
open_clicked = Proc.new do 
  dialog = Gtk::FileChooserDialog.new(
    "Choose a file ...",
    nil,
    Gtk::FileChooser::ACTION_OPEN,
    nil,
    [ Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL ],
    [ Gtk::Stock::APPLY, Gtk::Dialog::RESPONSE_APPLY ]
  )
  dialog.run do |response|
    if response == Gtk::Dialog::RESPONSE_APPLY
      file = dialog.filename
      content = IO.readlines(file)
      texeditor.textview.buffer.text = content.to_s
    end
  end
  dialog.destroy
end

# Save the content of the current buffer to a file.
save_clicked = Proc.new do     
  dialog = Gtk::FileChooserDialog.new(
    "Save the file ...",
    nil,
    Gtk::FileChooser::ACTION_SAVE,
    nil,
    [ Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL ],
    [ Gtk::Stock::SAVE, Gtk::Dialog::RESPONSE_APPLY ]
  )
  dialog.run do |response|
    if response == Gtk::Dialog::RESPONSE_APPLY
      file = dialog.filename
      content = texeditor.textview.buffer.text
      # Open for writing, write and close.
      File.open(file, "w") { |f| f <<  content } 
    end
  end
  dialog.destroy
end

# Copy the selected text to the clipboard and remove it from the buffer.
cut_clicked = Proc.new do 
  clipboard = Gtk::Clipboard.get(Gdk::Selection::CLIPBOARD)
  texeditor.textview.buffer.cut_clipboard(clipboard, true)
end

# Copy the selected text to the clipboard.
copy_clicked = Proc.new do |te|
  clipboard = Gtk::Clipboard.get(Gdk::Selection::CLIPBOARD)
  texeditor.textview.buffer.copy_clipboard(clipboard)
end

# Delete any selected text and insert the clipboard
# content into the document.
paste_clicked = Proc.new do 
  clipboard = Gtk::Clipboard.get(Gdk::Selection::CLIPBOARD)
  texeditor.textview.buffer.paste_clipboard(clipboard, nil, true)
end

# Search for a text string from the current cursor position
# if there is no selected text, or one character after the
# cursor if there is.
def find_clicked(txted)
  find = txted.search.text
  first, last, success = txted.textview.buffer.selection_bounds

  first = txted.textview.buffer.start_iter unless success

  #                   forward_search(find, flags,    limit=(nil==entire text buffer))
  first, last = first.forward_search(find, Gtk::TextIter::SEARCH_TEXT_ONLY, last)

  # Select the instance on the screen if the string is found. 
  # Otherwise, tell the user it has failed.
  if (first)
    mark = txted.textview.buffer.create_mark(nil, first, false)
    # Scrolls the Gtk::TextView the minimum distance so
    # that mark is contained within the visible area. 
    txted.textview.scroll_mark_onscreen(mark)

    txted.textview.buffer.delete_mark(mark)
    txted.textview.buffer.select_range(first, last)
  else
    # Gtk::MessageDialog.new(parent, flags, message_type, button_type, message = nil)
    dialogue = Gtk::MessageDialog.new(
            nil,
            Gtk::Dialog::MODAL,
            Gtk::MessageDialog::INFO, 
            Gtk::MessageDialog::BUTTONS_OK,
             "The text was not found!"
    )
    dialogue.run
    dialogue.destroy
  end
  first = last = nil # camcel any previous selections
end

entries = [
  [ "New",   Gtk::Stock::NEW,   nil, nil, "Create a new file",                   new_clicked ],
  [ "Open",  Gtk::Stock::OPEN,  nil, nil, "Open an existing file",               open_clicked ],
  [ "Save",  Gtk::Stock::SAVE,  nil, nil, "Save the document to a file",         save_clicked ],
  [ "Cut",   Gtk::Stock::CUT,   nil, nil, "Cut the selection to the clipboard",  cut_clicked ],
  [ "Copy",  Gtk::Stock::COPY,  nil, nil, "Copy the selection to the clipboard", copy_clicked ],
  [ "Paste", Gtk::Stock::PASTE, nil, nil, "Paste text from the clipboard",       paste_clicked ]
]

window = Gtk::Window.new(Gtk::Window::TOPLEVEL)
window.resizable = true
window.title = "Simple Text Editor & Toolbar"
window.border_width = 10
window.signal_connect('delete_event') { Gtk.main_quit }
window.set_size_request(300, 200)

texeditor.textview = Gtk::TextView.new
texeditor.search   = Gtk::Entry.new
texeditor.search.text = "Search for ..."
find  = Gtk::Button.new(Gtk::Stock::FIND)
find.signal_connect("clicked")  { find_clicked(texeditor) }

# Create a new action group and add all of the actions to it.
group = Gtk::ActionGroup.new("MainActionGroup")
group.add_actions(entries)

# Create a new UI manager and build the menu bar and toolbar.
uimanager = Gtk::UIManager.new
uimanager.insert_action_group(group, 0);
uimanager.add_ui("toolbar2.ui")

# Retrieve the necessary widgets and associate accelerators.
toolbar = uimanager.get_widget("/Toolbar")
toolbar.toolbar_style = Gtk::Toolbar::Style::BOTH
window.add_accel_group(uimanager.accel_group)

scrolled_win = Gtk::ScrolledWindow.new
scrolled_win.border_width = 5
scrolled_win.add(texeditor.textview)
scrolled_win.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_ALWAYS)

searchbar = Gtk::HBox.new(false, 5)
searchbar.pack_start(texeditor.search, false, false, 0);
searchbar.pack_start(find, false, false, 0);

vbox = Gtk::VBox.new(false, 5)
vbox.pack_start(toolbar,       false, false, 0)
vbox.pack_start(scrolled_win,  true,  true,  0)
vbox.pack_start(searchbar,     false, false, 0)

window.add(vbox)
window.show_all
Gtk.main
Exercise #2:

In the next exercise, implement the same application as above, but use a menu bar instead of toolbar this time. You should continue to use the Gtk:UIManager.

menu2.ui

<ui>
  <menubar name="MenuBar">
    <menu name="FileMenu" action="File">
      <menuitem name="FileNew" action="New"/>
      <menuitem name="FileOpen" action="Open"/>
      <menuitem name="FileSave" action="Save"/>
    </menu>
    <menu name="EditMenu" action="Edit">
      <menuitem name="EditCut" action="Cut"/>
      <menuitem name="EditCopy" action="Copy"/>
      <menuitem name="EditPaste" action="Paste"/>
    </menu>
  </menubar>
</ui>

mnstbs-tyu2.png


simtextedit-ui2.rb

#!/usr/bin/env ruby
require 'gtk2'

# Make sure "texteditor" will be part of the closures
class TextEditor
  attr_accessor :textview, :search
end
texeditor = TextEditor.new

# Verify that the user want to create a new document.
# If so, delete all of the text from the buffer.
new_clicked = Proc.new do 
  dialog = Gtk::MessageDialog.new(
      nil,
      Gtk::Dialog::MODAL,
      Gtk::MessageDialog::QUESTION,
      Gtk::MessageDialog::BUTTONS_YES_NO,
      "All changes will be lost. Do you want to continue?"
  )
  dialog.title = "Information"
  dialog.run do |r|
    texeditor.textview.buffer.text = "" if r == Gtk::Dialog::RESPONSE_YES
  end
  dialog.destroy
end

# Replace the content of the current buffer with the
# content of a file.
open_clicked = Proc.new do 
  dialog = Gtk::FileChooserDialog.new(
    "Choose a file ...",
    nil,
    Gtk::FileChooser::ACTION_OPEN,
    nil,
    [ Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL ],
    [ Gtk::Stock::APPLY, Gtk::Dialog::RESPONSE_APPLY ]
  )
  dialog.run do |response|
    if response == Gtk::Dialog::RESPONSE_APPLY
      file = dialog.filename
      content = IO.readlines(file)
      texeditor.textview.buffer.text = content.to_s
    end
  end
  dialog.destroy
end

# Save the content of the current buffer to a file.
save_clicked = Proc.new do     
  dialog = Gtk::FileChooserDialog.new(
    "Save the file ...",
    nil,
    Gtk::FileChooser::ACTION_SAVE,
    nil,
    [ Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL ],
    [ Gtk::Stock::SAVE, Gtk::Dialog::RESPONSE_APPLY ]
  )
  dialog.run do |response|
    if response == Gtk::Dialog::RESPONSE_APPLY
      file = dialog.filename
      content = texeditor.textview.buffer.text
      # Open for writing, write and close.
      File.open(file, "w") { |f| f <<  content } 
    end
  end
  dialog.destroy
end

# Copy the selected text to the clipboard and remove it from the buffer.
cut_clicked = Proc.new do 
  clipboard = Gtk::Clipboard.get(Gdk::Selection::CLIPBOARD)
  texeditor.textview.buffer.cut_clipboard(clipboard, true)
end

# Copy the selected text to the clipboard.
copy_clicked = Proc.new do |te|
  clipboard = Gtk::Clipboard.get(Gdk::Selection::CLIPBOARD)
  texeditor.textview.buffer.copy_clipboard(clipboard)
end

# Delete any selected text and insert the clipboard
# content into the document.
paste_clicked = Proc.new do 
  clipboard = Gtk::Clipboard.get(Gdk::Selection::CLIPBOARD)
  texeditor.textview.buffer.paste_clipboard(clipboard, nil, true)
end

# Search for a text string from the current cursor position
# if there is no selected text, or one character after the
# cursor if there is.
def find_clicked(txted)
  find = txted.search.text
  first, last, success = txted.textview.buffer.selection_bounds

  first = txted.textview.buffer.start_iter unless success

  #                   forward_search(find, flags,    limit=(nil==entire text buffer))
  first, last = first.forward_search(find, Gtk::TextIter::SEARCH_TEXT_ONLY, last)

  # Select the instance on the screen if the string is found. 
  # Otherwise, tell the user it has failed.
  if (first)
    mark = txted.textview.buffer.create_mark(nil, first, false)
    # Scrolls the Gtk::TextView the minimum distance so
    # that mark is contained within the visible area. 
    txted.textview.scroll_mark_onscreen(mark)

    txted.textview.buffer.delete_mark(mark)
    txted.textview.buffer.select_range(first, last)
  else
    # Gtk::MessageDialog.new(parent, flags, message_type, button_type, message = nil)
    dialogue = Gtk::MessageDialog.new(
            nil,
            Gtk::Dialog::MODAL,
            Gtk::MessageDialog::INFO, 
            Gtk::MessageDialog::BUTTONS_OK,
             "The text was not found!"
    )
    dialogue.run
    dialogue.destroy
  end
  first = last = nil # camcel any previous selections
end

entries = [
  [ "File",  nil,               "_File", nil, nil,                                   nil ],
  [ "New",   Gtk::Stock::NEW,    nil,    nil, "Create a new file",                   new_clicked ],
  [ "Open",  Gtk::Stock::OPEN,   nil,    nil, "Open an existing file",               open_clicked ],
  [ "Save",  Gtk::Stock::SAVE,   nil,    nil, "Save the document to a file",         save_clicked ],
  [ "Edit",  nil,               "_Edit", nil, nil,                                   nil ],
  [ "Cut",   Gtk::Stock::CUT,    nil,    nil, "Cut the selection to the clipboard",  cut_clicked ],
  [ "Copy",  Gtk::Stock::COPY,   nil,    nil, "Copy the selection to the clipboard", copy_clicked ],
  [ "Paste", Gtk::Stock::PASTE,  nil,    nil, "Paste text from the clipboard",       paste_clicked ]
]

window = Gtk::Window.new(Gtk::Window::TOPLEVEL)
window.resizable = true
window.title = "Simple Text Editor & Toolbar"
window.border_width = 10
window.signal_connect('delete_event') { Gtk.main_quit }
window.set_size_request(300, 200)

texeditor.textview = Gtk::TextView.new
texeditor.search   = Gtk::Entry.new
texeditor.search.text = "Search for ..."
find  = Gtk::Button.new(Gtk::Stock::FIND)
find.signal_connect("clicked")  { find_clicked(texeditor) }

# Create a new action group and add all of the actions to it.
group = Gtk::ActionGroup.new("MainActionGroup")
group.add_actions(entries)

# Create a new UI manager and build the menu bar and toolbar.
uimanager = Gtk::UIManager.new
uimanager.insert_action_group(group, 0)
uimanager.add_ui("menu2.ui")

# Retrieve the necessary widgets and associate accelerators.
menu = uimanager.get_widget("/MenuBar")

scrolled_win = Gtk::ScrolledWindow.new
scrolled_win.border_width = 5
scrolled_win.add(texeditor.textview)
scrolled_win.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_ALWAYS)

searchbar = Gtk::HBox.new(false, 5)
searchbar.pack_start(texeditor.search, false, false, 0);
searchbar.pack_start(find, false, false, 0);

vbox = Gtk::VBox.new(false, 5)
vbox.pack_start(menu,          false, false, 0)
vbox.pack_start(scrolled_win,  true,  true,  0)
vbox.pack_start(searchbar,     false, false, 0)

window.add(vbox)
window.show_all
Gtk.main
Exercise #3:

Modify the first application from "Exercise #1", to include the the toolbar as a child of a handle box.

Last modified:2012/11/21 07:59:34
Keyword(s):
References:[tut-gtk] [tut-gtk2-mnstbs]