how to make ticker in qgraphicsrectitem in Qt
i tried following
rect = new QGraphicsRectItem;
text = new QGraphicsTextItem;
scene = new QGraphicsScene(this);
ui->graphicsView->setScene(scene);
rect = scene->addRect(0,0,500,500);
text->setPlainText("HELLO from constructor");
scene->addItem(text);
QFont font;
fm = new QFontMetrics(font);
ntextWidth = fm->width(text->toPlainText());
text->setPos(0,rect->boundingRect().height()/2);
ntextHeight = fm->height();
QString strtxt = fm->elidedText(text->toPlainText(), Qt::ElideRight, ntextWidth-50, Qt::TextShowMnemonic);
qDebug() << "Here Lenght in pixels:" << ntextWidth <<"====" <<ntextHeight <<"-----"<<strtxt.contains("…");
connect(&timer, SIGNAL(timeout()),this,SLOT(update()));
timer.start(5);
}
GraphicsTicker::~GraphicsTicker()
{
delete ui;
}
void GraphicsTicker::update()
{
text->setPos(text->x()+1,text->y());
if(text->x()==rect->boundingRect().width()-ntextWidth)
{
ntextWidth = ntextWidth-1;
strNewText = fm->elidedText(text->toPlainText(),Qt::ElideRight,ntextWidth).remove("…");
text->setPlainText(strNewText);
}
if(text->x() == rect->boundingRect().width()-20)
{
text->setPlainText("HELLO from constructor");
ntextWidth = fm->width(text->toPlainText());
text->setPos(0,text->y());
}
I have used qgraphicstextitem and qgraphicsrectitem for this.
I moved the text depends on the position of rect using timer.
But it is not as perfect as Ticker
Please give me smart solution
Thanks in Advance
I have done with ticker in Qt.
I have simply used Qlable and using this I rotated text on it and then I added this lable in Qgraphicsscene with Qgraphicsproxywidget.
Thank you all.
you can make ticker by using Qpropertyanimation by simply adding Qlable into widget and set its start and end value so that it can look like a ticker is moving. For this "geometry" of label is consider for moving
Related
What is the 'proper' way to recreate a QMovie widget when it gets resized?
class Gif : public QPushButton
{
Q_OBJECT
public:
QMovie* movie = nullptr;
QTimer *timer = new QTimer(this);
int widget_width = 0;
int widget_height = 0;
Gif(QWidget* parent = 0) : QPushButton(parent) { }
void paintEvent(QPaintEvent* p)
{
// Check if the widget has been resized, if so
// delete the QLabel/QMovie and recreate them.
if (widget_width)
{
if (widget_width != width())
{
QLabel* label = this->findChild<QLabel*>("label");
label->deleteLater();
movie->deleteLater();
movie = nullptr;
}
}
// Load the gif into the QMovie/QLabel.
if (!movie)
{
auto StyleSheet = styleSheet();
QVector<QString> matches;
QRegularExpression re(R"(image:\s*url\((.*)\);)");
QRegularExpressionMatch m = re.match(StyleSheet);
for (int i = 0; i <= m.lastCapturedIndex(); i++)
matches.append(m.captured(i));
movie = new QMovie(matches[1]);
if (!movie->isValid())
qDebug() << "failed to create the QMovie.";
QLabel* label = new QLabel(this);
label->setObjectName("label");
widget_width = width();
widget_height = height();
label->setGeometry(0, 0, widget_width, widget_height);
label->setMovie(movie);
label->show();
movie->setScaledSize(QSize().scaled(widget_width, widget_height, Qt::IgnoreAspectRatio));
movie->setSpeed(150);
movie->start();
}
// Pause for 2 seconds after getting into the
// last frame.
if (movie->currentFrameNumber() == (movie->frameCount() -1))
{
movie->stop();
connect(timer, SIGNAL(timeout()), this, SLOT(resume()));
timer->start(2000);
}
}
public slots:
void resume()
{
movie->start();
}
};
Is it 'safe' to call deleteLater() and then assign movie to nullptr? won't cause any mem leak / UB?
if (widget_width != width())
{
QLabel* label = this->findChild<QLabel*>("label");
label->deleteLater();
movie->deleteLater();
movie = nullptr;
}
Also, is there any alternative to QMovie for playing a gif in a gui? it uses a lot of CPU when the gif is medium/high size
Sorry I can not offer the answer to your main question.
With your second question, "a lot of CPU usage" when playing QMovie. I found a method to set the CacheMode of QMovie. It will help to reduce the CPU usage of QMovie playing gif.
movie.setCacheMode(QMovie.CacheMode.CacheAll)
In my QGraphicsView, I have many QGraphicsItem. I want to search specific QGraphicsItem out of all the items present in view and highlight the matched item. For highlighting item, I am trying to use paintEvent.
So I am calling paintEvent, but not understanding how to heighlight border of the matched object ?
Should I need co-ordinates of that matched object ?
I tried like this:
foreach(QGraphicsItem* currentItem, _scene->items())
{
pEvent = false;
QGraphicsRectItem* rItem = qgraphicsitem_cast<QGraphicsRectItem*>(currentItem);
if(rItem)
{
// some logic to get i->Name()
QString name1 = i->Name();
QString name2 = "reN"; // I want to find reN named item in view
if(name1 == name2)
{
pEvent = true;
qDebug()<<"Object Found ";
this->repaint();
break;
}
}
}
void myClass::paintEvent(QPaintEvent *event) {
Q_UNUSED(event);
qDebug()<<"In paint event ";
if(pEvent)
{
QPainter qp(this);
drawBody(&qp);
}
}
void myClass::drawBody(QPainter *qp) {
Q_UNUSED(qp);
// want logic for heighlighting border of the item
}
I understand from your problem that you are overridng QGraphicsView in MyClass since paintEnent is defined QWidgets classes.
To solve your problem, if I understand correctly. It's necessary to save the QGraphicsRectItem coordinates:
QRectF rectF = item.boundingRect();
then use the following in the function drawBody:
qp.save();
const float width = 5;
QPen pen;
pen.setWidth(width );
pen.setColor("green");
painter->drawRect(rectF.x(), rectF().y(),
rectF().width(), rectF().height());
qp.restore();
I am trying to load images from a folder and view the previous and next images by clicking a button on the GUI (similar to Windows Image Viewer). The names of the images in this folder are xxxx_00.jpg to xxxx_99.jpg, so I use index++ and index-- to change the filename when clicking the buttons.
My codes work well for displaying the first image, but when I click a button to view the previous or the next images, it always shows
QPixmap::scaled: Pixmap is a null pixmap
and returns an empty image (the first image disappeared but new image didn't display).
Here's my code:
In mainwindow.cpp
void MainWindow::on_pushButton_4_clicked() //previous image
{
if (index < 1)
{
index = 99;
QLabel label;
label.setText("Go back");
label.show();
}
else
{
index--;
}
RefreshFilename();
loadimage();
}
void MainWindow::on_pushButton_5_clicked() //next image
{
if (index > 98)
{
index = 0;
QLabel label;
label.setText("ALL SET");
label.show();
}
else
{
index = index + 1;
}
RefreshFilename();
loadimage();
}
void MainWindow::loadimage()
{
// image.load(filename);
// im = image.scaled(500,500,Qt::KeepAspectRatio);
imageObject = new QImage();
imageObject->load(filename);
image = QPixmap::fromImage(*imageObject);
im = image.scaled(400,400,Qt::KeepAspectRatio);
scene = new QGraphicsScene(this);
scene->addPixmap(image);
scene->setSceneRect(image.rect());
ui->mainimage->setScene(scene);
}
I have spent 2 whole days on debugging this, but still have no idea. I am looking forward to any advice and support!
BTW The Refreshfilename Function works fine, so I did not paste it here.
Cause
Since I do not know what RefreshFilename(); does, I cannot tell exactly what the cause is.
However, I see a major flaw in your code, i.e. you create a new scene each time MainWindow::loadimage is called and this causes a memory leak.
When you provide more details I will be more specific here.
Solution
Set the scene once and add a QGraphicsPixmapItem to it, then in the loadImage update the pixmap of the item.
Keep the current number in a class attribute.
Again, I will be more specific once details are added.
Example
In any case (waiting for you to provide a MVCE), I have prepared a working example according to your task description:
#define IMAGE_COUNT 99
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
m_imgNum(0),
m_item(new QGraphicsPixmapItem())
{
auto *widget = new QWidget(this);
auto *layoutMain = new QVBoxLayout(widget);
auto *layoutButtons = new QHBoxLayout();
auto *btnPrev = new QPushButton(tr("Previous"), this);
auto *btnNext = new QPushButton(tr("Next"), this);
auto *view = new QGraphicsView(this);
view->setScene(new QGraphicsScene(this));
view->scene()->addItem(m_item);
layoutButtons->addStretch();
layoutButtons->addWidget(btnPrev);
layoutButtons->addWidget(btnNext);
layoutButtons->addStretch();
layoutMain->addWidget(view);
layoutMain->addLayout(layoutButtons);
setCentralWidget(widget);
resize(640, 480);
loadImage();
connect(btnPrev, &QPushButton::clicked, [this](){
if (m_imgNum > 0)
m_imgNum--;
else
m_imgNum = IMAGE_COUNT;
loadImage();
});
connect(btnNext, &QPushButton::clicked, [this](){
if (m_imgNum < IMAGE_COUNT)
m_imgNum++;
else
m_imgNum = 0;
loadImage();
});
}
void MainWindow::loadImage()
{
m_item->setPixmap(QString("images/image_%1.jpg").arg(m_imgNum, 2, 10, QChar('0')));
}
where m_imgNum, m_item and loadImage are declared in the header as:
private:
inline void loadImage();
int m_imgNum;
QGraphicsPixmapItem *m_item;
I need to scale a QtableWidget (the behaviour should be like a zoom).
But when I reimplement the paintEvent of QtableWidget and set the scale manually, like this:
void MyTableWidget::paintEvent ( QPaintEvent * event )
{
QTableWidget::paintEvent(event);
QPainter p(viewport());
p.scale(m_scale,m_scale);
p.drawRect( 0, 0, width()-1, height()-1);
}
only the border is rescaled :
And I don't see any paintEvent on QTableWidgetItem, so how can I rescale all my table ?
Thank you in advance and have a good day.
EDIT ----------------------------------------
The behaviour may seems strange so here is some explanations:
This QT window is a child of an acrobat reader window. When I unzoom the PDF on acrobat, I resize my QT window to keep the same proportions, but I would like to scale the content of the window.
example: If I unzoom my PDF, I decrease the size of my QT window to keep the same proportions, and I want to scale the content of my QT window to this new size (decrease the display size, like an unzoom). Is it clear ? :o
But for instance the view donesn't fit the window, I have this:
And I want this:
And when I zoom on my PDF, I increase the window size and scale up the content, like this:
Thank you very much for your help and your time.
Use QGraphicsScene with your QTableWidget instead, but it is not very difficult:
QGraphicsScene *scene = new QGraphicsScene(this);
ui->graphicsView->setScene(scene);
QTableWidget *wgt = new QTableWidget;
wgt->setColumnCount(10);
wgt->setRowCount(10);
for (int ridx = 0 ; ridx < wgt->rowCount() ; ridx++ )
{
for (int cidx = 0 ; cidx < wgt->columnCount() ; cidx++)
{
QTableWidgetItem* item = new QTableWidgetItem();
item->setText(QString("%1").arg(ridx));
wgt->setItem(ridx,cidx,item);
}
}
QGraphicsProxyWidget *pr = scene->addWidget( wgt );
pr->moveBy(10,10);
Scale view with:
ui->graphicsView->scale(2,2);
Better way for zooming is zoom in out by wheel. Subclass view or use eventFilter. For example:
Header:
#ifndef MYQGRAPHICSVIEW_H
#define MYQGRAPHICSVIEW_H
#include <QGraphicsView>
class MyQGraphicsView : public QGraphicsView
{
Q_OBJECT
public:
explicit MyQGraphicsView(QWidget *parent = 0);
signals:
protected:
void wheelEvent(QWheelEvent* event);
};
#endif // MYQGRAPHICSVIEW_H
Cpp:
#include "myqgraphicsview.h"
#include <QPointF>
MyQGraphicsView::MyQGraphicsView(QWidget *parent) :
QGraphicsView(parent)
{
}
void MyQGraphicsView::wheelEvent(QWheelEvent* event) {
setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
// Scale the view / do the zoom
double scaleFactor = 1.15;
if(event->delta() > 0) {
// Zoom in
scale(scaleFactor, scaleFactor);
} else {
// Zooming out
scale(1.0 / scaleFactor, 1.0 / scaleFactor);
}
// Don't call superclass handler here
// as wheel is normally used for moving scrollbars
}
Usage:
MyQGraphicsView *view = new MyQGraphicsView;
view->setScene(scene);//same scene
view->show();
Result as you want:
Note that user still able to edit data etc, functionality didn't change.
Additional example, like in the Qt books.
(same MyQGraphicsView class)
#include "myqgraphicsview.h"
#include <QGraphicsProxyWidget>//and other needed includes
//just to show that signals and slots works
//with widget which placed in graphicsview
void print(int row, int column)
{
qDebug() << row+1 << column+1;
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget *widget = new QWidget;//container
QVBoxLayout *lay = new QVBoxLayout;
MyQGraphicsView * view = new MyQGraphicsView;//view as part of UI
QGraphicsScene * scene = new QGraphicsScene;
QTableWidget *wgt = new QTableWidget;//table which will be added to graphicsView
QObject::connect(wgt,&QTableWidget::cellPressed,print);//connection using Qt5 style(and power)
wgt->setColumnCount(10);
wgt->setRowCount(10);
for (int ridx = 0 ; ridx < wgt->rowCount() ; ridx++ )
{
for (int cidx = 0 ; cidx < wgt->columnCount() ; cidx++)
{
QTableWidgetItem* item = new QTableWidgetItem();
item->setText(QString("%1").arg(ridx));
wgt->setItem(ridx,cidx,item);
}
}
QPushButton *butt = new QPushButton("click");
lay->addWidget(view);
lay->addWidget(butt);
widget->setLayout(lay);
QGraphicsProxyWidget *pr = scene->addWidget( wgt );
pr->moveBy(10,10);
view->setScene(scene);
widget->show();
return a.exec();
}
Result:
As you can see, user can scale table, edit data and signals and slots works. All fine.
I have the following problem with scrollbars in graphics view. My application takes a PDF file and creates (in some way) a QImage out of it. The QImage is then converted to QPixmap, which is used to create a QGraphicsScene and from the QGraphicsScene I create a QGraphicsView. The QGraphicsView is added to the central widget and displayed.
The code looks approximately like this
QImage image;
image = loadImage(path);
QPixmap pixmap;
pixmap.convertFromImage(image);
scene = new QGraphicsScene(this);
scene->addPixmap(pixmap);
view = new QGraphicsView(scene);
textEdit = new QTextEdit(this)
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(view);
layout->addWidget(textEdit);
QWidget *widget = new QWidget;
widget->setLayout(layout);
setCentralWidget(widget);
In the application, the view gets updated every time the PDF file changes. Now the problem is that the thing, which is in the PDF file, can also change in size and the scrollbars get messed up. I want the scrollbars after update to be in a position, such that I will see the same part of the PDF file as I saw before the update.
Can you give me some advice on how to accomplish this? I've searched for this issue, but nothing has worked in my case so far (I could have also be doing something wrong).
Thank you for your answers!
Before changing the view's contents remember scrollbar positions using view->horizontalScrollBar()->value() and view->verticalScrollBar()->value().
After changing reset previous values using setValue(int).
It's bad that you didn't provide any code that you have tried to use to accomplish this so I can't tell you what were you doing wrong.
Here is an example:
int pos_x = view->horizontalScrollBar()->value();
int pos_y = view->verticalScrollBar()->value();
pixmap_item->setPixmap(QPixmap::fromImage(new_image));
view->horizontalScrollBar()->setValue(pos_x);
view->verticalScrollBar()->setValue(pos_y);
Here pixmap_item is the stored result of scene->addPixmap(pixmap). It has QGraphicsPixmapItem* type.
I've changed the code, but it behaves strangely.
QImage image;
image = loadImage(path);
QPixmap pixmap;
pixmap.convertFromImage(image);
scene = new QGraphicsScene(this);
scene->addPixmap(pixmap);
int hsbv = -1;
int vsbv = -1;
if (view != NULL) {
hsbv = view->horizontalScrollBar()->value();
vsbv = view->verticalScrollBar()->value();
}
view = new QGraphicsView(scene);
textEdit = new QTextEdit(this)
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(view);
layout->addWidget(textEdit);
QWidget *widget = new QWidget;
widget->setLayout(layout);
setCentralWidget(widget);
view->horizontalScrollBar()->setVealue(hsbv);
view->verticalScrollBar()->setValue(vsbv);
When I print out the view->horizontalScrollBar()->value() at the end, I always get 83 and I get 479 for the view->verticalScrollBar()->value(), which is very wierd, but when I print hsbv and vsbv I get reasonable numbers.