Custom widget
In this part of the Ruby Qt programming tutorial, we will create a custom widget.
Toolkits usually provide only the most common widgets like buttons, text widgets, sliders etc. No toolkit can provide all possible widgets. Programmers must create such widgets by themselves. They do it by using the drawing tools provided by the toolkit. There are two possibilities. A programmer can modify or enhance an existing widget. Or he can create a custom widget from scratch.
The Burning widget
In the next example, we will create a custom burning widget. This widget can be seen in applications like Nero or K3B. The widget will be created from scratch.
#!/usr/bin/ruby
# ZetCode Ruby Qt tutorial
# In this program, we create
# a custom widget
#
# @author jan bodnar
# website zetcode.com
# last modified July 2009
require 'Qt'
PANEL_HEIGHT = 30
DISTANCE = 19
LINE_WIDTH = 5
DIVISIONS = 10
FULL_CAPACITY = 700
MAX_CAPACITY = 750
class Burning < Qt::Widget
def initialize(parent)
super(parent)
@num = [ "75", "150", "225", "300",
"375", "450", "525", "600", "675" ]
@redColor = Qt::Color.new 255, 175, 175
@yellowColor = Qt::Color.new 255, 255, 184
@parent = parent
setMinimumHeight PANEL_HEIGHT
end
def paintEvent event
painter = Qt::Painter.new self
drawWidget painter
painter.end
end
def drawWidget painter
w = width.to_f
slid_width = @parent.getCurrentWidth
step = (w / DIVISIONS).round.to_f
till = ((w / MAX_CAPACITY) * slid_width).to_f
full = ((w / MAX_CAPACITY) * FULL_CAPACITY).to_f
if slid_width > FULL_CAPACITY
painter.setPen @yellowColor
painter.setBrush Qt::Brush.new @yellowColor
painter.drawRect Qt::RectF.new 0, 0, full, PANEL_HEIGHT
painter.setPen @redColor
painter.setBrush Qt::Brush.new @redColor
painter.drawRect Qt::RectF.new full+1, 0, till-full, PANEL_HEIGHT
else
if slid_width > 0
painter.setPen @yellowColor
painter.setBrush Qt::Brush.new @yellowColor
painter.drawRect Qt::RectF.new 0, 0, till, PANEL_HEIGHT
end
end
painter.setPen Qt::Color.new 90, 90, 90
painter.setBrush Qt::NoBrush
painter.drawRect 0, 0, w-1, PANEL_HEIGHT-1
newFont = font
newFont.setPointSize 7
painter.setFont newFont
for i in (1..@num.length)
painter.drawLine Qt::LineF.new i*step, 1, i*step, LINE_WIDTH
metrics = Qt::FontMetrics.new newFont
w = metrics.width @num[i-1]
painter.drawText(Qt::PointF.new(i*step-w/2, DISTANCE), @num[i-1])
end
end
end
class QtApp < Qt::Widget
slots 'onChanged(int)'
def initialize
super
setWindowTitle "The Burning Widget"
initUI
resize 370, 200
move 300, 300
show
end
def initUI
@cur_width = 0
@slider = Qt::Slider.new Qt::Horizontal , self
@slider.setMaximum MAX_CAPACITY
@slider.setGeometry 50, 50, 130, 30
connect(@slider, SIGNAL("valueChanged(int)"), self, SLOT("onChanged(int)"))
vbox = Qt::VBoxLayout.new self
hbox = Qt::HBoxLayout.new
vbox.addStretch 1
@widget = Burning.new self
hbox.addWidget @widget, 0
vbox.addLayout hbox
setLayout vbox
end
def onChanged val
@cur_width = val
@widget.repaint
end
def getCurrentWidth
return @cur_width
end
end
app = Qt::Application.new ARGV
QtApp.new
app.exec
In this file, we create the Burning widget.
class Burning < Qt::Widget
The custom widget is based on the Widget widget.
PANEL_HEIGHT = 30 DISTANCE = 19 LINE_WIDTH = 5 DIVISIONS = 10 FULL_CAPACITY = 700 MAX_CAPACITY = 750
These are important constants. The PANEL_HEIGHT defines the height for the custom widget. The DISTANCE is the distance of the numbers on the scale from the top of their parent border. The LINE_WIDTH is the vertical line width. The DIVISIONS is the number of parts of the scale. The FULL_CAPACITY is the maximum capacity of the media. After it is reached, overburning happens. This is visualized by a red color. The MAX_CAPACITY is the maximum capacity of a medium.
@num = [ "75", "150", "225", "300",
"375", "450", "525", "600", "675" ]
We use these numbers to build the scale of the Burning widget.
def paintEvent event
painter = Qt::Painter.new self
drawWidget painter
painter.end
end
The drawing of the custom widget is delegated to the drawWidget method.
slid_width = @parent.getCurrentWidth
We use it to get the currently selected slider value.
w = width.to_f
We get the width of the widget. The width of the custom widget is dynamic. It can be resized by a user.
till = ((w / MAX_CAPACITY) * slid_width).to_f full = ((w / MAX_CAPACITY) * FULL_CAPACITY).to_f
We use the w variable to do the transformations. Between the values of the scale and the custom widget's measures. Note that we use floating point values. We get greater precision in drawing.
painter.setPen @redColor painter.setBrush Qt::Brush.new @redColor painter.drawRect Qt::RectF.new full+1, 0, till-full, PANEL_HEIGHT
These three lines draw the red rectangle, indicating the overburning.
painter.drawRect 0, 0, w-1, PANEL_HEIGHT-1
This is the perimeter of the widget. The outside rectangle.
painter.drawLine Qt::LineF.new i*step, 1, i*step, LINE_WIDTH
Here we draw the small vertical lines.
w = metrics.width @num[i-1] painter.drawText(Qt::PointF.new(i*step-w/2, DISTANCE), @num[i-1])
Here we draw the numbers of the scale. To precisely position the numbers, we must get the width of the string.
@widget = Burning.new self hbox.addWidget @widget, 0
We create the instance of the Burning widget and add it to the horizontal box.
def onChanged val
@cur_width = val
@widget.repaint
end
When the value of the slider changes, we store it inside the @cur_width variable and repaint the custom widget.
def getCurrentWidth
return @cur_width
end
This method is called by the custom widget to get the actual slider value.
In this part of the Ruby Qt tutorial, we have demonstrated how to create a custom widget.