So I have an GUI application which was created using a GridLayout. I made a bunch of QLineEdit widgets which need to be updated constantly with new values as they come in. What is the best and most efficient way to do this? Seems like a silly question but I have done some research online and can't seem to find the best and most efficient solution. Thanks in advance.
I tried doing QTimer, it compiles...but never seems to timeout for some reason(I have debug print statement in run() that never gets printed.
time = new QLineEdit();
operationMode->setFixedWidth(150);
layout->addWidget(time, 4,7);
/*Test*/
qDebug() << "Here 1";
QTimer* timer = new QTimer(this);
timer->setSingleShot(false);
timer->setInterval(10 * 100); // 1 seconds
connect(timer, SIGNAL(timeout()), this, SLOT(run()));
setLayout(layout);
}
void MainWindow::run(void)
{
qDebug() << "Here 2";
time->setText(QTime::currentTime().toString());
qDebug() << "Here 3";
//qApp->processEvents();
}
Got my QTimer to work, apparently I needed to run timer->start(1000)
Related
I am working on Unix 19.02 and Qt 5.13.0 .
I am trying to make an introduction for my application before anything is shown up in the window. So, what I thought is to make the QPainter change the background color in a while-loop. I tried it too many times and I had a problem in the timing : "When is the moment or the event that I should make the qpainter start the introduction?". I searched and found out the best thing is to reimplement this function :
void showEvent(QShowEvent* event) {...}
inside that function, I call "repaint()", and it will do all the introduction.
I tried all of these ways :
Making the while-loop inside the "paintEvent" function.
Making the while-loop outside the "paintEvent" function, and every time we change the color of the background I call "repaint()". Surely I made a small timeout for the computer to slow it down a bit.
-Note: using these two ways above, the window shows up, but it's background-color is black and nothing shows up neither the color doesn't change until the introduction is finished!
Lastely, a while-loop that does not use "QPainter", it uses stylesheet instead and after we change the "background-color" property. We call : ui->centeralWidget->style->polish(ui->centeralWidget);
The last way, forces the window not to show up until the introduction is finished completely (it takes 5 seconds to execute the introduction, the while-loop). this is the code :
QString ss; // for opacity
QString ss_prefix = "background-color: rgba(255, 255, 255, ";
QString ss_suffix = ");";
while(i<=50) {
i++;
Opacity += 5;
qDebug() << "Changing opacity : " << ss_prefix+std::to_string(Opacity).c_str()+ss_suffix;
ui->centralWidget->setStyleSheet(
ss_prefix+std::to_string(Opacity).c_str()+ss_suffix);
ui->centralWidget->style()->polish(ui->centralWidget);
QThread::currentThread()->msleep(100); // 100 * 50 = 5000/1000 = 5 seconds to execute!
}
Nevertheless, there are other ways to make an introduction, but I really would want to know what is happening and fix this problem!
The last equation is meant to calculate the time to execute :
100(sleep) * 50(The code will get executed 50 times) = 5000 / 1000 (it is in milli, so divide by 1000) = 5 seconds
Wish you understood my situation.
You're not providing complete code, so I am partially guessing here.
You need to wait for the main app event loop to get started before starting your animation. If you call that animation routine (with the msleep() at the end) from the QMainWindow (or whatever your top-level QWidget is) constructor, the main window is never given a chance to show itself (until the animation is done).
If I'm right, you could consider starting your animation from the first QWidget::showEvent() by using a bool flag to know when the first show happens. Another way would be to start a QTimer::singleShot(0, this, &MainWindow::animate) in the constructor (the timer will trigger once the main event loop starts). There are other ways, but the main point is that you need to let the QApplication event loop to be running and the window to have already been initialized.
Having said all that, I'd probably take a different approach, maybe with a QSplashScreen or a custom QWidget to show the animation before, or while, loading the main window in the background.
Also, look into QString template/replacement system, specifically the arg() methods and how they can be used to simplify string construction.
As #MaximPaperno points out, the problem is while-loop + msleep() since they block the eventloop preventing the GUI from working normally. For these cases, Qt provides strategies such as QTimer, QXAnimation, etc. In this case a simple solution is to use QVariantAnimation:
*.h
// ...
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private Q_SLOTS:
void applyOpacity(const QVariant &value);
// ...
*.cpp
// ...
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QVariantAnimation *animation = new QVariantAnimation(this);
animation->setStartValue(0);
animation->setEndValue(255);
animation->setDuration(5 * 1000);
connect(animation, &QVariantAnimation::valueChanged, this, &MainWindow::applyOpacity);
animation->start(QAbstractAnimation::DeleteWhenStopped);
}
// ...
void MainWindow::applyOpacity(const QVariant & value)
{
bool ok;
int opacity = value.toInt(&ok);
if(ok) {
QString qss = QString("background-color: rgba(255, 255, 255, %1)").arg(opacity);
ui->centralWidget->setStyleSheet(qss);
}
}
It's a trivial situation. Just show welcome window first. When it is closed, show your main window. Use signals to achieve it.
I want to create a simple clock in my Qt program: QLabel which is updated once a second.
QLabel name: label_clock
My clock "script":
while (true)
{
QString time1 = QTime::currentTime().toString();
ui->label_clock->setText(time1);
}
But when I pase it into my program, you already know that it will stop executing it at this script - while will always give true, so rest of code under script will never execute -> program crashes.
What should I do to make this script work? I wanna create a simple clock which is being updated once a second.
You can use QTimer for this. Try something like this:
QTimer *t = new QTimer(this);
t->setInterval(1000);
connect(t, &QTimer::timeout, [&]() {
QString time1 = QTime::currentTime().toString();
ui->label_clock->setText(time1);
} );
t->start();
Of course you should enable c++11 support (add to your pro file CONFIG += c++11).
I'm currently working on a piece of software that will query gatherer.magic.com in order to build a card database. While testing my functions I found that I'm getting weird results. My functions are as follows:
void cardDB::updateDB()
{
this->view = new QWebView;
QString urlString("http://gatherer.wizards.com/Pages/Card/Details.aspx? multiverseid=");
for(int i = 1; i <= 4; i++)
{
// Load the page
view->load(QUrl(urlString+QString::number(i)));
QObject::connect(view, SIGNAL(loadFinished(bool)), this, SLOT(saveFile()));
// Wait for saveFile() to finish
QEventLoop loop;
QObject::connect(this, SIGNAL(done()), &loop, SLOT(quit()));
loop.exec();
}
}
void cardDB::saveFile()
{
QString fileName("test");
// Grab the name tag
QWebElement e = view->page()->mainFrame()->findFirstElement("div#ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_nameRow");
QString pageString = e.toPlainText();
pageString.remove(0, 11);
QFile localFile(fileName +".txt");
if (!localFile.open(QIODevice::Append))
{
// Still need to implement error catching
}
else
{
localFile.write(pageString.toUtf8());
localFile.close();
}
emit done();
}
my results come out like this:
Ankh of Mishra
Basalt Monolith
Basalt Monolith
Black Lotus
Black Lotus
Black Lotus
Black Vise
Black Vise
Black Vise
Black Vise
Before I added the event loop I would just get the i card name i times now it seems to match based on what number in the loop it is.
The following line of code added at the end of the for loop fixed my issue:
QObject::disconnect(view, SIGNAL(loadFinished(bool)), this, SLOT(saveFile()));
I believe this was because on each iteration of the loop I would connect a new signal/slot combination so each would happen when the loadFinished signal came through.
Good time of day! I have a question you'll maybe find silly and obvious, but i've already broke my head trying to solve this.
I want to plot some curve by pressing a QPushButton. I wrote the slot and connected it to the corresponding signal of this button. But when I click on it, nothing happens on the plot, although this function executes, and it can be viewed on the debugger and qDebug() output.
On the other hand, if you call this function directly, and not as a slot, it works perfectly. The only difference is the calling method: as a slot in first case and as a method in the second case.
Some code examples:
//Slot
void MainWindow::buttonClick()
{
qDebug() << "Enter";
XRDDataReader *xrdr = new XRDDataReader();
xrdr->fromFile("/home/hippi/Документы/Sources/Qt/49-3.xy");
ui->plot->plotXRD(xrdr->xValues(), xrdr->yValues());
qDebug() << "Quit";
}
void Plotter::plotXRD(QVector<double> x, QVector<double> y)
{
QwtPlotCurve *curve = new QwtPlotCurve();
curve->setRenderHint
( QwtPlotItem::RenderAntialiased, true );
curve->setPen(Qt::black, 2);
curve->setSamples(x,y);
curve->attach(mainPlot);
}
As long as autoreplotting is not enabled, you have to call replot to make changes happen.
In my game I need a stopwatch to measure and show the elapsed time.
For this purpose I made a simple widget:
ZuulStopwatchWidget::ZuulStopwatchWidget(QWidget *parent) :
QWidget(parent)
{
num = new QLCDNumber(this); // create the display
num->setDigitCount(9);
time = new QTime();
time->setHMS(0,0,0,0); // set the time
timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(showTime()));
i=0;
QString text = time->toString("hh:mm:ss");
num->display(text);
//num->setStyleSheet("* { background-color:rgb(199,147,88);color:rgb(255,255,255); padding: 7px}}");
num->setSegmentStyle(QLCDNumber::Flat); //filled flat outline
//setStyleSheet("* { background-color:rgb(236,219,187)}}");
layout = new QVBoxLayout(this);
layout->addWidget(num);
setMinimumHeight(70);
}
ZuulStopwatchWidget::~ZuulStopwatchWidget()
{
// No need to delete any object that has a parent which is properly deleted.
}
void ZuulStopwatchWidget::resetTime()
{
time->setHMS(0,0,0);
QString text = time->toString("hh:mm:ss");
num->display(text);
i=0;
stopTime();
}
void ZuulStopwatchWidget::startTime()
{
//flag=0;
timer->start(1);
}
void ZuulStopwatchWidget::stopTime()
{
timer->stop();
}
void ZuulStopwatchWidget::showTime()
{
QTime newtime;
//if(flag==1)
//i=i-1;
i=i+1;
newtime=time->addMSecs(i);
QString text = newtime.toString("mm:ss:zzz");
num->display(text);
}
But when I run my game the CPU usage is at about 13% on a 2,5Ghz i5. I know this is not problematic but it sure is ridiculous for a stupid clock.
Am I doing it completely wrong or is this common practice ?!
Many thanks in advance.
Start(1) sets the timer to trigger every millisecond
You then want to format a string and print it on the screen 16times faster than the screen is probably updating anyway