Consider this problem.
I have a QLineEdit in my tool and I should organize a support as follows. The text of LineEdit must contain only double values, separated my comas.
F.e. 6.2 , 8, 9.0, 55
I also must validate, that user cannot input any other character but the numbers and comas. Also I should write a method which convert this text into vector.
Initially I thought about QRegExp and boost::spirit. But it can be hard using these technique.
Any ideas?
Use next custom Validator.
Header:
#ifndef VALIDATOR_H
#define VALIDATOR_H
#include <QValidator>
class Validator : public QValidator
{
Q_OBJECT
public:
explicit Validator(QObject *parent = 0);
signals:
public slots:
public:
QValidator::State validate(QString & input, int & pos) const;
};
#endif // VALIDATOR_H
Cpp:
#include "validator.h"
#include <QDoubleValidator>
#include <QDebug>
Validator::Validator(QObject *parent) :
QValidator(parent)
{
}
QValidator::State Validator::validate(QString &input, int &pos) const
{
qDebug() << input<< pos;
QDoubleValidator val;
val.setLocale(QLocale(QLocale::German,QLocale::Germany));
input.remove(" ");
QStringList list = input.split(",");
foreach ( QString var, list) {
int i = 0;
if(val.validate(var,i) == QValidator::Invalid)
return QValidator::Invalid;
}
return QValidator::Acceptable;
}
Usage:
ui->lineEdit->setValidator(new Validator);
Explanation about: val.setLocale(QLocale(QLocale::German,QLocale::Germany));
You said that you want use 6.2,... but . and , is different decimal point in different countries. So I wrote example accordingly to your question. German Locale always think that . is a correct point.
But I recommend you to use locale-specific decimal point and use for this Purpose ; as separator instead of coma.
There are mistakes, so try this. Edit(improved):
QValidator::State Validator::validate(QString &input, int &pos) const
{
qDebug() << input<< pos;
QRegExpValidator reg(QRegExp("[0-9]+(\\.[0-9]+)?$"));
input.remove(" ");
if(input.contains(",,") || input.startsWith(","))
return QValidator::Invalid;
QStringList list = input.split(",");
qDebug()<< list;
bool isOk = true;
foreach ( QString var, list) {
int i = 0;
if(reg.validate(var,i) == QValidator::Invalid)
return QValidator::Invalid;
if(reg.validate(var,i) == QValidator::Intermediate)
isOk = false;
}
if(isOk)
return QValidator::Acceptable;
else
return QValidator::Intermediate;
}
Related
Hi I have a class for generate MD5 of some file in Qt, (I use tuple to return multiple value from it), I want to run it on other thread because its possible that generate all file MD5s takes some time and its freeze gui
I decide to use QtConcurrentRun for run it on other Thread, but so far I don't have any Idea about How can I get all tuple return values
here is my code
HashGen.h
#pragma once
#include "stdafx.h"
class HashGen : public QObject
{
Q_OBJECT
public:
HashGen(QObject *parent = nullptr);
private:
QString Md5_gen(QString const& fname);
public slots:
std::tuple<int, int, int> check_sequential();
};
HashGen.cpp
#include "stdafx.h"
#include "HashGen.h"
HashGen::HashGen(QObject *parent)
: QObject(parent)
{
}
QString HashGen::Md5_gen(QString const& fname)
{
//Generate MD5 of giving file name
}
std::tuple<int, int, int> HashGen::check_sequential() {
QString file1[2] = { "8c0b1e6492078bdc113faae3d34fa5c5", "" }; // empty "" fill with other MD5 hash later
QString file2[4] = { "0547f42982dd9edb7b47931d00efff15", "", "", "" };
QString file3[2] = { "57f08e690e2655749291b2da4be8b021", "" };
QString file1_H = Md5_gen("/proj/file.zip");
QString file2_H = Md5_gen("/proj/file2.zip");
QString file3_H = Md5_gen("/proj/file3.zip");
int file1_status = 0;
int file2_status = 0;
int file3_status = 0;
for (int i = 0; i < 2; i++)
{
if (file1[i] != "nofile")
{
if (file1[i] == file1_H)
{
file1_status = i;
break;
}
else { file1_status = 422; } // Just a random number mean file doesn't match any MD5
}
else
{
file1_status = 404; // Just a random number mean file doesn't exist
break;
}
}
for (int i = 0; i < 4; i++)
{
if (file2[i] != "nofile")
{
if (file2[i] == file2_H)
{
file2_status = i;
break;
}
else { file2_status = 422; }
}
else
{
file2_status = 404;
break;
}
}
for (int i = 0; i < 2; i++)
{
if (file3[i] != "nofile")
{
if (file3[i] == file3_H)
{
file3_status = i;
break;
}
else { file3_status = 422; }
}
else
{
file3_status = 404;
break;
}
}
return { file1_status, file2_status, file3_status}; // Return all file status
mainwindow.cpp
void mainwindow::on_pushButton_clicked()
{
QFuture<std::tuple<int, int, int>> Hash = QtConcurrent::run(Gen, &HashGen::check_sequential);
QFutureWatcher<std::tuple<int, int, int>>* watcher = new QFutureWatcher<std::tuple<int, int, int>>;
connect(watcher, &QFutureWatcher<std::tuple<int, int, int>>::finished, this, &MafiaDEDLFox::AfterHash);
watcher->setFuture(Hash);
}
one other problem is I need to use QFuturewatcher to watch for QFuture, but I don't know where is best place to declare it (so its don't remove when function go out of scope)
sorry if I cant explain my problem right, but I hope someone help me, thanks
Instead of getting value from QFutureWatcher you can send it via signal. And we can create a disposable QFutureWatcher that delete itself when the job done.
class HashGen : public QObject
{
Q_OBJECT
public:
HashGen(QObject *parent = nullptr);
private:
QString Md5_gen(QString const& fname);
public slots:
std::tuple<int, int, int> check_sequential() {
...
// notify when completed
emit check_sequential_completed(file1_status, file2_status, file3_status);
return { file1_status, file2_status, file3_status}; // Return all file status
}
void async_check_sequential() {
// create future watcher as child
auto futureWatcher = new QFutureWatcher<void>(this);
// kill yourself when you done
connect(futureWatcher, &QFutureWatcher<void>::finished, futureWatcher, &QObject::deleteLater);
// Wait for finish of computation when HashGen about to die
connect(this, &QObject::destroyed, [futureWatcher](){ futureWatcher->waitForFinished(); });
// start check sequential in another thread
futureWatcher.setFuture(QtConcurrent::run(this, &HashGen::check_sequential));
}
signals:
void check_sequential_completed(int, int, int);
};
Connect check_sequential_completed to slot where you need.
connect(Gen, &HashGen::check_sequential_completed, this, &MafiaDEDLFox::AfterHash);
You don't have to make future watcher disposable of course. If you find it inefficient to create new object every time, you can keep it as member of HashGen.
Note:
Instead of passing 3 int atguments, it is possible to send std::tuple<int, int, int>. You have to register meta type for queued connections which is a connection type used in interthread connections.
Q_DECLARE_METATYPE(std::tuple<int, int, int>);
qRegisterMetaType<std::tuple<int, int, int>>();
Edit1:
QFutureWatcher<std::tuple<int,int,int>> replaced by QFutureWatcher<void>, no need store result in this solution.
Edit2:
Connection added that waits for asynchronous computation to finish, when HashGen object destroyed. So threads won't keep running on a dead object. But it will block the main thread for a while when HashGen destroyed. Actually that's the whole point of using QFutureWatcher, otherwise if you are sure there will be no asynchronous computation when destroying HashGen object, no need to use QFutureWatcher. Just QtConcurrent::run is suffice.
Example Using Only QtConcurrent::run
Following code snipped is from an actual working project.
class AddPrinterInvoker : public QObject
{
Q_OBJECT
public:
AddPrinterInvoker(QObject* parent = nullptr);
QStringList scanAddresses() {
...
emit addressListReady(addressList);
return addressList;
}
public slots:
void asyncScanAddresses() { QtConcurrent::run(this, &AddPrinterInvoker::scanAddresses); }
signals:
void addressListReady(QStringList addressList);
}
Connections
connect(scanButton, SIGNAL(clicked()), m_invoker, SLOT(asyncScanAddresses()));
connect(m_invoker, SIGNAL(addressListReady(QStringList)), this, SLOT(updateAddressList(QStringList)));
I have a vector of structures vector<S> sData which my structure contains 5 elements (name, age, techs, projects, language)
The user can input name and it should output the structures that equal to that name and for the other elements too.
My problem is that the user can input two of them only and it should only check for those like user can input age and language it should output the list of people who have the same age and language. If you imagine you can write so many if statements so it will give a right output because you have 5 elements,
and the user can choose 5 of them 4 and randomly he can input 3 or 4 of them and my program should understand that logic.
In my code I'm writing with Qt widget application. When the user didn't enter any of the strings(name, techs, projects, rate), the default value is NULL and the age is -1.
struct S
{
QString name;
int age;
QString techs;
QString projects;
QString rate;
};
QVector<S> sData;
QVector<int> indexData;
//this is how i did for the name
indexData.clear();
if(searchName!=NULL)//i don't want to write this if for name and age ||
// name and tech ... then age and tech etc.
{
for(int i=0;i<sData.count();++i)
{
if(searchName==sData[i].name)
{
indexData.push_back(i);
}
}
}
I'd suggest a little code refactor. Let's consider adding a helper structure SearchS which holds all params you are looking for (and comparison operator):
struct S {
QString name;
int age = -1;
QString techs;
QString projects;
QString rate;
};
struct SearchS {
std::optional<QString> name;
std::optional<int> age;
std::optional<QString> techs;
std::optional<QString> projects;
std::optional<QString> rate;
static bool string_match(const QString& s, const std::optional<QString>& search_s) {
return !search_s || *search_s == s;
}
bool operator==(const S& s) const noexcept {
return string_match(s.name, name) &&
string_match(s.techs, techs) &&
string_match(s.projects, projects) &&
string_match(s.rate, rate) &&
(!age || *age == s.age);
}
};
This allows you to iterate over data vector and filter results.
The cleanest way to do this I think is using ranges-v3:
std::vector<S> sData;
SearchS target;
target.name = "John";
target.age = 28;
// ... other params if needed ...
auto filtered_items = data | ranges::views::filter([&target](const auto& item) {
return target == item;
});
Here you can apply other filters... Or iterate over the filtered results:
for(auto&& item : filtered_items) {
// ... add your code here ...
}
Does this help?
struct S
{
QString name;
int age;
QString techs;
QString projects;
QString rate;
};
QVector<S> sData;
QVector<int> indexData;
int main() {
searchName = ...;
searchAge = ...;
searchTechs = ...;
...
auto it = std::find_if(std::begin(sData), std::end(sData),
[&searchName, &searchAge, &searchTecs, ...](const S& s)
{
bool eq = true;
if (searchName)
eq &= (searchName == s.name);
if (searchAge)
eq &= (searchAge == s.age);
if (searchTechs)
eq &= (searchTechs == s.techs);
....
return eq;
}
if (it == std::end(sData))
std::terminate(); //element not found
// *it is the desired element
}
or if you want to find every element matching
int i = 0;
for (const S& s : sData) {
bool eq = true;
if (searchName)
eq &= (searchName == s.name);
if (searchAge)
eq &= (searchAge == s.age);
if (searchTechs)
eq &= (searchTechs == s.techs);
....
if (eq)
indexData.push_back(i);
++i;
}
I am new and really unexperienced with coding, but we got a task at the university where I ran into a problem I could not solve by myself. In one class a vector causes a problem I do not understand. After hours of trial and error I am a bit tired of guessing... So, I try it here and hope for some help.
The program is supposed to draw an diagram based on some Input variables the user can change. At the moment I am trying to implement the painting of the graph. The Input and the calculation of the data works.
One aspect of the task is that we have to safe the calculated data in a vector and pass this vector as a pointer to other functions, for example the painter.
Now, here the problem occures. Any interaction with the vector m_punkte I passed to paintEvent causes a crash of the program. I could narrow the problem down to commands like: vector->begin() or vector->end(). In the instant the program tries to execute them, windows stops the program.
At first I thought the vector could be somehow passed wrong and I am trying to call an empty vector or the address is undefined, but even commands like vector->emty() causing the crash. So I believe it is a more general error which is caused by the framework or something like that.
After running the debugging tool, it also told me that the error is caused by the mentioned commands and further more told me this:
Der Prozess wurde wegen eines Signals vom Betriebssystem angehalten.
Name des Signals : SIGSEGV
Bedeutung : Segmentation fault
I guess that's some kind of memory problem, right?
I would be really happy if someone could help me, so I can work again on the actual task and not guessing why this vector is somehow broken
What I can also tell is that I am working with Qt Creator and the program is running as an QtWidget program if that helps.
Dropbox link to a copy of the programm:
https://www.dropbox.com/sh/l5e220lk5y7jx2z/AAAdG7AjHiiKJxOKWT-zrP8ia?dl=0
I Hope now my question is clear and this helps more to find the origin of the error.
datenpunkt.h
#ifndef DATENPUNKT_H
#define DATENPUNKT_H
#include <vector>
struct datenpunkt
{
float temperatur;
float widerstand;
float laenge;
};
typedef std::vector<datenpunkt> PunktVektor;
#endif // DATENPUNKT_H
diagrammwidget.h
#ifndef DIAGRAMMWIDGET_H
#define DIAGRAMMWIDGET_H
#include "datenpunkt.h"
#include <QWidget>
namespace Ui {
class DiagrammWidget;
}
class DiagrammWidget : public QWidget
{
Q_OBJECT
public:
explicit DiagrammWidget( QWidget *parent = 0);
~DiagrammWidget();
void setPunkte (PunktVektor *punkte);
void setUntere_temp(int untere_temp);
void setObere_temp(int obere_temp);
void setAufloesung(int aufloesung);
PunktVektor *punkte() const;
protected:
void paintEvent(QPaintEvent *event);
private:
Ui::DiagrammWidget *ui;
PunktVektor *m_punkte;
int m_untere_temp = 0;
int m_obere_temp = 0;
int m_aufloesung = 0;
};
#endif // DIAGRAMMWIDGET_H
dialog.h
#ifndef DIALOG_H
#define DIALOG_H
#include "datenpunkt.h"
#include <string.h>
#include <QDialog>
#include <QString>
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
void werteberechnung(PunktVektor *m_PunktListe);
float untere_temp() const;
void setUntere_temp(float untere_temp);
float obere_temp() const;
void setObere_temp(float obere_temp);
int aufloesung() const;
void setAufloesung(int aufloesung);
QString werkstoff() const;
void setWerkstoff(QString werkstoff);
float materialkonstante() const;
void setMaterialkonstante(float materialkonstante);
float bezugswiderstand() const;
void setBezugswiderstand(float bezugswiderstand);
float wiederstand() const;
void setWiederstand(float wiederstand);
float temperatur() const;
void setTemperatur(float temperatur);
private slots:
void on_speichern_clicked();
void on_verwerfen_clicked();
void on_zuruecksetzen_clicked();
private:
Ui::Dialog *ui;
PunktVektor *m_PunktListe;
QString m_werkstoff = "A 34-2/30 (SIEMENS)";
float m_materialkonstante = 3440; // B/ K
float m_bezugswiderstand = 5000; // Rto/ Ohm
float m_untere_temp = 0 + 272.15;
float m_obere_temp = 300 + 272.15;
int m_aufloesung = 10;
float m_RT= 0; // Rt/ Ohm
float m_To = 20 + 272.15;
float m_T = 0; // T/ C°
float m_UD; //Untere Dekadengrenze
float m_OD; //Obere Dekadengrenze
};
#endif // DIALOG_H
diagrammwidget.cpp
#include "diagrammwidget.h"
#include "ui_diagrammwidget.h"
#include "datenpunkt.h"
#include <vector>
#include <QtCore>
#include <QtGui>
DiagrammWidget::DiagrammWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::DiagrammWidget){
ui->setupUi(this);
m_punkte = 0;
}
DiagrammWidget::~DiagrammWidget(){
delete ui;
}
void DiagrammWidget::setPunkte(PunktVektor *punkte){
m_punkte = punkte;
}
void DiagrammWidget::paintEvent(QPaintEvent *event)
{
QPainter myPainter (this);
/*if((m_punkte == 0|| m_punkte->size() == 0)){
myPainter.drawText(100,100,"No Points found!");
return;
}*/
datenpunkt ersterPunkt=*m_punkte->begin();
datenpunkt startPunkt = ersterPunkt;
PunktVektor::iterator it;
for( it = m_punkte->begin()++; it != m_punkte->end(); it++){
datenpunkt p = *it;
myPainter.drawLine(startPunkt.temperatur,startPunkt.laenge,
p.temperatur,p.laenge);
startPunkt = p;
}
}
PunktVektor *DiagrammWidget::punkte() const{
return m_punkte;
}
void DiagrammWidget::setAufloesung(int aufloesung){
m_aufloesung = aufloesung;
}
void DiagrammWidget::setObere_temp(int obere_temp){
m_obere_temp = obere_temp;
}
void DiagrammWidget::setUntere_temp(int untere_temp){
m_untere_temp = untere_temp;
}
dialog.cpp
#include "dialog.h"
#include "ui_dialog.h"
#include "datenpunkt.h"
#include "diagrammwidget.h"
#include <QtCore>
#include <QtGui>
#include <QString>
#include <math.h>
#include <iostream>
using namespace std;
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
m_PunktListe = new PunktVektor;
//Standarteinstellungen
ui->werkstoff->setText("A 34-2/30 (SIEMENS)");
ui->materialkonstante->setValue(3440);
ui->materialkonstante->setSuffix(" K");
ui->bezugswiderstand->setValue(5000);
ui->bezugswiderstand->setSuffix(" Ω");
ui->unteretemperatur->setValue(0);
ui->unteretemperatur->setSuffix(" °C");
ui->oberetemperatur->setValue(300);
ui->oberetemperatur->setSuffix(" °C");
ui->aufloesung->setValue(10);
ui->aufloesung->setSuffix(" Δ°C");
ui->unteretemperatur->setMaximum(ui->oberetemperatur->value());
ui->oberetemperatur->setMinimum(ui->unteretemperatur->value());
ui->unteretemperatur->setSingleStep(ui->aufloesung->value());
ui->oberetemperatur->setSingleStep(ui->aufloesung->value());
werteberechnung(m_PunktListe);
//Diagramm aktualisieren und Anzeigen
DiagrammWidget dg;
dg.setUntere_temp(m_untere_temp);
dg.setObere_temp(m_obere_temp);
dg.setAufloesung(m_aufloesung);
dg.setPunkte(m_PunktListe);
dg.show();
}
Dialog::~Dialog()
{
delete m_PunktListe;
delete ui;
}
void Dialog::werteberechnung(PunktVektor *m_PunktListe)
{
datenpunkt p;
float temp = m_untere_temp;
//Anzahl der zu bestimmenden Datenpunkte
int anzahl = (m_obere_temp-m_untere_temp)/m_aufloesung;
//Bestimmen der Dekadengrenzen UD und OD
m_RT = m_bezugswiderstand * pow(M_E, (m_materialkonstante*
((1/m_obere_temp)-(1/m_To))));
m_UD = (int(log10(m_RT)));
m_RT = m_bezugswiderstand * pow(M_E, (m_materialkonstante*
((1/m_untere_temp)-(1/m_To))));
m_OD = (int(log10(m_RT))+1);
//Füllen des Datenvectors
for (int i = 0; i <= anzahl; i++){
m_RT = m_bezugswiderstand * pow(M_E, (m_materialkonstante*
((1/temp)-(1/m_To))));
p.widerstand = m_RT;
p.temperatur = temp;
float x = log10(m_RT);
if(m_UD != 0){
p.laenge = ((x-m_UD)*5);
}
else
p.laenge = x*5;
m_PunktListe->push_back(p);
temp = temp + m_aufloesung;
}
}
void Dialog::on_speichern_clicked()
{
setWerkstoff(ui->werkstoff->text());
setMaterialkonstante(ui->materialkonstante->value());
setBezugswiderstand(ui->bezugswiderstand->value());
setUntere_temp(ui->unteretemperatur->value()+ 272.15);
setObere_temp(ui->oberetemperatur->value()+ 272.15);
setAufloesung(ui->aufloesung->value());
ui->unteretemperatur->setMaximum(ui->oberetemperatur->value());
ui->oberetemperatur->setMinimum(ui->unteretemperatur->value());
ui->unteretemperatur->setSingleStep(ui->aufloesung->value());
ui->oberetemperatur->setSingleStep(ui->aufloesung->value());
//Berechnung des Datenvectors
werteberechnung(m_PunktListe);
//Diagramm aktualisieren
DiagrammWidget dg;
dg.setUntere_temp(m_untere_temp);
dg.setObere_temp(m_obere_temp);
dg.setAufloesung(m_aufloesung);
dg.setPunkte(m_PunktListe);
dg.show();
}
float Dialog::temperatur() const{
return m_T;
}
void Dialog::setTemperatur(float temperatur){
m_T = temperatur;
}
float Dialog::wiederstand() const{
return m_RT;
}
void Dialog::setWiederstand(float wiederstand){
m_RT = wiederstand;
}
float Dialog::bezugswiderstand() const{
return m_bezugswiderstand;
}
void Dialog::setBezugswiderstand(float bezugswiderstand){
m_bezugswiderstand = bezugswiderstand;
}
float Dialog::materialkonstante() const{
return m_materialkonstante;
}
void Dialog::setMaterialkonstante(float materialkonstante){
m_materialkonstante = materialkonstante;
}
QString Dialog::werkstoff() const{
return m_werkstoff;
}
void Dialog::setWerkstoff(QString werkstoff){
m_werkstoff = werkstoff;
}
int Dialog::aufloesung() const{
return m_aufloesung;
}
void Dialog::setAufloesung(int aufloesung){
m_aufloesung = aufloesung;
}
float Dialog::obere_temp() const{
return m_obere_temp;
}
void Dialog::setObere_temp(float obere_temp){
m_obere_temp = obere_temp;
}
float Dialog::untere_temp() const
{
return m_untere_temp;
}
void Dialog::setUntere_temp(float untere_temp)
{
m_untere_temp = untere_temp;
}
void Dialog::on_verwerfen_clicked()
{
ui->werkstoff->setText(m_werkstoff);
ui->materialkonstante->setValue(m_materialkonstante);
ui->bezugswiderstand->setValue(m_bezugswiderstand);
ui->unteretemperatur->setValue(m_untere_temp);
ui->oberetemperatur->setValue(m_obere_temp);
ui->aufloesung->setValue(m_aufloesung);
ui->unteretemperatur->setMaximum(ui->oberetemperatur->value());
ui->oberetemperatur->setMinimum(ui->unteretemperatur->value());
ui->unteretemperatur->setSingleStep(ui->aufloesung->value());
ui->oberetemperatur->setSingleStep(ui->aufloesung->value());
}
void Dialog::on_zuruecksetzen_clicked()
{
//Standarteinstellungen
ui->werkstoff->setText("A 34-2/30 (SIEMENS)");
ui->materialkonstante->setValue(3440);
ui->bezugswiderstand->setValue(5000);
ui->unteretemperatur->setValue(0);
ui->aufloesung->setValue(10);
ui->oberetemperatur->setValue(300);
setWerkstoff(ui->werkstoff->text());
setMaterialkonstante(ui->materialkonstante->value());
setBezugswiderstand(ui->bezugswiderstand->value());
setUntere_temp(ui->unteretemperatur->value()+ 272.15);
setObere_temp(ui->oberetemperatur->value()+ 272.15);
setAufloesung(ui->aufloesung->value());
ui->unteretemperatur->setMaximum(ui->oberetemperatur->value());
ui->oberetemperatur->setMinimum(ui->unteretemperatur->value());
ui->unteretemperatur->setSingleStep(ui->aufloesung->value());
ui->oberetemperatur->setSingleStep(ui->aufloesung->value());
werteberechnung(m_PunktListe);
}
main.cpp
#include "dialog.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Dialog w;
w.show();
return a.exec();
}
As noted in the comments, your example is not MCVE. But guessing, there are a couple of clues:
PunktVektor *m_punkte; //This is the mentioned vector
and
datenpunkt test = *m_punkte->begin(); <- //The programm crashs
Unless PunktVector overloads operator->(), your access of the vector should probably be
datenpunkt test = m_punkte->begin(); <- //no dereference (*)
REVISION:
With your updated example, the basic problem is dereferencing the end() iterator, which is a marker and does not refer to an element
datenpunkt test = *m_punkte->begin();
datenpunkt test2 =*m_punkte->end();
*m_punkte->empty();
m_punkte->begin() returns an iterator to the first element (if there is one; the vector could be empty). *( m_punkte->begin() ) accesses the element the iterator refers to.
m_punkte->end() return an iterator marking the end of the container. * (m_punkte->end() ) is undefined.
m_punkte->empty() returns a bool reflecting whether the container is empty or not. *( m_punkte->empty() ) will access either memory location 0 or 1 (both of which are likely to be very bad.
REVISION 2:
When working with containers, it is important to understand how you can hurt yourself. In particular, look here:
void DiagrammWidget::paintEvent(QPaintEvent *event)
{
QPainter myPainter (this);
/*if((m_punkte == 0|| m_punkte->size() == 0)){
myPainter.drawText(100,100,"No Points found!");
return;
}*/
datenpunkt ersterPunkt=*m_punkte->begin();
datenpunkt startPunkt = ersterPunkt;
PunktVektor::iterator it;
for( it = m_punkte->begin()++; it != m_punkte->end(); it++){
datenpunkt p = *it;
myPainter.drawLine(startPunkt.temperatur,startPunkt.laenge,
p.temperatur,p.laenge);
startPunkt = p;
}
}
If you are going to do this: datenpunkt ersterPunkt=*m_punkte->begin();
then you must first check to see of the container is empty.
Container.begin() returns an iterator to the first element of the container. If the container is empty, this iterator is equal to Container.end(). Dereferencing this iterator is a good way to crash your program.
Try refactoring your program like this:
void DiagrammWidget::paintEvent(QPaintEvent *event)
{
QPainter myPainter (this);
if( m_punkte == nullptr || m_punkte->empty() ){
// myPainter.drawText(100,100,"No Points found!");
return;
}
auto startPunkt = m_punkte->begin();
for( auto it = ++(m_punkte->begin()); it != m_punkte->end(); ++it){
auto const& p = *it;
myPainter.drawLine(startPunkt->temperatur,startPunkt->laenge,
p.temperatur,p.laenge);
startPunkt = it;
}
}
You see that this properly protects against your allowed condition of a null pointer or an empty container before making assumptions about the contents of the container. It also uses iterators as intended, rather than making unnecessary copies of the elements of the container.
I've created a QTableWidget. Some of the cells are filled with a cell widget (a modified QLabel, sending a clicked signal).
Now, I want to react on a click on this label. I add some debugging functions.
Clicking on an empty cell writes the correct row and column to console.
Clicking on the label is reckognized as a click, but with the wrong row and column (the previous cell data is used).
Question: How can I get the correct row and column for clicking on the label.
Greetings and thanks, Michael
MtTimeTable::MtTimeTable( QWidget* parent )
: QTableWidget { parent }, mIdArray { MtIdArray() },
mDate { QDate::currentDate() }
{
this->fillLesssonWidgets();
connect( this, &MtTimeTable::cellClicked,
this, &MtTimeTable::slotCellActivated );
}
void MtTimeTable::fillLessonWidgets()
{
MtLessonVec lessonVec { true }; // Data to show, loaded from file
auto cit { lessonVec.begin() };
while( cit != lessonVec.end() ) {
// Class MtLessonWidget derived from QLabel
MtLessonWidget* lessonWidget { new MtLessonWidget };
lessonWidget->setLesson( *cit );
// Using member functions of MtLessonwidget, working correct
this->setCellWidget( lessonWidget->startingRow(),
lessonWidget->startingCol(),
lessonWidget );
}
connect( lessonWidget, &MtLessonWidget::sigClicked,
this, &MtTimeTable::slotLessonWidgetClicked );
++cit;
}
}
I've tried to reduce the code to a minimum.
void MtTimeTable::slotLessonWidgetClicked()
{
std::cerr << "Table Cell: [" << this->currentRow() << ","
<< this->currentColumn() << "]" << std::endl;
}
According to the docs:
int QTableWidget::currentColumn() const
Returns the column of the current item.
int QTableWidget::currentRow() const
Returns the row of the current item.
That is, it is the position of the item, and it refers to a QTableWidgetItem, but if we use setCellWidget an item is not created, so those positions are not suitable, we must look for another means to obtain the row and column associated with the widget.
One way is to use the indexAt() method that returns a QModelIndex associated with the cell given its position relative to the viewport() of QTableWidget, and that is the one that should be used:
void MtTimeTable::slotLessonWidgetClicked(){
auto lbl = qobject_cast<MtLessonWidget *>(sender());
if(lbl){
auto ix = indexAt(lbl->pos());
qDebug()<<"Table Cell: [" <<ix.row()<< "," <<ix.column()<< "]";
}
}
Complete Example:
#include <QApplication>
#include <QLabel>
#include <QTableWidget>
#include <QDebug>
class CustomLabel: public QLabel{
Q_OBJECT
protected:
void mousePressEvent(QMouseEvent *){
emit clicked();
}
signals:
void clicked();
};
class TableWidget: public QTableWidget{
Q_OBJECT
public:
TableWidget(QWidget* parent=Q_NULLPTR):QTableWidget(parent){
setRowCount(10);
setColumnCount(10);
for(int i=0; i<rowCount(); i++){
for(int j=0; j<columnCount(); j++){
auto lbl = new CustomLabel;
setCellWidget(i, j, lbl);
connect(lbl, &CustomLabel::clicked, this, &TableWidget::onClicked);
}
}
}
private slots:
void onClicked(){
auto lbl = qobject_cast<CustomLabel *>(sender());
if(lbl){
auto ix = indexAt(lbl->pos());
qDebug()<<"Table Cell: [" <<ix.row()<< "," <<ix.column()<< "]";
}
}
};
#include "main.moc"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
TableWidget w;
w.show();
return a.exec();
}
In the following link I show the complete example.
I want to use Json in Qt and my data inside the QJsonObject must be in some particular order since we have some confirmation method via a token and getting a hash of the Json.
Since QJsonObject sorts its key-value pairs in alphabetical order I something like this to happen:
QJsonObject object;
object.insert("B","b");
object.insert("A","a");
qDebug() << QJsonDocument(object).toJson(); // this-line
I want this-line to printout something like this:
{"B":"b","A":"a"}
but instead i get this:
{
"A":"a",
"B":"b"
}
I have written a class:
jsonobject.h
class JsonObject
{
public:
JsonObject();
void insert(QString key,QJsonValue value);
void remove(QString key);
QString toJsonString();
QString convertQJsonValue2String(QJsonValue value);
QString convertQJsonArray2String(QJsonArray array);
QString convertQJsonObject2String(QJsonObject object);
private:
void appendKey(QString key,QString * out);
QJsonObject qJsonObject;
QStringList keysInOrder;
};
jsonobject.cpp
#include "jsonobject.h"
JsonObject::JsonObject()
{
keysInOrder.clear();
}
void JsonObject::insert(QString key, QJsonValue value)
{
if(!keysInOrder.contains(key))
keysInOrder.append(key);
qJsonObject.insert(key,value);
}
void JsonObject::remove(QString key)
{
keysInOrder.removeOne(key);
qJsonObject.remove(key);
}
QString JsonObject::toJsonString()
{
QString out;
for(int i = 0 ; i < keysInOrder.size() ; i++)
{
appendKey(keysInOrder[i],&out);
out.append(convertQJsonValue2String(qJsonObject.value(keysInOrder[i])));
if(i != (keysInOrder.size() - 1))
out.append(",");
}
return out.prepend("{").append("}");
}
QString JsonObject::convertQJsonObject2String(QJsonObject object)
{
QStringList keys = object.keys();
QString out;
for(int i = 0 ; i < keys.size() ; i++)
{
appendKey(keys[i],&out);
out.append(convertQJsonValue2String(object[keys[i]]));
if(i != keys.size() - 1)
out.append(",");
}
return out.prepend("{").append("}");
}
void JsonObject::appendKey(QString key, QString *out)
{
out->append("\"");
out->append(key);
out->append("\"");
out->append(":");
}
QString JsonObject::convertQJsonValue2String(QJsonValue value)
{
switch(value.type())
{
case QJsonValue::Null:
return QString("null");
break;
case QJsonValue::Bool:
return QString("%1").arg(value.toBool());
break;
case QJsonValue::Double:
return QString("%1").arg(value.toDouble());
break;
case QJsonValue::String:
return value.toString().prepend("\"").append("\"");
break;
case QJsonValue::Object:
return convertQJsonObject2String(value.toObject());
break;
case QJsonValue::Array:
return convertQJsonArray2String(value.toArray());
break;
case QJsonValue::Undefined:
return QString();
break;
}
return QString();
}
QString JsonObject::convertQJsonArray2String(QJsonArray array)
{
QString out;
for(int i = 0 ; i < array.size() ; i++)
{
QJsonValue value = array.at(i);
out.append(convertQJsonValue2String(value));
if(i != (array.size() - 1))
out.append(",");
}
return out.prepend("[").append("]");
}
the problem here is that I have QJsonObjects inside and my converQJSonObject2String(QJsonObject object) still don't have that order.
To me this looks like you're trying to use json for something it is not meant to do. Json does not have a canonical representation, so even if you manage to order the keys correctly, you might still run into problems. (Keys with non-ascii characters, whitespace differences, ...)
While it is certainly possible to do what you're looking for (I suggest using another library that offers this possibility), try to take a step back and think about what you're trying to achieve.
Would it be possible to take the hash of the json you receive from the server directly?