Книга: Программирование на языке Ruby

12.4.5. Прочие виджеты

12.4.5. Прочие виджеты

В библиотеке Qt есть еще много встроенных виджетов, например переключатели, флажки и т.п. В листинге 12.16 продемонстрированы некоторые из них, а на рис. 12.9 показано, как выглядит окно приложения.

Листинг 12.16. Прочие виджеты в Qt

require 'Qt'
class MyWindow < Qt::Widget
 slots 'somethingClicked(QAbstractButton *)'
 def initialize(parent = nil)
  super(parent)
  groupbox = Qt::GroupBox.new("Some Radio Button",self)
  radio1 = Qt::RadioButton.new("Radio Button 1", groupbox)
  radio2 = Qt::RadioButton.new("Radio Button 2", groupbox)
  check1 = Qt::CheckBox.new("Check Box 1", groupbox)
  vbox = Qt::QBoxLayout.new
  vbox.addWidget(radio1)
  vbox.addWidget(radio2)
  vbox.addWidget(check1)
  groupbox.setLayout(vbox)
  bg = Qt::ButtonGroup.new(self)
  bg.addButton(radio1)
  bg.addButton(radio2)
  bg.addButton(check1)
  connect(bg, SIGNAL('buttonClicked(QAbscractButton *)'),
   self, SLOT('somethingClicked(QAbstractButton *)') )
  @label = Qt::Label.new(self)
  vbox = Qt::VBoxLayout.new
  vbox.addWidget(groupbox)
  vbox.addWidget(@label)
  setLayout(vbox)
 end
 def somethingClicked(who)
  @label.setText("You clicked on a " + who.className)
 end
end
app = Qt::Application.new(ARGV)
widget = MyWindow.new
widget.show
app.exec


Рис. 12.9. Простое приложение Tk

В этом классе мы сначала создаем объект Qt::GroupBox — контейнер с рамкой и необязательным заголовком, в который можно помещать другие виджеты. Далее создаются два переключателя Qt::RadioButtons и флажок Qt::CheckBox, а в качестве их родителя указывается ранее созданный контейнер.

Затем создается менеджер размещения Qt::VBoxLayout, в который помещаются переключатели и флажок, после чего этот менеджер связывается с групповым контейнером и начинает управлять его размещением на экране.

Следующий важный шаг — создание объекта Qt::ButtonGroup, в который помещаются флажок и переключатели. Qt::ButtonGroup предназначен для логической группировки кнопок, флажков и переключателей. На их визуальное расположение он никак не влияет, зато обеспечивает, к примеру, взаимное исключение (гарантирует, что только один из группы виджетов может быть отмечен). В данном случае этот объект будет источником сигнала buttonClicked, который испускается при нажатии любой кнопки в группе.

Этот сигнал отличается от виденных ранее тем, что ему сопутствует аргумент, а именно объект, по которому щелкнули мышкой. Обратите внимание на то, как синтаксис — QAbstractButton* — напоминает о C++-ных корнях Qt. В некоторых случаях употребления принятой в C++ нотации для обозначения типов параметров не избежать (хотя в будущих версиях это, возможно, и исправят).

В результате такого вызова метода connect при щелчке по любому виджету, принадлежащему группе, этот виджет будет передан слоту somethingClicked. Наконец, мы создаем метку Qt::Label, контейнер Qt::QBoxLayout и увязываем все вместе.

Внутри слота somethingClicked мы модифицируем текст метки при щелчке по любому переключателю или флажку. В данном случае выводится имя класса объекта, который испустил сигнал, приведший к вызову слота.

Если встроенных виджетов недостаточно, то Qt предоставляет мощную систему рисования для создания собственных. В листинге 12.17 приведен небольшой пример, иллюстрирующий малую часть возможностей.

Листинг 12.17. Нестандартный виджет TimerClock

require 'Qt'
class TimerClock < Qt::Widget
 def initialize(parent = nil)
  super(parent)
  @timer = Qt::Timer.new(self)
  connect(@timer, SIGNAL('timeout()'), self, SLOT('update()'))
  @timer.start(25)
  setWindowTitle('Stop Watch')
  resize(200, 200)
 end
 def paintEvent(e)
  fastHand = Qt::Polygon.new([Qt::Point.new(7, 8),
   Qt::Point.new(-7, 8),
   Qt::Point.new(0, -80)])
  secondHand = Qt::Polygon.new([Qt::Point.new(7, 8),
   Qt::Point.new(-7, 8),
   Qt::Point.new(0, -65)])
  secondColor = Qt::Color.new(100, 0, 100)
  fastColor = Qt::Color.new(0, 150, 150, 150)
  side = [width, height].min
  time = Qt::Time.currentTime
  painter = Qt::Painter.new(self)
  painter.renderHint = Qt::Painter::Antialiasing
  painter.translate(width() / 2, height() / 2)
  painter.scale(side / 200.0, side / 200.0)
  painter.pen = Qt::NoPen
  painter.brush = Qt::Brush.new(secondColor)
  painter.save
  painter.rotate(6.0 * time.second)
  painter.drawConvexPolygon(secondHand)
  painter.restore
  painter.pen = secondColor
   (0...12).each do |i|
   painter.drawLine(88, 0, 96, 0)
   painter.rotate(30.0)
  end
  painter.pen = Qt::NoPen
  painter.brush = Qt::Brush.new(fastColor)
  painter.save
  painter.rotate(36.0 * (time.msec / 100.0))
  painter.drawConvexPolygon(fastHand)
  painter.restore
  painter.pen = fastColor
   (0...60).each do |j|
   if (j % 5) != 0
    painter.drawLine(92, 0, 96, 0)
   end
   painter.rotate(6.0)
  end
  painter.end
 end
end
app = Qt::Application.new(ARGV)
wid = TimerClock.new
wid.show
app.exec

Созданный в этом примере виджет называется TimerClock. В инициализаторе мы создаем объект Qt::Timer, который конфигурируется для периодического испускания сигнала. Его сигнал timeout мы соединяем со слотом update нашего виджета. Это встроенный слот, он заставляет виджет перерисовать себя.

Таймер запускается методом start. Переданный ему аргумент говорит, что таймер должен срабатывать (и испускать сигнал timeout) каждые 25 миллисекунд. Следовательно, слот update будет вызываться каждые 25 миллисекунд.

Далее определяется метод paintEvent. Мы переопределяем одноименный метод класса Qt::Widget. Когда виджет собирается перерисовать себя (то есть при срабатывании таймера), он вызывает этот метод. Переопределяя его, мы решаем, как виджет должен отображаться на экране. Код этого метода вызывает различные графические примитивы рисования.

Начиная с этого места идет сплошная геометрия. Мы создаем несколько многоугольников Qt::Polygon, представляющих стрелки часов. Ориентация многоугольников не имеет значения, потому что манипулировать ими мы будем позже.

Задаются значения нескольких свойств. Устанавливаются цвета Qt::Color обеих стрелок. Аргументами инициализатора Qt::Color являются значения в формате RGB с необязательной альфа-прозрачностью.

Часы должны быть квадратными, поэтому в переменную side (длина стороны) записывается минимум из ширины и высота виджета. Кроме того, мы запоминаем текущее время, обращаясь к методу Qt::Time.currentTime.

Далее создается объект Qt::Painter, и с его помощью мы начинаем рисовать. Задается режим сглаживания (antialiasing), чтобы на стрелках часов не было «лесенки». Начало координат помещается в центр области рисования (painter.translate (width/2, height/2)). Для объекта Painter устанавливается масштаб в предположении, что сторона квадрата составляет 200 единиц. Если размер окна изменится, то масштабирование будет произведено автоматически.

Затем выполняется последовательность операций рисования. Различные геометрические преобразования (например, поворот), сопровождаются парой вызовов painter.save и painter.restore. Метод save сохраняет текущие свойства объекта Painter в стеке, чтобы их можно было позднее восстановить.

Программа рисует обе стрелки, предварительно повернув их на нужный угол в соответствии с текущим временем. Кроме того, мы наносим риски вдоль границы циферблата.

И напоследок мы сообщаем объекту Painter, что рисование закончилось (вызывая метод painter.end). Довершают картину четыре строчки, в которых создаются объект приложения Qt::Application и наш виджет, а затем запускается цикл обработки событий. На рис. 12.10 показан конечный результат.


Рис. 12.10. Виджет TimerClock

Оглавление книги


Генерация: 1.186. Запросов К БД/Cache: 2 / 0
поделиться
Вверх Вниз