Im searching for an circular progress indication Widget for Qt5 like this:
http://anthonyterrien.com/knob/
Is there something similar or is it possible to do this in Qt?
I want to set the percentage manually, it shouldn't be a spinning circle or something like that
It is very easy to write. You need just special paintEvent() and slot to setProgress(). Of course if you want to add more beauty, then you need spend some time, but here is example:
Header:
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QPaintEvent>
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
signals:
public slots:
void setProgress(int val);
protected:
void paintEvent(QPaintEvent *);
private:
double progress;
};
#endif // WIDGET_H
cpp:
void Widget::setProgress(int val)
{
progress = (double)val/100;
//yes, it is not very good, the best approach is to
//create something similar to QProgressBar
this->update();
}
void Widget::paintEvent(QPaintEvent *)
{
QPainter p(this);
QPen pen;
pen.setWidth(7);
pen.setColor(Qt::red);
p.setPen(pen);
p.setRenderHint(QPainter::Antialiasing);
QRectF rectangle(10.0, 20.0, 80.0, 80.0);
//to understand these magic numbers, look drawArc method in Qt doc
int startAngle = -90 * 16;
int spanAngle = progress * 360 * 16;
p.drawArc(rectangle, startAngle, spanAngle);
p.drawText(rectangle,Qt::AlignCenter,QString::number(progress*100)+" %");
}
Usage:
Widget wd;
wd.show();
QSlider sl;
sl.show();
QObject::connect(&sl,SIGNAL(valueChanged(int)),&wd,SLOT(setProgress(int)));
Result:
I showed here main idea, but I think that my code can be improved, for example add methods setMinimum/Maximum and setValue, as in QProgressBar, but I hope you will add additional functionality manually if you need this.
In addition to the above: who needs a simple implementation of what Chernobyl has posted there is a ready class that demonstrates a circular loading.
Related
Hi I am interested in eliding text in a QLabel in the middle of the text in C++. I found the text elide mode property but it looks it only applies to QAbstractItemViews. Also here it says QLabel can elide text that doesn't fit within it, but only in one line but then doesn't say how to do it. Also here it is super easy but it's in QML/Qt Quick. Is there an easy way to do this? Do I have to override the paint event and use QFontMetrics or something like that? I'd imagine this would be as simple as setting a text elide middle property but perhaps I am mistaken. Thanks!
Here's the code thanks to Scheff's Cat.
//elidedlabel.h
#ifndef ELIDEDLABEL_H
#define ELIDEDLABEL_H
#include <QLabel>
#include <QPainter>
class ElidedLabel : public QLabel
{
Q_OBJECT
public:
explicit ElidedLabel(QWidget *parent = nullptr);
void setText(const QString &text);
const QString & text() const { return content; }
protected:
void paintEvent(QPaintEvent *event) override;
private:
QString content;
};
#endif // ELIDEDLABEL_H
//elidedlabel.cpp
#include "elidedlabel.h"
ElidedLabel::ElidedLabel(QWidget *parent)
: QLabel(parent)
{
}
void ElidedLabel::setText(const QString &newText)
{
content = newText;
update();
}
void ElidedLabel::paintEvent(QPaintEvent *event)
{
QLabel::paintEvent(event);
QPainter painter(this);
QFontMetrics fontMetrics = painter.fontMetrics();
QString elidedLine = fontMetrics.elidedText(content, Qt::ElideMiddle, width());
painter.drawText(QPoint(0, fontMetrics.ascent()), elidedLine);
}
I've found several similar questions pertaining to this topic, but haven't pieced together a workable solution as of yet. I've got non-rectangular Qt Quick Images, each with a child MouseArea. The current onClicked event registers clicks of the parent Image in areas of transparency, which I would like to ignore. Is there a QML-only solution to do this? Would a QML-only solution be adviseable, or should the transparency determination be done in c++?
Pseudocode in the main.qml file:
Image {
id: testImage
x: 200
y: 100
width: 100
height: 150
fillMode: Image.PreserveAspectFit
source: "TestImage.svg"
MouseArea {
id: testMouseArea
anchors.fill: parent
onClicked: {
//if alpha of (mouseX, mouseY) > 0 {
//DO STUFF
//}
}
}
}
My understanding is that getImageData() won't work for this purpose, so it may not be possible to determine the alpha level of the parent Image at the (mouseX, mouseY) coordinates in QML directly.
You could use a QImage in C++ to enable querying the color of a pixel.
A short, very incomplete solution (just to outlay the idea) would be this:
*//MyMask.h
#ifndef MYMASK_H
#define MYMASK_H
#include <QObject>
#include <QImage>
#include <QString>
class MyMask : public QObject, public QImage
{
Q_OBJECT
Q_PROPERTY(QString source READ source WRITE setSource NOTIFY sourceChanged)
public:
explicit MyMask(QObject *parent = nullptr);
QString source() const;
void setSource(const QString &source);
signals:
void sourceChanged();
public slots:
QColor color(int x, int y);
private:
QString m_source;
};
#endif // MYMASK_H
// MyMask.cpp
#include "mymask.h"
#include <QRgb>
#include <QColor>
#include <QDebug>
MyMask::MyMask(QObject *parent)
: QObject(parent),
QImage()
{
QObject::connect(this, &MyMask::sourceChanged, this, [this]() { QImage::load(m_source); });
}
QColor MyMask::color(int x, int y)
{
return QImage::pixelColor(x, y);
}
QString MyMask::source() const
{
return m_source;
}
void MyMask::setSource(const QString &source)
{
if (source != m_source) {
m_source = source;
emit sourceChanged();
}
}
It's probably wiser to use the QImage as a member rather than a base class but I started out as QImage until I realized, that it is no QObject.
As already mentioned, this is no complete implementation. For example it is not possible to adjust the size, e.g. to stretch it. Further you can't take Items as source and so on. There is also no control whether it is done with loading... But maybe this serves as a start for you.
You could even install it as a EventFilter to the Item that shall be clickable, to directly filter the MouseEvents for relevance.
I'm a beginner at Qt
and c++ and I wanted to see how to use a QPainter and events in Qt but I got stuck because of an error message during the execution, my original code:
the main.cpp
#include "customwidget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QScopedPointer<QWidget> widget(new customWidget());
widget->resize(240, 120);
widget->show();
return a.exec();
}
and the header:
#ifndef CUSTOMWIDGET_H
#define CUSTOMWIDGET_H
#include <QWidget>
#include <QMouseEvent>
#include <QPoint>
#include <QPainter>
class customWidget : public QWidget
{
Q_OBJECT
public:
explicit customWidget(QWidget *parent = 0);
void paintEvent(QPaintEvent *);
void mouseMoveEvent(QMouseEvent *event);
void mousePressEvent(QMouseEvent *event);
private:
QPoint m_mousePos;
QRect m_r2;
signals:
void needToRepaint();
public slots:
};
#endif // CUSTOMWIDGET_H
and the .cpp:
#include "customwidget.h"
customWidget::customWidget(QWidget *parent) : QWidget(parent)
{
QRect m_r2;
QPoint m_mousePos;
QObject::connect(this, SIGNAL(needToRepaint()), this, SLOT(repaint()));
}
void customWidget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
// ############ First Rectangle ****************************************
QRect r1 = rect().adjusted(10, 10, -10, -10);
painter.setPen(QColor("#FFFFFF"));
painter.drawRect(r1);
// ############ Seconde Rectangle ****************************************
QRect r2(QPoint(0, 0), QSize(100, 100));
m_r2.moveCenter(m_mousePos);
QPainter painter2;
QPen pen;
painter2.setPen(QColor("#000000"));
pen.setWidth(3);
painter2.setPen(pen);
painter2.drawRect(m_r2);
update();
}
void customWidget::mouseMoveEvent(QMouseEvent *event)
{
m_mousePos = event->pos();
emit needToRepaint();
}
I tried to search it on the web and saw that it's because the QPainter isn't located in the paintEvent but it's not the case in my code, thanks for your help.
You only need one painter. The second one wasn't activated, and you don't need it anyway.
Don't ever call repaint() unless you somehow absolutely need the painting to be done before repaint() returns (that's what happens!). If you keep the event loop running properly, you won't ever need that.
Don't call update() from paintEvent(): it's nonsense (literally).
When you wish to repaint the widget, call update(): it schedules an update from the event loop. Multiple outstanding updates are coalesced to keep the event loop functional and prevent event storms.
Let the compiler generate even more memory management code for you. You've done the first step by using smart pointers - that's good. Now do the second one: hold the instance of CustomWidget by value. It doesn't have to be explicitly dynamically allocated. C++ is not C, you can leverage values.
In a simple test case, you don't want three files. Your code should fit in as few lines as possible, in a single main.cpp. If you need to moc the file due to Q_OBJECT macros, add #include "main.moc" at the end, and re-run qmake on the project to take notice of it.
This is how such a test case should look, after fixing the problems. Remember: it's a test case, not a 100kLOC project. You don't need nor want the meager 35 lines of code spread across three files. Moreover, by spreading out the code you're making it harder for yourself to comprehend.
Even in big projects, unless you can show significant build time improvements if doing the contrary, you can have plenty of small classes implemented Java-style completely in the header files. That's about the only Java-style-anything that belongs in C++.
// https://github.com/KubaO/stackoverflown/tree/master/questions/simple-paint-38796140
#include <QtWidgets>
class CustomWidget : public QWidget
{
QPoint m_mousePos;
public:
explicit CustomWidget(QWidget *parent = nullptr) : QWidget{parent} {}
void paintEvent(QPaintEvent *) override;
void mouseMoveEvent(QMouseEvent *event) override {
m_mousePos = event->pos();
update();
}
};
void CustomWidget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
auto r1 = rect().adjusted(10, 10, -10, -10);
painter.setPen(Qt::white);
painter.drawRect(r1);
auto r2 = QRect{QPoint(0, 0), QSize(100, 100)};
r2.moveCenter(m_mousePos);
painter.setPen(QPen{Qt::black, 3, Qt::SolidLine, Qt::SquareCap, Qt::MiterJoin});
painter.drawRect(r2);
}
int main(int argc, char ** argv) {
QApplication app{argc, argv};
CustomWidget w;
w.show();
return app.exec();
}
This error can happen too when the QPixmap used to create the QPainter(QPixmap) is invalid (if there is no file at such path).
Be sure your QPixmap is correct before painting on it.
I don't have much experience with Qt and I am having trouble using QPainter.
I am trying to make a simple graphing widget which takes in a number of points and to create a QVector of QPoints, and then uses this vector to draw a polygon. However, nothing is appearing right now with my implementation. I am fairly certain that I have added the widget correctly to the window, as I can see the empty space it should occupy. This leads me to believe the problem to be in the graphing widget.
Any assistance is appreciated.
header:
//graph.h
#ifndef GRAPH_H
#define GRAPH_H
#include <QWidget>
#include <QPainter>
#include <QVector>
class Graph : public QWidget
{
Q_OBJECT
public:
Graph(QWidget *parent = 0);
QSize minimumSizeHint() const;
QSize maximumSizeHint() const;
QSize sizeHint() const;
void addPoint(int w, int h);
void clearPoints();
void drawGraph();
protected:
void paintEvent(QPaintEvent *event);
private:
QPen pen;
QBrush brush;
QPixmap pixmap;
QVector<QPoint> points;
};
#endif // GRAPH_H
source:
//graph.cpp
#include "graph.h"
Graph::Graph(QWidget *parent)
: QWidget(parent)
{
points.resize(0);
setBackgroundRole(QPalette::Base);
setAutoFillBackground(true);
}
void Graph::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.setPen(QPen(Qt::NoPen));
painter.setBrush(QBrush(Qt::green, Qt::SolidPattern));
painter.setRenderHint(QPainter::Antialiasing, true);
painter.drawPolygon(points);
}
QSize Graph::minimumSizeHint() const
{
return sizeHint();
}
QSize Graph::maximumSizeHint() const
{
return sizeHint();
}
QSize Graph::sizeHint() const
{
return QSize(500, 200);
}
void Graph::addPoint(int w, int h)
{
points.append(QPoint(w*2, h*2));
}
void Graph::clearPoints()
{
points.clear();
}
void Graph::drawGraph() {
points.prepend(QPoint(0,0)); //The base points of the graph
points.append(QPoint(500,0));
update();
points.clear();
}
In drawGraph(), the call to update() posts an event notifying the widget to paint itself. You then clear the points and the drawGraph() call exits. After that, the event loop will process the update event and trigger a call to the paintEvent() but by then, there are no points in the vector of points to paint.
Don't think of the paintEvent() as painting something permanent onto the widget once that will be displayed forever until you clear it and paint something else. The paintEvent() needs to be able to paint the widget from scratch whenever it needs to be redrawn. This is often due to a request from the system when the widget is moved, minimized and restored etc. This means your vector of points needs to remain until you no longer want a polygon to be displayed or the points are changed.
It looks like you might be adding only two points to your points-list. I don't think it is possible to have a polygon with only two points; try adding a third point and see if you get a triangle.
Im having a problem of my Rectangle class not being seen as a type. I've included the proper header, and so I am confused.
shapes.h
#ifndef SHAPES_H
#define SHAPES_H
#include "Colors.h"
#include <QPoint>
#include "glwidget.h"
//class GLWidget;
class Shape
{
public:
virtual void draw();
};
class Rectangle : Shape
{
public:
Rectangle(GLWidget *w, QPoint tl, QPoint br){
glWidget = w;
topLeft = tl;
btmRight = br;
}
virtual void draw(){
// top horizontal
for(int i = topLeft.x(); i < btmRight.x(); i++){
glWidget->setPixel(i,topLeft.y(), color);
}
}
private:
QPoint topLeft,btmRight;
GLWidget *glWidget;
RGBColor color;
};
#endif // SHAPES_H
glwidget.cpp
#include <QtGui>
#include <QtOpenGL>
#include <math.h>
#include <stdio.h>
#include "glwidget.h"
#include "Shapes.h"
#ifndef GL_MULTISAMPLE
#define GL_MULTISAMPLE 0x809D
#endif
// ... a bunch of code that doesn't need to be included
void GLWidget::mousePressEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton){
// do some drawing stuff
QPoint mPos = event->pos();
switch(drawmode)
{
case 1:
currentShape = new Rectangle(this,mPos, mPos); /*** This is the error ***/
}
}
}
glwidget.h
#ifndef AGLWIDGET_H
#define AGLWIDGET_H
#include <QGLWidget>
#include "Colors.h"
class Shape;
class GLWidget : public QGLWidget
{
Q_OBJECT
public:
GLWidget(QWidget *parent = 0);
~GLWidget();
QSize minimumSizeHint() const;
QSize sizeHint() const;
void setPixel(int x, int y, RGBColor c);
public slots:
void setColor(RGBColor c);
void setDrawRectangle();
protected:
void initializeGL();
void paintGL();
void resizeGL(int width, int height);
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
private:
QPoint lastPos;
QVector<QPoint> drawPoints;
RGBColor paintColor;
int drawmode;
Shape *currentShape;
};
Sorry for the load of code... the exact error is
'Rectangle' is not a type glwidget.cpp line 85
Anybody have an idea why it wouldn't be seeing Rectangle as a type in glwidget.cpp despite my including "Shapes.h"?
Thanks in advance!
This is a bit of a longshot, but are you sure you're using moc correctly in regards to the GLWidget code? IE, have you added #include "glwidget.moc to the .cpp file or included it in your build system (qmake knows to do this for you), as well as running moc first. I only mention this because forgetting to do this many moons ago caused me to see a pile of inscrutable type-related warnings and errors.
Perhaps somewhere in the ancestry of GLWidget there is a method or member called Rectangle and there is a confusion. See the documentation for GLWidget and its ancestors
Looks like the compiler believes Rectangle is a template
Well I'm gonna go with it had something to do with the virtual function within Shape not being defined as in g++ undefined reference to typeinfo. The machine I had the strange error on is using an older version of Qt than I have on my personal machine, and my personal is having no issues with this code.
Thanks for the suggestions everyone, but I'm just gonna put this one to rest.