PDA

View Full Version : [PyQt4] Smooth text scrolling in QLabel (again)



Jay-D
28th October 2011, 23:13
Hello,

I'm trying to implement a QLabel with fixed width, which scrolls the text it shows if it's too long (as many multimedia players do, also known as Marquee effect).
It's basically the same question as in this thread: http://www.qtcentre.org/threads/30051-Smooth-Text-Scrolling,
however I don't understand how I have to "alter QLabel and use QWidget::scroll() with a timer", which was the solution there.
What I've tried so far is using a QTimer together with QLabel.setIndent(QLabel.indent() + 1), which works fine, but scrolls in the wrong direction (values < 0 are ignored so QLabel.indent() - 1 wouldn't work).
So the question is how can I use the scroll function on the QLabel, because right now nothing happens with QLabel.scroll(x, y).

Sidenote: I'm using PyQt 4.8.6-1.

Thanks in advance,
Jay-D

cw9000
18th November 2011, 20:36
so far I've been at least able to get scrolling by sticking a QLabel inside a QWidget and then calling
name_of_qwidget->scroll(-5,0) which makes it scroll to the left.
I haven't been able to actually see any MORE text than what's shown in the first place, though. I also don't know how to monitor when the text is off the widget and out of sight
here is some of my code

mainwindow.h:
#include <QTimer>

private slots:
void onScrollTimerTimeout();

private:
QTimer* m_scrollTimer;


mainwindow.cpp

m_scrollTimer=new QTimer();
connect(m_scrollTimer,SIGNAL(timeout()),this,SLOT( onScrollTimerTimeout()));
m_scrollTimer->start(500); //to start the timout process

void MainWindow::onScrollTimerTimeout()
{
//scrolls -5 pixels every .5 seconds
ui->scrollWidget->scroll(-5,0);
m_scrollTimer->start(500); //reset the timer to 500 milliseconds (half a second)
}



HOWEVER here is some code you might want to look at and use
http://qt-apps.org/content/show.php/MarqueeLabel?content=113690

you can see it in action herehttp://www.youtube.com/watch?v=AirpRX5EPF4
if I learn something after looking at the code for it I'll post it here

cw9000
19th November 2011, 18:15
I changed WidgetMarqueeLabel.h and.cpp to the following.


#ifndef _WIDGETMARQUEELABEL_H_
#define _WIDGETMARQUEELABEL_H_

#include <QLabel>
#include <QTimer>

class WidgetMarqueeLabel : public QLabel
{
Q_OBJECT

public: //Member Functions
enum Direction{LeftToRight,RightToLeft};
WidgetMarqueeLabel(QWidget *parent = 0,int spd=50,int dir=WidgetMarqueeLabel::RightToLeft,bool start=FALSE);
~WidgetMarqueeLabel();
void show();
void setAlignment(Qt::Alignment);
int getSpeed();

public slots: //Public Member Slots
void setSpeed(int s);
void setDirection(int d);
void updateCoordinates();
protected: //Member Functions
void paintEvent(QPaintEvent *evt);
void resizeEvent(QResizeEvent *evt);


private: //Data Members
bool autostart;
int px;
int py;
QTimer timer;
Qt::Alignment m_align;
int speed;
int direction;
int fontPointSize;
int textLength;

private slots: //Private Member Slots
void refreshLabel();
};
#endif /*_WIDGETMARQUEELABEL_H_*/


#include "WidgetMarqueeLabel.h"
#include <QPainter>
#include <QDebug>

WidgetMarqueeLabel::WidgetMarqueeLabel(QWidget *parent,int spd,int dir,bool start)
:QLabel(parent)
{
px = 0;
py = 15;
speed = spd;
direction = dir;
autostart=start;
connect(&timer, SIGNAL(timeout()), this, SLOT(refreshLabel()));
if (autostart==FALSE){
if (fontMetrics().width(text())> width())
timer.start(10);
}
else
timer.start(10);
}

void WidgetMarqueeLabel::refreshLabel()
{
if(direction==RightToLeft)
{
px -=1;
if(px <= (-textLength))
px = width();
}
else
{

px +=1;
if(px >= width())
px = - textLength;
}
timer.start(speed);
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);

p.drawText(px, py + fontPointSize, text());
p.translate(px,0);

}

void WidgetMarqueeLabel::resizeEvent(QResizeEvent *evt)
{
updateCoordinates();
QLabel::resizeEvent(evt);
}

void WidgetMarqueeLabel::updateCoordinates()
{
px=0;
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());
if (textLength>width())
timer.start(10);
else{
timer.stop();
repaint();
}
}

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();
}

I changed a few things, including the paintEvent function. The scroll was too fast the way it was, so I made sure it was setup to the timer
that they had instead of just the paintEvent. I also added a few features, like only scrolling if the text amount is larger than the space.
If the text changes in my MainWindow.cpp I emit a SIGNAL songChanged and connect it to WidgetMarqueeLabel's SLOT updateCoordinates;
here is a snippet from my code:


song=new WidgetMarqueeLabel(ui->centralWidget,1);
song->setObjectName(QString::fromUtf8("song"));
song->setGeometry(QRect(60, 10, 281, 21));
connect(this,SIGNAL(songChanged()),song,SLOT(updat eCoordinates()));
song->setText("hello");
song->setSpeed(100);

void MainWindow::onSourceChanged(Phonon::MediaSource source)
{
QMultiMap<QString,QString> tags=getTags(source);
QString file;
if(tags.value("TITLE").isEmpty())
file=source.fileName().mid(source.fileName().lastI ndexOf("/")+1,source.fileName().size());
else
file=tags.value("ARTIST")+"-"+tags.value("TITLE");
song->setText(file);
emit songChanged();
global_index=sources.indexOf(source);
populateList();
}

There isn't any reason to include 1 as a speed in my instantiation of song but at least you can see me setting the speed.
take a look at the examples in the zip file.
I tried to make a plugin for this to Designer. I was able to get Designer to display the widget, but as soon as I went to click on it it would crash Designer/Qt Creator. So I'm stuck with programatically adding a WidgetMarqueeLabel. If you have any info on this feel free to share cw9000@hotmail.com

Lykurg
20th November 2011, 16:41
After a brief look, I would advice to cache the text in a pixmap and draw that. It should be much faster. Also do not use repaint(). Use update().

enaud
17th May 2012, 21:16
Hi,
I wondered if it was possibile to 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
Wi dgetMarqueeLabel
tMarqueeLabel Widge
eLabel WidgetMarque
el WidgetMarqueeLa
WidgetMarqueeLabel

Is possibile?

thanks

enaud
18th May 2012, 12:03
up nothing here?

Lykurg
18th May 2012, 13:36
Ehm, yes, it is possible. What exactly is the problem?

enaud
19th May 2012, 21:51
Look here: I no want wait the end of text for restart text at beginning..
But i want restart text before the beginning of screen.


Hi,
I wondered if it was possibile to 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
Wi dgetMarqueeLabel
tMarqueeLabel Widge
eLabel WidgetMarque
el WidgetMarqueeLa
WidgetMarqueeLabel

Is possibile?

thanks

prsnot
30th September 2014, 10:00
Hi enaud,

Since I'm on something realy simillar to your implemetation, did you make this Marquee effect happen in the end?

thanks.