Jun 04 2007

ruby Qt custom widget example

Category: Ruby, X Windowsetd @ 1:08 am

With Qt’s custom widgets you can create the building blocks of the GUI of your application.

In this case we are creating a graphical command line. The command line will consist mainly of a text input box (Qt::LineEdit). The widget will have memory, that is, every line entered by the user will be added to the internal history of the widget and will be accessible by means of Up and Down arrows as the standard *nix command line.

Here is the code:-

=begin
**
** commandline.rb
** 03/JUN/2007
** ETD-Software
**  - Daniel Martin Gomez <etd[-at-]nomejortu.com>
**
** Desc:
**   Qt custom widget that behaves as a standard command line. It keeps a 
** buffer of commands that can be accessed by pressing Up and Down keys.
**
** Version:
**  v1.0 [03/Jun/2007]: first released
**
** This file may be used under the terms of the GNU General Public
** License version 2.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of
** this file.  Please review the following information to ensure GNU
** General Public Licensing requirements will be met:
** http://www.trolltech.com/products/qt/opensource.html
**
=end

require 'Qt4'

class CommandLine < Qt::LineEdit
  slots 'clear_history()'

  def initialize(parent=nil)
    super(parent)
    #initialize internal history buffer
    @history = []
    @pointer = 0
  end

  #override some event handlers
  def keyPressEvent(event)

    case event.key 
      when Qt::Key_Up:
        if ((@history.size > 0) && (@pointer >= 0) )
          if (@pointer == @history.size)
            @history << self.text 
          end
          @pointer -= 1 if @pointer > 0
          self.text = @history[@pointer]
        end
        return

      when Qt::Key_Down:
        if ((@history.size > 0) && (@pointer < @history.size) )
          @pointer += 1 if @pointer < @history.size - 1
          self.text = @history[@pointer]
        end
        return

      #add a new element to the local @history
      when Qt::Key_Return:

        #keep an eye on the last entry to avoid empty entries in the list
        if ((@history.size > 0) && (@history.last.strip.size == 0) )
          @history.pop
        end

        if self.text.strip.size > 0
          @history << self.text
          @pointer = @history.size
          self.clear
        else
          return
        end
    end

    super
  end


  def clear_history()
    @history.clear
  end

  def last_command()
    @history.last
  end
end

if $0 == __FILE__
    a = Qt::Application.new(ARGV)
    w = CommandLine.new
    w.show
    a.exec
end

As you can see, out widget inherits from standard Qt LineEdit widget:-
class CommandLine < Qt::LineEdit

When instancing a copy of the widget we set up two variables to handle the internal history:-

def initialize(parent=nil)
  super(parent)
  #initialize internal history buffer
  @history = []
  @pointer = 0
end

@history array contains the list of commands typed by the user. @pointer is a pointer to the element of the list that will be displayed when Up or Down keys are pressed.

All the widget’s functionality is coded in the keyPressEvent of the class. This method is called everytime the user introduces a new letter in the box. We are adding special functionality for three letters: Qt::Key_Up, Qt::Key_Down and Qt::Key_Return.

when Qt::Key_Up:
  if ((@history.size > 0) && (@pointer >= 0) )
    if (@pointer == @history.size)
      @history << self.text 
    end
    @pointer -= 1 if @pointer > 0
    self.text = @history[@pointer]
  end
  return

If the @history contains elements and the Qt::Key_Up is pressed we move the pointer to the correct place in the @history list and then replace the current text of the box. However, if the user decides to press the Up arrow in the middle of a new line, we add this new line to the @history before continuing.

when Qt::Key_Down:
  if ((@history.size > 0) && (@pointer < @history.size) )
    @pointer += 1 if @pointer < @history.size - 1
    self.text = @history[@pointer]
  end
  return

Likewise, if the Down arrow is pressed we move the @pointer down the list and replace the text.

The last case when we intercept the KeyPress event is when the user hits the Return key. The first if gets rid of the last element of the @history if it is empty. Reme,ber when we added the current line to the history in the Qt::Key_Up case? This if removes that element if it was an empty line.

when Qt::Key_Return:
  #keep an eye on the last entry to avoid empty entries in the list
  if ((@history.size > 0) && (@history.last.strip.size == 0) )
    @history.pop
  end

  if self.text.strip.size > 0
    @history << self.text
    @pointer = @history.size
    self.clear
  else
    return
  end

We then check that the current line is not empty. We add the text to the widget’s history and clear the box. If the line is empty we return, so no keyPressed() signal is raised.

The script contains helper code to test it by creating a Qt application and showing the widget.

Share and Enjoy: These icons link to social bookmarking sites where readers can share and discover new web pages.
  • Digg
  • del.icio.us
  • Slashdot
  • Technorati

Leave a Reply