# Ruby Qt 中的自定义小部件

> 原文： [http://zetcode.com/gui/rubyqt/customwidget/](http://zetcode.com/gui/rubyqt/customwidget/)

在 Ruby Qt 编程教程的这一部分中，我们将创建一个自定义窗口小部件。

工具箱通常仅提供最常见的窗口小部件，例如按钮，文本窗口小部件，滑块等。没有工具箱可以提供所有可能的窗口小部件。 程序员必须自己创建此类小部件。 他们使用工具箱提供的绘图工具来完成此任务。 有两种可能性。 程序员可以修改或增强现有的小部件。 或者，他可以从头开始创建自定义窗口小部件。

## 刻录小部件

在下一个示例中，我们将创建一个自定义刻录小部件。 可以在 Nero 或 K3B 之类的应用中看到此小部件。 该小部件将从头开始创建。

```rb
#!/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

```

在这个文件中，我们创建了烧录小部件。

```rb
class Burning < Qt::Widget 

```

自定义窗口小部件基于`Widget`小部件。

```rb
PANEL_HEIGHT = 30
DISTANCE = 19
LINE_WIDTH = 5
DIVISIONS = 10
FULL_CAPACITY = 700
MAX_CAPACITY = 750

```

这些是重要的常数。 `PANEL_HEIGHT`定义自定义窗口小部件的高度。 `DISTANCE`是比例尺上的数字与其父边框顶部之间的距离。 `LINE_WIDTH`是垂直线的宽度。 `DIVISIONS`是秤的数量。 `FULL_CAPACITY`是媒体的最大容量。 达到目标后，就会发生过度刻录。 这通过红色可视化。 `MAX_CAPACITY`是介质的最大容量。

```rb
@num = [ "75", "150", "225", "300", 
    "375", "450", "525", "600", "675" ]

```

我们使用这些数字来构建`Burning`小部件的比例。

```rb
def paintEvent event

    painter = Qt::Painter.new self

    drawWidget painter
    painter.end
end

```

自定义窗口小部件的图形委托给`drawWidget`方法。

```rb
slid_width = @parent.getCurrentWidth

```

我们使用它来获取当前选定的滑块值。

```rb
w = width.to_f

```

我们得到小部件的宽度。 自定义窗口小部件的宽度是动态的。 用户可以调整大小。

```rb
till = ((w / MAX_CAPACITY) * slid_width).to_f
full = ((w / MAX_CAPACITY) * FULL_CAPACITY).to_f

```

我们使用`w`变量进行转换。 在比例尺值和自定义小部件的度量之间。 请注意，我们使用浮点值。 我们在绘图中获得了更高的精度。

```rb
painter.setPen @redColor
painter.setBrush Qt::Brush.new @redColor
painter.drawRect Qt::RectF.new full+1, 0, till-full, PANEL_HEIGHT

```

这三行画出红色矩形，表示过度燃烧。

```rb
painter.drawRect 0, 0, w-1, PANEL_HEIGHT-1

```

这是小部件的外围，即外部矩形。

```rb
painter.drawLine Qt::LineF.new i*step, 1, i*step, LINE_WIDTH

```

在这里，我们画出小的垂直线。

```rb
w = metrics.width @num[i-1]
painter.drawText(Qt::PointF.new(i*step-w/2, DISTANCE), @num[i-1])

```

在这里，我们绘制刻度的数字。 为了精确定位数字，我们必须获得字符串的宽度。

```rb
@widget = Burning.new self
hbox.addWidget @widget, 0

```

我们创建`Burning`小部件的实例并将其添加到水平框中。

```rb
def onChanged val
    @cur_width = val
    @widget.repaint
end

```

当滑块的值更改时，我们将其存储在`@cur_width`变量中，然后重新绘制自定义窗口小部件。

```rb
def getCurrentWidth
return @cur_width
end

```

定制小部件调用此方法以获取实际的滑块值。

![The Burning widget](img/8c8b2b7270eee32a743a32c456c17ab8.jpg)

图：烧录小部件

在 Ruby Qt 教程的这一部分中，我们已经演示了如何创建自定义窗口小部件。