I create my scene in my dialog.cpp and draw some QGraphicsItem's in my scene.cpp. When I add my QTimer to my dialog.cpp it makes whenever I move the cursor over the scene to crash.
dialog.cpp
#include "dialog.h"
#include "scene.h"
#include "ui_dialog.h"
#include "instructions.h"
#include "settings.h"
#include "highscore.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
// Create and configure scene
scene = new Scene;
scene->setBackgroundBrush(Qt::black);
scene->setItemIndexMethod(QGraphicsScene::NoIndex);
ui->graphicsView->setScene(scene);
scene->setSceneRect(-200, -150, 400, 300);
ui->graphicsView->setMouseTracking(true);
QPixmap tankbase1(":/images/tankbase.jpg");
ui->tankbaseplay1->setPixmap(tankbase1);
//\/\/\/This is my problem. And not sure why\/\/\/\/\/\/
// timer = new QTimer(this);
// QObject::connect(timer, SIGNAL(timeout()), scene, SLOT(advance()));
// timer->start(10);
}
Dialog::~Dialog()
{
delete ui;
}
//void Dialog::shoot()
//{
//}
void Dialog::on_startButton_clicked()
{
ui->settingsButton->hide();
ui->titlescreen->hide();
ui->highscoreButton->hide();
ui->instructionButton->hide();
ui->startButton->hide();
QGraphicsTextItem *FirstP;
QString P1 = "Player1";
FirstP = scene->addText(P1);
FirstP->setFont(QFont("Nimbus Mono L", 12,QFont::Bold));
FirstP->setDefaultTextColor(Qt::white);
FirstP->setPos(-300, -220);
QGraphicsTextItem *SecondP;
QString P2 = "Player2";
SecondP = scene->addText(P2);
SecondP->setFont(QFont("Nimbus Mono L", 12,QFont::Bold));
SecondP->setDefaultTextColor(Qt::white);
SecondP->setPos(230, -220);
}
void Dialog::on_instructionButton_clicked()
{
Instructions intDialog;
intDialog.setModal(true);
intDialog.exec();
}
void Dialog::on_settingsButton_clicked()
{
settings intDialog;
intDialog.setModal(true);
intDialog.exec();
}
void Dialog::on_highscoreButton_clicked()
{
highscore intDialog;
intDialog.setModal(true);
intDialog.exec();
}
scene.cpp
#include "scene.h"
#include <QGraphicsEllipseItem>
#include <QGraphicsLineItem>
#include <QGraphicsSceneMouseEvent>
#include <QTimer>
#include "qmath.h"
#include <math.h>
class GraphicsCircle : public QGraphicsEllipseItem
// class for the fire bullets
{
public:
GraphicsCircle(qreal dirx, qreal diry)
: m_Speed(3)
, m_DirX(dirx)
, m_DirY(diry)
{
setRect(-3.0,-3.0,8.0,8.0);
setPos(-195, 130);
QRadialGradient rGrad( 0.0, 0.0, 20.0, 0.0, 0.0);
rGrad.setColorAt(0.0, QColor(255,255,255));
rGrad.setColorAt(0.7, QColor(255,255,225));
rGrad.setColorAt(1.0, QColor(255,0,0,0));
setBrush(QBrush(rGrad) );
setPen(QPen(Qt::NoPen));
}
virtual ~GraphicsCircle() {}
void advance(int phase)
{
if(!phase) return;
setPos(x()+m_Speed*m_DirX, y()+m_Speed*m_DirY);
}
private:
qreal m_Speed;
qreal m_DirX;
qreal m_DirY;
};
Scene::Scene() : QGraphicsScene()
{
// added the lines below to setup an item, pointing in the positive x direction
int x1 = 0;
int y1 = 0;
cannon = new QGraphicsLineItem(x1, y1, x1 + 50, y1);
cannon->setPen(QPen(Qt::white, 6));
this->addItem(cannon);
cannon->setPos(-195, 130);
//Create bullets
m_FireTimer= new QTimer();
QObject::connect(m_FireTimer, SIGNAL(timeout()), this, SLOT(fire()));
}
void Scene::mousePressEvent(QGraphicsSceneMouseEvent *e)
{
m_FireTarget = e->scenePos();
m_FireTimer->start();
QGraphicsScene::mousePressEvent(e);
}
void Scene::mouseMoveEvent(QGraphicsSceneMouseEvent *e)
{
// emit mouseMoving(e->scenePos());
// FirstPlayer->setPos(e->scenePos());
// qAtan2(cannon->pos(), e->scenePos());
m_FireTarget = e->scenePos();
QGraphicsScene::mouseMoveEvent(e);
QLineF arm(cannon->pos(), e->scenePos());
cannon->setRotation(360 - arm.angle());
}
void Scene::mouseReleaseEvent ( QGraphicsSceneMouseEvent * e )
{
m_FireTimer->stop();
QGraphicsScene::mouseReleaseEvent(e);
}
void Scene::fire()
// creates a fire bullet
// the bullet will move in the direction of the mouse cursor
// the trajectory is sligthly perturbated by a random small angle
{
qreal dirx = m_FireTarget.x()-195;
qreal diry = m_FireTarget.y()-195;
qreal length = sqrt(dirx*dirx+diry*diry);
if (length!=0)
{
// normalized direction vector
qreal invLength= 1.0/length;
dirx *= invLength;
diry *= invLength;
// creating an angle perturbation of +/- 3°
qreal alphaPerturbation = static_cast<qreal>(qrand()%6-3) * M_PI / 180.0;
qreal xPerturbation = cos(alphaPerturbation);
qreal yPerturbation = sin(alphaPerturbation);
dirx = dirx*xPerturbation - diry*yPerturbation;
diry = diry*xPerturbation + dirx*yPerturbation;
GraphicsCircle * circle = new GraphicsCircle(dirx, diry);
addItem(circle);
}
}
void Scene::advance()
{
// first remove the pellet out of the sceneRect
for (int i=0; i<items().count(); ++i)
{
QGraphicsItem * item = items().at(i);
qreal x= item->x();
qreal y= item->y();
qreal sx=sceneRect().width();
qreal sy= sceneRect().height();
if ( (x < 0.0) || (y < 0.0) || (x > sx) || (y > sy))
{
removeItem(item);
delete item;
}
}
QGraphicsScene::advance();
}
When I run the code without the QTimer code in dialog.cpp it runs and my QGraphicsItems are displayed and move accordingly. When I add the QTimer the QGraphicsItem disappears. Completely lost to what the issue is.
Also I have taken the scene code and ran it separately and it works. The only difference is the scene and QTimer is created in main.cpp.
Help greatly needed!!!!!
You are iterating through a list of items while you are deleting items in the list. That sounds like trouble.
http://qt-project.org/doc/qt-5/qrect.html#intersects
http://qt-project.org/doc/qt-5/qgraphicsitem.html#boundingRect
http://qt-project.org/doc/qt-5/qrectf.html#contains
I think this might be a little cleaner in your advance() function.
QList <QGraphicsItem *> itemsToRemove;
foreach( QGraphicsItem * item, this->items() )
{
if( !this->sceneRect().intersects(item->boundingRect()) )
{
// The item is no longer in the scene rect, get ready to delete it
itemsToRemove.append(item);
}
}
foreach( QGraphicsItem * item, itemsToRemove )
{
this->removeItem(item);
delete(item);
}
Also reading the description of QGraphicsScene,
http://qt-project.org/doc/qt-5/qgraphicsscene.html#details
there a number of helper methods that can make finding items in an area or one item colliding with another, much easier.
Hope that helps.
Related
I am originally loading image in QGraphicsView and using this method for basic zoom out and zoom in functionality.
However, I am unable to retrieve actual image pixel position using mapToScene functionality in eventFilter function of Graphics_view_zoom class. The below code produces behaviour exactly as windows photo viewer zooming only selected region.
MapToScene() returns same Point as mouse event position.
Here is the class which deals with zooming.
#include "Graphics_view_zoom.h"
#include <QMouseEvent>
#include <QApplication>
#include <QScrollBar>
#include <qmath.h>
Graphics_view_zoom::Graphics_view_zoom(QGraphicsView* view)
: QObject(view), _view(view)
{
_view->viewport()->installEventFilter(this);
_view->setMouseTracking(true);
_modifiers = Qt::ControlModifier;
_zoom_factor_base = 1.0015;
}
void Graphics_view_zoom::gentle_zoom(double factor) {
_view->scale(factor, factor);
_view->centerOn(target_scene_pos);
QPointF delta_viewport_pos = target_viewport_pos - QPointF(_view->viewport()->width() / 2.0,
_view->viewport()->height() / 2.0);
QPointF viewport_center = _view->mapFromScene(target_scene_pos) - delta_viewport_pos;
_view->centerOn(_view->mapToScene(viewport_center.toPoint()));
emit zoomed();
}
void Graphics_view_zoom::set_modifiers(Qt::KeyboardModifiers modifiers) {
_modifiers = modifiers;
}
void Graphics_view_zoom::set_zoom_factor_base(double value) {
_zoom_factor_base = value;
}
bool Graphics_view_zoom::eventFilter(QObject *object, QEvent *event) {
if (event->type() == QEvent::MouseMove) {
QMouseEvent* mouse_event = static_cast<QMouseEvent*>(event);
QPointF delta = target_viewport_pos - mouse_event->pos();
// Here I want to get absolute image coordinates
if (qAbs(delta.x()) > 5 || qAbs(delta.y()) > 5) {
target_viewport_pos = mouse_event->pos();
target_scene_pos = _view->mapToScene(mouse_event->pos());
}
} else if (event->type() == QEvent::Wheel) {
QWheelEvent* wheel_event = static_cast<QWheelEvent*>(event);
if (QApplication::keyboardModifiers() == _modifiers) {
if (wheel_event->orientation() == Qt::Vertical) {
double angle = wheel_event->angleDelta().y();
double factor = qPow(_zoom_factor_base, angle);
gentle_zoom(factor);
return true;
}
}
}
Q_UNUSED(object)
return false;
In mainwindow.cpp,
I am creating object of this class and loading an image as below:
m_GraphicsScene = new QGraphicsScene();
pixmapItem = new QGraphicsPixmapItem();
m_GraphicsScene->addItem(multiview[i].pixmapItem);
view_wrapper = new Graphics_view_zoom(ui->GraphicsView);
ui->GraphicsView->setScene(multiview[i].m_GraphicsScene);
pixmapItem->setPixmap(QPixmap::fromImage("img.jpg"));
multiview[view].m_GraphicsView->fitInView(QRectF(0,0,640,320),Qt::KeepAspectRatio);
Can anyone help with how do I achieve this ?
Keep in mind that the scaling you use only scales the scene, not the items. Given this, the position of the pixel can be obtained, so the algorithm is:
Obtain the mouse position with respect to the QGraphicsView
Transform that position with respect to the scene using mapToScene
Convert the coordinate with respect to the scene in relation to the item using mapFromScene of the QGraphicsItem.
Considering the above, I have implemented the following example:
#include <QtWidgets>
#include <random>
static QPixmap create_image(const QSize & size){
QImage image(size, QImage::Format_ARGB32);
image.fill(Qt::blue);
std::random_device rd;
std::mt19937_64 rng(rd());
std::uniform_int_distribution<int> uni(0, 255);
for(int i=0; i< image.width(); ++i)
for(int j=0; j < image.height(); ++j)
image.setPixelColor(QPoint(i, j), QColor(uni(rng), uni(rng), uni(rng)));
return QPixmap::fromImage(image);
}
class GraphicsView : public QGraphicsView
{
Q_OBJECT
Q_PROPERTY(Qt::KeyboardModifiers modifiers READ modifiers WRITE setModifiers)
public:
GraphicsView(QWidget *parent=nullptr): QGraphicsView(parent){
setScene(new QGraphicsScene);
setModifiers(Qt::ControlModifier);
auto item = scene()->addPixmap(create_image(QSize(100, 100)));
item->setShapeMode(QGraphicsPixmapItem::BoundingRectShape);
item->setPos(40, 40);
fitInView(QRectF(0, 0, 640, 320),Qt::KeepAspectRatio);
resize(640, 480);
}
void setModifiers(const Qt::KeyboardModifiers &modifiers){
m_modifiers = modifiers;
}
Qt::KeyboardModifiers modifiers() const{
return m_modifiers;
}
signals:
void pixelChanged(const QPoint &);
protected:
void mousePressEvent(QMouseEvent *event) override{
if(QGraphicsPixmapItem *item = qgraphicsitem_cast<QGraphicsPixmapItem *>(itemAt(event->pos()))){
QPointF p = item->mapFromScene(mapToScene(event->pos()));
QPoint pixel_pos = p.toPoint();
emit pixelChanged(pixel_pos);
}
QGraphicsView::mousePressEvent(event);
}
void wheelEvent(QWheelEvent *event) override{
if(event->modifiers() == m_modifiers){
double angle = event->orientation() == Qt::Vertical ? event->angleDelta().y(): event->angleDelta().x();
double factor = qPow(base, angle);
applyZoom(factor, event->pos());
}
}
private:
void applyZoom(double factor, const QPoint & fixedViewPos)
{
QPointF fixedScenePos = mapToScene(fixedViewPos);
centerOn(fixedScenePos);
scale(factor, factor);
QPointF delta = mapToScene(fixedViewPos) - mapToScene(viewport()->rect().center());
centerOn(fixedScenePos - delta);
}
Qt::KeyboardModifiers m_modifiers;
const double base = 1.0015;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
GraphicsView *view = new GraphicsView;
QLabel *label = new QLabel;
QObject::connect(view, &GraphicsView::pixelChanged, label, [label](const QPoint & p){
label->setText(QString("(%1, %2)").arg(p.x()).arg(p.y()));
});
label->setAlignment(Qt::AlignCenter);
QWidget w;
QVBoxLayout *lay = new QVBoxLayout(&w);
lay->addWidget(view);
lay->addWidget(label);
w.show();
return a.exec();
}
#include "main.moc"
It may be better to use a custom graphics scene subclassed from QGraphicsScene as this makes extracting the necessary coordinates much simpler. The only snag is you have to have the QGraphicsPixmapItem::pos available in the custom QGraphicsScene class - I have included a full working example which uses Graphics_view_zoom.h and Graphics_view_zoom.cpp from the linked question. The position of the QGraphicsPixmapItem is passed to a member of the QGraphicsScene subclass, Frame, in order to make the necessary correction.
#include <QPixmap>
#include <QGraphicsPixmapItem>
#include <QGraphicsTextItem>
#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsView>
#include <qfont.h>
#include "Graphics_view_zoom.h"
class Frame : public QGraphicsScene {
Q_OBJECT
public:
QGraphicsTextItem * coords;
QPointF pic_tl;
Frame::Frame(QWidget* parent)
: QGraphicsScene(parent) {
coords = new QGraphicsTextItem();
coords->setZValue(1);
coords->setFlag(QGraphicsItem::ItemIgnoresTransformations, true);
addItem(coords);
}
void Frame::tl(QPointF p) {
pic_tl = p;
}
protected:
void Frame::mouseMoveEvent(QGraphicsSceneMouseEvent* event) {
QPointF pos = event->scenePos();
coords->setPlainText("(" + QString("%1").arg(int(pos.x() - pic_tl.x())) + ", "
+ QString("%1").arg(int(pos.y() - pic_tl.y())) + ")");
coords->setPos(pos);
coords->adjustSize();
}
};
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
QMainWindow* main = new QMainWindow();
QGraphicsView* GraphicsView = new QGraphicsView(main);
Graphics_view_zoom* view_wrapper = new Graphics_view_zoom(GraphicsView);
Frame* frame = new Frame(main);
QGraphicsPixmapItem* pixmapItem = new QGraphicsPixmapItem();
frame->addItem(pixmapItem);
GraphicsView->setScene(frame);
// Loads a 497x326 pixel test image
pixmapItem->setPixmap(QPixmap(":/StackOverflow/test"));
// small offset to ensure it works for pictures which are not at
// (0,0). Larger offsets produce the same result but require manual
// adjustments of the view, I have neglected those for brevity as
// they are not in the scope of the question.
pixmapItem->setPos(-20, 20);
frame->tl(pixmapItem->pos());
GraphicsView->fitInView(QRectF(0, 0, 640, 320), Qt::KeepAspectRatio);
GraphicsView->centerOn(pixmapItem->pos());
main->resize(1920, 1080);
main->show();
GraphicsView->resize(main->width(), main->height());
return a.exec();
}
This will display the image coordinates of the pixel under the mouse relative to (0,0) at the top left corner.
The mouse is not visible in these screenshots but in the first it is in exactly the upper left corner and the second it is in exactly the lower right corner. If these coordinates are needed inside the Graphics_view_zoom object then you simply have to scope the Frame instance appropriately, or pass the value as needed.
Note - the exact coordinates displayed may not precisely represent the position of the mouse in this example since they are cast to ints for demonstration, but the floating point values can be easily accessed since QGraphicsSceneMoveEvent::scenePos() returns a QPointF. Additionally, note that in running this demonstration there may be some (hopefully very small) variation on where the mouse appears to be relative to it's 'actual' position - I recommend using Qt::CrossCursor to allay this. For example on my system the default cursor is off by about a pixel for certain areas on my smaller display, this is also affected by the zoom level - higher zoom will produce more accurate results, less zoom will be less accurate.
I am building a map widget (something like the google map) using Qt, basically I used a QGraphicsScene to display the map tile.
Now I want to add a scale ruler to the widget just like the one in google map.
Any suggestions about how could I realize this?
Take a look at this example:
Structure your code base as following:
Write a class inheriting descendants class of QAbstractScrollArea (As example QGraphicsView, QMdiArea, QPlainTextEdit, QScrollArea, QTextEdit, QColumnView, QHeaderView, QListView, QTableView, QTreeView etc.)
In the constructor of your class call setViewportMargins and set the margins of left/top/right/bottom areas length.
Create a QGridLayout and adds your custom Ruler/Scale in the layout.
Set this layout calling setLayout
Example:
setViewportMargins(RULER_BREADTH,RULER_BREADTH,0,0);
QGridLayout* gridLayout = new QGridLayout();
gridLayout->setSpacing(0);
gridLayout->setMargin(0);
mHorzRuler = new QDRuler(QDRuler::Horizontal);
mVertRuler = new QDRuler(QDRuler::Vertical);
QWidget* fake = new QWidget();
fake->setBackgroundRole(QPalette::Window);
fake->setFixedSize(RULER_BREADTH,RULER_BREADTH);
gridLayout->addWidget(fake,0,0);
gridLayout->addWidget(mHorzRuler,0,1);
gridLayout->addWidget(mVertRuler,1,0);
gridLayout->addWidget(this->viewport(),1,1);
this->setLayout(gridLayout);
QDRuler: The ruler class
#define RULER_BREADTH 20
class QDRuler : public QWidget
{
Q_OBJECT
Q_ENUMS(RulerType)
Q_PROPERTY(qreal origin READ origin WRITE setOrigin)
Q_PROPERTY(qreal rulerUnit READ rulerUnit WRITE setRulerUnit)
Q_PROPERTY(qreal rulerZoom READ rulerZoom WRITE setRulerZoom)
public:
enum RulerType { Horizontal, Vertical };
QDRuler(QDRuler::RulerType rulerType, QWidget* parent)
: QWidget(parent), mRulerType(rulerType), mOrigin(0.), mRulerUnit(1.),
mRulerZoom(1.), mMouseTracking(false), mDrawText(false)
{
setMouseTracking(true);
QFont txtFont("Goudy Old Style", 5,20);
txtFont.setStyleHint(QFont::TypeWriter,QFont::PreferOutline);
setFont(txtFont);
}
QSize minimumSizeHint() const
{
return QSize(RULER_BREADTH,RULER_BREADTH);
}
QDRuler::RulerType rulerType() const
{
return mRulerType;
}
qreal origin() const
{
return mOrigin;
}
qreal rulerUnit() const
{
return mRulerUnit;
}
qreal rulerZoom() const
{
return mRulerZoom;
}
public slots:
void setOrigin(const qreal origin)
{
if (mOrigin != origin)
{
mOrigin = origin;
update();
}
}
void setRulerUnit(const qreal rulerUnit)
{
if (mRulerUnit != rulerUnit)
{
mRulerUnit = rulerUnit;
update();
}
}
void setRulerZoom(const qreal rulerZoom)
{
if (mRulerZoom != rulerZoom)
{
mRulerZoom = rulerZoom;
update();
}
}
void setCursorPos(const QPoint cursorPos)
{
mCursorPos = this->mapFromGlobal(cursorPos);
mCursorPos += QPoint(RULER_BREADTH,RULER_BREADTH);
update();
}
void setMouseTrack(const bool track)
{
if (mMouseTracking != track)
{
mMouseTracking = track;
update();
}
}
protected:
void mouseMoveEvent(QMouseEvent* event)
{
mCursorPos = event->pos();
update();
QWidget::mouseMoveEvent(event);
}
void paintEvent(QPaintEvent* event)
{
QPainter painter(this);
painter.setRenderHints(QPainter::TextAntialiasing | QPainter::HighQualityAntialiasing);
QPen pen(Qt::black,0); // zero width pen is cosmetic pen
//pen.setCosmetic(true);
painter.setPen(pen);
// We want to work with floating point, so we are considering
// the rect as QRectF
QRectF rulerRect = this->rect();
// at first fill the rect
//painter.fillRect(rulerRect,QColor(220,200,180));
painter.fillRect(rulerRect,QColor(236,233,216));
// drawing a scale of 25
drawAScaleMeter(&painter,rulerRect,25,(Horizontal == mRulerType ? rulerRect.height()
: rulerRect.width())/2);
// drawing a scale of 50
drawAScaleMeter(&painter,rulerRect,50,(Horizontal == mRulerType ? rulerRect.height()
: rulerRect.width())/4);
// drawing a scale of 100
mDrawText = true;
drawAScaleMeter(&painter,rulerRect,100,0);
mDrawText = false;
// drawing the current mouse position indicator
painter.setOpacity(0.4);
drawMousePosTick(&painter);
painter.setOpacity(1.0);
// drawing no man's land between the ruler & view
QPointF starPt = Horizontal == mRulerType ? rulerRect.bottomLeft()
: rulerRect.topRight();
QPointF endPt = Horizontal == mRulerType ? rulerRect.bottomRight()
: rulerRect.bottomRight();
painter.setPen(QPen(Qt::black,2));
painter.drawLine(starPt,endPt);
}
private:
void drawAScaleMeter(QPainter* painter, QRectF rulerRect, qreal scaleMeter, qreal startPositoin)
{
// Flagging whether we are horizontal or vertical only to reduce
// to cheching many times
bool isHorzRuler = Horizontal == mRulerType;
scaleMeter = scaleMeter * mRulerUnit * mRulerZoom;
// Ruler rectangle starting mark
qreal rulerStartMark = isHorzRuler ? rulerRect.left() : rulerRect.top();
// Ruler rectangle ending mark
qreal rulerEndMark = isHorzRuler ? rulerRect.right() : rulerRect.bottom();
// Condition A # If origin point is between the start & end mard,
//we have to draw both from origin to left mark & origin to right mark.
// Condition B # If origin point is left of the start mark, we have to draw
// from origin to end mark.
// Condition C # If origin point is right of the end mark, we have to draw
// from origin to start mark.
if (mOrigin >= rulerStartMark && mOrigin <= rulerEndMark)
{
drawFromOriginTo(painter, rulerRect, mOrigin, rulerEndMark, 0, scaleMeter, startPositoin);
drawFromOriginTo(painter, rulerRect, mOrigin, rulerStartMark, 0, -scaleMeter, startPositoin);
}
else if (mOrigin < rulerStartMark)
{
int tickNo = int((rulerStartMark - mOrigin) / scaleMeter);
drawFromOriginTo(painter, rulerRect, mOrigin + scaleMeter * tickNo,
rulerEndMark, tickNo, scaleMeter, startPositoin);
}
else if (mOrigin > rulerEndMark)
{
int tickNo = int((mOrigin - rulerEndMark) / scaleMeter);
drawFromOriginTo(painter, rulerRect, mOrigin - scaleMeter * tickNo,
rulerStartMark, tickNo, -scaleMeter, startPositoin);
}
}
void drawFromOriginTo(QPainter* painter, QRectF rulerRect, qreal startMark, qreal endMark, int startTickNo, qreal step, qreal startPosition)
{
bool isHorzRuler = Horizontal == mRulerType;
int iterate = 0;
for (qreal current = startMark;
(step < 0 ? current >= endMark : current <= endMark); current += step)
{
qreal x1 = isHorzRuler ? current : rulerRect.left() + startPosition;
qreal y1 = isHorzRuler ? rulerRect.top() + startPosition : current;
qreal x2 = isHorzRuler ? current : rulerRect.right();
qreal y2 = isHorzRuler ? rulerRect.bottom() : current;
painter->drawLine(QLineF(x1,y1,x2,y2));
if (mDrawText)
{
QPainterPath txtPath;
txtPath.addText(x1 + 1,y1 + (isHorzRuler ? 7 : -2),this->font(),QString::number(qAbs(int(step) * startTickNo++)));
painter->drawPath(txtPath);
iterate++;
}
}
}
void drawMousePosTick(QPainter* painter)
{
if (mMouseTracking)
{
QPoint starPt = mCursorPos;
QPoint endPt;
if (Horizontal == mRulerType)
{
starPt.setY(this->rect().top());
endPt.setX(starPt.x());
endPt.setY(this->rect().bottom());
}
else
{
starPt.setX(this->rect().left());
endPt.setX(this->rect().right());
endPt.setY(starPt.y());
}
painter->drawLine(starPt,endPt);
}
}
private:
RulerType mRulerType;
qreal mOrigin;
qreal mRulerUnit;
qreal mRulerZoom;
QPoint mCursorPos;
bool mMouseTracking;
bool mDrawText;
};
Is there a way to use Qt library to paint parabola or any other polynomial?
I tried using QPainter but there is no such option there.
Thank you
If you want to use QPainter then you should to use an QImage or QPixmap to display or save your output.
This is a simple code to show how it's done in a Qt Widgets Application.
This will be the MainWindow.h file.
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QImage>
#include <QPainter>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
void drawMyFunction(qreal xMin,qreal xMax);
qreal myFunction(qreal x);
QImage * pic;
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
and this will be your MainWindow.cpp:
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
drawMyFunction(-3,3);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::drawMyFunction(qreal xMin, qreal xMax)
{
qreal step = (xMax - xMin) / 1000;
QPainterPath painterPath;
QSize picSize(300,300);
for(qreal x = xMin ; x <= xMax ; x = x + step)
{
if(x == xMin)
{
painterPath.moveTo(x,myFunction(x));
}
else
{
painterPath.lineTo(x,myFunction(x));
}
}
// Scaling and centering in the center of image
qreal xScaling = picSize.width() / painterPath.boundingRect().width();
qreal yScaling = picSize.height() / painterPath.boundingRect().height();
qreal scale;
if(xScaling > yScaling)
scale = yScaling;
else
scale = xScaling;
for(int i = 0 ; i < painterPath.elementCount() ; i++ )
{
painterPath.setElementPositionAt(i,painterPath.elementAt(i).x*scale + picSize.width()/2,-painterPath.elementAt(i).y*scale + picSize.height()/2);
}
// Drawing to the image
pic = new QImage(picSize,QImage::Format_RGB32);
pic->fill(Qt::white);
QPainter picPainter(pic);
QPen myPen;
myPen.setColor(Qt::black);
myPen.setWidth(10);
picPainter.drawPath(painterPath);
ui->label->setPixmap(QPixmap::fromImage(*pic)); // label is an added label to the ui
// you can also do this instead of just showing the picture
// pic->save("myImage.bmp");
}
qreal MainWindow::myFunction(qreal x)
{
return x*x; // write any function you want here
}
Have you tried QPainterPath? It has QPainterPath::quadTo and QPainterPath::cubicTo member functions that could be of help.
compute discrete points from your polynomial function and use QPainter::drawLines to draw the figure.
For instance, y = x^2:
float xmin = 0;
float xmax = 2;
float step = 0.1; // experiment with values
QVector<QPointF> points;
float x = xmin;
while(x < xmax)
{
float y = x^x; //f(x)
lines.push_back(QPointF(x,y));
x+= step;
}
painter.drawLines(points);
Because the top corner in qt coordonates is (0,0) You need to make a geometric translation for x and y after computing y.
EDIT
I am creating all objects initially when the program is started in my dialog.cpp and storing all QPixmaps in an array then picking a random one from them all. That random QPixmap I want to pass to my maintargets class and draw in the scene (which is also created in the dialog.cpp).
// dialog.cpp
#include "dialog.h"
#include "scene.h"
#include "ui_dialog.h"
#include "instructions.h"
#include "settings.h"
#include "highscore.h"
#include "maintargets.h"
#include <stdlib.h>
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
// Create and configure scene
scene = new Scene;
scene->setBackgroundBrush(Qt::black);
scene->setItemIndexMethod(QGraphicsScene::NoIndex);
ui->graphicsView->setScene(scene);
scene->setSceneRect(-200, -150, 400, 300);
ui->graphicsView->setMouseTracking(true);
QPixmap tankbase1(":/images/tankbase.jpg");
ui->tankbaseplay1->setPixmap(tankbase1);
//Store targets in array and random generator
index = 0;
main_targets[0] = QPixmap(":images/darkbluelogo.jpg)");
main_targets[1] = QPixmap(":images/graylogo.jpg");
main_targets[2] = QPixmap(":images/lightbluelogo.jpg");
main_targets[3] = QPixmap(":images/limE.jpg");
main_targets[4] = QPixmap(":images/pink.jpg");
main_targets[5] = QPixmap(":images/purple.jpg");
main_targets[6] = QPixmap(":images/redlogo.jpg");
main_targets[7] = QPixmap(":images/yellow.jpg");
main_targets[8] = QPixmap(":images/brown.jpg");
index = qrand((index % 9) + 1);
//scene->addItem(main_targets[index]);
//Timer for scene advancement
QTimer *timer = new QTimer();
QObject::connect(timer, SIGNAL(timeout()), scene, SLOT(advance()));
timer->start(100);
}
Dialog::~Dialog()
{
delete ui;
}
//maintargets.h
#ifndef MAINTARGETS_H
#define MAINTARGETS_H
#include "dialog.h"
#include <QGraphicsItem>
#include <QGraphicsScene>
#include <QPainter>
#include <QRect>
class MainTargets : public QGraphicsScene
{
public:
MainTargets();
QRectF boundingRect() const;
QPainterPath shape() const;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
protected:
void advance(int step);
private:
qreal dx, dy;
qreal x, y;
qreal w, h;
};
#endif // MAINTARGETS_H
//maintargets.cpp
#include "maintargets.h"
MainTargets::MainTargets()
{
dx = -0.005;
dy = 0.0;
x = 1.5;
y = 0.0;
w = 100.0;
h = 70.0;
}
QRectF MainTargets::boundingRect() const
{
qreal shift = 1;
return QRectF(-w/2 -shift, - h/2
- shift, w + shift, h + shift);
}
QPainterPath MainTargets::shape() const
{
QPainterPath path;
path.addRect(boundingRect());
return path;
}
void MainTargets::paint(QPainter *painter,
const QStyleOptionGraphicsItem *option,
QWidget *widget)
{
painter->drawPixmap(-w/2, -h/2, main_targets[index]);
}
void MainTargets::advance(int step)
{
if(step == 0) return;
x = x + dx;
y = y + dy;
setPos(mapToParent(x, y));
}
After it is drawn it moves in x-direction.
Your question is very broad unfortunately, and the exact solution depends on your use case. I will mention a few different solutions for your issue, and then you can take your peek, but please read the documentation about how to ask questions on Stack Overflow because your question is very low-quality at the moment.
1) If your operation is supposed to build the other class, you can pass it as a constructor argument if it is not against your design for the construction of this class.
2) You can use a void setPixmap(QPixmap); setter if it is possible to extend your class this way, and you have access to an instance of the object in that method.
3) You can use a proxy class dealing with all this, if that is all you have access in your operation getting the random index.
4) You can set a static variable in the same source file if the other class needs this in the same source file. This is not a good idea in general, but I saw this happening, too.
5) You can set a global variable that the other class method is using. Again, this is a very bad practice.
6) You can just pass this QPixmap as an argument to the drawing function directly in the other class.
7) You can pass this to a proxy class object that will pass it to the drawing method of the other class.
8) If the other class is in a different process, you have get at least another many ways to pass it through.
As I said, it really depends on your scenario, and there is loads of ways to do it. That being said, I will remove this answer later because this question is too broad, but I wished to show you how broad what you are asking is.
Simple pass by reference was what I was lost on. This explains the process of what was needed to get the QPixmap variable in the maintargets class.
//dialog.h
private:
Ui::Dialog *ui;
//add a pointer
MainTargets* pmap;
//dialog.cpp
#include "dialog.h"
#include "ui_dialog.h"
#include "maintargets.h"
#include <stdlib.h>
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
// Create and configure scene
scene = new Scene;
scene->setBackgroundBrush(Qt::black);
scene->setItemIndexMethod(QGraphicsScene::NoIndex);
ui->graphicsView->setScene(scene);
scene->setSceneRect(-200, -150, 400, 300);
ui->graphicsView->setMouseTracking(true);
QPixmap tankbase1(":/images/tankbase.jpg");
ui->tankbaseplay1->setPixmap(tankbase1);
//Store targets in array and random generator
index = 1;
main_targets[0] = QPixmap(":images/darkbluelogo.jpg)");
main_targets[1] = QPixmap(":images/graylogo.jpg");
main_targets[2] = QPixmap(":images/lightbluelogo.jpg");
main_targets[3] = QPixmap(":images/lime.jpg");
main_targets[4] = QPixmap(":images/pink.jpg");
main_targets[5] = QPixmap(":images/purple.jpg");
main_targets[6] = QPixmap(":images/redlogo.jpg");
main_targets[7] = QPixmap(":images/yellow.jpg");
main_targets[8] = QPixmap(":images/brown.jpg");
//Timer for scene advancement
QTimer *timer = new QTimer();
QObject::connect(timer, SIGNAL(timeout()), scene, SLOT(advance()));
timer->start(10);
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::on_startButton_clicked()
{
ui->settingsButton->hide();
ui->titlescreen->hide();
ui->highscoreButton->hide();
ui->instructionButton->hide();
ui->startButton->hide();
QGraphicsTextItem *FirstP;
QString P1 = "Player1";
FirstP = scene->addText(P1);
FirstP->setFont(QFont("Nimbus Mono L", 12,QFont::Bold));
FirstP->setDefaultTextColor(Qt::white);
FirstP->setPos(-300, -220);
QGraphicsTextItem *SecondP;
QString P2 = "Player2";
SecondP = scene->addText(P2);
SecondP->setFont(QFont("Nimbus Mono L", 12,QFont::Bold));
SecondP->setDefaultTextColor(Qt::white);
SecondP->setPos(230, -220);
//Initializes function
setPixmaps();
}
void Dialog::setPixmaps()
{
index = (qrand() % (9));
//Add a new MainTarget and set equal to new pointer created in header file
pmap = new MainTargets(main_targets[index]);
pmap->setPos(355,0);
scene->addItem(pmap);
}
//maintargets.h
private:
//Add a new QPixmap to header
QPixmap p;
//maintargets.cpp
Reference in QPixmap variable in constructor and set equal to newly created pointer
#include "maintargets.h"
MainTargets::MainTargets(QPixmap& nomTargets)
{
dx = -0.005;
dy = 0.0;
x = 0.0;
y = 0.0;
w = 100.0;
h = 70.0;
p = nomTargets;
}
QRectF MainTargets::boundingRect() const
{
qreal shift = 1;
return QRectF(-w/2 -shift, - h/2
- shift, w + shift, h + shift);
}
QPainterPath MainTargets::shape() const
{
QPainterPath path;
path.addRect(boundingRect());
return path;
}
void MainTargets::paint(QPainter *painter,
const QStyleOptionGraphicsItem *option,
QWidget *widget)
{
//Set that pointer variable as the source for the
//given drawPixmap memeber
painter->drawPixmap(-w/2, -h/2, p);
I have an image stored in a Qlabel in the scene. I want to get the Qlabel image to follow where ever the cursor moves when inside the scene. I've tried QGraphicsSceneMouseMove and haven't come close yet.
void scene::mouseMoveEvent(QGraphicsSceneMouseEvent /*mouseEvent*/)
{
QPointF P1 = ui->tankarmplay1->mapFromParent(QCursor.pos());
int x = P1.x();
int y = P1.y();
ui->tankarmplay1->setGeometry(x,y, 50, 50);
}
UPDATE: Added a QGraphicsLineItem that points at the mouse. This could be replaced by a full drawing of a turret of some sort using a QGraphicsItemGroup, and using the same rotation calculation.
The following links talks about a lot of the things you should be familiar with:
http://qt-project.org/doc/qt-5/graphicsview.html
http://qt-project.org/doc/qt-5/application-windows.html
void scene::mouseMoveEvent(QGraphicsSceneMouseEvent * e /*mouseEvent*/)
{
// QPointF P1 = (e->pos());
// int x = P1.x();
// int y = P1.y();
// ui->tankarmplay1->setGeometry(x, y, 50, 50);
ui->tankarmplay1->move((int) e->pos().x(), (int) e->pos().y());
}
http://qt-project.org/doc/qt-5/qgraphicsscenemouseevent.html#pos
I haven't personally used QCursor. I think it is a very round-about way of finding about the mouse when you have the mouse event's pos information handy. If you did you QCursor, you would probably need to use mapFromGlobal not mapFromParent.
http://qt-project.org/doc/qt-5/qcursor.html#details
http://qt-project.org/doc/qt-5/qcursor.html#pos
Here is something I wrote up before using the specific QGraphicsSceneMouseEvent methods.
To get those to work I had to use mapToScene() to get the coordinates to probably match.
How to draw a point (on mouseclick) on a QGraphicsScene?
In the pos property of QWidget, you modify it with move() usually. setGeometry also works, but you also end up referencing the width and height a lot.
http://qt-project.org/doc/qt-4.8/qwidget.html#pos-prop
http://qt-project.org/doc/qt-4.8/qwidget.html#mouseTracking-prop
UPDATE: Awesome example that shows mouse tracking used in the scene and outside the scene
Note, using a QGraphicsTextItem would probably be cleaner than using a QLabel + QGraphicsProxyWidget to move some text around in the scene.
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QFrame>
#include <QLabel>
#include <QPointF>
#include "mygraphicsscene.h"
#include <QGraphicsView>
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = 0);
~Widget();
qreal map(const qreal & x1, qreal x, const qreal & x2, const qreal & y1, const qreal & y2);
public slots:
void on_sceneMouseMove(QPointF);
private:
QLabel * m_label;
MyGraphicsScene * m_scene;
QGraphicsView * m_view;
QFrame * m_labelContainer;
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include <QGraphicsView>
#include "mygraphicsscene.h"
#include <QVBoxLayout>
#include <QLabel>
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
QVBoxLayout * vbox = new QVBoxLayout;
m_view = new QGraphicsView;
m_scene = new MyGraphicsScene;
m_view->setScene(m_scene);
m_view->setMouseTracking(true);
m_scene->setSceneRect(-300,-300, 600, 600);
m_view->fitInView(m_scene->sceneRect());
vbox->addWidget(m_view, 1);
m_labelContainer = new QFrame;
m_labelContainer->setFrameShape(QFrame::Box);
m_label = new QLabel("Tracking Label");
m_labelContainer->setFixedSize(300, 300);
m_label->setParent(m_labelContainer);
vbox->addWidget(m_labelContainer, 1);
QObject::connect(m_scene, SIGNAL(mouseMoved(QPointF)),
this, SLOT(on_sceneMouseMove(QPointF)));
this->setLayout(vbox);
}
void Widget::on_sceneMouseMove(QPointF pt)
{
QPointF pt2;
pt2.setX(map(m_scene->sceneRect().left(), pt.x(), m_scene->sceneRect().right(),
m_labelContainer->rect().left(), m_labelContainer->rect().right()));
pt2.setY(map(m_scene->sceneRect().top(), pt.y(), m_scene->sceneRect().bottom(),
m_labelContainer->rect().top(), m_labelContainer->rect().bottom()));
// qDebug() << pt << pt2 << m_scene->sceneRect() << m_labelContainer->rect();
m_label->move(pt2.x(), pt2.y());
// m_label->setGeometry(pt.x(), pt.y(),
// m_label->width(), m_label->height());
}
qreal Widget::map(const qreal & x1, qreal x, const qreal & x2, const qreal & y1, const qreal & y2)
{
if(x < x1)
x = x1;
if(x > x2)
x = x2;
return (x - x1) * (y2 - y1) / (x2 - x1) + y1;
}
Widget::~Widget()
{
}
main.cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
mygraphicsview.h
#ifndef MYGRAPHICSSCENE_H
#define MYGRAPHICSSCENE_H
#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsProxyWidget>
#include <QGraphicsLineItem> // Added this
class MyGraphicsScene : public QGraphicsScene
{
Q_OBJECT
public:
explicit MyGraphicsScene(QObject *parent = 0);
signals:
void mouseMoved(QPointF);
public slots:
void mouseMoveEvent(QGraphicsSceneMouseEvent * );
private:
QGraphicsProxyWidget * m_labelProxy;
QGraphicsLineItem * m_lineItem; // Added this
};
#endif // MYGRAPHICSSCENE_H
mygraphicsview.cpp
#include "mygraphicsscene.h"
#include <QDebug>
#include <QLabel>
#include <QVector2D>
#include <qmath.h>
#include <QLineF>
MyGraphicsScene::MyGraphicsScene(QObject *parent) :
QGraphicsScene(parent)
{
QLabel * label = new QLabel("Tracking Widget\n in Scene");
m_labelProxy = this->addWidget(label);
// added the lines below to setup an item, pointing in the positive x direction
int x1 = 0;
int y1 = 0;
m_lineItem = new QGraphicsLineItem(x1, y1, x1 + 20, y1);
// m_lineItem->setTransformOriginPoint(x1, y1);
this->addItem(m_lineItem);
m_lineItem->setPos(-100, -100);
}
void MyGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent * e)
{
// qDebug() << e->pos() << e->screenPos() << e->scenePos();
emit mouseMoved(e->scenePos());
m_labelProxy->setPos(e->scenePos());
// Added these lines below to calculate and set the rotation.
// QVector2D v;
// v.setX(e->scenePos().x() - m_lineItem->pos().x());
// v.setY(e->scenePos().y() - m_lineItem->pos().y());
// m_lineItem->setRotation(qAtan2(v.y(), v.x())*180./(3.1459));
QLineF line(m_lineItem->pos(), e->scenePos());
m_lineItem->setRotation(360 - line.angle());
}
Hope that helps.