Автор: Andi Peredri
Довольно часто при работе с классом QCanvas требуется обеспечить индивидуальное ручное масштабирование элементов QCanvasItem. В этой статье показано, как реализуется выбор, перемещение и масштабирование элементов QCanvasItem на примере небольшой демонстрационной программы.
Мы используем 3 класса:
Scale::Scale() : QMainWindow() { <a href="http://doc.trolltech.com/qwidget.html#setCaption">setCaption</a>("Scale Example"); // Создаем и настраиваем QCanvas и ScaleView QCanvas* canvas = new <a href="http://doc.trolltech.com/qcanvas.html#QCanvas">QCanvas</a>(this); canvas-><a href="http://doc.trolltech.com/qcanvas.html#setBackgroundColor">setBackgroundColor</a>(black); canvas-><a href="http://doc.trolltech.com/qcanvas.html#resize">resize</a>(500, 370); ScaleView* view = new ScaleView(canvas, this); <a href="http://doc.trolltech.com/qmainwindow.html#setCentralWidget">setCentralWidget</a>(view); // Добавляем планеты new ScaleItem("Sun", <a href="http://doc.trolltech.com/qimage.html#QImage">QImage</a>("pics/sun.png"), 300, 150, 50, 50, canvas); new ScaleItem("Mars", QImage("pics/mars.png"), 100, 100, 30, 30, canvas); new ScaleItem("Saturn", QImage("pics/saturn.png"), 400, 50, 50, 50, canvas); new ScaleItem("Jupiter", QImage("pics/jupiter.png"), 50, 200, 150, 150, canvas); new ScaleItem("Mercury", QImage("pics/mercury.png"), 300, 200, 10, 10, canvas); new ScaleItem("Neptune", QImage("pics/neptune.png"), 350, 250, 30, 30, canvas); ... }
void ScaleView::<a href="http://doc.trolltech.com/qscrollview.html#contentsMousePressEvent">contentsMousePressEvent</a>(<a href="http://doc.trolltech.com/qmouseevent.html">QMouseEvent</a>* e) { pressed = true; int cursor = <a href="http://doc.trolltech.com/qscrollview.html#viewport">viewport</a>()-><a href="http://doc.trolltech.com/qwidget.html#cursor">cursor</a>().<a href="http://doc.trolltech.com/qcursor.html#shape">shape</a>(); // Определяем форму курсора if( cursor == <a href="http://doc.trolltech.com/qt.html#CursorShape-enum">SizeVerCursor</a> || cursor == <a href="http://doc.trolltech.com/qt.html#CursorShape-enum">SizeFDiagCursor</a> || cursor == <a href="http://doc.trolltech.com/qt.html#CursorShape-enum">SizeHorCursor</a> || cursor == <a href="http://doc.trolltech.com/qt.html#CursorShape-enum">SizeBDiagCursor</a>) { // Осуществляем захват границы планеты, сохраняем положение курсора. selectedPos = e-><a href="http://doc.trolltech.com/qmouseevent.html#pos">pos</a>(); return; } // Осуществляем выбор планеты // Получаем список всех объектов QCanvasItem, находящихся под курсором <a href="http://doc.trolltech.com/qcanvasitemlist.html">QCanvasItemList</a> l = <a href="http://doc.trolltech.com/qcanvasview.html#canvas">canvas</a>()-><a href="http://doc.trolltech.com/qcanvas.html#collisions">collisions</a>(e->pos()); for(QCanvasItemList::Iterator it = l.<a href="http://doc.trolltech.com/qvaluelist.html#begin">begin</a>(); it != l.<a href="http://doc.trolltech.com/qvaluelist.html#end">end</a>(); ++it) { // Выбираем только планеты if((*it)-><a href="http://doc.trolltech.com/qcanvasitem.html#rtti">rtti</a>() != ItemRtti) continue; ScaleItem* si = (ScaleItem*)(*it); // Выбранная точка не должна быть прозрачной if(!si->hit(e->pos())) continue; selectedPos = e->pos(); selectItem(si); // делаем выбор return; } // Под курсором не обнаружено ни одной планеты selectItem(0); }
void ScaleView::<a href="http://doc.trolltech.com/qscrollview.html#contentsMouseMoveEvent">contentsMouseMoveEvent</a>(QMouseEvent* e) { if(!selected) return; // Ни одна из планет не выбрана if(!pressed) // Если мы не удерживаем планету... { // ...то определяем положение указателя мыши относительно нее static const int sens = 3; // Чувствительность мыши int x = e-><a href="http://doc.trolltech.com/qmouseevent.html#x">x</a>(); int y = e-><a href="http://doc.trolltech.com/qmouseevent.html#y">y</a>(); int x1 = (int) selected-><a href="http://doc.trolltech.com/qcanvasitem.html#x">x</a>(); // Наружный левый край int x2 = x1 + sens; // Внутренний левый край int x4 = x1 + selected-><a href="http://doc.trolltech.com/qcanvasrectangle.html#width">width</a>(); // Наружный правый край int x3 = x4 - sens; // Внутренний правый край int y1 = (int) selected-><a href="http://doc.trolltech.com/qcanvasitem.html#y">y</a>(); // Наружный верхний край int y2 = y1 + sens; // Внутренний верхний край int y4 = y1 + selected-><a href="http://doc.trolltech.com/qcanvasrectangle.html#height">height</a>(); // Наружный нижний край int y3 = y4 - sens; // Внутренний нижний край int cursor = ArrowCursor; // Форма курсора мыши по-умолчанию if(x > x4 || y > y4); // Ничего не делаем, если курсор за пределами планеты else if(x > x3) if(y > y3) cursor = SizeFDiagCursor; // Курсор над нижним правым углом else if(y > y2) cursor = SizeHorCursor; // Курсор над правым краем else if(y > y1) cursor = SizeBDiagCursor; // Курсор над верхним правым углом else ; else if(x > x2) if(y > y3) cursor = SizeVerCursor; // Курсор над нижним краем else if(y > y2) ; // Курсор над внутренней частью планеты else if(y > y1) cursor = SizeVerCursor; // Курсор над верхним краем else ; else if(x > x1) if(y > y3) cursor = SizeBDiagCursor; // Курсор над нижним левым углом else if(y > y2) cursor = SizeHorCursor; // Курсор над левым краем else if(y > y1) cursor = SizeFDiagCursor; // Курсор над верхним левым углом else ; else ; viewport()-><a href="http://doc.trolltech.com/qwidget.html#setCursor">setCursor</a>(cursor); // Устанавливаем форму курсора мыши } else // Мы удерживаем планету { static const int minimum = 8; // Минимальный размер планеты <a href="http://doc.trolltech.com/qpoint.html">QPoint</a> offset = e->pos() - selectedPos; // Смещение мыши int w = selected->width(); int h = selected->height(); int cursor = viewport()->cursor().shape(); // Определяем форму курсора // Требуется изменить ширину планеты if(cursor == SizeHorCursor || cursor == SizeFDiagCursor || cursor == SizeBDiagCursor) // Курсор находится справа от центра планеты if(selectedPos.<a href="http://doc.trolltech.com/qpoint.html#x">x</a>() > selected-><a href="http://doc.trolltech.com/qcanvasrectangle.html#rect">rect</a>().<a href="http://doc.trolltech.com/qrect.html#center">center</a>().x()) { w = QMAX(w + offset.x(), minimum); selectedPos.setX(QMAX(selected->x() + minimum, e->x())); } else // Курсор находится слева от центра планеты { selected->setX(QMIN(selected->x() + offset.x(), selected->rect().right() - minimum + 1)); w = QMAX(w - offset.x(), minimum); selectedPos.<a href="http://doc.trolltech.com/qpoint.html#setX">setX</a>(QMIN(e->x(), selected->x())); } // Требуется изменить высоту планеты if(cursor == SizeVerCursor || cursor == SizeFDiagCursor || cursor == SizeBDiagCursor) // Курсор находится ниже центра планеты if(selectedPos.<a href="http://doc.trolltech.com/qpoint.html#y">y</a>() > selected->rect().center().y()) { h = QMAX(h + offset.y(), minimum); selectedPos.setY(QMAX(selected->y() + minimum, e->y())); } else // Курсор находится выше центра планеты { selected->setY(QMIN(selected->y() + offset.y(), selected->rect().bottom() - minimum + 1)); h = QMAX(h - offset.y(), minimum); selectedPos.<a href="http://doc.trolltech.com/qpoint.html#setY">setY</a>(QMIN(e->y(), selected->y())); } // Необходимо переместить планету if(cursor == ArrowCursor) { selected-><a href="http://doc.trolltech.com/qcanvasitem.html#moveBy">moveBy</a>(offset.x(), offset.y()); selectedPos = e->pos(); } else selected->scale(w, h); // Осуществляем масштабирование планеты updateInformation(); // Обновляем информацию о планете canvas()-><a href="http://doc.trolltech.com/qcanvas.html#update">update</a>(); // Обновляем содержимое QCanvas } }
void ScaleItem::<a href="http://doc.trolltech.com/qcanvasrectangle.html#drawShape">drawShape</a>(<a href="http://doc.trolltech.com/qpainter.html">QPainter</a>& p) { // Прорисовка масштабированного изображения p.<a href="http://doc.trolltech.com/qpainter.html#drawImage">drawImage</a>(x(), y(), scaled, 0, 0, width(), height()); if(isSelected()) p.<a href="http://doc.trolltech.com/qpainter.html#drawRect">drawRect</a>(rect()); // Прорисовка размерной рамки } void ScaleItem::scale(int w, int h) { // Создание масштабированного изображения if(scaling == Simple) // Используется быстрое масштабирование scaled = image.scale(w, h, mode); else // Используется масштабирование со сглаживанием scaled = image.smoothScale(w, h, mode); <a href="http://doc.trolltech.com/qcanvasrectangle.html#setSize">setSize</a>(w, h); <a href="http://doc.trolltech.com/qcanvasitem.html#update">update</a>(); } bool ScaleItem::hit(const QPoint& p) const { // Проверка прозрачности в данной точке изображения int dx = p.x() - (int)x(); int dy = p.y() - (int)y(); if(!scaled.<a href="http://doc.trolltech.com/qimage.html#valid">valid</a>(dx, dy)) return false; return <a href="http://doc.trolltech.com/qcolor.html#qAlpha">qAlpha</a>(scaled.<a href="http://doc.trolltech.com/qimage.html#pixel">pixel</a>(dx, dy)); } void ScaleItem::adjust() { // Сокращение размеров объекта до размеров изображения планеты setSize(QMIN(width(), scaled.<a href="http://doc.trolltech.com/qimage.html#width">width</a>()), QMIN(height(), scaled.<a href="http://doc.trolltech.com/qimage.html#height">height</a>())); // Выравнивание планеты по границам области QCanvas int xmax = canvas()-><a href="http://doc.trolltech.com/qcanvas.html#width">width</a>() - width(); int ymax = canvas()-><a href="http://doc.trolltech.com/qcanvas.html#height">height</a>() - height(); if(x() > xmax) setX(xmax); if(y() > ymax) setY(ymax); if(x() < 0) setX(0); if(y() < 0) setY(0); }