Qt: Отсутствие объектов при загрузке из файла с использованием перегруженных операторов ввода-вывода и QDatastream - PullRequest
0 голосов
/ 03 марта 2020

Я создал приложение со списком, как показано на этом скриншоте

У меня проблема с функциями сохранения / загрузки. Текст из заголовков (QLineEdit) сохраняется и загружается как обычно. Но когда я пытаюсь сохранить и загрузить строки под заголовками (определяемый пользователем класс с именем «Row»), QString из объекта QLineEdit загружается без символов.

Я проверил, что все сохраненные элементы все еще там, когда я выводил их в QDataStream, и что только QString, принадлежащая объекту QLineEdit объекта Row, пуста, когда предполагается, что он вводится из QDataStream в loading.

Заголовки создаются при запуске программы, а объекты Row создаются, когда пользователь нажимает кнопку добавления (значок + в верхней части), и сохраняются в приватном списке QList listOfRows.

// mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QObject>
#include <QFile>
#include <QLabel>
#include <QMessageBox>
#include "row.h"

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void on_actionNew_Row_triggered();
    void on_actionDelete_Rows_triggered();
    void on_actionSelect_All_triggered();
    void on_actionSave_triggered();
    void on_actionLoad_triggered();

    void on_actionShortcuts_triggered();

private:
    Ui::MainWindow *ui;
    QList<Row*> listOfRows;
    void AddHeaderRow();

    QLineEdit* headerOne;
    QLineEdit* headerTwo;
    QLineEdit* headerThree;
    QCheckBox* deleteAllSelect;
    QLabel* select;
};
#endif // MAINWINDOW_H

-

// mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "row.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    AddHeaderRow();
}

MainWindow::~MainWindow()
{
    delete ui;
}


void MainWindow::on_actionNew_Row_triggered()
{
    // new row
    Row* newRow = new Row(this, listOfRows.size());
    listOfRows.push_back(newRow);
}

void MainWindow::on_actionDelete_Rows_triggered()
{
    // delete row
    if(listOfRows.size() != 0)
    {
        for(auto row : listOfRows)
        {
            if(row->isToDelete())
            {
                row->hide();
                listOfRows.removeOne(row);
            }
        }

        for(int i = 0; i < listOfRows.size(); i++)
        {
            listOfRows[i]->reAlign(i);
        }
    }
}

void MainWindow::on_actionSelect_All_triggered()
{
    if(deleteAllSelect->checkState())
    {
        for(auto row : listOfRows)
        {
            row->setStateCheckBoxDelete(true);
        }
    }else
    {
        for(auto row : listOfRows)
        {
            row->setStateCheckBoxDelete(false);
        }
    }
}

void MainWindow::AddHeaderRow()
{
    this->headerOne = new QLineEdit;
    this->headerTwo = new QLineEdit;
    this->headerThree = new QLineEdit;
    this->deleteAllSelect = new QCheckBox;
    this->select = new QLabel("Select");

    ui->horizontalLayout->addWidget(headerOne, 2, Qt::AlignHCenter);
    ui->horizontalLayout->addWidget(headerTwo, 2, Qt::AlignHCenter);
    ui->horizontalLayout->addWidget(headerThree, 3);
    ui->horizontalLayout->addWidget(select, 1, Qt::AlignHCenter);
}

void MainWindow::on_actionSave_triggered()
{
    QMessageBox msgBox;
    msgBox.setIcon(QMessageBox::Question);
    msgBox.setWindowIcon(MainWindow::windowIcon());
    msgBox.setWindowTitle("Save");
    msgBox.setText("Do you want to save your changes?");
    msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Cancel);
    msgBox.setDefaultButton(QMessageBox::Save);
    int ret = msgBox.exec();

    if(ret == QMessageBox::Save)
    {
        QString path = "rows.bin";
        QFile file(path);

        if (file.open(QIODevice::WriteOnly))
        {
            qDebug() << "File opened!";
            QDataStream out(&file);
            out << this->headerOne->text() << this->headerTwo->text() << this->headerThree->text();
            for(auto row : listOfRows)
            {
                out << row;
            }
            file.close();
        }
    }
}

void MainWindow::on_actionLoad_triggered()
{
    QString path = "rows.bin";
    QFile file(path);
    if(file.exists())
    {
      if (file.open(QIODevice::ReadOnly))
      {
         QDataStream in(&file);
         QString textOne;
         QString textTwo;
         QString textThree;
         in >> textOne >> textTwo >> textThree;
         this->headerOne->setText(textOne);
         this->headerTwo->setText(textTwo);
         this->headerThree->setText(textThree);
         while(!in.atEnd())
         {
             Row* tempRow = new Row(this);
             in >> tempRow;
             listOfRows.push_back(tempRow);
             qDebug() << "To file: " << tempRow->checkStateOne() << tempRow->checkStateTwo() << tempRow->textFromBox();
         }
         file.close();
         }
    }
}

void MainWindow::on_actionShortcuts_triggered()
{
    QMessageBox shortcuts;
    shortcuts.setWindowTitle("Shortcuts");
    shortcuts.setIcon(QMessageBox::Information);
    shortcuts.setWindowIcon(MainWindow::windowIcon());
    shortcuts.setText("New Row - Ctrl + N\n"
                      "Delete Selected - Ctrl + D\n"
                      "Save - Ctrl + S\n"
                      "Load - Ctrl + L\n");
    shortcuts.exec();
}

-

    // row.h
    #ifndef ROW_H
    #define ROW_H

    #include <QFrame>
    #include <QPainter>
    #include <QCheckBox>
    #include <QLineEdit>
    #include <QList>
    #include <QDebug>
    #include <QHBoxLayout>
    #include <QDataStream>

    class Row : public QFrame
    {
    Q_OBJECT

    static const int containerHeight = 40;

    public:
        Row(QWidget* parent = nullptr, int yPos = 0);
        ~Row() override;
        bool isToDelete() const;
        void reAlign(int);
        void setStateCheckBoxDelete(bool);
        bool checkStateOne() const;
        bool checkStateTwo() const;
        QString textFromBox() const;
        void setCheckStateOne(bool);
        void setCheckStateTwo(bool);
        void setTextToBox(QString);

    private:
        QWidget* parent;
        QCheckBox* checkBoxOne;
        QCheckBox* checkBoxTwo;
        QLineEdit* textBox;
        QCheckBox* checkBoxDelete;
    };

    QDataStream& operator<<(QDataStream&, Row&);
    QDataStream& operator>>(QDataStream&, Row*);

    #endif // ROW_H

-

// row.cpp
#include "row.h"

Row::Row(QWidget* parent, int yPos) : QFrame(parent)
{
    QHBoxLayout* layout = new QHBoxLayout();

    checkBoxOne = new QCheckBox(this);
    checkBoxTwo = new QCheckBox(this);
    textBox = new QLineEdit(this);
    checkBoxDelete = new QCheckBox(this);

    layout->addWidget(checkBoxOne, 2, Qt::Alignment(Qt::AlignHCenter));
    layout->addWidget(checkBoxTwo, 2, Qt::Alignment(Qt::AlignHCenter));
    layout->addWidget(textBox, 3);
    layout->addWidget(checkBoxDelete, 1, Qt::Alignment(Qt::AlignHCenter));

    this->parent = parent;
    this->setLayout(layout);
    this->resize(parent->size().width(), containerHeight);
    this->move(0, 50 + containerHeight * (1 + yPos));

    this->show();
}

Row::~Row()
{
    this->parent = nullptr;
    delete parent;
    delete checkBoxOne;
    delete checkBoxTwo;
    delete textBox;
    delete checkBoxDelete;
}

bool Row::isToDelete() const
{
    return checkBoxDelete->checkState();
}

void Row::reAlign(int yPos)
{
    this->move(0, 50 + containerHeight * (1 + yPos));
}

void Row::setStateCheckBoxDelete(bool check)
{
    this->checkBoxDelete->setCheckState(Qt::CheckState(check));
}

bool Row::checkStateOne() const
{
    return checkBoxOne->checkState();
}

bool Row::checkStateTwo() const
{
    return checkBoxTwo->checkState();
}

QString Row::textFromBox() const
{
    return textBox->text();
}

void Row::setCheckStateOne(bool check)
{
    this->checkBoxOne->setCheckState(Qt::CheckState(check));
}

void Row::setCheckStateTwo(bool check)
{
    this->checkBoxTwo->setCheckState(Qt::CheckState(check));
}

void Row::setTextToBox(QString text)
{
    this->textBox->setText(text);
}

QDataStream& operator<<(QDataStream& out, Row& row)
{
    out << row.checkStateOne();
    out << row.checkStateTwo();
    out << row.textFromBox();

    return out;
}

QDataStream& operator>>(QDataStream& in, Row* row)
{
    bool checkOne;
    bool checkTwo;
    QString text;
    in >> checkOne;
    in >> checkTwo;
    in >> text;

    row->setCheckStateOne(checkOne);
    row->setCheckStateTwo(checkTwo);
    row->setTextToBox(text);

    return in;
}

-

// main.cpp
#include "mainwindow.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

-

<?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>801</width>
    <height>600</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Monika's Awesome Widget</string>
  </property>
  <property name="windowIcon">
   <iconset>
    <normaloff>../MoniquesUnique/Icons/noun_Elephant_5119.svg</normaloff>../MoniquesUnique/Icons/noun_Elephant_5119.svg</iconset>
  </property>
  <widget class="QWidget" name="centralwidget">
   <widget class="QWidget" name="layoutWidget">
    <property name="geometry">
     <rect>
      <x>10</x>
      <y>0</y>
      <width>781</width>
      <height>31</height>
     </rect>
    </property>
    <layout class="QHBoxLayout" name="horizontalLayout"/>
   </widget>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>801</width>
     <height>26</height>
    </rect>
   </property>
   <widget class="QMenu" name="menuFile">
    <property name="title">
     <string>File</string>
    </property>
    <addaction name="actionSave"/>
    <addaction name="actionLoad"/>
   </widget>
   <widget class="QMenu" name="menuEdit">
    <property name="title">
     <string>Edit</string>
    </property>
    <addaction name="actionNew_Row"/>
    <addaction name="actionDelete_Rows"/>
   </widget>
   <widget class="QMenu" name="menuHelp">
    <property name="title">
     <string>Help</string>
    </property>
    <addaction name="actionShortcuts"/>
   </widget>
   <addaction name="menuFile"/>
   <addaction name="menuEdit"/>
   <addaction name="menuHelp"/>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
  <widget class="QToolBar" name="toolBar">
   <property name="windowTitle">
    <string>toolBar</string>
   </property>
   <attribute name="toolBarArea">
    <enum>TopToolBarArea</enum>
   </attribute>
   <attribute name="toolBarBreak">
    <bool>false</bool>
   </attribute>
   <addaction name="actionNew_Row"/>
   <addaction name="actionDelete_Rows"/>
   <addaction name="actionSave"/>
   <addaction name="actionLoad"/>
  </widget>
  <action name="actionNew_Row">
   <property name="icon">
    <iconset>
     <normaloff>../MoniquesUnique/Icons/plus-circle.svg</normaloff>../MoniquesUnique/Icons/plus-circle.svg</iconset>
   </property>
   <property name="text">
    <string>New Row</string>
   </property>
   <property name="shortcut">
    <string>Ctrl+N</string>
   </property>
  </action>
  <action name="actionDelete_Rows">
   <property name="icon">
    <iconset>
     <normaloff>../MoniquesUnique/Icons/delete.svg</normaloff>../MoniquesUnique/Icons/delete.svg</iconset>
   </property>
   <property name="text">
    <string>Delete Selected</string>
   </property>
   <property name="shortcut">
    <string>Ctrl+D</string>
   </property>
  </action>
  <action name="actionSave">
   <property name="icon">
    <iconset>
     <normaloff>../MoniquesUnique/Icons/save.svg</normaloff>../MoniquesUnique/Icons/save.svg</iconset>
   </property>
   <property name="text">
    <string>Save</string>
   </property>
   <property name="shortcut">
    <string>Ctrl+S</string>
   </property>
  </action>
  <action name="actionLoad">
   <property name="icon">
    <iconset>
     <normaloff>../MoniquesUnique/Icons/briefcase.svg</normaloff>../MoniquesUnique/Icons/briefcase.svg</iconset>
   </property>
   <property name="text">
    <string>Load</string>
   </property>
   <property name="shortcut">
    <string>Ctrl+L</string>
   </property>
  </action>
  <action name="actionShortcuts">
   <property name="icon">
    <iconset>
     <normaloff>../MoniquesUnique/Icons/info.svg</normaloff>../MoniquesUnique/Icons/info.svg</iconset>
   </property>
   <property name="text">
    <string>Shortcuts</string>
   </property>
  </action>
 </widget>
 <resources/>
 <connections/>
</ui>

1 Ответ

0 голосов
/ 03 марта 2020

Вы должны разыменовывать указатели строк при сохранении, потому что ваш оператор << принимает ссылку, а не указатель:

QDataStream& operator<<(QDataStream&, Row&); // also it would be better to accept "const Row&"

Редактировать внутри void MainWindow::on_actionSave_triggered():

for(auto row : listOfRows)
{
    out << *row; // dereferenced here
}

а также кажется, что вам нужно удалить все существующие строки во время загрузки. Изменить внутри void MainWindow::on_actionLoad_triggered():

listOfRows.clear();        

while (!in.atEnd())
{
    Row* tempRow = new Row(this, listOfRows.size());
    in >> tempRow;
    listOfRows.push_back(tempRow);

    qDebug() << "FROM file: " << tempRow->checkStateOne() << tempRow->checkStateTwo() << tempRow->textFromBox(); // Here was a typo: FROM file, not TO
}

Обратите также внимание:

  • Ваши флажки являются флажками с тремя состояниями, поэтому может потребоваться использовать setTristate(false) или сохраните / загрузите их как int, а не как bool QCheckBox::setCheckState()
  • Возможно, вам потребуется использовать некоторый недвоичный файл для таких данных, например XML или json
  • См. Библиотеки сериализации для упрощения и автоматизации сохранения / загрузки структур и классов, таких как cereal
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...