Custom widget in Qyoto

In this part of the Visual Basic Qyoto 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.

burning.vb
Imports Qyoto

NameSpace Burning

Public Class Burning 
    Inherits QWidget

    Const PANEL_HEIGHT As Integer = 30
    Const DISTANCE As Integer = 19
    Const LINE_WIDTH As Integer = 5
    Const DIVISIONS As Integer = 10
    
    Const FULL_CAPACITY As Double = 700.0
    Const MAX_CAPACITY As Double = 750.0
    
    Dim redColor As New QColor(255, 175, 175)
    Dim yellowColor As New QColor(255, 255, 184)

    Dim parent As QWidget 
    
    Dim num() As String = { _
        "75", "150", "225", "300", _
        "375", "450", "525", "600", _
        "675" _
    }
    
    Public Sub New(ByVal parent As QWidget) 
        Me.parent = parent
        MinimumHeight = PANEL_HEIGHT
    End Sub
    

    Protected Overrides Sub PaintEvent(ByVal e As QPaintEvent) 

        Dim painter As New QPainter(Me)
        
        Me.DrawWidget(painter)
        painter.End()
        
    End Sub

    Private Sub DrawWidget(ByVal painter As QPainter) 
        
        Dim burn As CustomWidget.VBQApp = CType(parent, CustomWidget.VBQApp)

        Dim slid_width As Double = burn.GetCurrentWidth()
        Dim width As Double = Size.Width()
        Dim move As Double = width / DIVISIONS

        Dim till As Double = (width / MAX_CAPACITY) * slid_width
        Dim full As Double = (width / MAX_CAPACITY) * FULL_CAPACITY

        If slid_width > FULL_CAPACITY

            painter.SetPen(New QPen(New QBrush(yellowColor), 1))
            painter.SetBrush(New QBrush(yellowColor))
            painter.DrawRect(New QRectF(0, 0, full, PANEL_HEIGHT))
            painter.SetPen(New QPen(New QBrush(redColor), 1))
            painter.SetBrush(New QBrush(redColor))
            painter.DrawRect(New QRectF(full+1, 0, till-full, PANEL_HEIGHT))

        Else
        
            If (slid_width > 0) 
               painter.SetPen(New QPen(New QBrush(yellowColor), 1))
               painter.SetBrush(New QBrush(yellowColor))
               painter.DrawRect(New QRectF(0, 0, till, PANEL_HEIGHT))
            End If
            
        End If
        
        
        painter.SetPen(New QColor(90, 90, 90))
        painter.SetBrush(BrushStyle.NoBrush)
        painter.DrawRect(0, 0, Size.Width()-1, PANEL_HEIGHT-1)
        
        Dim newFont As QFont = painter.Font()
        newFont.SetPointSize(7)
        painter.SetFont(newFont)

        Dim metrics As New QFontMetrics(newFont)

        For i As Integer = 1 to num.Length
            painter.DrawLine(New QLineF(i*move, 1, i*move, LINE_WIDTH))

            Dim w As Integer = metrics.Width(num(i-1))

            painter.DrawText(New QPointF(i*move-w/2, DISTANCE), num(i-1))
        Next
        
    End Sub
    
End Class
    
End Namespace

In this file, we create the Burning widget.

Public Class Burning 
    Inherits QWidget

The custom widget is based on the QWidget widget.

Const PANEL_HEIGHT As Integer = 30
Const DISTANCE As Integer = 19
Const LINE_WIDTH As Integer = 5
Const DIVISIONS As Integer = 10

Const FULL_CAPACITY As Double = 700.0
Const MAX_CAPACITY As Double = 750.0

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 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.

Dim num() As String = { _
    "75", "150", "225", "300", _
    "375", "450", "525", "600", _
    "675" _
}

We use these numbers to build the scale of the Burning widget.

Protected Overrides Sub PaintEvent(ByVal e As QPaintEvent) 

    Dim painter As New QPainter(Me)
    
    Me.DrawWidget(painter)
    painter.End()
    
End Sub

The drawing of the custom widget is delegated to the DrawWidget() method.

Dim burn As CustomWidget.VBQApp = CType(parent, CustomWidget.VBQApp)

We retrieve the reference to the parent widget.

Dim slid_width As Double = burn.GetCurrentWidth()

We use it to get the currently selected slider value.

Dim width As Double = Size.Width()

We get the width of the widget. The width of the custom widget is dynamic. It can be resized by a user.

Dim till As Double = (width / MAX_CAPACITY) * slid_width
Dim full As Double = (width / MAX_CAPACITY) * FULL_CAPACITY

We use the width 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(New QPen(New QBrush(redColor), 1))
painter.SetBrush(New QBrush(redColor))
painter.DrawRect(New QRectF(full+1, 0, till-full, PANEL_HEIGHT))

These three lines draw the red rectangle, indicating the overburning.

painter.DrawRect(0, 0, Size.Width()-1, PANEL_HEIGHT-1)

This is the perimeter of the widget. The outside rectangle.

painter.DrawLine(New QLineF(i*move, 1, i*move, LINE_WIDTH))

Here we draw the small vertical lines.

Dim w As Integer = metrics.Width(num(i-1))

painter.DrawText(New QPointF(i*move-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.

main.vb
Imports Qyoto

' ZetCode Mono Visual Basic Qt tutorial
'
' In this program, we create
' a custom widget
'
' @author jan bodnar
' website zetcode.com
' last modified May 2009

NameSpace CustomWidget

Public Class VBQApp 
    Inherits QWidget

    Const MAX_CAPACITY As Integer = 750

    Dim slider As QSlider
    Dim widget As QWidget
    Dim cur_width As Integer

    Public Sub New()
        Me.SetWindowTitle("The Burning Widget")

        Me.InitUI()

        Me.Resize(370, 200)
        Me.Move(300, 300)
        Me.Show()
    End Sub

    Private Sub InitUI() 
       
       slider = New QSlider(Qt.Orientation.Horizontal , Me)
       slider.Maximum = MAX_CAPACITY
       slider.SetGeometry(50, 50, 130, 30)

       Connect(slider, SIGNAL("valueChanged(int)"), Me, _
                SLOT("ValueChanged(int)"))
       
       Dim vbox As New QVBoxLayout(Me)
       Dim hbox As New QHBoxLayout

       vbox.AddStretch(1)

       widget = New Burning.Burning(Me)
       hbox.AddWidget(widget, 0)

       vbox.AddLayout(hbox)

       SetLayout(vbox)
       
    End Sub

    <Q_SLOT()> _
    Public Sub ValueChanged(ByVal val As Integer) 
        cur_width = val
        widget.Repaint()
    End Sub
    

    Public Function GetCurrentWidth() As Integer
      Return cur_width
    End Function
    

    Public Shared Sub Main(ByVal args() As String)
        Dim qapp As New QApplication(args)
        Dim app As New VBQApp
        QApplication.Exec()
    End Sub

End Class

NameSpace CustomWidget

This is the main file. Here we create the slider widget and use our custom widget.

widget = New Burning.Burning(Me)
hbox.AddWidget(widget, 0)

We create the instance of the Burning widget and add it to the horizontal box.

<Q_SLOT()> _
Public Sub ValueChanged(ByVal val As Integer) 
    cur_width = val
    widget.Repaint()
End Sub

When the value of the slider changes, we store it inside the cur_width variable and repaint the custom widget.

Public Function GetCurrentWidth() As Integer
  Return cur_width
End Function

This method is called by the custom widget to get the actual slider value.

The Burning widget
Figure: The Burning widget

In this part of the Visual Basic Qyoto tutorial, we have demonstrated how to create a custom widget.