I have a simple vertex and fragment shader. I use them in a class derived from QtOpenGLWidget.the program is supposed to draw a triangle, but it does not show anything. The glClearColor works fine and I am able to change the background color. When I run the program from command line, the program outputs one single line, but draws nothing:
QOpenGLShaderProgram::attributeLocation( position ): shader program is not linked
When I insert getGLError after gl commands, none of them report an error.
here is the class definition:
class GLCanvas:public QOpenGLWidget , protected QOpenGLFunctions
{
QMatrix4x4 matrix;
QOpenGLShaderProgram* shader;
GLuint posLocation;
GLuint colLocation;
GLuint matLocation;
void cleanup();
public:
GLCanvas(QWidget* parent);
void initializeGL() override;
void paintGL() override;
void resizeGL(int w, int h) override;
QStringList getGLinfo();
};
static const char *vertexShaderSource =
R"(
#version 330
in vec3 position;
void main()
{
gl_Position = vec4(position, 1);
})";
static const char *fragmentShaderSource =
R"(
#version 330
void main()
{
gl_FragColor = vec4(0.4,0.4,0.8,1.0);
})";
GLCanvas::GLCanvas(QWidget* parent): QOpenGLWidget(parent)
{
setFormat(QSurfaceFormat::defaultFormat());
matrix.setToIdentity();
posLocation = -1;
colLocation = -1;
matLocation = -1;
}
void GLCanvas::initializeGL()
{
makeCurrent();
connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &GLCanvas::cleanup);
constexpr int glNoError = 0;
GLenum error = -1;
initializeOpenGLFunctions();
glClearColor(.1f, .1f, .1f, 1.0f);
error = glGetError();
if (error != glNoError)
{
qDebug() << "glclearcolor failed";
}
shader = new QOpenGLShaderProgram(this);
shader->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource);
error = glGetError();
if (error != glNoError)
{
qDebug() << "failed after adding vertex shader.";
}
shader->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource);
error = glGetError();
if (error != glNoError)
{
qDebug() << "failed after adding fragment shader.";
}
Q_ASSERT(shader->link());
error = glGetError();
if (error != glNoError)
{
qDebug() << "gl error at link.";
}
//colLocation = shader->attributeLocation("color");
//matLocation = shader->uniformLocation("matrix");
//shader->release();
posLocation = shader->attributeLocation("position");
error = glGetError();
if (error != glNoError)
{
qDebug() << "gl error at link.";
}
glEnable(GL_DEPTH_TEST);
//colLocation = shader->attributeLocation("color");
//matLocation = shader->uniformLocation("matrix");
//shader->release();
//glEnable(GL_CULL_FACE);
}
//###################################################################
void GLCanvas::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
GLfloat vertices[] = { 0.0f, 0.707f, .0f, 0.5f, -0.5f, .0f ,-0.5f, -0.5f, .0f};
Q_ASSERT(shader->bind());
shader->setAttributeArray(posLocation, vertices, 3, 0);
glEnableVertexAttribArray(posLocation);
glDrawArrays(GL_LINES, 0, 3);
/*
GLfloat lvertices[] = { 0.907f, 0.907f, 0.5f, -0.957f, -0.957f, 0.5f };
shader->setAttributeArray(posLocation, lvertices, 0, 3);
glDrawArrays(GL_LINE_STRIP, 0, 2);
glDisableVertexAttribArray(posLocation);
*/
//glDisableVertexAttribArray(colLocation);
shader->release();
// qDebug() << getGLinfo();
}
void GLCanvas::resizeGL(int w, int h)
{
Q_UNUSED(w);
Q_UNUSED(h);
//matrix.setToIdentity();
//matrix.perspective(45.0f, w / float(h), 0.01f, 1000.0f);
glViewport(0, 0, w, h);
}
/*
void GLCanvas::mousePressEvent(QMouseEvent *event)
{
Q_UNUSED(event);
}
void GLCanvas::mouseMoveEvent(QMouseEvent *event)
{
Q_UNUSED(event);
}
*/
void GLCanvas::cleanup()
{
if (shader == nullptr)
return;
makeCurrent();
delete shader;
//shader = nullptr;
doneCurrent();
}
I did everything carefully from the beginning and things worked. probably a stupid mistake was the cause of it all. Thank you all.
Related
New Qt user here (also not an avid C++ programmer, so excuse me if my terminology isn't spot on). I am learning how to develop my own digital instrument cluster. I have managed to create my own graphical objects (horizontal bar graph) using QQuickPaintedItem. I have also managed to connect said objects to my own custom designed ECU over a network.
I want to add an "onClicked" event to the horizontal bar graphs, so that a new window (QWidget?) is displayed when the user clicks/presses an object. This new window would show the configuration parameters for the respective device that the horizontal bar graph is tied to. My intent is to design a generic QML window specific to pressure, temperature, etc. devices. For example, pressing the "Oil Temp", "Intake Air Temp", or "Coolant Temp" objects would take you to the generic QML window for temperature devices, and the parameters for whatever device was pressed would be displayed.
A couple questions that I have are as follows:
How do I add an "onClicked" event to my horizontal bar graph? I am assuming that I need to use MouseEvents, however, exact implementation of mouse events in my case is escaping me. I am assuming that once a click event is registered, that I can just send a signal to open my new window. Examples would be extremely helpful.
Should I be using a QWidget to open the new window, or is there a better alternative? I want the new window to completely replace the main window. When the new window is open, I also do not want the main window sending/receiving network requests in the background. Any examples you could point me to?
horizontalbargraph.cpp
#include "horizontalBarGraph.h"
HorizontalBarGraph::HorizontalBarGraph(QQuickItem *parent)
:QQuickPaintedItem(parent),
_horizontalBarGraphWidth(266),
_minValue(0),
_maxValue(100),
_actualValue(50),
_sensorName("Sensor Name"),
_units("Units"),
_llEnabled(true),
_llValue(20),
_lEnabled(true),
_lValue(40),
_hhEnabled(true),
_hhValue(80),
_hEnabled(true),
_hValue(60)
{
}
void HorizontalBarGraph::paint(QPainter *painter)
{
QRectF rect = this->boundingRect();
painter->setRenderHint(QPainter::Antialiasing);
QPen pen = painter->pen();
pen.setCapStyle(Qt::FlatCap);
QFont bottomFont("Arial", 14, QFont::Bold);
QFont topFont("Arial", 16, QFont::Bold);
QColor yellow(245, 225, 27);
QColor red(226, 32, 40);
QColor blue(71, 92, 167);
QColor gray192(192, 192, 192);
QColor gray212(212, 212, 212);
QColor gray224(224, 224, 224);
//minValue, actualValue, maxValue
painter->save();
painter->setFont(bottomFont);
pen.setColor(gray224);
painter->setPen(pen);
painter->drawText(rect.adjusted(0, _horizontalBarGraphHeight - _yBarChart2, _widthValue - _horizontalBarGraphWidth, 0), Qt::AlignHCenter | Qt::AlignTop, QString::number((_minValue), 'f', 0)); //Draws minValue
painter->drawText(rect.adjusted(_horizontalBarGraphWidth - _widthValue, _horizontalBarGraphHeight - _yBarChart2, 0, 0), Qt::AlignHCenter | Qt::AlignTop, QString::number((_maxValue), 'f', 0)); //Draws maxValue
painter->drawText(rect.adjusted(0, _horizontalBarGraphHeight - _yBarChart2, 0, 0), Qt::AlignHCenter | Qt::AlignTop, QString::number((_actualValue), 'f', 1)); //Draws actualValue
painter->restore();
//Bar chart background
painter->save();
painter->fillRect(rect.adjusted(_xBarChart, _yBarChart1, -_xBarChart, -_yBarChart2), gray224);
painter->restore();
//Lo
painter->save();
if(_lEnabled)
{
_scaleOutput = scale(_lValue, _minValue, _maxValue, _xBarChart, _horizontalBarGraphWidth - _xBarChart);
if(_actualValue <= _lValue)
{
painter->fillRect(rect.adjusted(_xBarChart, _yBarChart1, _scaleOutput - _horizontalBarGraphWidth, -_yBarChart2), yellow);
} else
{
painter->fillRect(rect.adjusted(_xBarChart, _yBarChart1, _scaleOutput - _horizontalBarGraphWidth, -_yBarChart2), gray212);
}
}
painter->restore();
//LoLo
painter->save();
if(_llEnabled)
{
_scaleOutput = scale(_llValue, _minValue, _maxValue, _xBarChart, _horizontalBarGraphWidth - _xBarChart);
if(_actualValue <= _llValue)
{
painter->fillRect(rect.adjusted(_xBarChart, _yBarChart1, _scaleOutput - _horizontalBarGraphWidth, -_yBarChart2), red);
} else
{
painter->fillRect(rect.adjusted(_xBarChart, _yBarChart1, _scaleOutput - _horizontalBarGraphWidth, -_yBarChart2), gray192);
}
}
painter->restore();
//Hi
painter->save();
if(_hEnabled)
{
_scaleOutput = scale(_hValue, _minValue, _maxValue, _xBarChart, _horizontalBarGraphWidth - _xBarChart);
if(_actualValue >= _hValue)
{
painter->fillRect(rect.adjusted(_scaleOutput, _yBarChart1, -_xBarChart, -_yBarChart2), yellow);
} else
{
painter->fillRect(rect.adjusted(_scaleOutput, _yBarChart1, -_xBarChart, -_yBarChart2), gray212);}
}
painter->restore();
//HiHi
painter->save();
if(_hhEnabled)
{
_scaleOutput = scale(_hhValue, _minValue, _maxValue, _xBarChart, _horizontalBarGraphWidth - _xBarChart);
if(_actualValue >= _hhValue)
{
painter->fillRect(rect.adjusted(_scaleOutput, _yBarChart1, -_xBarChart, -_yBarChart2), red);
} else
{
painter->fillRect(rect.adjusted(_scaleOutput, _yBarChart1, -_xBarChart, -_yBarChart2), gray192);}
}
painter->restore();
//Sensor name, Units
painter->save();
painter->setFont(topFont);
pen.setColor(gray224);
painter->setPen(pen);
painter->drawText(rect.adjusted(_xBarChart, 0, 0, -_horizontalBarGraphHeight + _yBarChart1 - _heightBarChart / 2), Qt::AlignLeft | Qt::AlignBottom, _sensorName); //Draws sensor name
painter->drawText(rect.adjusted(0, 0, -_xBarChart, -_horizontalBarGraphHeight + _yBarChart1 - _heightBarChart / 2), Qt::AlignRight | Qt::AlignBottom, _units); //Draws units
painter->restore();
//Arrow
painter->save();
QPainterPath path;
_scaleOutput = scale(_actualValue, _minValue, _maxValue, _xBarChart, _horizontalBarGraphWidth - _xBarChart);
path.moveTo(_scaleOutput - _heightBarChart / 2, _yBarChart1 - _heightBarChart / 2);
path.lineTo(_scaleOutput + _heightBarChart / 2, _yBarChart1 - _heightBarChart / 2);
path.lineTo(_scaleOutput, _yBarChart1 + _heightBarChart / 2);
path.closeSubpath();
painter->fillPath(path, blue);
painter->restore();
//Bounding box
painter->save();
_drawBoundingBox = false;
if(_llEnabled && _actualValue <= _llValue)
{
_drawBoundingBox = true;
_boundingBoxColor = red;
} else if (_lEnabled && _actualValue <= _lValue)
{
_drawBoundingBox = true;
_boundingBoxColor = yellow;
} else if (_hhEnabled && _actualValue >= _hhValue)
{
_drawBoundingBox = true;
_boundingBoxColor = red;
} else if (_hEnabled && _actualValue >= _hValue)
{
_drawBoundingBox = true;
_boundingBoxColor = yellow;
}
if(_drawBoundingBox && _boundingBoxFlash)
{
pen.setColor(_boundingBoxColor);
pen.setWidthF(_boundingBoxStrokeWidth);
painter->setPen(pen);
painter->drawRect(rect.adjusted(0, 0, 0, 0));
}
painter->restore();
}
qreal HorizontalBarGraph::getHorizontalBarGraphWidth()
{
return _horizontalBarGraphWidth;
}
qreal HorizontalBarGraph::getHorizontalBarGraphHeight()
{
return _horizontalBarGraphHeight;
}
qreal HorizontalBarGraph::getMinValue()
{
return _minValue;
}
qreal HorizontalBarGraph::getMaxValue()
{
return _maxValue;
}
qreal HorizontalBarGraph::getActualValue()
{
return _actualValue;
}
QString HorizontalBarGraph::getSensorName()
{
return _sensorName;
}
QString HorizontalBarGraph::getUnits()
{
return _units;
}
bool HorizontalBarGraph::isLlEnabled()
{
return _llEnabled;
}
qreal HorizontalBarGraph::getLlValue()
{
return _llValue;
}
bool HorizontalBarGraph::isLEnabled()
{
return _lEnabled;
}
qreal HorizontalBarGraph::getLValue()
{
return _lValue;
}
bool HorizontalBarGraph::isHEnabled()
{
return _hEnabled;
}
qreal HorizontalBarGraph::getHValue()
{
return _hValue;
}
bool HorizontalBarGraph::isHhEnabled()
{
return _hhEnabled;
}
qreal HorizontalBarGraph::getHhValue()
{
return _hhValue;
}
bool HorizontalBarGraph::isBoundingBoxFlashing()
{
return _boundingBoxFlash;
}
void HorizontalBarGraph::setHorizontalBarGraphWidth(qreal width)
{
if(_horizontalBarGraphWidth == width)
return;
_horizontalBarGraphWidth = width;
update();
emit widthChanged();
}
void HorizontalBarGraph::setHorizontalBarGraphHeight(qreal height)
{
if(_horizontalBarGraphHeight == height)
return;
//This object was designed to have a constant height. This method is technically not needed,
//but qt appears to require it.
//_horizontalBarGraphHeight = height;
//update();
//emit heightChanged();
}
void HorizontalBarGraph::setMinValue(qreal minValue)
{
if(_minValue == minValue)
return;
_minValue = minValue;
update();
emit minValueChanged();
}
void HorizontalBarGraph::setMaxValue(qreal maxValue)
{
if(_maxValue == maxValue)
return;
_maxValue = maxValue;
update();
emit maxValueChanged();
}
void HorizontalBarGraph::setActualValue(qreal actualValue)
{
if(_actualValue == actualValue)
return;
_actualValue = actualValue;
update();
emit actualValueChanged();
}
void HorizontalBarGraph::setSensorName(QString sensorName)
{
if(_sensorName == sensorName)
return;
_sensorName = sensorName;
update();
emit sensorNameChanged();
}
void HorizontalBarGraph::setUnits(QString units)
{
if(_units == units)
return;
_units = units;
update();
emit unitsChanged();
}
void HorizontalBarGraph::enableLl(bool llEnabled)
{
if(_llEnabled == llEnabled)
return;
_llEnabled = llEnabled;
update();
emit enableLlChanged();
}
void HorizontalBarGraph::setLlValue(qreal llValue)
{
if(_llValue == llValue)
return;
_llValue = llValue;
update();
emit llValueChanged();
}
void HorizontalBarGraph::enableL(bool lEnabled)
{
if(_lEnabled == lEnabled)
return;
_lEnabled = lEnabled;
update();
emit enableLChanged();
}
void HorizontalBarGraph::setLValue(qreal lValue)
{
if(_lValue == lValue)
return;
_lValue = lValue;
update();
emit lValueChanged();
}
void HorizontalBarGraph::enableH(bool hEnabled)
{
if(_hEnabled == hEnabled)
return;
_hEnabled = hEnabled;
update();
emit enableHChanged();
}
void HorizontalBarGraph::setHValue(qreal hValue)
{
if(_hValue == hValue)
return;
_hValue = hValue;
update();
emit hValueChanged();
}
void HorizontalBarGraph::enableHh(bool hhEnabled)
{
if(_hhEnabled == hhEnabled)
return;
_hhEnabled = hhEnabled;
update();
emit enableHhChanged();
}
void HorizontalBarGraph::setHhValue(qreal hhValue)
{
if(_hhValue == hhValue)
return;
_hhValue = hhValue;
update();
emit hhValueChanged();
}
void HorizontalBarGraph::setBoundingBoxFlash(bool flash)
{
if(_boundingBoxFlash == flash)
return;
_boundingBoxFlash = flash;
update();
emit boundingBoxFlashChanged();
}
qreal HorizontalBarGraph::scale(qreal input, qreal inputMin, qreal inputMax, qreal outputMin, qreal outputMax)
{
qreal output = (outputMax - outputMin) * (input - inputMin) / (inputMax - inputMin) + outputMin;
if(output > outputMax)
output = outputMax;
if(output < outputMin)
output = outputMin;
return output;
}
horizontalbargraph.h
#ifndef HORIZONTALBARGRAPH_H
#define HORIZONTALBARGRAPH_H
#include <QObject>
#include <QQuickPaintedItem>
#include <QPainter>
class HorizontalBarGraph : public QQuickPaintedItem
{
Q_OBJECT
Q_PROPERTY(qreal horizontalBarGraphWidth READ getHorizontalBarGraphWidth WRITE setHorizontalBarGraphWidth NOTIFY horizontalBarGraphWidthChanged)
Q_PROPERTY(qreal horizontalBarGraphHeight READ getHorizontalBarGraphHeight WRITE setHorizontalBarGraphHeight NOTIFY horizontalBarGraphHeightChanged)
Q_PROPERTY(qreal minValue READ getMinValue WRITE setMinValue NOTIFY minValueChanged)
Q_PROPERTY(qreal maxValue READ getMaxValue WRITE setMaxValue NOTIFY maxValueChanged)
Q_PROPERTY(qreal actualValue READ getActualValue WRITE setActualValue NOTIFY actualValueChanged)
Q_PROPERTY(QString sensorName READ getSensorName WRITE setSensorName NOTIFY sensorNameChanged)
Q_PROPERTY(QString units READ getUnits WRITE setUnits NOTIFY unitsChanged)
Q_PROPERTY(bool llEnable READ isLlEnabled WRITE enableLl NOTIFY enableLlChanged)
Q_PROPERTY(qreal llValue READ getLlValue WRITE setLlValue NOTIFY llValueChanged)
Q_PROPERTY(bool lEnable READ isLEnabled WRITE enableL NOTIFY enableLChanged)
Q_PROPERTY(qreal lValue READ getLValue WRITE setLValue NOTIFY lValueChanged)
Q_PROPERTY(bool hhEnable READ isHhEnabled WRITE enableHh NOTIFY enableHhChanged)
Q_PROPERTY(qreal hhValue READ getHhValue WRITE setHhValue NOTIFY hhValueChanged)
Q_PROPERTY(bool hEnable READ isHEnabled WRITE enableH NOTIFY enableHChanged)
Q_PROPERTY(qreal hValue READ getHValue WRITE setHValue NOTIFY hValueChanged)
Q_PROPERTY(bool boundingBoxFlash READ isBoundingBoxFlashing WRITE setBoundingBoxFlash NOTIFY boundingBoxFlashChanged)
public:
HorizontalBarGraph(QQuickItem *parent = nullptr);
virtual void paint(QPainter *painter);
qreal getHorizontalBarGraphWidth();
qreal getHorizontalBarGraphHeight();
qreal getMinValue();
qreal getMaxValue();
qreal getActualValue();
QString getSensorName();
QString getUnits();
bool isLlEnabled();
qreal getLlValue();
bool isLEnabled();
qreal getLValue();
bool isHhEnabled();
qreal getHhValue();
bool isHEnabled();
qreal getHValue();
bool isBoundingBoxFlashing();
void setHorizontalBarGraphWidth(qreal width);
void setHorizontalBarGraphHeight(qreal height);
void setMinValue(qreal minValue);
void setMaxValue(qreal maxValue);
void setActualValue(qreal actualValue);
void setSensorName(QString sensorName);
void setUnits(QString units);
void enableLl(bool llEnabled);
void setLlValue(qreal llValue);
void enableL(bool lEnabled);
void setLValue(qreal lValue);
void enableHh(bool hhEnabled);
void setHhValue(qreal hhValue);
void enableH(bool hEnabled);
void setHValue(qreal hValue);
void setBoundingBoxFlash(bool flash);
signals:
void horizontalBarGraphWidthChanged();
void horizontalBarGraphHeightChanged();
void minValueChanged();
void maxValueChanged();
void actualValueChanged();
void sensorNameChanged();
void unitsChanged();
void enableLlChanged();
void llValueChanged();
void enableLChanged();
void lValueChanged();
void enableHhChanged();
void hhValueChanged();
void enableHChanged();
void hValueChanged();
void boundingBoxFlashChanged();
private:
qreal _horizontalBarGraphWidth;
qreal _horizontalBarGraphHeight = 75;
qreal _minValue;
qreal _maxValue;
qreal _actualValue;
QString _sensorName;
QString _units;
bool _llEnabled;
qreal _llValue;
bool _lEnabled;
qreal _lValue;
bool _hhEnabled;
qreal _hhValue;
bool _hEnabled;
qreal _hValue;
qreal _xBarChart = 25;
qreal _yBarChart1 = 35;
qreal _yBarChart2 = 25;
qreal _heightBarChart = _horizontalBarGraphHeight - _yBarChart1 - _yBarChart2;
qreal _widthValue = _xBarChart * 2;
qreal _scaleOutput;
qreal _boundingBoxStrokeWidth = 7.5;
bool _drawBoundingBox;
QColor _boundingBoxColor;
bool _boundingBoxFlash;
qreal scale(qreal input, qreal inputMin, qreal inputMax, qreal outputMin, qreal outputMax);
};
#endif // HORIZONTALBARGRAPH_H
there is many virtual functions that you can rempliment for get mouse event and best function for mouse click is mouseRelease
if I understand correctly, you want to block all mouse signal in main window while the new windows is open. you can create a QDialog instead of using pure QWidget to ban all mouse interaction from main window .
I am trying to write a 2D application with openGL (do not ask why, it a test for a stage) where I have a texture I can move around with a drag and drop, rotate or scal with the mouse. My texture is applied on a square.
For these operations, I need to know if I am clicking on the polygon, and the distance and angle between my cursor and the polygon center (which can be moved).
I did not find how to do these with the tutorial I am following does not cover that.
How can I find those 2 informations?
Here is the code :
#include "glwidget.h"
#include <QMouseEvent>
#include <QKeyEvent>
#ifdef WIN32
#include <GL/glext.h>
PFNGLACTIVETEXTUREPROC pGlActiveTexture = NULL;
#define glActiveTexture pGlActiveTexture
#endif //WIN32
GlWidget::GlWidget(QWidget *parent) :
QGLWidget(QGLFormat(), parent)
{
}
GlWidget::~GlWidget(){}
QSize GlWidget::sizeHint() const
{
return QSize(640,480);
}
void GlWidget::resizeGL(int w, int h){
h = (h<=0?1:h);
pMatrix.setToIdentity();
pMatrix.perspective(60.0,(float)w /(float)h,0.1,10);
glViewport(0,0,w,h);
//glScissor(w*0.8,0,w*0.2,h);
//glDisable(GL_SCISSOR_TEST);
}
void GlWidget::initializeGL(){
#ifdef WIN32
glActiveTexture = (PFNGLACTIVETEXTUREPROC) wglGetProcAddress((LPCSTR) "glActiveTexture");
#endif
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
qglClearColor(QColor(Qt::black));
shaderProgram.addShaderFromSourceFile(QGLShader::Vertex, ":/vertexShader.vsh");
shaderProgram.addShaderFromSourceFile(QGLShader::Fragment, ":/fragmentShader.fsh");
shaderProgram.link();
vertices << QVector3D(-1,-1,-2) << QVector3D(1,-1,-2) << QVector3D(-1,1,-2)
<< QVector3D(-1,1,-2) << QVector3D(1,-1,-2) << QVector3D(1,1,-2);
textureCoordinates << QVector2D(0, 0) << QVector2D(1, 0) << QVector2D(0, 1)
<< QVector2D(0, 1) << QVector2D(1, 0) << QVector2D(1, 1);
texture = bindTexture(QPixmap(":/icone.png"));
//mMatrix.setToIdentity();
}
void GlWidget::paintGL(){
//qglClearColor(QColor(Qt::white));
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
/*qglClearColor(QColor(Qt::black));
glScissor(this->width()*0.8,0,this->width()*0.2,this->height());
glEnable(GL_SCISSOR_TEST);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDisable(GL_SCISSOR_TEST);*/
QMatrix4x4 mMatrix;
QMatrix4x4 vMatrix;
/* QMatrix4x4 cameraTransformation;
cameraTransformation.rotate(25, 0, 1, 0);
cameraTransformation.rotate(-25, 1, 0, 0);
QVector3D cameraPosition = cameraTransformation * QVector3D(0, 0, 2.5);
QVector3D cameraUpDirection = cameraTransformation * QVector3D(0, 1, 0);
vMatrix.lookAt(cameraPosition, QVector3D(0, 0, 0), cameraUpDirection);*/
shaderProgram.bind();
shaderProgram.setUniformValue("mvpMatrix", pMatrix*vMatrix*mMatrix);
shaderProgram.setUniformValue("texture", 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glActiveTexture(0);
shaderProgram.setAttributeArray("vertex", vertices.constData());
shaderProgram.enableAttributeArray("vertex");
shaderProgram.setAttributeArray("textureCoordinate", textureCoordinates.constData());
shaderProgram.enableAttributeArray("textureCoordinate");
glDrawArrays(GL_TRIANGLES,0,vertices.size());
shaderProgram.disableAttributeArray("vertex");
shaderProgram.disableAttributeArray("textureCoordinate");
shaderProgram.release();
}
void GlWidget::mousePressEvent(QMouseEvent *event){
if(event->button()==Qt::LeftButton)
lastPos = event->pos();
event->accept();
}
void GlWidget::mouseMoveEvent(QMouseEvent *event){
if(!event->buttons().testFlag(Qt::LeftButton)) return;
int dx = event->x() - lastPos.x();
int dy = event->y() - lastPos.y();
if(keysPressed.contains(Qt::Key_Control) && !keysPressed.contains(Qt::Key_Shift)){
//TODO : implement scaling
}
else if(!keysPressed.contains(Qt::Key_Control) && keysPressed.contains(Qt::Key_Shift)){
//TODO : implement rotate
}
else if(!keysPressed.contains(Qt::Key_Control) && !keysPressed.contains(Qt::Key_Shift)){
//TODO : implement drag
}
}
void GlWidget::keyPressEvent(QKeyEvent *event){
keysPressed.append(event->key());
}
void GlWidget::keyReleaseEvent(QKeyEvent *event){
keysPressed.remove(keysPressed.lastIndexOf(event->key()));
}
All the comments are from test I've done and the original tutorial
When I want to create a new Qt Quick Item in C++ in I extend QQuickItem. It happens that I want my new item to have the same basic properties of a Rectangle. Is there any class I can extend to have a Rectangle directly?
Rectangle is actually QQuickRectangle, but it is not exported to be used in C++. The header file is QtQuick\private\qquickrectangle_p.h.
Check my version of an AdvancedRectangle that can have a different radius for each corner. I had to re-implement Rectangle's behavior.
advancedrectangle.h
#ifndef ADVANCEDRECTANGLE_H
#define ADVANCEDRECTANGLE_H
#include <QQuickPaintedItem>
#include <QPainter>
#include <QPen>
class AdvancedRectangle : public QQuickPaintedItem
{
Q_OBJECT
public:
explicit AdvancedRectangle(QQuickItem *parent = 0);
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
QColor color() const;
void setColor(QColor color);
Q_PROPERTY(qreal radius READ radius WRITE setRadius NOTIFY radiusChanged)
qreal radius() const;
void setRadius(qreal r);
Q_PROPERTY(qreal radiusTopLeft READ radiusTopLeft WRITE setRadiusTopLeft NOTIFY radiusTopLeftChanged)
qreal radiusTopLeft() const;
void setRadiusTopLeft(qreal r);
Q_PROPERTY(qreal radiusTopRight READ radiusTopRight WRITE setRadiusTopRight NOTIFY radiusTopRightChanged)
qreal radiusTopRight() const;
void setRadiusTopRight(qreal r);
Q_PROPERTY(qreal radiusBottomRight READ radiusBottomRight WRITE setRadiusBottomRight NOTIFY radiusBottomRightChanged)
qreal radiusBottomRight() const;
void setRadiusBottomRight(qreal r);
Q_PROPERTY(qreal radiusBottomLeft READ radiusBottomLeft WRITE setRadiusBottomLeft NOTIFY radiusBottomLeftChanged)
qreal radiusBottomLeft() const;
void setRadiusBottomLeft(qreal r);
void paint(QPainter *painter);
enum START_ANGELS {
START_E = 0,
START_N = 90,
START_W = 180,
START_S = 270
};
signals:
void colorChanged();
void radiusChanged();
void radiusTopLeftChanged();
void radiusTopRightChanged();
void radiusBottomRightChanged();
void radiusBottomLeftChanged();
public slots:
private:
QColor color_ = QColor(255, 255, 255);
qreal radius_ = 0.0;
qreal radiusTopLeft_ = -1;
qreal radiusTopRight_ = -1;
qreal radiusBottomRight_ = -1;
qreal radiusBottomLeft_ = -1;
private slots:
void onAppearanceChanged();
};
#endif // ADVANCEDRECTANGLE_H
advancedrectangle.cpp
#include "advancedrectangle.h"
AdvancedRectangle::AdvancedRectangle(QQuickItem *parent) :
QQuickPaintedItem(parent)
{
connect(this, SIGNAL(widthChanged()), SLOT(onAppearanceChanged()));
connect(this, SIGNAL(heightChanged()), SLOT(onAppearanceChanged()));
connect(this, SIGNAL(colorChanged()), SLOT(onAppearanceChanged()));
}
QColor AdvancedRectangle::color() const
{
return color_;
}
void AdvancedRectangle::setColor(QColor color)
{
if (color_ == color) return;
color_ = color;
emit colorChanged();
}
qreal AdvancedRectangle::radius() const
{
return radius_;
}
void AdvancedRectangle::setRadius(qreal r)
{
if (r == radius_) return;
radius_ = r;
emit radiusChanged();
}
qreal AdvancedRectangle::radiusTopLeft() const
{
if (radiusTopLeft_ >= 0) return radiusTopLeft_;
else return radius_;
}
void AdvancedRectangle::setRadiusTopLeft(qreal r)
{
if (r == radiusTopLeft_) return;
radiusTopLeft_ = r;
emit radiusTopLeftChanged();
}
qreal AdvancedRectangle::radiusTopRight() const
{
if (radiusTopRight_ >= 0) return radiusTopRight_;
else return radius_;
}
void AdvancedRectangle::setRadiusTopRight(qreal r)
{
if (r == radiusTopRight_) return;
radiusTopRight_ = r;
emit radiusTopRightChanged();
}
qreal AdvancedRectangle::radiusBottomRight() const
{
if (radiusBottomRight_ >= 0) return radiusBottomRight_;
else return radius_;
}
void AdvancedRectangle::setRadiusBottomRight(qreal r)
{
if (r == radiusBottomRight_) return;
radiusBottomRight_ = r;
emit radiusBottomRightChanged();
}
qreal AdvancedRectangle::radiusBottomLeft() const
{
if (radiusBottomLeft_ >= 0) return radiusBottomLeft_;
else return radius_;
}
void AdvancedRectangle::setRadiusBottomLeft(qreal r)
{
if (r == radiusBottomLeft_) return;
radiusBottomLeft_ = r;
emit radiusBottomLeftChanged();
}
void AdvancedRectangle::paint(QPainter *painter)
{
QPen pen(Qt::NoPen);
painter->setPen(pen);
QBrush brush(color_);
painter->setBrush(brush);
painter->setRenderHints(QPainter::Antialiasing, true);
int _width = width();
int _height = height();
QRectF rectangle;
// top-left
rectangle = QRectF(0, 0, 2*radiusTopLeft(), 2*radiusTopLeft());
painter->drawPie(rectangle, START_W * 16, 90 * -1 * 16);
// top-right
rectangle = QRectF(_width-2*radiusTopRight(), 0, 2*radiusTopRight(), 2*radiusTopRight());
painter->drawPie(rectangle, START_N * 16, 90 * -1 * 16);
// bottom-right
rectangle = QRectF(_width-2*radiusBottomRight(), _height-2*radiusBottomRight(), 2*radiusBottomRight(), 2*radiusBottomRight());
painter->drawPie(rectangle, START_E * 16, 90 * -1 * 16);
// bottom-left
rectangle = QRectF(0, _height-2*radiusBottomLeft(), 2*radiusBottomLeft(), 2*radiusBottomLeft());
painter->drawPie(rectangle, START_S * 16, 90 * -1 * 16);
QPointF points[12] = {
QPointF(radiusTopLeft(), 0),
QPointF(radiusTopLeft(), radiusTopLeft()),
QPointF(0, radiusTopLeft()),
QPointF(0, _height-radiusBottomLeft()),
QPointF(radiusBottomLeft(), _height-radiusBottomLeft()),
QPointF(radiusBottomLeft(), _height),
QPointF(_width-radiusBottomRight(), _height),
QPointF(_width-radiusBottomRight(), _height-radiusBottomRight()),
QPointF(_width, _height-radiusBottomRight()),
QPointF(_width, radiusTopRight()),
QPointF(_width-radiusTopRight(), radiusTopRight()),
QPointF(_width-radiusTopRight(), 0)
};
painter->drawPolygon(points, 12);
}
void AdvancedRectangle::onAppearanceChanged()
{
int new_width = width();
int new_height = height();
update(QRect(0, 0, new_width, new_height));
}
Let me know if you need that MIT licensed.
so I am trying to draw a line between two points. Left mouse click starts the line then I would like the line to by dynamically drawn as the mouse moves (almost like a preview of the line). Left mouse click again and the line will be permanently drawn. I know there are a lot of other posts about QPaintEvents and I have combined some of the techniques used, but for some reason nothing is being drawn to the canvas. Below is the code:
void Main::mousePressEvent(QMouseEvent * event)
{
if (event->button() == Qt::LeftButton) {
QPointF pos = event->pos();
if( mStartPoint.isNull() ) {
if(josh.contains(pos))
mStartPoint = pos;
} else {
canvas.addLine(mStartPoint.x(),mStartPoint.y(),pos.x(),pos.y());
mStartPoint = QPointF();
}
}
}
bool Main::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::MouseMove)
{
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
if (!mStartPoint.isNull()) {
m_targetImage = QImage(canvas.width(),canvas.height(),QImage::Format_ARGB32);
QPainter p;
p.begin(&m_targetImage);
p.drawLine(mStartPoint, mouseEvent->pos());
p.end();
}
statusBar()->showMessage(QString("Mouse move (%1,%2)").arg(mouseEvent->pos().x()).arg(mouseEvent->pos().y()));
}
return false;
}
void Main::paintEvent(QPaintEvent *pe)
{
QPainter painter(this);
QPen pen(Qt::red);
pen.setWidth(10);
painter.setPen(pen);
painter.drawImage(0, 0, m_targetImage);
}
Any help is appreciated! Thanks! Josh
I think this is what you want. Change parameters according to your requirements.
//In your constructor
m_nInitialX = 0;
m_nInitialY = 0;
m_nFinalX = 0;
m_nFinalY = 0;
m_nPTargetPixmap = 0;
m_nPTargetPixmap = new QPixmap(400,400);
m_nbMousePressed = false;
void Main::mousePressEvent(QMouseEvent* event)
{
m_nbMousePressed = true;
m_nInitialX = event->pos().x();
m_nInitialY = event->pos().y();
}
void Main::mouseReleaseEvent(QMouseEvent *event)
{
m_nbMousePressed = false;
}
void Main::paintEvent(QPaintEvent *e)
{
if(m_nbMousePressed)
{
QPainter PixmapPainter(m_nPTargetPixmap);
QPen pen(Qt::green);
PixmapPainter.setPen(pen);
PixmapPainter.drawLine(m_nInitialX, m_nInitialY, m_nFinalX, m_nFinalY);
}
QPainter painter(this);
painter.drawPixmap(0, 0, *m_nPTargetPixmap);
}
void Main::mouseMoveEvent(QMouseEvent *event)
{
if (event->type() == QEvent::MouseMove)
{
QPainter PixmapPainter(m_nPTargetPixmap);
QPen pen(Qt::black);
PixmapPainter.setPen(pen);
PixmapPainter.drawLine(m_nInitialX, m_nInitialY, m_nFinalX, m_nFinalY);
update(); // update your view
m_nFinalX = event->pos().x();
m_nFinalY = event->pos().y();
}
update(); // update your view
}
This piece of code will draw a 2 point line that you want.
Here's my example of how to paint lines in your way directly on the widget.
Declaration:
private:
void drawLines(QPainter *p);
QPoint startPos;
QPoint endPos;
bool inDrawing;
QVector<QLine> lines;
Setting initial values in constructor:
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
startPos = QPoint();
endPos = QPoint();
inDrawing = false;
setMouseTracking(true);
}
Starting or ending the line drawing:
void Widget::mousePressEvent(QMouseEvent *event)
{
if (event->buttons() & Qt::LeftButton)
{
if (!inDrawing)
{
startPos = event->pos();
}
else
{
endPos = event->pos();
QLine line = QLine(startPos, event->pos());
lines.append(line);
}
inDrawing = !inDrawing;
}
}
void Widget::mouseMoveEvent(QMouseEvent *event)
{
if (inDrawing)
{
endPos = event->pos();
update();
}
}
Drawing current and saved lines:
void Widget::drawLines(QPainter *p)
{
if (!startPos.isNull() && !endPos.isNull())
{
p->drawLine(startPos, endPos);
}
p->drawLines(lines);
}
Drawing:
void Widget::paintEvent(QPaintEvent *event)
{
QPainter p(this);
QPen pen;
pen.setColor(Qt::red);
pen.setWidth(4);
p.setPen(pen);
drawLines(&p);
}
I'm studying WidgetMarqueeLabel class:
#include "WidgetMarqueeLabel.h"
#include <QPainter>
#include <QWidget>
WidgetMarqueeLabel::WidgetMarqueeLabel(QWidget *parent)//*parent)
{
px = 0;
py = 10;
speed = 1;
direction = RightToLeft;
connect(&timer3, SIGNAL(timeout()), this, SLOT(refreshLabel()));
timer3.start(10);
}
void WidgetMarqueeLabel::refreshLabel()
{
repaint();
}
WidgetMarqueeLabel::~WidgetMarqueeLabel()
{}
void WidgetMarqueeLabel::show()
{
QLabel::show();
}
void WidgetMarqueeLabel::setAlignment(Qt::Alignment al)
{
m_align = al;
updateCoordinates();
QLabel::setAlignment(al);
}
void WidgetMarqueeLabel::paintEvent(QPaintEvent *evt)
{
QPainter p(this);
if(direction==RightToLeft)
{
px -= speed;
if(px <= (-textLength))
px = width();
}
else
{
px += speed;
if(px >= width())
px = - textLength;
}
p.drawText(px, py+fontPointSize, text());
p.translate(px,0);
}
void WidgetMarqueeLabel::resizeEvent(QResizeEvent *evt)
{
updateCoordinates();
QLabel::resizeEvent(evt);
}
void WidgetMarqueeLabel::updateCoordinates()
{
switch(m_align)
{
case Qt::AlignTop:
py = 10;
break;
case Qt::AlignBottom:
py = height()-10;
break;
case Qt::AlignVCenter:
py = height()/2;
break;
}
fontPointSize = font().pointSize()/2;
textLength = fontMetrics().width(text());
}
void WidgetMarqueeLabel::setSpeed(int s)
{
speed = s;
}
int WidgetMarqueeLabel::getSpeed()
{
return speed;
}
void WidgetMarqueeLabel::setDirection(int d)
{
direction = d;
if (direction==RightToLeft)
px = width() - textLength;
else
px = 0;
refreshLabel();
}
void WidgetMarqueeLabel::close()
{
QLabel::close();
}
I was wondering if it was possible to make the text reappear before the text that reaches the end of the last letter on the right. I want something like this: for example (white space are 25):
WidgetMarqueeLabel
tMarqueeLabel Widge
eLabel WidgetMarque
el WidgetMarqueeLa
WidgetMarqueeLabel
WidgetMarqueeLabel
WidgetMarqueeLabel
WidgetMarqueeLabel
Is this possible?
For this purpose, I once wrote a class.
Example screenshot showing the text "This is an example text. It will be scrolled horizontally.". Note the alpha blending at both sides.
The code:
scrolltext.h:
#ifndef SCROLLTEXT_H
#define SCROLLTEXT_H
#include <QWidget>
#include <QStaticText>
#include <QTimer>
class ScrollText : public QWidget
{
Q_OBJECT
Q_PROPERTY(QString text READ text WRITE setText)
Q_PROPERTY(QString separator READ separator WRITE setSeparator)
public:
explicit ScrollText(QWidget *parent = 0);
public slots:
QString text() const;
void setText(QString text);
QString separator() const;
void setSeparator(QString separator);
protected:
virtual void paintEvent(QPaintEvent *);
virtual void resizeEvent(QResizeEvent *);
private:
void updateText();
QString _text;
QString _separator;
QStaticText staticText;
int singleTextWidth;
QSize wholeTextSize;
int leftMargin;
bool scrollEnabled;
int scrollPos;
QImage alphaChannel;
QImage buffer;
QTimer timer;
private slots:
virtual void timer_timeout();
};
#endif // SCROLLTEXT_H
scrolltext.cpp:
#include "scrolltext.h"
#include <QPainter>
ScrollText::ScrollText(QWidget *parent) :
QWidget(parent), scrollPos(0)
{
staticText.setTextFormat(Qt::PlainText);
setFixedHeight(fontMetrics().height());
leftMargin = height() / 3;
setSeparator(" --- ");
connect(&timer, SIGNAL(timeout()), this, SLOT(timer_timeout()));
timer.setInterval(50);
}
QString ScrollText::text() const
{
return _text;
}
void ScrollText::setText(QString text)
{
_text = text;
updateText();
update();
}
QString ScrollText::separator() const
{
return _separator;
}
void ScrollText::setSeparator(QString separator)
{
_separator = separator;
updateText();
update();
}
void ScrollText::updateText()
{
timer.stop();
singleTextWidth = fontMetrics().width(_text);
scrollEnabled = (singleTextWidth > width() - leftMargin);
if(scrollEnabled)
{
scrollPos = -64;
staticText.setText(_text + _separator);
timer.start();
}
else
staticText.setText(_text);
staticText.prepare(QTransform(), font());
wholeTextSize = QSize(fontMetrics().width(staticText.text()), fontMetrics().height());
}
void ScrollText::paintEvent(QPaintEvent*)
{
QPainter p(this);
if(scrollEnabled)
{
buffer.fill(qRgba(0, 0, 0, 0));
QPainter pb(&buffer);
pb.setPen(p.pen());
pb.setFont(p.font());
int x = qMin(-scrollPos, 0) + leftMargin;
while(x < width())
{
pb.drawStaticText(QPointF(x, (height() - wholeTextSize.height()) / 2) + QPoint(2, 2), staticText);
x += wholeTextSize.width();
}
//Apply Alpha Channel
pb.setCompositionMode(QPainter::CompositionMode_DestinationIn);
pb.setClipRect(width() - 15, 0, 15, height());
pb.drawImage(0, 0, alphaChannel);
pb.setClipRect(0, 0, 15, height());
//initial situation: don't apply alpha channel in the left half of the image at all; apply it more and more until scrollPos gets positive
if(scrollPos < 0)
pb.setOpacity((qreal)(qMax(-8, scrollPos) + 8) / 8.0);
pb.drawImage(0, 0, alphaChannel);
//pb.end();
p.drawImage(0, 0, buffer);
}
else
{
p.drawStaticText(QPointF(leftMargin, (height() - wholeTextSize.height()) / 2), staticText);
}
}
void ScrollText::resizeEvent(QResizeEvent*)
{
//When the widget is resized, we need to update the alpha channel.
alphaChannel = QImage(size(), QImage::Format_ARGB32_Premultiplied);
buffer = QImage(size(), QImage::Format_ARGB32_Premultiplied);
//Create Alpha Channel:
if(width() > 64)
{
//create first scanline
QRgb* scanline1 = (QRgb*)alphaChannel.scanLine(0);
for(int x = 1; x < 16; ++x)
scanline1[x - 1] = scanline1[width() - x] = qRgba(0, 0, 0, x << 4);
for(int x = 15; x < width() - 15; ++x)
scanline1[x] = qRgb(0, 0, 0);
//copy scanline to the other ones
for(int y = 1; y < height(); ++y)
memcpy(alphaChannel.scanLine(y), (uchar*)scanline1, width() * 4);
}
else
alphaChannel.fill(qRgb(0, 0, 0));
//Update scrolling state
bool newScrollEnabled = (singleTextWidth > width() - leftMargin);
if(newScrollEnabled != scrollEnabled)
updateText();
}
void ScrollText::timer_timeout()
{
scrollPos = (scrollPos + 2)
% wholeTextSize.width();
update();
}
Really easy. Simply repaint the text displaced by the width of the control:
void WidgetMarqueeLabel::paintEvent(QPaintEvent *evt)
{
QPainter p(this);
if(direction==RightToLeft)
{
px -= speed;
if(px <= (-textLength))
px = width();
}
else
{
px += speed;
if(px >= width())
px = - textLength;
}
p.drawText(px, py+fontPointSize, text());
__p.drawText(px-width(), py+fontPointSize, text());
p.drawText(px+width(), py+fontPointSize, text());
p.translate(px,0);
}
Something like the following should work. The padding is hard-coded at 25, which is what is sounded like you wanted. If you wanted the label to always be a certain size, you could use something like QString::leftJustified.
class MarqueeLabel : public QLabel {
public:
explicit MarqueeLabel(const QString &text) : QLabel(text), pos_(0) {
QString pad(25, ' ');
actual_text_ = text + pad;
startTimer(100);
}
protected:
void timerEvent(QTimerEvent *) {
pos_ = ++pos_ % actual_text_.length();
setText(actual_text_.mid(pos_).append(actual_text_.left(pos_)));
}
private:
QString actual_text_;
int pos_;
};
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
MarqueeLabel lbl("WidgetMarqueeLabel");
lbl.show();
return a.exec();
}