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.
Related
I am working on Falling ball application,
About the application: the balls keep on falling at regular intervals, and there is a basket that catches the balls. If caught then we earn a point if not then lose one life.
For this application, I am using qt and CPP.
I have created one class Sprite with specifications regarding the initial position of the ball(x and y) and speed (dx and dy).
I am using one timer for the speed of the ball, but I need to another timer for ball creation at regular intervals. Could anyone help me with how to implement the ball creation at regular intervals?
sprite.h
--------
#ifndef SPRITE_H
#define SPRITE_H
#include <QTimer>
#include <QPainter>
#include <QWidget>
/**
* Header class for Sprite
*/
class Sprite : public QTimer
{
public:
Sprite(QWidget *parent);
//draw() to draw a sprite
void draw(QPainter &painter);
protected:
//This timerEvent will be called after certain time prescribed.
virtual void timerEvent(QTimerEvent *e) override;
int x;//position of sprite in x-direction.
int y;//position of sprite in y-direction.
int dx;//difference in x-direction position.
int dy;//difference in y-direction position.
int x1;//position of the basket in x-direction
int y1;//position of the basket in y-direction.
QWidget *parent;//parent class for all widgets.
};
#endif // SPRITE_H
sprite.cpp
-----------
#include "sprite.h"
#include <QDebug>
Sprite::Sprite(QWidget *parent):parent(parent)
{
QRect rct = parent->rect();
x = rand() % rct.width();//randomly initialize the x-position for the sprite.
y=rct.height()*0.05;//start position for the sprite is about 5% after the top of the menu bar.
dx = rand() % 10;//the speed is randomly set in x-direction.
// dy = rand() % 10;//the speed is randomly set in y-direction.
dy = 4;
x1=rct.width()/2;
y1 = rct.height()-80;
start(10);
}
void Sprite::draw(QPainter &painter)
{
qDebug() <<"Sprite::draw() called";
painter.drawEllipse(x, y, 15, 15);//ball
painter.drawRect( x1, y1, 80, 30);//basket
}
void Sprite::timerEvent(QTimerEvent *)
{
qDebug("timerEvent called");
QRect rct = parent->rect();
if ( x > rct.width() || x < 0)
dx *= -1;
if ( y > rct.height() || y < 0){}
x += dx;
y += dy;
parent->update();
}
mainwindow.cpp
-----------------
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QKeyEvent>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
qDebug() <<"mainWindow constructor";
QTimer timer;
QObject::connect(&timer, &QTimer::timeout, this,&MainWindow::onTimer);
timer.start(1000);
}
MainWindow::~MainWindow()
{
qDebug() <<"mainWindow destructor";
delete ui;
}
void MainWindow::onTimer()
{
std::cout << "Tick!-----------------------------------------------------------------------------" << std::endl;
}
void MainWindow::paintEvent(QPaintEvent *)
{
qDebug() <<"painEvent() called";
QPainter painter(this);
painter.fillRect(rect(), QBrush(QColor(Qt::white)));
painter.setPen(Qt::black);
painter.setBrush(QBrush(QColor(Qt::darkBlue)));
emit draw(painter);
}
MainWindow.h
-----------
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "sprite.h"
#include <balls.h>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
signals:
void draw(QPainter &painter);
void move(int nDirection);
protected:
//This event handler captures all the paint events request to repaint all or part of the widget.
void paintEvent(QPaintEvent* event)override;
public slots:
void onTimer();
private slots:
void on_actionStart_triggered();
void on_actionStop_triggered();
private:
Ui::MainWindow *ui;
Sprite *sprite;
};
#endif // MAINWINDOW_H
How can I create balls at regular intervals, any sudo code will help.
Or any better way to implement this is also welcome.
Calling QObject::startTimer twice, will run two timers simultaneously:
int timerId1 = startTimer(2000);
int timerId2 = startTimer(10);
void MyObject::timerEvent(QTimerEvent *event)
{
if (event->timerId() == timerId1)
; // 2000ms timer is fired
else if (event->timerId() == timerId2)
; // 10ms timer is fired
}
Also the Qt documentation itself contains a clear example of this use case.
An alternative is to use two QTimer objects, and connect a slot to the timeout signal of each QTimer object.
I'm trying to create a simple frame in Qt with a tick and some text. I made two new label implementations because I wanted the labels to dynamically fill all the available space but when I resize the window the sizes are off, as shown by the qDebug output, which represents the size of the image label:
Resized: 244 , 244 <-- Window first created
Resized: 305 , 305 <-- Window maximized
Resized: 135 , 135 <-- Window restored to original size
As you can see, when the window is restored to its original size the image is not. The last size should be 244, 244.
The code which describes the behaviour of the two widgets is the following:
"widgets.h":
/*
* This file includes many custom widgets.
*/
#ifndef APOCRYPHA_WIDGETS
#define APOCRYPHA_WIDGETS
#include <QWidget>
#include <QLabel>
#include <QTimer>
#include <QPixmap>
#include <QResizeEvent>
#include <QPaintEvent>
class AutoTextLabel : public QLabel {
Q_OBJECT
public:
explicit AutoTextLabel(QWidget* parent);
AutoTextLabel(QWidget* parent, QString text);
protected:
void resizeEvent(QResizeEvent* event) override;
private:
QTimer* resizeTimer;
private slots:
void onResizeEnd();
};
class AutoImageLabel : public QLabel {
Q_OBJECT
public:
explicit AutoImageLabel(QWidget* parent);
AutoImageLabel(QWidget* parent, const QPixmap& pixmap);
void setFillOrientation(int orientation);
QSize sizeHint() const override;
public slots:
void setPixmap(const QPixmap &newPix);
void resizeEvent(QResizeEvent* event) override;
protected:
// void paintEvent(QPaintEvent* event) override;
private:
int fillOrientation;
int widthForHeight(int h) const;
int heightForWidth(int w) const override;
QPixmap scaledPixmap() const;
QPixmap labelPixmap;
};
#endif //APOCRYPHA_WIDGETS
"widgets.cpp":
/*
* This file includes many custom widgets.
*/
#include "widgets.h"
#include <QPainter>
#include <QDebug>
AutoTextLabel::AutoTextLabel(QWidget *parent, QString text) : QLabel(text, parent){
// Enable antialiasing
QFont aaFont(font());
aaFont.setStyleStrategy(QFont::PreferAntialias);
setFont(aaFont);
// This timer is used to fire a slot when a window is resized
resizeTimer = new QTimer();
resizeTimer->setSingleShot(true);
connect(resizeTimer, SIGNAL(timeout()), SLOT(onResizeEnd()));
}
AutoTextLabel::AutoTextLabel(QWidget *parent) : AutoTextLabel(parent, "") {}
void AutoTextLabel::resizeEvent(QResizeEvent *event) {
QWidget::resizeEvent(event);
// Only fire when 25ms have passed since the last resize.
resizeTimer->start(25);
}
void AutoTextLabel::onResizeEnd() {
QFont updatedFont(font());
// Resize Text
if (!text().isEmpty()){
int fontSize = 1;
updatedFont.setPixelSize(fontSize);
QRect boundingRectangle;
// Update bounding rectangle
if (wordWrap())
boundingRectangle = QFontMetrics(updatedFont).boundingRect(contentsRect(), Qt::TextWordWrap, text());
else
boundingRectangle = QFontMetrics(updatedFont).boundingRect(text());
while (boundingRectangle.height() <= contentsRect().height()) {
fontSize++;
updatedFont.setPixelSize(fontSize);
// Update bounding rectangle
if (wordWrap())
boundingRectangle = QFontMetrics(updatedFont).boundingRect(contentsRect(), Qt::TextWordWrap, text());
else
boundingRectangle = QFontMetrics(updatedFont).boundingRect(text());
}
updatedFont.setPixelSize(fontSize - 1);
setFont(updatedFont);
}
}
/* Auto Image Label */
AutoImageLabel::AutoImageLabel(QWidget *parent, const QPixmap &pixmap) : QLabel(parent) {
setMinimumSize(1, 1);
setScaledContents(false);
setPixmap(pixmap);
}
AutoImageLabel::AutoImageLabel(QWidget *parent) : QLabel(parent) {
setScaledContents(false);
}
void AutoImageLabel::resizeEvent(QResizeEvent *event) {
QWidget::resizeEvent(event);
if(!labelPixmap.isNull())
QLabel::setPixmap(scaledPixmap());
qDebug() << "Resized: " << scaledPixmap().width() << ", " << scaledPixmap().height();
}
int AutoImageLabel::widthForHeight(int h) const {
return labelPixmap.isNull() ? width() : (labelPixmap.width() * h) / labelPixmap.height();
}
int AutoImageLabel::heightForWidth(int w) const {
return labelPixmap.isNull() ? height() : (labelPixmap.height() * w) / labelPixmap.width();
}
void AutoImageLabel::setFillOrientation(int orientation) {
this->fillOrientation = orientation;
}
QSize AutoImageLabel::sizeHint() const {
if (fillOrientation == Qt::Horizontal)
return QSize(width(), heightForWidth(width()));
else
return QSize(widthForHeight(height()), height());
}
QPixmap AutoImageLabel::scaledPixmap() const {
return labelPixmap.scaled(sizeHint(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
}
void AutoImageLabel::setPixmap(const QPixmap &newPix) {
labelPixmap = newPix;
QLabel::setPixmap(scaledPixmap());
}
"other_frames.h":
//
// Created by Riccardo on 18/09/2017.
//
#ifndef APOCRYPHA_OTHER_FRAMES_H
#define APOCRYPHA_OTHER_FRAMES_H
#include <QFrame>
#include <QLabel>
#include <QGridLayout>
#include <QWidget>
#include <QResizeEvent>
#include <QPixmap>
#include <QTimer>
#include "widgets.h"
class ConfirmationFrame : public QFrame {
Q_OBJECT
public:
explicit ConfirmationFrame(QWidget* parent);
ConfirmationFrame(QWidget* parent, const QString& text);
private:
QGridLayout* layout;
AutoImageLabel* imageLabel;
AutoTextLabel* textLabel;
};
#endif //APOCRYPHA_OTHER_FRAMES_H
"other_frames.cpp":
//
// Created by Riccardo on 18/09/2017.
//
#include "other_frames.h"
#include <QDebug>
ConfirmationFrame::ConfirmationFrame(QWidget* parent, const QString &text) : QFrame(parent) {
textLabel = new AutoTextLabel(this, text);
QPixmap pix(":/images/check-tick.png");
imageLabel = new AutoImageLabel(this, pix);
textLabel->setAlignment(Qt::AlignCenter);
imageLabel->setAlignment(Qt::AlignCenter);
textLabel->setWordWrap(true);
// Green Background
setStyleSheet("background-color: rgba(106, 242, 94, 1);");
layout = new QGridLayout();
layout->setSpacing(0);
layout->setContentsMargins(32, 32, 32, 32);
layout->setRowStretch(0, 1);
layout->setRowStretch(1, 1);
layout->addWidget(imageLabel, 0, 1);
layout->addWidget(textLabel, 1, 1);
setLayout(layout);
}
ConfirmationFrame::ConfirmationFrame(QWidget *parent) : ConfirmationFrame(parent, "") {
}
"window_main.h":
#ifndef WINDOW_MAIN_H
#define WINDOW_MAIN_H
#include <QMainWindow>
#include <QMenuBar>
#include <QMenu>
#include <QGridLayout>
#include <QFrame>
#include <QScreen>
class MainWindow : public QMainWindow {
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
QFrame *mainFrame;
void center(QScreen* screen);
void autoSetSize(QScreen* screen);
private:
void createMenu();
// Components
QGridLayout *mainLayout;
QMenuBar *menuBar;
QMenu *fileMenu;
};
#endif // WINDOW_MAIN
"window_main.cpp":
#include "window_main.h"
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
mainFrame = new QFrame();
mainLayout = new QGridLayout();
mainLayout->setSpacing(0);
mainLayout->setContentsMargins(0, 0, 0, 0);
createMenu();
mainFrame->setStyleSheet("background-color: red;");
mainFrame->setLayout(mainLayout);
setCentralWidget(mainFrame);
}
void MainWindow::createMenu(){
menuBar = new QMenuBar;
fileMenu = new QMenu(tr("&File"), this);
menuBar->addMenu(fileMenu);
setMenuBar(menuBar);
}
void MainWindow::center(QScreen *screen) {
QSize size = screen->availableSize();
int x = size.width() / 2 - width() / 2;
int y = size.height() / 2 - height() / 2;
move(x, y);
}
void MainWindow::autoSetSize(QScreen *screen) {
QSize screenSize = screen->availableSize();
// TODO Math.round
setMinimumSize(QSize((int)(screenSize.width() / 1.25), (int)(screenSize.height() / 1.25)));
}
"main.cpp":
#include <QApplication>
#include <iostream>
#include <QFile>
#include "quiz/choice.h"
#include "quiz/question.h"
#include "quiz/quizmaker.h"
#include <QSettings>
#include <QStandardPaths>
#include <QDebug>
#include <src/user_interface/other_frames.h>
#include "user_interface/window_main.h"
#include <QScreen>
#include <QFontDatabase>
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
// Set Application Parameters
QCoreApplication::setOrganizationName("Riccardo Fagiolo");
QCoreApplication::setOrganizationDomain("kopharex.me");
QCoreApplication::setApplicationName("Apocrypha");
// Set application font
const int id = QFontDatabase::addApplicationFont(":/fonts/montserrat/Montserrat-Regular.otf");
QString family = QFontDatabase::applicationFontFamilies(id).at(0);
QFont font(family);
font.setStyleStrategy(QFont::PreferAntialias);
a.setFont(font);
// App Settings
QSettings settings;
settings.setValue("data_dir", QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
// Create UI
auto* window = new MainWindow();
ConfirmationFrame* cframe = new ConfirmationFrame(window, "But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses to enjoy a pleasure that has no annoying consequences, or one who avoids a pain that produces no resultant pleasure?");
window->mainFrame->layout()->addWidget(cframe);
window->autoSetSize(a.primaryScreen());
//cframe->updateTextLabel();
window->show();
window->center(a.primaryScreen());
// [...] - Nothing related to user interface.
return a.exec();
}
Here is a screenshot of the current MainWindow and ConfirmationFrame to give you an idea of what i'm trying to accomplish:
Window Screenshot
All comments regarding the code are welcome.
Thanks for any help,
Riccardo
Hello I tried to fix the resizing issue with an hack.
Before starting the timer to resize the text, just reduce its font to a 1 pixel font:
void AutoTextLabel::resizeEvent(QResizeEvent *event) {
QWidget::resizeEvent(event);
// set a very small font, then start the timer
QFont updatedFont(font());
updatedFont.setPixelSize(1);
setFont(updatedFont);
// Only fire when 25ms have passed since the last resize.
resizeTimer->start(25);
}
Can the effect be acceptable in your opinion?
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.
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.
I'm messing around with QGraphicsView and QGraphicsScene to create a Tic Tac Toe clone. I add some QGraphicsLineItems to my scene and override the resizeEvent method of the Widget that contains the view, so that when the window is resized, the view and its contents are scaled appropriately. This works fine, except for the first time that I run the program:
Once I resize the window by any amount, the scene is scaled correctly:
Here's the code:
main.cpp:
#include <QtGui>
#include "TestApp.h"
int main(int argv, char **args)
{
QApplication app(argv, args);
TestApp window;
window.show();
return app.exec();
}
TestApp.h:
#ifndef TEST_APP_H
#define TEST_APP_H
#include <QtGui>
class TestApp : public QMainWindow
{
Q_OBJECT
public:
TestApp();
protected:
void resizeEvent(QResizeEvent* event);
QGraphicsView* view;
QGraphicsScene* scene;
};
#endif
TestApp.cpp:
#include "TestApp.h"
TestApp::TestApp()
: view(new QGraphicsView(this))
, scene(new QGraphicsScene(this))
{
resize(220, 220);
scene->setSceneRect(0, 0, 200, 200);
const int BOARD_WIDTH = 3;
const int BOARD_HEIGHT = 3;
const QPoint SQUARE_SIZE = QPoint(66, 66);
const int LINE_WIDTH = 10;
const int HALF_LINE_WIDTH = LINE_WIDTH / 2;
QBrush lineBrush = QBrush(Qt::black);
QPen linePen = QPen(lineBrush, LINE_WIDTH);
for(int x = 1; x < BOARD_WIDTH; ++x)
{
int x1 = x * SQUARE_SIZE.x();
scene->addLine(x1, HALF_LINE_WIDTH, x1, scene->height() - HALF_LINE_WIDTH, linePen);
}
for(int y = 1; y < BOARD_HEIGHT; ++y)
{
int y1 = y * SQUARE_SIZE.y();
scene->addLine(HALF_LINE_WIDTH, y1, scene->width() - HALF_LINE_WIDTH, y1, linePen);
}
view->setScene(scene);
view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
view->show();
view->installEventFilter(this);
setCentralWidget(view);
}
void TestApp::resizeEvent(QResizeEvent* event)
{
view->fitInView(0, 0, scene->width(), scene->height());
QWidget::resizeEvent(event);
}
I've tried adding a call to fitInView at the end of TestApp's constructor, but it doesn't seem to do anything - resizeEvent seems to be called once at the start of the program's execution anyway.
Cheers.
Handle the view fit also inside the showEvent:
void TestApp::showEvent ( QShowEvent * event )
{
view->fitInView(0, 0, scene->width(), scene->height());
QWidget::showEvent(event);
}