我正在我的类中实现一个方法,它将把TableView对象中的数据写入CSV文件。但是,当程序运行时,程序会以非常慢的速度(3或4秒)将数据写入USB驱动器上的文件,但与系统的内部驱动器一起正常工作。这是因为我在写完文件后没有使用flush()或close()
这是我的代码
bool ThicknessCalibrationDataDisplay::WriteCSVFileChanges()
{
QModelIndex tableViewModelindex = tableViewModel_->index(0,0);
QFile file(CSVFileName_);
if(!file.exists())
return false;
if(!file.open(QIODevice::WriteOnly))
return false;
for(int i = 0; i < totalRows_ ; i++)
{
for(int j = 0 ; j < totalColumns_; j++)
{
tableViewModelindex = tableViewModel_->index(i,j);
qDebug()<<tableViewModelindex.data();
QString text = tableViewModelindex.data().toString();
QTextStream OutputStream(&file);
if(j == totalColumns_ - 1)
OutputStream<<"\n\r";
else
OutputStream<<',';
}
}
}
这是我之前的代码,现在我打算关闭文件流,以便优雅退出。 QFile :: close()的Qt API说
调用QFile :: flush()并关闭文件。来自冲洗的错误是 忽略。
那么我应该调用close(),还是调用flush()更好,记录任何错误然后调用close()?
是否还需要进行其他修改,以改善写入操作?
答案 0 :(得分:6)
flush()
与close()
是一个红色的鲱鱼,它不会影响你的
销毁QTextStream
会强制刷新文件。刷新文件慢。每次迭代循环时,你都会破坏你的文本流!在循环之外创建流!
这是来源,来自Qt 5.1.1:
QTextStream::~QTextStream()
{
if (!d->writeBuffer.isEmpty())
d->flushWriteBuffer();
}
在Qt 5.1或更高版本上,如果要确保在文件关闭后刷新磁盘缓冲区,则应使用QSaveFile
。只有这样才能让您确信,一旦您认为自己已完成保存,数据实际上就在磁盘上。
QTextStream
有自己的缓冲区。因此,如果您想在刷新时捕获错误,则需要在流本身使用flush()
方法,而不是QFile
。
从GUI线程进行任何文件访问是一个非常糟糕的主意。任何时候阻止,你的UI线程也会阻塞。
现在的问题是:如何让您的模型安全地从另一个线程访问?如果使用自定义模型,则可能只需要在写入文件期间将模型切换为只读模式。只读模式将是模型的自定义属性,因此所有setData
调用都将失败。您当然需要向用户说明这一点。模型的视图是只读的,但这比阻止整个GUI更友好。
如果您认为仅仅使用QMutex阻止对模型的并发访问就足够了,请再想一想。对模型进行的任何修改都可能会改变其结构,因此您的编写者需要正确处理模型发出的所有信号。这会使作者更加复杂。临时只读模型使您可以使响应式GUI对用户造成暂时的不便,同时最大限度地提高代码复杂性。
class MyModel : public QStandardItemModel /* or whatever other class you derive from */ {
typedef QStandardItemModel inherited;
Q_OBJECT
Q_PROPERTY(bool writable READ isWritable WRITE setWritable NOTIFY writableChanged)
bool m_writable;
public:
explicit MyModel(QObject * parent) : inherited(parent), m_writable(true) {}
Q_SLOT void setReadOnly() {
if (!m_writable) return;
m_writable = false;
emit writableChanged(m_writable);
}
Q_SLOT void setReadWrite(){
if (m_writable) return;
m_writable = true;
emit writableChanged(m_writable);
}
Q_SIGNAL void writableChanged(bool);
bool isWritable() const { return m_writable; }
void setWritable(bool writable) {
if (m_writable == writable) return;
m_writable = writable;
emit writableChanged(m_writable);
}
bool setData(const QModelIndex & index, const QVariant & val, int role = Qt::EditRole) {
if (! m_writable) return false;
return inherited::setData(index, val, role);
}
};
有些USB驱动器非常慢。
您的返工代码至少应如下所示:
bool ThicknessCalibrationDataDisplay::WriteCSVFileChanges()
{
#if QT_VERSION < QT_VERSION_CHECK(5,1,0)
QFile file(CSVFileName_);
#else
QSaveFile file(CSVFileName_); // does a disk commit at the end
#endif
if(!file.exists())
return false;
if(!file.open(QIODevice::WriteOnly))
return false;
QTextStream os(&file);
for(int i = 0; i < totalRows_ ; i++)
{
for(int j = 0 ; j < totalColumns_; j++)
{
QModelIndex index = tableViewModel_->index(i,j);
qDebug() << index.data();
os << index.data();
if(j == totalColumns_ - 1)
os<<"\n\r";
else
os<<',';
}
}
os.flush();
#if QT_VERSION >= QT_VERSION_CHECK(5,1,0)
return os.status() == QTextStream::Ok && file.commit();
#else
return os.status() == QTextStream::Ok;
#endif
}
答案 1 :(得分:6)
我可以告诉你,QFile::close()
在函数末尾隐式调用,因为QFile对象的析构函数在文件变量范围的末尾被调用。来自QFile文档:
QFile::~QFile ()
Destroys the file object, closing it if necessary.
您可能想要尝试的是操作系统的基础同步功能。 AFAIK Qt没有为此提供任何API,因此它不可移植。调用QFile::flush()
函数时,可能发生的情况(至少在Linux上)是数据从用户空间缓冲区刷新到底层系统缓存而不一定是磁盘。在linux / unix上,您需要fsync
函数来确保文件实际写入磁盘。代码看起来像:
file.flush();
fsync( file.handle() );
file.close();
我相信相同的代码也适用于Windows。
有关fflush与fsync的更多信息,请参阅Difference between fflush and fsync