User:Declan.mcgrath: Difference between revisions

From KDE TechBase
No edit summary
(Final changes to the overall structure after introducing Single Inheritance)
Line 1: Line 1:
'''Developing Ruby Qt Applications using Qt Designer and Ruby on Ubuntu'''  
'''Developing Ruby Qt Applications using Qt Designer and Ruby on Ubuntu'''  


NOTE: THIS ARTICLE IS CURRENTLY BEING EDITED. SHOULD BE FINISHED IN A COUPLE OF HOURS
 
NOTE: THIS ARTICLE IS IN THE PROCESS OF PROOF-READING BEFORE FINAL EDITS ARE MADE. THIS SHOULD BE CONCLUDED WITHIN 1 DAY


<br>  
<br>  
Line 102: Line 103:
{{Note|
{{Note|
To some, the latter is more Rubyesque. If you look in the autogenerated file dashboard_ui.rb, you will that the function sampleUi() is wrapped by another function sample_ui().
To some, the latter is more Rubyesque. If you look in the autogenerated file dashboard_ui.rb, you will that the function sampleUi() is wrapped by another function sample_ui().
In the same way, we change Ui_Dialog.new to Ui::Dialog.new


This is just aesthetics. Syntactic sugar, but important syntactic sugar nonetheless!}}
This is just aesthetics. Syntactic sugar, but important syntactic sugar nonetheless!}}
Line 112: Line 115:
a = Qt::Application.new(ARGV)
a = Qt::Application.new(ARGV)


u = Dashboard.new
u = Ui::Dialog.new
w = Qt::Dialog.new
w = Qt::Dialog.new
u.setup_ui(w)
u.setup_ui(w)
Line 120: Line 123:
Note:&nbsp;Don't forget to remove the require 'Qt' (or it might be require 'Qt4' for you) from the top of the dashboard_ui.rb<br>  
Note:&nbsp;Don't forget to remove the require 'Qt' (or it might be require 'Qt4' for you) from the top of the dashboard_ui.rb<br>  


Ok, so where are we at now? Well if you run 'ruby main.rb' from the commandline your dinky little dialog should appear. The way we are using the dialog in our application is called The Direct Approach. For more information (which is beyond the scope of this article) check out http://doc.trolltech.com/4.0/designer-using-a-component.html to see what the different approaches are. That article is very C++ based but you should get a gist of what is going on. But basically a lot of the important work is being done in the main.rb file; this means that we don't have much control over our widgets programatically.
Ok, so where are we at now? Well if you run 'ruby main.rb' from the commandline your dinky little dialog should appear. The way we are using the dialog in our application is called the Direct Approach. For more information (which is beyond the scope of this article) check out http://doc.trolltech.com/4.0/designer-using-a-component.html to see what the different approaches are. That article is very C++ based but you should get a gist of what is going on. But basically a lot of the important work is being done in the main.rb file; this means that we don't have much control over our widgets programatically.
 
In the next section, we will switch to using the Single Inheritance Approach. Then we'll show you how to programatically populate the Item List on application startup. Fasten your seatbelts!
 
= The Single Inheritance Approach - Separating generated code from our 'real' code =
Ok, everything you learned is a lie! Well, not really, but definitely you'll want to do take the Single Inheritance Approach over the Direct Approach.
 
== Subclassing the dashboard_ui.rb file  ==
 
In Qt4, this is the primary way to separate autogenerated from code from our own logic. Using a text editor '''create a new file in your my_first_project folder called dashboard.rb'''. (Note:&nbsp;For the rest of this article be careful to pay attention as to whether we are talking about dashboard_ui.rb or dashboard.rb). The comments should explain what's going on
 
<code ruby="ruby">
# We pull in the file containg the class containing the generated code
require 'dashboard_ui'
 
# We inherit from Qt:Dialog gives us access to User Interface functionality
# such as connecting slots and signals
class Dashboard < Qt::Dialog
 
    # We are then free to put our own code into this class without fear
    # of it being overwritten. Here we add a initialize function which
    # can be used to customise how the form looks on startup. The method
    # initialize() is a constructor in Ruby
 
    def initialize(parent = nil)
 
      # Widgets in Qt can be optionally be children of other widgets.
      # That's why we accept parent as a parameter
 
      # This super call causes the constructor of the base class (Qt::Widget)
      # to be called, shepherding on the parent argument
 
      super(parent)
 
      # We'll be putting some code here real soon...
    end


In the next section, we will switch to using The Single Inheritance Approach. Then we'll show you how to programatically populate the Item List on application startup. Fasten your seatbelts!
end
</code>


== The Single Inheritance Approach ==
Next we need to move the creation of the actual dialog out of main.rb and into the dashboard.rb to make the Dashboard class responsible for managing the widget. This is a core concept to understand. At the same time, we make the following changes to the main.rb file
# We change require 'dashboard_ui' to require 'dashboard' because we are using  the Dashboard class that '''we''' have defined and control rather than the Ui::Dialog class autogenerated by rbuic4
# Create an instance of Dashboard rather than Ui::Dialog and rename variables to be more meaningful
# Call the show method on the Dashboard object rather than some separate Qt::Dialog object. Why? Because of the subclassing we are implementing, Dashboard now '''is''' a Qt::Dialog object! The main.rb file will then look like this
 
<code ruby="ruby">
require 'Qt4'
require 'dashboard'
 
a = Qt::Application.new(ARGV)
dashboard = Dashboard.new
dashboard.show
a.exec
</code>
 
So where has that setup_ui() function ended up? As we said, it's going straight to '''our''' Dashboard class in dashboard.rb. The comments should explain what's going on.
 
<code ruby="ruby">
# We pull in the file containg the class containing the generated code
require 'dashboard_ui'
 
# We inherit from Qt:Dialog gives us access to User Interface functionality
# such as connecting slots and signals
class Dashboard < Qt::Dialog
 
    # We are then free to put our own code into this class without fear
    # of it being overwritten. Here we add a initialize function which
    # can be used to customise how the form looks on startup. The method
    # initialize() is a constructor in Ruby
 
    def initialize(parent = nil)
 
      # Widgets in Qt can optionally be children of other widgets.
      # That's why we accept parent as a parameter
 
      # This super call causes the constructor of the base class (Qt::Widget)
      # to be called, shepherding on the parent argument
 
      super(parent)
 
      # The Dashboard class we are in holds presentation logic and exists
      # to 'manage' the dialog widget we created in Qt Designer earlier.
      # An instance of this dialog widget is created and stored in @ui variable
 
      @ui = Ui::Dialog.new
 
      # Calling setup_ui causes the dialog widget to be initialised with the
      # defaults you may have specified in Qt Designer. For example, it
      # - populates the 'First Line' and 'Second Line' items in the List Widget
      # - sets the drag drop mode to 'Internal'
      # - and much much more. Peer into the dashboard_ui.rb if you want to the
      #  full gory details
 
      @ui.setup_ui(self)
 
    end
 
end
</code>




Line 143: Line 240:


This gives us a dashboard.rb file that looks like  
This gives us a dashboard.rb file that looks like  


<code ruby="ruby">
<code ruby="ruby">
# We pull in the file containg the class containing the generated code
# We pull in the file containg the class containing the generated code
require 'dashboard_ui'
require 'dashboard_ui'
 
# We inherit from the class containing the generated code.
# We inherit from Qt:Dialog gives us access to User Interface functionality
class Dashboard < Ui::Dialog
# such as connecting slots and signals
class Dashboard < Qt::Dialog


     # We are then free to put our own code into this class without fear
     # We are then free to put our own code into this class without fear
     # of it being overwritten. Here we add a setup_ui function which
     # of it being overwritten. Here we add a initialize function which
     # can be used to customise how the form looks on startup
     # can be used to customise how the form looks on startup. The method
    # initialize() is a constructor in Ruby
     def setup_ui(dialog)
 
       super
     def initialize(parent = nil)
       # We'll be putting some code here real soon...
 
   
      # Widgets in Qt can optionally be children of other widgets.
      # That's why we accept parent as a parameter
 
      # This super call causes the constructor of the base class (Qt::Widget)
      # to be called, shepherding on the parent argument
 
       super(parent)
 
      # The Dashboard class we are in holds presentation logic and exists
       # to 'manage' the dialog widget we created in Qt Designer earlier.
      # An instance of this dialog widget is created and stored in @ui variable
 
      @ui = Ui::Dialog.new
 
      # Calling setup_ui causes the dialog widget to be initialised with the
      # defaults you may have specified in Qt Designer. For example, it
      # - populates the 'First Line' and 'Second Line' items in the List Widget
      # - sets the drag drop mode to 'Internal'
      # - and much much more. Peer into the dashboard_ui.rb if you want to the
      #  full gory details
 
      @ui.setup_ui(self)
 
       # As promised...
       # As promised...
      Qt::ListWidgetItem.new(@listWidget)
      Qt::ListWidgetItem.new(@listWidget)


       @listWidget.item(0).text = Qt::Application.translate("Dialog", "First Line", nil, Qt::Application::UnicodeUTF8)
      Qt::ListWidgetItem.new(@ui.listWidget)
       @listWidget.item(1).text = Qt::Application.translate("Dialog", "Second Line", nil, Qt::Application::UnicodeUTF8)
      Qt::ListWidgetItem.new(@ui.listWidget)
   
 
     end  
      #And also...
       @ui.listWidget.item(0).text = Qt::Application.translate("Dialog", "First Line", nil, Qt::Application::UnicodeUTF8)
       @ui.listWidget.item(1).text = Qt::Application.translate("Dialog", "Second Line", nil, Qt::Application::UnicodeUTF8)
 
     end
 
end
end
</code>  
</code>


<br>  
<br>  

Revision as of 23:02, 19 August 2009

Developing Ruby Qt Applications using Qt Designer and Ruby on Ubuntu


NOTE: THIS ARTICLE IS IN THE PROCESS OF PROOF-READING BEFORE FINAL EDITS ARE MADE. THIS SHOULD BE CONCLUDED WITHIN 1 DAY


Introduction

Part of Qt's great power lies in the fact that you get a top notch GUI design kit to please the most avid designer as well as an elegant development framework to keep the app development team happy. In this article we will take you through both sides of this equation as we show you how to create a small application which displays a list of items (pieces of text) and let's you move these items up and down via drag'n'drop. You'll learn how to

  • Install the packages you need to get started
  • Draw a GUI with Qt Designer
  • Relate the GUI to the code that runs underneath it through subclassing
  • Programatically control the display of widgets on screen
  • Do some simple debugging using ruby-debug


Ground breaking in the extreme! Note: These instructions are tested with Ubuntu Jaunty Jackalope 9.04. Your mileage may vary depending on your chosen distro.


Install the appropriate packages

From a command line terminal run

  • sudo aptitude install libkorundum4-ruby1.8
  • sudo aptitude qt4-designer
  • sudo aptitude libqt4-ruby1.8-dev
  • sudo aptitude install libqt4-ruby1.8-examples
  • sudo aptitude install qt4-doc qt4-doc-html  qt4-demos


Create a simple Form with Qt4 Designer

  • Create a folder on disk for your project, for example, my_first_project
  • Start up Qt4 Designer
  1. Select Qt 4 Designer from the Applications->Programming menu or...
  2. From a command line terminal run: designer-qt4
  • The default form is a Dialog with Buttons Right. Stick with that and click Create
  • Drag a List Widget from the Item Widgets (Item-Based) selection of widgets on the left onto the form. Be careful NOT to choose the List Widget from the Item Views (Model-Based)
  • Right-click the List Widget that is now dragged on the form
  1. Select Edit Items
  2. Click the green plus icon on the left to add an item
  3. Enter the text: First line
  4. Click the green plus icon on the left to add an item
  5. Enter the text: Second line
  6. Click OK to finish editing items
  • In the Property Editor on the right, scroll down to the section QAbstractItemView
  • Keep scrolling down a little more to the Property DragDropMode and change it's value to internal. This will let us move items up and down in the list easily
  • Save what we've done by selecting Save As in the File Menu
  1. Navigate to the folder you created earlier, my_first_project
  2. Choose the filename dashboard.ui for your file and click save
  3. This file contains and XML representation of the form you just created
  • If you want to take a quick look at your form select Preview from the Form menu
  • But we're done with GUI stuff now! Let's get hacking...


Creating the basic application structure

  • Now that we've got a user interface, we need to run a command (rbuic4) that creates a Ruby class that we can use based on the interface
  • From the command line terminal
  1. Navigate to the my_first_project folder you created earlier using the cd command
  2. Run the command 'rbuic4 dashboard.ui -x -o dashboard_ui.rb'
  3. For instant gratification run the command 'ruby dashboard_ui.rb'
  4. Woaa!!! You should see your beautiful form before you very eyes. But when the excitement dies down, be aware that if you were to add any code to your dashboard_ui.rb file it would get blown away every time we run rbuic4 on this file to pick up the latest changes we made to the UI using Qt4 Designer. But it needn't be this way...
Note
The '-x' bit of the rbuic4 command says to create a little stub (like a main function in C/C++ or public static void main in Java) that kicks off the application. Otherwise your application would have no entry point.


The '-o dashboard_ui.rb' bit of the command says dump the resulting ruby code into a file called dashboard_ui.rb

We need to rerun this command every time we change the form using Qt4 Designer. Usually we won't add the '-x' flag


Move application startup code into a main.rb file

  • Create a new file in the my_first_project folder called main.rb
  • Move the following code from the dashboard_ui.rb file to main.rb

if $0 == __FILE__

   a = Qt::Application.new(ARGV)
   u = Ui_Dialog.new
   w = Qt::Dialog.new
   u.setupUi(w)
   w.show
   a.exec

end

  • Because we have our own main file now, there is no need for the opening 'if $0 == __FILE__' line. So delete that line and it's enclosing 'end' statement
  • We can move the inclusion of the Qt framework (require 'Qt') from the dashboard_ui.rb file to main.rb
Note
Actually, we have to do this because in future we will run rbuic4 without the '-x' parameter; this would mean that the autogenerated code won't contain require 'Qt' so we'd run into problems


  • We change the line 'u.setupUi(w)' to 'u.setup_ui(w)'
Note
To some, the latter is more Rubyesque. If you look in the autogenerated file dashboard_ui.rb, you will that the function sampleUi() is wrapped by another function sample_ui().

In the same way, we change Ui_Dialog.new to Ui::Dialog.new

This is just aesthetics. Syntactic sugar, but important syntactic sugar nonetheless!


  • This gives us a main.rb file that looks like

require 'Qt' require 'dashboard'

a = Qt::Application.new(ARGV)

u = Ui::Dialog.new w = Qt::Dialog.new u.setup_ui(w) w.show a.exec

Note: Don't forget to remove the require 'Qt' (or it might be require 'Qt4' for you) from the top of the dashboard_ui.rb

Ok, so where are we at now? Well if you run 'ruby main.rb' from the commandline your dinky little dialog should appear. The way we are using the dialog in our application is called the Direct Approach. For more information (which is beyond the scope of this article) check out http://doc.trolltech.com/4.0/designer-using-a-component.html to see what the different approaches are. That article is very C++ based but you should get a gist of what is going on. But basically a lot of the important work is being done in the main.rb file; this means that we don't have much control over our widgets programatically.

In the next section, we will switch to using the Single Inheritance Approach. Then we'll show you how to programatically populate the Item List on application startup. Fasten your seatbelts!

The Single Inheritance Approach - Separating generated code from our 'real' code

Ok, everything you learned is a lie! Well, not really, but definitely you'll want to do take the Single Inheritance Approach over the Direct Approach.

Subclassing the dashboard_ui.rb file

In Qt4, this is the primary way to separate autogenerated from code from our own logic. Using a text editor create a new file in your my_first_project folder called dashboard.rb. (Note: For the rest of this article be careful to pay attention as to whether we are talking about dashboard_ui.rb or dashboard.rb). The comments should explain what's going on

  1. We pull in the file containg the class containing the generated code

require 'dashboard_ui'

  1. We inherit from Qt:Dialog gives us access to User Interface functionality
  2. such as connecting slots and signals

class Dashboard < Qt::Dialog

   # We are then free to put our own code into this class without fear
   # of it being overwritten. Here we add a initialize function which
   # can be used to customise how the form looks on startup. The method
   # initialize() is a constructor in Ruby
   def initialize(parent = nil)
      # Widgets in Qt can be optionally be children of other widgets.
      # That's why we accept parent as a parameter
      # This super call causes the constructor of the base class (Qt::Widget)
      # to be called, shepherding on the parent argument
      super(parent)
      # We'll be putting some code here real soon...
   end

end

Next we need to move the creation of the actual dialog out of main.rb and into the dashboard.rb to make the Dashboard class responsible for managing the widget. This is a core concept to understand. At the same time, we make the following changes to the main.rb file

  1. We change require 'dashboard_ui' to require 'dashboard' because we are using the Dashboard class that we have defined and control rather than the Ui::Dialog class autogenerated by rbuic4
  2. Create an instance of Dashboard rather than Ui::Dialog and rename variables to be more meaningful
  3. Call the show method on the Dashboard object rather than some separate Qt::Dialog object. Why? Because of the subclassing we are implementing, Dashboard now is a Qt::Dialog object! The main.rb file will then look like this

require 'Qt4' require 'dashboard'

a = Qt::Application.new(ARGV) dashboard = Dashboard.new dashboard.show a.exec

So where has that setup_ui() function ended up? As we said, it's going straight to our Dashboard class in dashboard.rb. The comments should explain what's going on.

  1. We pull in the file containg the class containing the generated code

require 'dashboard_ui'

  1. We inherit from Qt:Dialog gives us access to User Interface functionality
  2. such as connecting slots and signals

class Dashboard < Qt::Dialog

   # We are then free to put our own code into this class without fear
   # of it being overwritten. Here we add a initialize function which
   # can be used to customise how the form looks on startup. The method
   # initialize() is a constructor in Ruby
   def initialize(parent = nil)
      # Widgets in Qt can optionally be children of other widgets.
      # That's why we accept parent as a parameter
      # This super call causes the constructor of the base class (Qt::Widget)
      # to be called, shepherding on the parent argument
      super(parent)
      # The Dashboard class we are in holds presentation logic and exists
      # to 'manage' the dialog widget we created in Qt Designer earlier.
      # An instance of this dialog widget is created and stored in @ui variable
      @ui = Ui::Dialog.new
      # Calling setup_ui causes the dialog widget to be initialised with the
      # defaults you may have specified in Qt Designer. For example, it 
      # - populates the 'First Line' and 'Second Line' items in the List Widget
      # - sets the drag drop mode to 'Internal'
      # - and much much more. Peer into the dashboard_ui.rb if you want to the
      #   full gory details
      @ui.setup_ui(self)
   end

end


Programmatically control how the List Widget is populated on app startup

Now currently all the code to insert the items into the Item List is controlled in the autogenerated file dashboard_ui.rb. Let's take the power back!

From the the setupUi function in dashboard_ui.rb file to our own dashboard.rb file's setup_ui function, move the lines

Qt::ListWidgetItem.new(@listWidget) Qt::ListWidgetItem.new(@listWidget)

From the the retranslateUi function in dashboard_ui.rb file to our dashboard.rb file's setup_ui function, move the lines

@listWidget.item(0).text = Qt::Application.translate("Dialog", "First Line", nil, Qt::Application::UnicodeUTF8) @listWidget.item(1).text = Qt::Application.translate("Dialog", "Second Line", nil, Qt::Application::UnicodeUTF8)

This gives us a dashboard.rb file that looks like


  1. We pull in the file containg the class containing the generated code

require 'dashboard_ui'

  1. We inherit from Qt:Dialog gives us access to User Interface functionality
  2. such as connecting slots and signals

class Dashboard < Qt::Dialog

   # We are then free to put our own code into this class without fear
   # of it being overwritten. Here we add a initialize function which
   # can be used to customise how the form looks on startup. The method
   # initialize() is a constructor in Ruby
   def initialize(parent = nil)
      # Widgets in Qt can optionally be children of other widgets.
      # That's why we accept parent as a parameter
      # This super call causes the constructor of the base class (Qt::Widget)
      # to be called, shepherding on the parent argument
      super(parent)
      # The Dashboard class we are in holds presentation logic and exists
      # to 'manage' the dialog widget we created in Qt Designer earlier.
      # An instance of this dialog widget is created and stored in @ui variable
      @ui = Ui::Dialog.new
      # Calling setup_ui causes the dialog widget to be initialised with the
      # defaults you may have specified in Qt Designer. For example, it 
      # - populates the 'First Line' and 'Second Line' items in the List Widget
      # - sets the drag drop mode to 'Internal'
      # - and much much more. Peer into the dashboard_ui.rb if you want to the
      #   full gory details
      @ui.setup_ui(self)
      # As promised...
      Qt::ListWidgetItem.new(@ui.listWidget)
      Qt::ListWidgetItem.new(@ui.listWidget)
      #And also...
      @ui.listWidget.item(0).text = Qt::Application.translate("Dialog", "First Line", nil, Qt::Application::UnicodeUTF8)
      @ui.listWidget.item(1).text = Qt::Application.translate("Dialog", "Second Line", nil, Qt::Application::UnicodeUTF8)
   end

end


Debugging your application

You should now be able to run 'ruby main.rb' and see you're lovely form appear. Try to drag the first line and second line up and down. Not bad, eh?

One last thing if you need to do some simple debugging on your app, make sure that you have the ruby-debug gem installed ('sudo gem install ruby-debug') and stick the line 'debugger' in your source code anywhere you want to put a breakpoint. Then instead of the ruby command run 'rdebug main.rb' and this will drop you into a nifty command line debugger. Initially it will be at line 1 in your source file but if you type 'c' it will bring you to your breakpoint. For more on using command line debugging see ruby-debug in 30 seconds (we don't need no stinkin' GUI!).

Ya, sure we don't need no stinkin' GUI! But don't tell those Qt guys - they seem to produce quite nice ones!


Useful Links and Articles