How can I read and write to a text file date enter to a QTableView?
This is what I have but I would like to save the data when it is added to the table and of course be able to read it back when the application is reopened. Is there any tutorial I can refer to?
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
model = new QStandardItemModel();
model->setRowCount(0);
ui->tableView->setModel(model);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
QStandardItem *userName = new QStandardItem(ui->lineEdit_Name->text());
QStandardItem *userNumber = new QStandardItem(ui->lineEdit_Number->text());
QList<QStandardItem*> row;
row <<userName << userNumber;
model->appendRow(row);
}
Thanks a lot
EDIT --------------------------------
This is what worked for me:
Add Function:
void MainWindow::on_pushButto_Add_clicked() {
QStandardItem *userInput = new QStandardItem(ui->lineEdit->text());
QStandardItem *userInput2= new QStandardItem(ui->lineEdit_2->text());
QList<QStandardItem*> row;
row <<userInput << userInput2;
model->appendRow(row);
}
Save Function:
void MainWindow::on_pushButton_Save_clicked()
{
QFile file("C:/Users/UserName/Practicing/Resources_Files/someFile.bin");
if (file.open(QIODevice::WriteOnly))
{
QDataStream stream(&file);
qint32 n = model->rowCount();
qint32 m = model->columnCount();
stream << n << m;
for (int i=0; i<n; ++i)
{
for (int j=0; j<m; j++)
{
model->item(i,j)->write(stream);
}
}
file.close();
}
}
Load Function:
void MainWindow::on_pushButton_Load_clicked()
{
QFile file("C:/Users/UserName/Practicing/Resources_Files/someFile.bin");
if (file.open(QIODevice::ReadOnly))
{
QDataStream stream(&file);
qint32 n, m;
stream >> n >> m;
model->setRowCount(n);
model->setColumnCount(m);
for (int i = 0; i < n ; ++i) {
for (int j = 0; j < m; j++) {
QStandardItem *item = new QStandardItem;
item->read(stream);
model->setItem(i, j, item);
}
}
file.close();
}
}
QFile file("somefile.bin");
if (file.open(QIODevice::WriteOnly)) {
QDataStream stream(&file);
stream << *(model->invisibleRootItem());
file.close();
}
http://qt-project.org/doc/qt-5.0/qtgui/qstandarditemmodel.html#invisibleRootItem
Edit:
Here is correction (I've checked that it works).
void MainWindow::save()
{
QFile file("somefile.bin");
if (file.open(QIODevice::WriteOnly)) {
QDataStream stream(&file);
qint32 n(model->rowCount()), m(model->columnCount());
stream << n << m;
for (int i=0; i<n; ++i)
for (int j=0; j<m; j++)
model->item(i,j)->write(stream);
file.close();
}
}
void MainWindow::load()
{
QFile file("somefile.bin");
if (file.open(QIODevice::ReadOnly)) {
QDataStream stream(&file);
qint32 n, m;
stream >> n >> m;
model->setRowCount(n);
model->setColumnCount(m);
for (int i=0; i<n; ++i)
for (int j=0; j<m; j++)
model->item(i,j)->read(stream);
file.close();
}
}
You can browse your model row by row, column by column and fill a file with a format like CSV (a row by line and columns separated by coma or tabs).
But, I don't think that is a good idea to modify the file when an item has changed. You should write the file when your application is closed.
model->item(i,j)->write(stream); will lead to segmentation fault if item(i,j) is empty. Assign some dummy value like whitespace in empty cells.
Related
I am reading a QByteArray from binary file and need to convert it to a QVector<quint32>. I have tried the following code:
QVector<quint32> qByteArrayToQuint32Vector(const QByteArray bytes, QDataStream::ByteOrder byteOrder) {
QByteArray temp;
QVector<quint32> convertedData;
QDataStream stream(&temp, QIODevice::ReadWrite);
for (int i = 0; i < bytes.length(); i++) {
stream << bytes[i];
}
stream.setByteOrder(byteOrder);
quint32 t = 0;
for (int i = 0; i < bytes.length(); i += 4) {
stream >> t;
convertedData.push_back(t);
}
return convertedData;
}
It does not work, and the converted data is just zeroes.
It appears I've misunderstood the use of QDataStream. The working solution is below:
QVector<quint32> qByteArrayToQuint32Vector(const QByteArray bytes, QDataStream::ByteOrder byteOrder) {
int byteArraySize = bytes.length();
int vectorLen = byteArraySize/4;
QVector<quint32> convertedData;
convertedData.resize(vectorLen);
QDataStream in(bytes);
in.setByteOrder(byteOrder);
for (int i = 0; i < vectorLen; i++) {
in >> convertedData[i];
}
return convertedData;
}
#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 need to perform some regexp operations on binary data. I wrote a function to convert QByteArray data in a hexa string representation. Each byte is prepended by 'x' for parsing purpose.
How could this code be optimized?
QByteArray data;
QByteArray newData;
for (int i = 0; i < data.size(); i++) {
QString hex;
hex.setNum(data[i], 16);
if (data[i] < 10) {
hex.prepend("x0");
} else {
hex.prepend("x");
}
newData.append(hex.toLatin1());
}
The code you posted has two bugs in it that I corrected.
1) Assuming you always want two hex digits you want to check if the value is less than 16, not 10.
2) QString::setNum has no overload for char, so the value is promoted to a larger type. For a value like 128, which is negative in a signed char, you would get x0ffffffffffffff80 due to sign extension.
The function foo1 is your original code with the bugs fixed, and foo2 is a more optimal version that avoids creating a temporary QString since the conversion to unicode and back isn't free, and prepending values to a string requires additional copying.
I used QElapsedTimer because on Windows where I am testing it uses the high resolution PerformanceCounter clock. If you are on another platform it might be less accurate. You can see the different types of clocks it may use in the documentation.
Set display_converted_string to true if you want the converted string printed to verify they are identical.
#include <QString>
#include <QByteArray>
#include <QElapsedTimer>
#include <iostream>
QByteArray foo1(QByteArray data)
{
QByteArray newData;
for (int i = 0; i < data.size(); i++) {
unsigned char c = data[i];
QString hex;
hex.setNum(c, 16);
if (c < 16) {
hex.prepend("x0");
} else {
hex.prepend("x");
}
newData.append(hex.toLatin1());
}
return newData;
}
QByteArray foo2(QByteArray data)
{
static const char digits[] = {'0','1','2','3','4','5','6','7',
'8','9','a','b','c','d','e','f'};
QByteArray newData;
newData.reserve(data.size() * 3);
for (int i = 0; i < data.size(); i++)
{
unsigned char c = data[i];
newData.append('x');
newData.append(digits[(c >> 4) & 0x0f]);
newData.append(digits[c & 0x0f]);
}
return newData;
}
int main()
{
const int iterations = 10000;
const bool display_converted_string = false;
QElapsedTimer t;
std::cout << "Using clock type " << t.clockType() << ".\n";
QByteArray data(256, 0);
QByteArray newData;
qint64 elapsed1 = 0, elapsed2 = 0;
//Set the values in data to 0-255 to make sure all values are converted properly.
for(int i = 0; i < data.size(); ++i)
{
data[i] = i;
}
t.start();
for(int i = 0; i < iterations; ++i)
{
newData = foo1(data);
}
elapsed1 = t.nsecsElapsed();
std::cout << "foo1 elapsed time = " << elapsed1 << "\n";
if(display_converted_string)
{
std::cout << "newData = " << newData.data() << "\n";
}
t.restart();
for(int i = 0; i < iterations; ++i)
{
newData = foo2(data);
}
elapsed2 = t.nsecsElapsed();
std::cout << "foo2 elapsed time = " << elapsed2 << "\n";
if(display_converted_string)
{
std::cout << "newData = " << newData.data() << "\n";
}
return 0;
}
I want to pass selected QRadioButton's value from one Window to another. I am confused with the function declaration to accept the text value in Second Window, here is my code.
Window1.cpp
void SelectOS :: processNextButton(){
if(ui->win32->isChecked()){
QString loc = "WIN/32Bit";
SelectSoftware *ss = new SelectSoftware (loc);
this->hide();
ss->show();
}
else
{
//QMessageBox:warning();
}
}
Window2.h
public:
SelectSoftware(const QString &text, QWidget *parent=0);
Window2.cpp
SelectSoftware::SelectSoftware(const QString &text, QWidget *parent):QMainWindow(parent),ui(new ui::SelectSoftware)
{
QString softpath = text;
qDebug << softpath;
}
But when I call
ss = new SelectSoftware();
or
ss= new SelectSoftware(const QString &text, QWidget *parent);
in Window2.cpp, I get the error : no matching function for call to SelectSoftware::SelectSoftware()
Where am I wrong?
UPDATE
Window2.cpp
#include "selectsoftware.h"
#include "ui_selectsoftware.h"
SelectSoftware *ss;
QStringList selectedModuleList;
SelectSoftware::SelectSoftware(const QString &text, QWidget *parent) :
QMainWindow(parent),
ui(new Ui::SelectSoftware)
{
ui->setupUi(this);
softpath = text;
setWindowPosition();
getSoftwareDetails();
initializeUi();
}
SelectSoftware::~SelectSoftware()
{
delete ui;
}
void SelectSoftware::setWindowPosition()
{
QDesktopWidget *desktop = QApplication::desktop();
int x = (desktop->width() - size().width())/2;
int y = (desktop->height() - size().height())/2;
move(x, y-50);
setFixedSize(size().width(), size().height());
}
void SelectSoftware::cancel()
{
qApp->exit(0);
}
void SelectSoftware::showMainPage()
{
ss = new SelectSoftware(softpath); // here its creating problem, not going forward and app is crashing!!!
for(int j = 0; j < softwareList.size(); j++){
if(checkBox[j]->isChecked()){
if(!comboBox[j]->currentIndex()){
QMessageBox::warning(this, "Select version !", "Select version number for all selected software");
return;
}
}
}
for(int i = 0; i < softwareList.size(); i++){
if(checkBox[i]->isChecked()){
ss->selectedSoftList.push_back(checkBox[i]->text());
ss->selectedVerList.push_back(comboBox[i]->currentText());
}
}
if(!ss->selectedSoftList.size()){
QMessageBox::warning(this, "No product Selected !", "Select one");
return;
}
else{
SelectionPage* sp = new SelectionPage;
this->hide();
sp->show();
}
}
void SelectSoftware::test(const int id)
{
if(checkBox[id]->isChecked()){
comboBox[id]->setEnabled(true);
comboBox[id]->addItem(" Select anyone ");
QString path = qApp->applicationDirPath() + "/products/" + checkBox[id]->text();
QDir dir;
dir.cd(path);
dir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
QFileInfoList list = dir.entryInfoList();
for (int i = 0; i < list.size(); ++i) {
QFileInfo fileInfo = list.at(i);
comboBox[id]->addItem(fileInfo.fileName());
}
}else{
comboBox[id]->clear();
comboBox[id]->setDisabled(true);
}
}
void SelectSoftware::getSoftwareDetails()
{
QString fileName = qApp->applicationDirPath() + "/abc/" + SOFTWARELIST;
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)){
QString msg = "Could not find the file " + fileName;
errorExit(msg);
}
QTextStream in(&file);
while (!in.atEnd()) {
QString line = in.readLine();
processLine(line.toLower());
}
}
void SelectSoftware::processLine(QString str)
{
QStringList list = str.split(",");
QDir path = qApp->applicationDirPath() + "/products/" + list[0];
if(path.exists() && (list.size() == 2)){
QString tmp = list[0];
tmp = tmp.toLower();
softwareList.push_back(tmp);
}
}
void SelectOption::initializeUi()
{
this->setWindowTitle("Window2");
QGridLayout *gridLayout1 = new QGridLayout();
gridLayout1->setMargin(5);
gridLayout1->setSpacing(5);
QSignalMapper* signalMapper = new QSignalMapper();
for(int i = 0; i < list.size(); i++){
radioButton[i] = new QRadioButton();
radioButton[i]->setText(softwareList[i]);
signalMapper->setMapping(radioButton[i], i);
gridLayout1->addWidget(radioButton[i], i/1, i%1);
connect(radioButton[i], SIGNAL(clicked()),signalMapper, SLOT(map()));
}
connect(signalMapper, SIGNAL(mapped(const int &)),this, SIGNAL(radioChecked(const int &)));
connect(this, SIGNAL(radioChecked(const int &)),this, SLOT(test(const int)));
QGridLayout *gridLayout2 = new QGridLayout();
gridLayout2->setMargin(5);
gridLayout2->setSpacing(5);
for(int j = 0; j < list.size(); j++){
comboBox[j] = new QComboBox();
comboBox[j]->setDisabled(true);
gridLayout2->addWidget(comboBox[j], j/1, j%1);
}
QPushButton *nextButton = new QPushButton("Next >");
nextButton->setDefault(true);
connect(nextButton, SIGNAL(clicked()), this, SLOT(showMainPage()));
QPushButton *backButton = new QPushButton("< Back");
backButton->setDefault(true);
connect(backButton, SIGNAL(clicked()), this, SLOT(showSelectOS()));
QPushButton *cancelButton = new QPushButton("Cancel");
cancelButton->setDefault(true);
connect(cancelButton, SIGNAL(clicked()), this, SLOT(cancel()));
QHBoxLayout *hboxlayout;
hboxlayout = new QHBoxLayout();
hboxlayout->addLayout(gridLayout1);
hboxlayout->addLayout(gridLayout2);
QHBoxLayout *layout;
layout = new QHBoxLayout();
layout->addStretch(10);
layout->addWidget(nextButton);
layout->addWidget(backButton);
layout->addWidget(cancelButton);
layout->addStretch(10);
QVBoxLayout *mainLayout;
mainLayout = new QVBoxLayout();
mainLayout->addLayout(hboxlayout);
mainLayout->addLayout(layout);
ui->centralwidget->setLayout(mainLayout);
}
QVector<QString> SelectSoftware::getSelectedSoftware()
{
return ss->selectedSoftList;
}
QVector<QString> SelectSoftware::getSelectedVersion()
{
return ss->selectedVerList;
}
QStringList SelectSoftware::getSelectedModules()
{
return selectedModuleList;
}
First of all - use signals and slots, Luke
Second of all, you cannot call ss = new SelectSoftware();, since you haven't declared SelectSoftware constructor without parameters, and calling ss= new SelectSoftware(const QString &text, QWidget *parent); is illegal in C++.
SelectSoftware *ss = new SelectSoftware (loc); is correct, though.
1. In void SelectSoftware::processLine(QString str) addressing to list[0] without checking that list is not empty might be dangerous. I recomend you to add:
if (!list.size())
return;
right after initialization.
2. In void SelectOption::initializeUi() what is list? Are you sure list.size() <= softwareList.size()? If not, it's a potential problem.
3. What is radioButton? I don't see it's initialization. If it is QList < QRadioButton * >, than radioButton[i] = new QRadioButton(); is a bad one and you should do this:
radioButton.append(new QRadioButton());
4. Same goes to comboBox.
Each of the list can cause the crash of your application. And I could easily miss something.
This is more or less Qt's example with some small changes.
The output is PcPcPcPc...etc. I don't understand why.
Namely, I am confused about how sProducer.acquire(256); works. I believe I understand how sProducer.acquire(1); works. It doesn't make sense to me to acquire anything more than 1 because I don't see how acquiring more than 1 makes any difference logically. Could someone explain this? On the surface, writing 1 byte and reading 1 byte doesn't seem very efficient due to semaphore overhead...but acquiring more resources doesn't seem to make a performance difference nor does the code make sense.
Logically I think both the acquire and release have to have the same number (whatever that number is). But how can I modify this code so I can acquire more (say 256) and thus reduce semaphore overhead? The code bellow just doesn't make sense to me when acquire and release is not 1.
#include <QtCore>
#include <iostream>
#include <QTextStream>
//Global variables.
QTextStream out(stdout);
QTextStream in(stdin);
const int DataSize = 1024;
const int BufferSize = 512;
char buffer[BufferSize];
QSemaphore sProducer(BufferSize);
QSemaphore sConsumer(0);
//-----------------------------
class Producer : public QThread
{
public:
void run();
};
void Producer::run()
{
for (int i = 0; i < DataSize; ++i) {
sProducer.acquire(256);
buffer[i % BufferSize] = 'P';
sConsumer.release(256);
}
}
class Consumer : public QThread
{
public:
void run();
};
void Consumer::run()
{
for (int i = 0; i < DataSize; ++i) {
sConsumer.acquire(256);
std::cerr << buffer[i % BufferSize];
out << "c";
out.flush();
sProducer.release(256);
}
std::cerr << std::endl;
}
int main()
{
Producer producer;
Consumer consumer;
producer.start();
consumer.start();
producer.wait();
consumer.wait();
in.readLine(); //so i can read console text.
return 0;
}
Since there is only one producer and one consumer, they can move freely their own private cursor, their i variable, of the amount of bytes they want, as long as there is enough room to do that (something higher that 256 on both sides with a 512 buffer would cause a deadlock).
Basically, when a thread successfully acquire 256 bytes, it means it can safely read or write these 256 bytes in one single operation, so you just have to put another loop inside the acquire/release block to handle that number of bytes.
For the producer:
void Producer::run()
{
for (int i = 0; i < DataSize; ++i) {
const int blockSize = 256;
sProducer.acquire(blockSize);
for(int j = 0; j < blockSize; ++i, ++j) {
buffer[i % BufferSize] = 'P';
}
sConsumer.release(blockSize);
}
}
And for the consumer
void Consumer::run()
{
for (int i = 0; i < DataSize; ++i) {
const int blockSize = 128;
sConsumer.acquire(blockSize);
for(int j = 0; j < blockSize; ++i, ++j) {
std::cerr << buffer[i % BufferSize];
out << "c";
out.flush();
}
sProducer.release(blockSize);
}
std::cerr << std::endl;
}