I've just started working with Arduino. I seem to write code like this often:
int input_pins[] = {6, 3, 8, 2};
int input_state[4];
void setup() {
}
void loop() {
for (int i = 0; i < 4; i++){
input_state[i] = digitalRead(input_pins[i]);
}
}
Is there a preprocessor (or something like it) to produce the following effective code?
int input_state[4];
void setup() {
}
void loop() {
input_state[0] = digitalRead(6);
input_state[1] = digitalRead(3);
input_state[2] = digitalRead(8);
input_state[3] = digitalRead(2);
}
Arduino programming has C++ underneath, why do you want to use something as limited as preprocessing?
If you start out with the following code:
///////////////////////////////////////
class DigitalRead
{
public:
DigitalRead(int pins[]);
int operator[](const int index);
int len;
int *mypins;
};
DigitalRead::DigitalRead(int pins[])
{
mypins = pins;
for(len = 0; pins[len] != -1; len++) {};
}
int DigitalRead::operator[](const int index)
{
if (index > len) return -1;
return digitalRead(mypins[index]);
}
///////////////////////////////////////
int pins[] = {6, 3, 8, 2, -1};
DigitalRead input_state = DigitalRead(pins);
void setup()
{
}
void loop()
{
// print the state of pin 6, change as needed
Serial.println(input_state[0]);
}
you can improve on things (setting the pins for input in DigitalRead::DigitalRead e.g.)j. Once things work move the stuff between /////// lines into a library.
Related
I am getting these two errors when initializing vc below.
#include <iostream>
#include <complex>
#include <math>
#include <vector>
#include <limits>
#include <list>
#include <string>
class Vector {
private:
double* elem; // elem points to an array of sz doubles
int sz;
public:
Vector(int s) :elem{ new double [s] }, sz{ s } // constructor: acquire resources
{
for (int i = 0; i != s; ++i) elem[i] = 0; // initialize elements
~Vector() { delete[] elem; } // destructor: release resources
double& operator[](int i);
int size() const;
void push_back(double);
};
double& Vector::operator[](int i)
{
// TODO: insert return statement here
// added below since the funtion needs to return a double and
return elem[i];
}
int Vector::size() const
{
return sz;
}
void Vector::push_back(double)
{
}
class Container {
public:
virtual double& operator[](int) = 0; // pure virtual function
virtual int size() const = 0; // const member function (§3.2.1.1)
virtual ~Container() {} // destructor (§3.2.1.2)
};
// use function uses Container interface.
void use(Container& c)
{
const int sz = c.size();
for (int i=0; i!=sz; ++i)
cout << c[i] << '\n';
}
class Vector_container : public Container { // List_container implements Container
Vector v;
public:
Vector_container(int s) : v(s) {} // Vector of s elements
void ˜Vector_container() {}
double& operator[](int i) { return v[i]; }
int size() const { return v.size(); }
};
void main()
{
Vector_container vc = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
use(vc)
}
I receive both errors pointing at this line Vector_container vc = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }
Error E0289 is - no instance of constructor "Vector_container::Vector_container" matches the argument list
Error C2440 'initializing': cannot convert from 'initializer list' to 'Vector_container'
I was able to solve the issue by modifying the Vector_container and initializing using Initializer-list constructor below:
class Vector_container : public Container {
std::list<double> ld;
public:
Vector_container() { }
Vector_container(initializer_list<double> il) : ld{ il } {}
~Vector_container() {}
double& operator[](int i);
int size() const { return ld.size(); }
};
double& Vector_container::operator[](int i)
{
for (auto& x : ld) {
if (i == 0) return x;
--i;
}
}
#include <iostream>
#include <vector>
#include <algorithm>
#include <queue> // std::priority_queue
using std::vector;
using std::cin;
using std::cout;
struct fj{
int indexI=0;
int freeT=0;
};
struct DereferenceCompareNode : public std::binary_function<fj, fj, bool>
{
bool operator()(const fj lhs, const fj rhs) const
{
return lhs.freeT > rhs.freeT;
}
};
class JobQueue {
private:
int num_workers_;
vector<int> jobs_;
vector<int> assigned_workers_;
vector<long long> start_times_;
void WriteResponse() const {
for (int i = 0; i < jobs_.size(); ++i) {
cout << assigned_workers_[i] << " " << start_times_[i] << "\n";
}
}
void ReadData() {
int m;
cin >> num_workers_ >> m;
jobs_.resize(m);
std::cout<<"Read fault"<<"\n";
for(int i = 0; i < m; i++)
cin >> jobs_[i];
std::cout<<"Read fault ends"<<"\n";
}
void AssignJobs() {
// TODO: replace this code with a faster algorithm.
std::cout<<"Fault point 1"<<"\n";
assigned_workers_.resize(jobs_.size());
start_times_.resize(jobs_.size());
vector<long long> next_free_time(num_workers_, 0);
std::priority_queue<int, vector<int>, std::greater<int> > thread;
std::priority_queue<fj, vector<fj>, DereferenceCompareNode > freeJob;
/*
for (int i = 0; i < jobs_.size(); ++i) {
int duration = jobs_[i];
int next_worker = 0;
for (int j = 0; j < num_workers_; ++j) {
if (next_free_time[j] < next_free_time[next_worker])
next_worker = j;
}
assigned_workers_[i] = next_worker;
start_times_[i] = next_free_time[next_worker];
next_free_time[next_worker] += duration;
}
*/
std::cout<<"dump point 2"<<"\n";
for(int i=0;i<num_workers_;i++){
thread.push(i);
}
std::cout<<"dump point 1"<<"\n";
int counter = 0;
while(jobs_.size()!=0){
std::cout<<"jobs_.size:"<<jobs_.size()<<"\n";
std::cout<<"freeJob.size:"<<freeJob.size()<<"\n";
//check logic
do{
if(freeJob.top().freeT == counter){
std::cout<<"freeJob.top().freeT:"<<freeJob.top().freeT<<"\n";
std::cout<<"counter:"<<counter<<"\n";
thread.push(freeJob.top().indexI);
freeJob.pop();
}else{
break;
}
}
while(freeJob.size()!=0);
std::cout<<"Thread:"<<thread.size()<<"\n";
while(thread.size()!=0){
if(jobs_.size()!=0){
fj currA;
currA.indexI = thread.top();
currA.freeT = jobs_.at(0)+counter;
std::cout<<"currA.indexI:"<<currA.indexI<<"\n";
std::cout<<"currA.freeT:"<<currA.freeT<<"\n";
thread.pop();
jobs_.erase(jobs_.begin());
assigned_workers_.push_back(currA.indexI);
start_times_.push_back(currA.freeT);
}else{
break;
}
}
counter++;
}
}
public:
void Solve() {
ReadData();
AssignJobs();
WriteResponse();
}
};
int main() {
std::ios_base::sync_with_stdio(false);
JobQueue job_queue;
job_queue.Solve();
return 0;
}
I am getting segmentation fault in function ReadData while taking inputs for vector jobs.
I am getting fault even when I am inside bounds of defined size.
Everything was fine when have not written AssignJob function.
Am I doing something wrong with some bounds or taking illegal inputs format or messing with some other stuff?
Am I doing something wrong
Yes, you are: freeJob starts out empty, so this is undefined behavior:
if(freeJob.top().freeT == counter){
In fact, you never push anything into freeJob, you only pop() things from it.
I created 2 libraries to use in my Arduino code. One is a HwSwitch library, the other is a HwServo library which uses the HwSwitch library.
HwSwitch Library:
HwSwitch::HwSwitch(String switchName, int switchPort, int inputType, int pressedState)
{
Name = switchName;
SwitchPort = switchPort;
_pressedState = pressedState;
_lastCheckMillis = 0;
pinMode(switchPort, inputType);
_lastPinState = digitalRead(SwitchPort);
}
bool HwSwitch::IsPressed()
{
int currentPinState = GetPinState();
return currentPinState == _pressedState;
}
bool HwSwitch::SwitchStateChanged()
{
int currentPinState = GetPinState();
if (_lastPinState != currentPinState)
{
Serial.println("---");
Serial.println("1. Now: " + String(currentPinState) + " - Prev: " + String(_lastPinState));
_lastPinState = currentPinState;
Serial.println("2. Now: " + String(currentPinState) + " - Prev: " + String(_lastPinState));
return true;
}
return false;
}
int HwSwitch::GetPinState()
{
unsigned long ms = millis();
if ((ms - _lastCheckMillis) < 50)
{
return _lastPinState;
}
_lastCheckMillis = ms;
return digitalRead(SwitchPort);
}
HwServo Library:
HwServo::HwServo(int servoPort, int zeroPoint, HwSwitch limitSwitch)
{
_servo.attach(servoPort);
_servo.write(zeroPoint);
ServoPort = servoPort;
ZeroPoint = zeroPoint;
LimitSwitch = limitSwitch;
}
void HwServo::RotateUp()
{
_servo.write(ZeroPoint + UP);
}
void HwServo::RotateDown()
{
if (!LimitSwitch.IsPressed())
{
_servo.write(ZeroPoint + DOWN);
}
}
void HwServo::Stop()
{
_servo.write(ZeroPoint);
}
And this is how I initialized it in the Arduino code:
HwServo HwServos[] = {
HwServo(9, 94, HwSwitch("S1", 14, INPUT_PULLUP, HIGH)),
HwServo(5, 90, HwSwitch("S2", 8, INPUT_PULLUP, HIGH)),
};
void setup() { }
void loop() {
for(int i = 0; i < 2; i++)
{
HwServo hwServo = HwServos[i];
if (hwServo.LimitSwitch.SwitchStateChanged())
{
SendSwitchStateUpdate(hwServo.LimitSwitch);
if (hwServo.LimitSwitch.IsPressed())
{
hwServo.Stop();
}
}
}
}
Now finally to the problem! As you can see in the HwSwitch library I output some data using Serial.println. Here I can see that _lastPinState is successfully updated, but gets reset after every loop. However, when I create a HwSwitch directly and use it, _lastPinState is not reset. In other words, the resetting of the value only seems to occur when the HwSwitch library is used inside the HwServo library.
Appearently this has something to do with the pointers? I am probably initializing my classes incorrectly, but I have no idea how to fix it. Anyone that can help with (and preferably explain) this issue?
I don't have my Arduino on me right now, but I took look and re-wrote your code, added the omitted constructors at my best guess, and got it to compile. There were some things which needed corrected. I'm sure there are other ways, but this is what I did.
For complete code, go here.
First, I created some pointers to objects I'd like to stick around, like so:
HwServo *HwServos[2];
HwSwitch *s1;
HwSwitch *s2;
HwServo *sv1;
HwServo *sv2;
Now each is reserved in memory on the Arduino.
Now, construct the objects in setup():
void setup() {
s1 = new HwSwitch("S1", 14, INPUT_PULLUP, HIGH);
s2 = new HwSwitch("S2", 8, INPUT_PULLUP, HIGH);
sv1 = new HwServo(9, 94, *s1);
sv2 = new HwServo(5, 90, *s2);
//Now, since you're going through an array:
HwServos[0] = sv1;
HwServos[1] = sv2;
}
Use that setup function!!! Maybe not always necessary, or in some cases even recommended, but it's nice to collect things which only need created once there, especially is this case.
Note that new was not used inside the scope of either object, but rather in the scope of the program... So no fancy destructors in your objects are required. Normally, you'd worry about deleting them all before program termination (or whenever best suited), but in Arduino's case, it'll just lose power and kill everything anyway.
You should change your class definitions to this:
class HwSwitch {
public:
String Name;
int SwitchPort;
int _pressedState;
int _lastCheckMillis;
int _lastPinState;
HwSwitch(String, int, int, int);
bool IsPressed();
bool SwitchStateChanged();
int GetPinState();
};
class HwServo {
public:
HwServo();
HwServo(int, int, HwSwitch &);
int ServoPort;
int ZeroPoint;
HwSwitch & LimitSwitch;
void RotateUp();
void RotateDown();
void Stop();
Servo _servo;
};
Note: I made everything public, feel free to move private stuff back to private if you wish.
I changed the constructors to:
HwSwitch::HwSwitch(String switchName, int switchPort, int inputType, int pressedState)
{
Name = switchName;
SwitchPort = switchPort;
_pressedState = pressedState;
_lastCheckMillis = 0;
pinMode(switchPort, inputType);
_lastPinState = digitalRead(SwitchPort);
}
HwServo::HwServo(int servoPort, int zeroPoint, HwSwitch &limitSwitch)
{
_servo.attach(servoPort);
_servo.write(zeroPoint);
ServoPort = servoPort;
ZeroPoint = zeroPoint;
LimitSwitch = limitSwitch;
}
And I modified loop() like so:
void loop() {
// put your main code here, to run repeatedly:
for(int i = 0; i < 2; i++)
{
if (HwServos[i]->LimitSwitch.SwitchStateChanged())
{
SendSwitchStateUpdate(HwServos[i]->LimitSwitch);
if (HwServos[i]->LimitSwitch.IsPressed())
{
HwServos[i]->Stop();
}
}
}
}
Getting a message "Got a buffer underflow!" after each write in this simple program.
Beep.hpp:
#pragma once
#include <QTimer>
#include <QAudioOutput>
class Beep: public QObject
{
Q_OBJECT
public:
explicit Beep();
virtual ~Beep();
void onTimer();
private:
QAudioOutput m_out;
QIODevice *m_outDev;
QTimer m_timer;
};
Beep.cpp:
#include "Beep.hpp"
int ms = 100;
const QAudioFormat defaultAudioFormat = []()
{
QAudioFormat format;
format.setSampleRate(8000);
format.setChannelCount(1);
format.setSampleSize(16);
format.setCodec("audio/pcm");
format.setByteOrder(QAudioFormat::LittleEndian);
format.setSampleType(QAudioFormat::SignedInt);
return format;
}();
Beep::Beep() :
m_out(defaultAudioFormat),
m_outDev()
{
m_out.setBufferSize(16 * ms);
m_outDev = m_out.start();
QObject::connect(&m_timer, &QTimer::timeout, this, &Beep::onTimer);
m_timer.setSingleShot(false);
m_timer.start(ms);
}
Beep::~Beep()
{
}
void Beep::onTimer()
{
std::vector<uint8_t> samples(16 * ms);
m_outDev->write((char*) &samples.front(), samples.size());
}
main.cpp:
#include <QCoreApplication>
#include "Beep.hpp"
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
Beep beep;
return app.exec();
}
This test program is just writing buffers with zeros. With real data there are cracking sounds.
Writing more data or changing timings makes it worse. What's wrong with this code?
Using a Timer is the wrong way to do it.
Use the notify() signal
void AudioManager::init_audio(AudioManager *mgr) {
if (mgr->stream_id == -1) return;
mgr->audio_format.setSampleRate(mgr->context->time_base.den);
mgr->audio_format.setSampleSize(16);
mgr->audio_format.setChannelCount(2);
mgr->audio_format.setCodec("audio/pcm");
mgr->audio_format.setSampleType(QAudioFormat::SignedInt);
QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());
if (!info.isFormatSupported(mgr->audio_format)) {
mgr->audio_format = info.nearestFormat(mgr->audio_format);
}
mgr->audio_out = new QAudioOutput(mgr->audio_format, nullptr);
mgr->audio_out->setNotifyInterval(15);
mgr->audio_out->setBufferSize(mgr->context->time_base.den * 4); // 1 second worth of stereo data
connect(mgr->audio_out, SIGNAL(notify()), mgr, SLOT(audio_out_notify()));
connect(mgr->audio_out, SIGNAL(stateChanged(QAudio::State)), mgr, SLOT(audio_out_state_changed(QAudio::State)));
qreal volume_out = (qreal)parent->volume / 100.0f;
mgr->audio_out->setVolume(volume_out);
mgr->audio_out_device = mgr->audio_out->start();
}
This will be called when the audio playback requires more data
void AudioManager::audio_out_notify() {
qDebug() << "Audio notify";
check_audio_playback();
}
Most of the below code will be irrelevant but it is also called is audio has stopped playing.
void AudioManager::check_audio_playback() {
if (stream_id == -1) return;
pthread_mutex_lock(&audio_mutex);
if (!audio_out->state() == QAudio::State::IdleState) {
pthread_mutex_unlock(&audio_mutex);
return;
}
if (parent->pts_start_time < 0.0) {
if (parent->Video.stream_id == -1 && decode_pos > 65) { // start playback
parent->pts_start_time = buffers[0].frame_time;
parent->sys_start_time = (double)parent->timer.elapsed() / 1000.0;
qDebug() << "Audio playback started";
} else {
pthread_mutex_unlock(&audio_mutex);
return;
}
}
if (playback_pos == decode_pos) {
pthread_mutex_unlock(&audio_mutex);
return;
}
AudioBuffer *buffer = nullptr;
double current_sys_time = ((double)parent->timer.elapsed() / 1000.0) - parent->sys_start_time;
bool bounds = false;
int skipped = 0;
while (!bounds) {
if (playback_pos == decode_pos) bounds = true;
else {
AudioBuffer *temp_buffer = &buffers[playback_pos];
double temp_time = temp_buffer->frame_time - parent->pts_start_time;
if (temp_time < current_sys_time ) {
if (buffer) {
buffer->used = false;
skipped++;
}
buffer = temp_buffer;
playback_pos++; playback_pos %= MAX_AUD_BUFFERS;
} else {
bounds = true;
}
}
}
if (skipped > 0) qDebug("Skipped %d audio buffers on playback", skipped);
if (buffer) {
audio_out_device->write((const char *)buffer->data, buffer->buffer_size);
buffer->used = false;
}
pthread_mutex_unlock(&audio_mutex);
}
The example on the Qt website wasn't that obvious http://qt.apidoc.info/5.1.1/qtmultimedia/audiooutput.html at first but when I put it in to test it wasn't too bad.
The reason was that the source of audio data wasn't a "production-quality module" (it's a dummy testing class): the timer was drifting because its real interval was 10ms plus the processing time.
Other observations:
make QAudioOutput::setBufferSize() bigger
do QAudioInput::read() and QAudioOutput::write() in chunks with size that matches QAudioInput::periodSize() and QAudioOutput::periodSize()
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();
}