QTableView row height animation - c++

In a form, I have 2 instances of QTableView (tableView1 & tableView2) using the same model. The goal is to link items together:
In the first table view, select the object you want to define as the parent.
In the second table view, add a tick mark on the object(s) you want to define as the child. An object cannot be its own parent (it can have no parent).
The way I wanted to proceed is by hiding in tableView2 the object selected in tableView1, and to be precise, by setting the row height to 0 with an animation.
That very part works but problems start to arise when I try to implement the creation/deletion of new objects (rows get inserted or removed) or with fast change of selection with the keyboard.
Inserting a new row makes any row under it reappear if it was hidden.
Deleting the row that was selected in tableView1 makes the next row be automatically selected, with no effect on tableView2.
Selecting rows faster than the animation can handle sometimes leaves row with a different height than normal.
I strongly suspect this behavior is caused by how the selection model needs to react to new rows inserted above the selection and how in contrary, it does not need to react to the current row being deleted (the next row takes the position of the deleted one and appears to be selected instead).
Even with that in mind, I cannot get my head around it; I have tried adding more signal/slot connections (on rowsAboutToBeInserted/rowsAboutToBeRemoved) to no avail.
Below is the code I have so far.
Note: I created the sample on QtCreator because it is quicker but for some reason, the animation is extremely crappy; my actual longer code is on Visual Studio, which makes it smooth. Either some optimization is missing or using QTableWidget makes it slower than my actual code with separate QTableView and subclass of QAbstractItemModel.
Even if the animation is bad, the only way in that code for rows visible in tableView1 to appear/reappear in tableView2 is the animations.
MainWindow.h
#pragma once
#include <QtWidgets/QMainWindow>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
public slots:
void onPrependClicked();
void onInsertClicked();
void onAppendClicked();
void onDeleteClicked();
private:
Ui::MainWindow *ui;
};
MainWindow.cpp
#include "MainWindow.h"
#include "ui_MainWindow.h"
#include <QtCore/QParallelAnimationGroup>
#include <QtCore/QVariantAnimation>
#include <QtWidgets/QStyledItemDelegate>
#include <QtWidgets/QWidget>
class RowAnimator : public QVariantAnimation {
public:
RowAnimator(QObject* parent) : QVariantAnimation(parent), Row(-1) { setDuration(500); /* for the investigation, we keep a large value to make it possible to see it */ }
int Row;
};
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
//ui->tableView1 and ui->tableView2 will share the same model().
//ui->tableView1 is set to select a single row at any one time.
auto model = ui->tableView1->model();
ui->tableView2->setModel(model);
RowAnimator* hideRowAnimator = new RowAnimator(ui->tableView2),
* showRowAnimator = new RowAnimator(ui->tableView2);
{ //Setup the RowAnimator
int rowHeight = ui->tableView2->rowHeight(0);
hideRowAnimator->setStartValue(rowHeight); hideRowAnimator->setEndValue(0);
showRowAnimator->setStartValue(0); showRowAnimator->setEndValue(rowHeight);
QObject::connect(hideRowAnimator, &QVariantAnimation::valueChanged, [=](const QVariant& value) {
if (hideRowAnimator->Row >= 0)
ui->tableView2->setRowHeight(hideRowAnimator->Row, value.toInt());
});
QObject::connect(showRowAnimator, &QVariantAnimation::valueChanged, [=](const QVariant& value) {
if (showRowAnimator->Row >= 0)
ui->tableView2->setRowHeight(showRowAnimator->Row, value.toInt());
});
//Update the RowAnimator when rows are inserted or removed
QObject::connect(model, &QAbstractItemModel::rowsAboutToBeInserted, hideRowAnimator, [hideRowAnimator](const QModelIndex&, int first, int last) {
if (first <= hideRowAnimator->Row)
hideRowAnimator->Row += (1 + last - first);
}, Qt::DirectConnection);
QObject::connect(model, &QAbstractItemModel::rowsAboutToBeInserted, showRowAnimator, [showRowAnimator](const QModelIndex&, int first, int last) {
if (first <= showRowAnimator->Row)
showRowAnimator->Row += (1 + last - first);
}, Qt::DirectConnection);
QObject::connect(model, &QAbstractItemModel::rowsAboutToBeRemoved, hideRowAnimator, [hideRowAnimator](const QModelIndex&, int first, int last) {
if (last < hideRowAnimator->Row)
hideRowAnimator->Row -= (1 + last - first);
else if (first <= hideRowAnimator->Row)
hideRowAnimator->Row = -1;
}, Qt::DirectConnection);
QObject::connect(model, &QAbstractItemModel::rowsAboutToBeRemoved, showRowAnimator, [showRowAnimator](const QModelIndex&, int first, int last) {
if (last < showRowAnimator->Row)
showRowAnimator->Row -= (1 + last - first);
else if (first <= showRowAnimator->Row)
showRowAnimator->Row = -1;
}, Qt::DirectConnection);
}
QParallelAnimationGroup* parallelAnimation = new QParallelAnimationGroup(ui->tableView2);
parallelAnimation->addAnimation(hideRowAnimator);
parallelAnimation->addAnimation(showRowAnimator);
//Setup the events to animate.
//When a row gets selected in tableView1 -> hide the corresponding row in tableView2 + show the previously hiidden row.
QObject::connect(ui->tableView1->selectionModel(), &QItemSelectionModel::selectionChanged, parallelAnimation,
[hideRowAnimator, showRowAnimator, parallelAnimation](const QItemSelection& selected) {
if (selected.isEmpty() || selected.indexes().isEmpty()) {
showRowAnimator->Row = hideRowAnimator->Row;
hideRowAnimator->Row = -1;
}
else if (QModelIndex selectedIndex = selected.indexes().first(); selectedIndex.isValid()) {
showRowAnimator->Row = hideRowAnimator->Row;
hideRowAnimator->Row = selectedIndex.row();
}
else {
showRowAnimator->Row = hideRowAnimator->Row;
hideRowAnimator->Row = -1;
}
parallelAnimation->start();
}
);
}
void MainWindow::onPrependClicked()
{
auto model = ui->tableView1->model();
model->insertRow(0, QModelIndex());
// This makes the hidden row reappear
}
void MainWindow::onInsertClicked()
{
auto model = ui->tableView1->model();
model->insertRow(1, QModelIndex());
// This makes the hidden row reappear except if it was row 0 (only rows under the inserted one reappear).
}
void MainWindow::onAppendClicked()
{
auto model = ui->tableView1->model();
model->insertRow(model->rowCount(), QModelIndex());
// Apparently no animation (there is no row are under that new one).
}
void MainWindow::onDeleteClicked()
{
auto selectionModel = ui->tableView1->selectionModel();
if (selectionModel->currentIndex().isValid()) {
auto model = ui->tableView1->model();
model->removeRow(selectionModel->currentIndex().row());
}
//This triggers an animation on the row right under the one being deleted.
//At the end, that row is shown (showRowAnimator is evaluated last) even though deleting a row apparently changes the selection.
//The correct behavior is either to have nothing selected or have the newly selected row hidden in tableView2.
}
MainWindow.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QTableWidget" name="tableView1">
<property name="mouseTracking">
<bool>false</bool>
</property>
<property name="focusPolicy">
<enum>Qt::WheelFocus</enum>
</property>
<property name="styleSheet">
<string notr="true">QTableView { outline:none; }
QTableView::item:selected { background:#0078D7; }</string>
</property>
<property name="locale">
<locale language="English" country="UnitedStates"/>
</property>
<property name="inputMethodHints">
<set>Qt::ImhLatinOnly</set>
</property>
<property name="autoScroll">
<bool>false</bool>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="tabKeyNavigation">
<bool>false</bool>
</property>
<property name="showDropIndicator" stdset="0">
<bool>false</bool>
</property>
<property name="dragDropOverwriteMode">
<bool>false</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="showGrid">
<bool>false</bool>
</property>
<property name="gridStyle">
<enum>Qt::NoPen</enum>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
<property name="cornerButtonEnabled">
<bool>false</bool>
</property>
<attribute name="horizontalHeaderVisible">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>true</bool>
</attribute>
<row>
<property name="text">
<string>1</string>
</property>
</row>
<row>
<property name="text">
<string>2</string>
</property>
</row>
<row>
<property name="text">
<string>3</string>
</property>
</row>
<column>
<property name="text">
<string>1</string>
</property>
</column>
<column>
<property name="text">
<string>2</string>
</property>
</column>
<item row="0" column="0">
<property name="text">
<string>Item 1,1</string>
</property>
</item>
<item row="0" column="1">
<property name="text">
<string>Item 1,2</string>
</property>
</item>
<item row="1" column="0">
<property name="text">
<string>Item 2,1</string>
</property>
</item>
<item row="1" column="1">
<property name="text">
<string>Item 2,2</string>
</property>
</item>
<item row="2" column="0">
<property name="text">
<string>Item 3,1</string>
</property>
</item>
<item row="2" column="1">
<property name="text">
<string>Item 3,2</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QPushButton" name="appendButton">
<property name="text">
<string>Append</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QTableView" name="tableView2"/>
</item>
<item row="2" column="0">
<widget class="QPushButton" name="insertButton">
<property name="text">
<string>Insert</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="prependButton">
<property name="text">
<string>Prepend</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QPushButton" name="deleteButton">
<property name="text">
<string>Delete</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>21</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections>
<connection>
<sender>appendButton</sender>
<signal>clicked()</signal>
<receiver>MainWindow</receiver>
<slot>onAppendClicked()</slot>
<hints>
<hint type="sourcelabel">
<x>202</x>
<y>530</y>
</hint>
<hint type="destinationlabel">
<x>399</x>
<y>299</y>
</hint>
</hints>
</connection>
<connection>
<sender>insertButton</sender>
<signal>clicked()</signal>
<receiver>MainWindow</receiver>
<slot>onInsertClicked()</slot>
<hints>
<hint type="sourcelabel">
<x>202</x>
<y>559</y>
</hint>
<hint type="destinationlabel">
<x>399</x>
<y>299</y>
</hint>
</hints>
</connection>
<connection>
<sender>prependButton</sender>
<signal>clicked()</signal>
<receiver>MainWindow</receiver>
<slot>onPrependClicked()</slot>
<hints>
<hint type="sourcelabel">
<x>596</x>
<y>530</y>
</hint>
<hint type="destinationlabel">
<x>399</x>
<y>299</y>
</hint>
</hints>
</connection>
<connection>
<sender>deleteButton</sender>
<signal>clicked()</signal>
<receiver>MainWindow</receiver>
<slot>onDeleteClicked()</slot>
<hints>
<hint type="sourcelabel">
<x>596</x>
<y>559</y>
</hint>
<hint type="destinationlabel">
<x>399</x>
<y>299</y>
</hint>
</hints>
</connection>
</connections>
<slots>
<slot>onAppendClicked()</slot>
<slot>onInsertClicked()</slot>
<slot>onPrependClicked()</slot>
<slot>onDeleteClicked()</slot>
</slots>
</ui>

Related

Qt with C++: class "AddDialog" has no member "carNameEdit"

i am following this tutorial from Qt to create my first porject. I have renamed some Widgets, but I am then consistently using my own names.
But when I now try to acces the text of a label in AddDialog, AddDialog doesn´t seem to have that Label, even tough the names are the same and I have it like in the tutorial.
Here is the code:
carrental.cpp
#include "carrental.h"
#include "stdafx.h"
#include "adddialog.h"
#include "ui_adddialog.h"
CarRental::CarRental(QWidget* parent)
: QWidget(parent)
{
ui.setupUi(this);
}
void CarRental::on_addButton_clicked()
{
AddDialog dialog(this);
if (dialog.exec()) {
QString carName = dialog.carNameEdit->text();
QString carModel = dialog.carModelEdit->text();
if (!carName.isEmpty() && !carModel.isEmpty()) {
QListWidgetItem* item = new QListWidgetItem(carName, ui.carList);
item->setData(Qt::UserRole, carModel);
ui.carList->setCurrentItem(item);
}
}
}
adddialog.h:
#pragma once
#include <QDialog>
#include "ui_adddialog.h"
class AddDialog : public QDialog
{
Q_OBJECT
public:
AddDialog(QWidget* parent = Q_NULLPTR);
private:
Ui::AddDialog ui;
};
adddialog.ui:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AddDialog</class>
<widget class="QDialog" name="AddDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>345</width>
<height>230</height>
</rect>
</property>
<property name="windowTitle">
<string>AddDialog</string>
</property>
<widget class="QWidget" name="layoutWidget">
<property name="geometry">
<rect>
<x>60</x>
<y>50</y>
<width>221</width>
<height>131</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QGridLayout" name="inputLayout">
<item row="0" column="0">
<widget class="QLabel" name="carNameText">
<property name="text">
<string>Car Name:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="nameEdit">
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="carModelText">
<property name="text">
<string>Car Model:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="carModelEdit"/>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="1" column="0">
<layout class="QHBoxLayout" name="okLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="okButton">
<property name="text">
<string>OK</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections>
<connection>
<sender>okButton</sender>
<signal>clicked()</signal>
<receiver>AddDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>251</x>
<y>167</y>
</hint>
<hint type="destinationlabel">
<x>100</x>
<y>196</y>
</hint>
</hints>
</connection>
</connections>
</ui>
Thanks
carNameEdit and carModelEdit are attributes of the private ui object so you cannot access them, a possible solution is to make them public but another better option is to create public methods that expose the strings:
public:
AddDialog(QWidget* parent = Q_NULLPTR);
QString carName() const;
QString carModel() const;
private:
Ui::AddDialog ui;
QString AddDialog::carName() const
{
return ui.carNameEdit->text();
}
QString AddDialog::carModel() const
{
return ui.carModelEdit->text();
}
Then change to:
QString carName = dialog.carName();
QString carModel = dialog.carModel();
Small addition to good answer from #eyllanesc; You can use QObject::findChild Qt feature:
In your case it might be:
QLineEdit *carModelEdit= dialog.findChild<QLineEdit*>("carModelEdit");
qDebug() << carModelEdit->text();
It works, because after ui.setupUi(this); all elements of your UI become children of your widget.
Quite useful for unit testing, but whether you should use it in your production code is a bit questionable.

Wrong returned position value

I'm trying to link pushbuttons using a line that I draw using the paintEvent function, but it's returning wrong values when reading the position of the buttons. this stuff has been tried on simpler projects and it worked but when using several widget container things gets wrong I don't know why.
Image of project result:
Simpler projects:
code's header file:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QPushButton>
#include <QPainter>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
virtual void paintEvent(QPaintEvent* event);
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
code's source file:
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::paintEvent(QPaintEvent *event)
{
QPainter paint(this);
QPen pen;
pen.setColor(QColor(56, 163, 220));
pen.setWidth(2);
paint.setPen(pen);
int xstart1 = ui->pushButton_4->pos().x() + ui->pushButton_4->width();
int ystart1 = ui->pushButton_4->pos().y() + (ui->pushButton_4->height() / 2);
int xfinish1 = ui->pushButton_6->pos().x();
int yfinish1 = ui->pushButton_6->pos().y() + (ui->pushButton_6->height() / 2);
paint.drawLine(xstart1, ystart1, xfinish1, yfinish1);
int xstart2 = ui->pushButton_5->pos().x() + ui->pushButton_5->width();
int ystart2 = ui->pushButton_5->pos().y() + (ui->pushButton_5->height() / 2);
int xfinish2 = ui->pushButton_7->pos().x();
int yfinish2 = ui->pushButton_7->pos().y() + (ui->pushButton_7->height() / 2);
paint.drawLine(xstart2, ystart2, xfinish2, yfinish2);
}
code's ui:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>590</width>
<height>435</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QWidget" name="widget" native="true">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QPushButton" name="pushButton">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_2">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_3">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>268</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QPushButton" name="pushButton_4">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_5">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>183</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QPushButton" name="pushButton_6">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_7">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="widget_2" native="true">
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QTableWidget" name="tableWidget"/>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QWidget" name="widget_3" native="true">
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QPushButton" name="pushButton_8">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>590</width>
<height>21</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>

Make a Character Counter for a QLineEdit

I am trying to make a simple character counter, like the one in twitter, in QT specifically with the QLineEdit feature. Ideally, it should record the number of characters entered in a QLineEdit and display the number recorded in a separate display label. For example, spotify has a character counter when naming and adding a description to a playlist.
The function that I have declared to count the number of characters entered in the QLineEdit is defined like so:
void MainWindow::countChar()
{
QString tempString = ui->displayLabel->text(); //temp variable to hold the lineEdit's text
int output = tempString.size(); //to get the number of characters in the lineEdit
QString s = QString::number(output);//convert an int to QString
ui->CharCounter->setText(s); //display the number in the displayLabel(CharCounter)
ui->CharCounter->adjustSize();
}
I call this function in my main.cpp under the object, w.
w.countChar();
The reason I want to create a character counter function is because I have set a character limit for the QLineEdit so whatever the user enters can be filled into the main displayLabel at the minimum size of the window. I have done that by writing another function:
void MainWindow::setlineInputTextCharLimit(int limit)
{
ui->inputText->setMaxLength(limit);
}
Which I called under the same object, w:
w.setLineInputTextCharLimit(200);
QTCreator is able to successfully build but the charCounter displayLabel does not change in value after I enter some amount of text into the QLineEdit.
image of the application built
It is clear that the displayLabel for the character counter is being read and has been activated however when I enter any amount of text, the value does not change.
The result after there is some text entered into the QLineEdit
So if the displayLabel is registered and a value is being shown, the function should be working but there is definitely something wrong with it too because the value not change to anything from that '0'?.
Edit: the UI file:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>554</width>
<height>463</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralWidget">
<widget class="QWidget" name="layoutWidget">
<property name="geometry">
<rect>
<x>80</x>
<y>20</y>
<width>311</width>
<height>211</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLineEdit" name="inputText"/>
</item>
<item>
<widget class="QPushButton" name="textBtn">
<property name="text">
<string>Display Text</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="displayLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>100</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="CharCounter">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>554</width>
<height>22</height>
</rect>
</property>
</widget>
<widget class="QToolBar" name="mainToolBar">
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
</widget>
<widget class="QStatusBar" name="statusBar"/>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>
And heres the signal-slot connection:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(ui->textBtn, &QPushButton::clicked, this, &MainWindow::setText);
connect(ui->inputText, &QLineEdit::textChanged, this, &MainWindow::countChar);
}
If you want to count the texts you must count each time the text changes and for that you must use the textChanged() signal, in the following code I show an example:
#include <QApplication>
#include <QLabel>
#include <QLineEdit>
#include <QVBoxLayout>
#include <QWidget>
class CounterWidget: public QWidget
{
Q_OBJECT
Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
Q_PROPERTY(int maxLenght READ maxLenght WRITE setMaxLenght)
Q_PROPERTY(int length READ length)
public:
CounterWidget(QWidget *parent=nullptr):
CounterWidget(200, parent)
{
}
CounterWidget(int maxLength, QWidget *parent=nullptr):
QWidget(parent),
layout(this)
{
layout.addWidget(&lineEdit);
layout.addWidget(&counterLabel, 0, Qt::AlignTop | Qt::AlignRight);
connect(&lineEdit, &QLineEdit::textChanged, this, &CounterWidget::countChar);
connect(&lineEdit, &QLineEdit::textChanged, this, &CounterWidget::textChanged);
lineEdit.setMaxLength(maxLength);
countChar("");
}
QString text() const{
return lineEdit.text();
}
void setText(const QString &text){
lineEdit.setText(text);
}
int maxLenght() const{
return lineEdit.maxLength();
}
void setMaxLenght(int maxLenght){
lineEdit.setMaxLength(maxLenght);
}
int length() const{
return lineEdit.text().size();
}
signals:
void textChanged(const QString & text);
private slots:
void countChar(const QString & text){
QString text_label = QString("%1/%2").arg(text.size()).arg(lineEdit.maxLength());
counterLabel.setText(text_label);
}
private:
QVBoxLayout layout;
QLineEdit lineEdit;
QLabel counterLabel;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
CounterWidget w;
w.show();
return a.exec();
}
#include "main.moc"
I was reading the text from QLabel and not QLineEdit. So the part in my code:
QString tempString = ui->displayLabel->text();
was only reading the text from the label when I wanted to read the text entered in the QLineEdit. So I changed the code to:
QString tempString = ui->inputText->text();
and that fixed the problem.
Thanks #FrozenM and #eyllanesc

Qt Get Label To Stay Certain % Off Of Bottom

I am making a math application that will be used by kids to learn basic math skills. So far I have the title label re-sizing, and staying a certain distance from the top, but since Qt's origin is in the top-left, I couldn't line up the bottom start button. Any help of suggestion are greatly appreciated, and thank you!
Here is how far the start label should be off of the bottom:
Here is what happens when I re-size too far widthwize:
Chalkboard.cpp:
#include "chalkboard.h"
#include "ui_chalkboard.h"
Chalkboard::Chalkboard(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::Chalkboard)
{
ui->setupUi(this);
//setWindowFlags(Qt::Window | Qt::FramelessWindowHint);
id = QFontDatabase::addApplicationFont(":/fonts/chawp.ttf");
family = QFontDatabase::applicationFontFamilies(id).at(0);
chawp = family;
setFont(chawp);
scene = new QGraphicsScene(100, 100, 100, 100);
ui->graphicsView->setScene(scene);
image = new QImage(":/images/depositphotos_40177799-Seamless-Chalkboard-Texture.jpg");
brush = new QBrush(*image);
ui->graphicsView->setBackgroundBrush(*brush);
titleEffect = new QGraphicsDropShadowEffect();
titleEffect->setBlurRadius(10);
titleEffect->setColor(QColor("#e0dbd1"));
titleEffect->setXOffset(0);
titleEffect->setYOffset(0);
startEffect = new QGraphicsDropShadowEffect();
startEffect->setBlurRadius(10);
startEffect->setColor(QColor("#e0dbd1"));
startEffect->setXOffset(0);
startEffect->setYOffset(0);
ui->labelTitle->setStyleSheet("color: #e0dbd1;font: url(:/font/chawp.ttf);");
ui->labelStart->setStyleSheet("color: #e0dbd1;font: url(:/font/chawp.ttf);");
ui->labelTitle->setGraphicsEffect(titleEffect);
ui->labelStart->setGraphicsEffect(startEffect);
}
Chalkboard::~Chalkboard()
{
delete ui;
}
void Chalkboard::resizeEvent(QResizeEvent *event)
{
QFontMetrics temp(chawp);
if (windowState() != Qt::WindowFullScreen)
{
setMaximumSize(1920, 1080);
}
QFont temp1(chawp);
QFont temp2(chawp);
temp1.setPixelSize(width()/10);
temp2.setPixelSize(width()/15);
ui->graphicsView->move(0, 0);
ui->graphicsView->resize(width(), height());
ui->labelTitle->resize(width(), height());
ui->labelTitle->move(0, 15);
ui->labelTitle->setFont(temp1);
//My failed attempt at it:
if (height()/5 < 75)
{
ui->labelStart->resize(width(), height());
ui->labelStart->move(0, height() - (height() / 5));
ui->labelStart->setFont(temp2);
}
}
Main.cpp:
#include "chalkboard.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Chalkboard w;
w.show();
return a.exec();
}
Chalkboard.h:
#ifndef CHALKBOARD_H
#define CHALKBOARD_H
#include <QMainWindow>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QFontDatabase>
#include <QDebug>
#include <QGraphicsDropShadowEffect>
namespace Ui {
class Chalkboard;
}
class Chalkboard : public QMainWindow
{
Q_OBJECT
public:
explicit Chalkboard(QWidget *parent = 0);
~Chalkboard();
public slots:
void resizeEvent(QResizeEvent* event);
private:
QGraphicsDropShadowEffect * titleEffect;
QGraphicsDropShadowEffect * startEffect;
QFont chawp;
QGraphicsScene *scene;
QString family;
int id;
QImage *image;
QBrush *brush;
Ui::Chalkboard *ui;
};
#endif // CHALKBOARD_H
Chalkboard.ui:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Chalkboard</class>
<widget class="QMainWindow" name="Chalkboard">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>644</width>
<height>468</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>644</width>
<height>468</height>
</size>
</property>
<property name="windowTitle">
<string>Chalkboard</string>
</property>
<widget class="QWidget" name="centralWidget">
<widget class="QGraphicsView" name="graphicsView">
<property name="geometry">
<rect>
<x>20</x>
<y>20</y>
<width>61</width>
<height>71</height>
</rect>
</property>
</widget>
<widget class="QLabel" name="labelStart">
<property name="geometry">
<rect>
<x>120</x>
<y>290</y>
<width>311</width>
<height>131</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>35</pointsize>
</font>
</property>
<property name="text">
<string>Start</string>
</property>
<property name="alignment">
<set>Qt::AlignHCenter|Qt::AlignTop</set>
</property>
</widget>
<widget class="QLabel" name="labelTitle">
<property name="geometry">
<rect>
<x>100</x>
<y>50</y>
<width>391</width>
<height>151</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>50</pointsize>
</font>
</property>
<property name="text">
<string>Chalkboard</string>
</property>
<property name="alignment">
<set>Qt::AlignHCenter|Qt::AlignTop</set>
</property>
</widget>
<widget class="QPushButton" name="pushButton">
<property name="geometry">
<rect>
<x>40</x>
<y>220</y>
<width>131</width>
<height>61</height>
</rect>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="resource.qrc">
<normaloff>:/images/liberty-technology-arrow-1.png</normaloff>:/images/liberty-technology-arrow-1.png</iconset>
</property>
</widget>
<widget class="QPushButton" name="pushButton_2">
<property name="geometry">
<rect>
<x>444</x>
<y>200</y>
<width>111</width>
<height>61</height>
</rect>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="resource.qrc">
<normaloff>:/images/liberty-technology-arrow-2.png</normaloff>:/images/liberty-technology-arrow-2.png</iconset>
</property>
</widget>
</widget>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources>
<include location="resource.qrc"/>
</resources>
<connections/>
</ui>
I finally solved this by simply setting the QLabel's Y-Axis to (height()-(ui->pushButtonStart->height()))
Another solution:
Create a QFrame widget and put your labels and buttons inside, together with the necessary layouts and spacers.
In your resize event handler just set the proper width and height for this frame like you do for QGraphicsView (to occupy whole parent widget area).
I have modified your .ui file as a hint:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Chalkboard</class>
<widget class="QMainWindow" name="Chalkboard">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>644</width>
<height>468</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>644</width>
<height>468</height>
</size>
</property>
<property name="windowTitle">
<string>Chalkboard</string>
</property>
<widget class="QWidget" name="centralWidget">
<widget class="QGraphicsView" name="graphicsView">
<property name="geometry">
<rect>
<x>20</x>
<y>20</y>
<width>311</width>
<height>341</height>
</rect>
</property>
</widget>
<widget class="QFrame" name="frame">
<property name="geometry">
<rect>
<x>210</x>
<y>10</y>
<width>349</width>
<height>401</height>
</rect>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="labelTitle">
<property name="font">
<font>
<pointsize>50</pointsize>
</font>
</property>
<property name="text">
<string>Chalkboard</string>
</property>
<property name="alignment">
<set>Qt::AlignHCenter|Qt::AlignTop</set>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="pushButton">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>:/images/liberty-technology-arrow-1.png</normaloff>:/images/liberty-technology-arrow-1.png</iconset>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="pushButton_2">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>:/images/liberty-technology-arrow-2.png</normaloff>:/images/liberty-technology-arrow-2.png</iconset>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="labelStart">
<property name="font">
<font>
<pointsize>35</pointsize>
</font>
</property>
<property name="text">
<string>Start</string>
</property>
<property name="alignment">
<set>Qt::AlignHCenter|Qt::AlignTop</set>
</property>
</widget>
</item>
</layout>
</widget>
<zorder>graphicsView</zorder>
<zorder>frame</zorder>
<zorder>labelTitle</zorder>
</widget>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources>
<include location="resource.qrc"/>
</resources>
<connections/>
</ui>

Link QLabel to variable (two different classes) in Qt C++

Currently working on getting threading to work with Qt C++ along with using the clearest method to solve the problem. Below is some code to start 3 threads and count to 10000. What i have at the moment is when u press the start and stop buttons the counters do work (using qDebug). However what I want to do is link the counter variable to 3 labels so that when the counter goes up the label for each thread does too (however had no luck with connect as not sure how to link class methods (specifically ui->label->settext to Thread::get_time())).
Currently I am stuck on the final part, as can't find a way to link to label from inside thread.cpp if anyone can help that would be great. As you can imagine gotten a bit lost and not sure where to go from here.
object.h
#ifndef OBJECT_H
#define OBJECT_H
#include <QObject>
#include <QDebug>
#include <QThread>
#include <QApplication>
#include <QMutex>
class Object : public QObject
{
Q_OBJECT
private:
int timealive;
public:
explicit Object(QObject *parent = 0);
void setup(QThread &thread, QObject &object);
void set_time(int number);
int get_time();
bool Stop;
signals:
public slots:
void worker();
};
#endif // OBJECT_H
dialog.h
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
private slots:
void on_pushButton_clicked();
void on_pushButton_2_clicked();
void on_pushButton_3_clicked();
void on_pushButton_4_clicked();
void on_pushButton_5_clicked();
void on_pushButton_6_clicked();
private:
Ui::Dialog *ui;
};
#endif // DIALOG_H
Dialog.cpp
#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::on_pushButton_clicked()
{
ui->label->setText(QString::number(12));
}
void Dialog::on_pushButton_2_clicked()
{
}
void Dialog::on_pushButton_3_clicked()
{
}
void Dialog::on_pushButton_4_clicked()
{
}
void Dialog::on_pushButton_5_clicked()
{
}
void Dialog::on_pushButton_6_clicked()
{
}
Main.cpp
#include "dialog.h"
#include "object.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Dialog w;
w.show();
QThread thread;
Object thread_object;
thread_object.setup(thread,thread_object);
return a.exec();
}
object.cpp
#include "object.h"
Object::Object(QObject *parent) :
QObject(parent)
{
}
void Object::setup(QThread &thread,QObject &object)
{
object.moveToThread(&thread);
thread.start();
connect(&thread,SIGNAL(started()),this,SLOT(worker()));
}
void Object::worker()
{
for(int i = 0; i < 10000; i++)
{
QMutex mutex;
QMutexLocker locker(&mutex);
this->set_time(i);
qDebug() << i;
if(this->Stop)
{
break;
}
}
}
void Object::set_time(int number)
{
timealive = number;
}
int Object::get_time()
{
return timealive;
}
Dialog.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>286</width>
<height>168</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<widget class="QWidget" name="layoutWidget">
<property name="geometry">
<rect>
<x>10</x>
<y>40</y>
<width>261</width>
<height>25</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Number</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="pushButton_3">
<property name="text">
<string>Start</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_4">
<property name="text">
<string>Stop</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="layoutWidget_2">
<property name="geometry">
<rect>
<x>10</x>
<y>70</y>
<width>261</width>
<height>25</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Number</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="pushButton_5">
<property name="text">
<string>Start</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_6">
<property name="text">
<string>Stop</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="layoutWidget">
<property name="geometry">
<rect>
<x>9</x>
<y>9</y>
<width>261</width>
<height>25</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Number</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="pushButton">
<property name="text">
<string>Start</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_2">
<property name="text">
<string>Stop</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>
Cheers ION
P.s changed code to reflect that I am now using the correct way for threading.