How to solve the problem with Thread in QT (C++) - c++

I am writing a program in C++ using QT. Task: The user can specify a directory that consists of other folders, and files are located inside these folders. It is necessary to archive the folders with the file and place the archives in another directory. I am working with an additional thread for processing (I created a Worker class, etc.). I did everything according to the example from the QT documentation. The problem is the following: Everything works on my computer and on computers with Windows 10. But on Windows 7 computers, after processing one large folder, the program crashes. Error: The program does not work, code 0xc0000005. What to do? Is this a problem in the code or do I need to install something additionally? Net Framework, Visual C++, etc. updated, but nothing helped. The program code is attached.
P.S. If you don't use an additional thread, then everything works correctly. But an additional thread is needed so that the processing progress can be displayed, that is, how many directories have been processed
#include "renamewidget.h"
#include "ui_renamewidget.h"
#include <private/qzipwriter_p.h>
QString RenameWidget::filePath; //static field
QString RenameWidget::directoryPath; //static field
QString RenameWidget::resultPath; //static field
RenameWidget::RenameWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::RenameWidget)
{
ui->setupUi(this);
filePath = "C:/Users/user/Desktop/1.xlsx";
directoryPath = "C:/Users/user/Desktop/2";
resultPath = "C:/Users/user/Desktop/3";
connect(this, &RenameWidget::doWork, &worker, &Worker::doWork);
connect(&worker, &Worker::workProgress, this, &RenameWidget::setText);
worker.moveToThread(&thread);
thread.start();
}
void RenameWidget::setText(QString message)
{
ui->lableLoad->setText(message); // setProgress
}
void RenameWidget::on_pb_Rename_clicked()
{
emit doWork();
}
void Worker::doWork()
{
emit workProgress("");
QDir dir;
dir.setPath(RenameWidget::getDirectoryPath());
QStringList listDir = dir.entryList(QDir::Dirs | QDir::NoDot | QDir::NoDotDot);
for (int i = 0; i<listDir.size();i++)
{
QString article = QString::number(i);
dir.setPath(RenameWidget::getDirectoryPath() + "/" + listDir.at(i));
QStringList tempstr = dir.entryList(QDir::Dirs | QDir::NoDot | QDir::NoDotDot);
QString temppath = RenameWidget::getDirectoryPath() + "/" + listDir.at(i) + "/" + tempstr .at(0);
QString tempres = RenameWidget::getResultPath() + "/" + "archives" + "/" + article;
dir.mkpath(tempres);
doArchive(temppath + "/", tempres + "/" + article + ".zip");
emit workProgress("Обработано каталогов: " + QString::number(i+1) + "/" + QString::number(listDir.size()));
}
emit workProgress("Готово");
}
void Worker::doArchive(QString path, QString zippath)
{
QZipWriter zip(zippath);
zip.setCompressionPolicy(QZipWriter::AutoCompress);
QDirIterator it(path, QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
while (it.hasNext())
{
QString file_path = it.next();
if (it.fileInfo().isFile())
{
QFile file(file_path);
if (!file.open(QIODevice::ReadOnly))
continue;
zip.setCreationPermissions(QFile::permissions(file_path));
QByteArray ba = file.readAll();
zip.addFile(file_path.remove(path), ba);
}
}
zip.close();
}

Related

QLabel not showing images using QToolButton

I have a small minimal example of a user interface for visualizing images (both .tif, .tiff, .jpg etc) composed of:
1) N.1 QLabel (used to show the image)
2) N.1 Pushbutton (used to upload a folder)
3) N.1 QLineEdit (used to visualize the path)
4) N.2 QToolbuttons (used as left and right to look through images)
I am trying to look through images using the left and the right QToolbuttons but something is not correct and I am not able to see any image. I was looking at this source as an example in order to develop my own implementation and use it for other projects I am developing.
mainwindow.h
private slots:
void on_imageCroppedABtn_clicked();
void on_leftArrowCroppedA_clicked();
void on_rightArrowCroppedA_clicked();
private:
Ui::MainWindow *ui;
QString camADir;
QString fileCamA;
int croppedIndexA;
QStringList croppedFilenamesA;
QDir croppedA;
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
croppedIndexA = 0;
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_imageCroppedABtn_clicked()
{
QString cdir = QFileDialog::getExistingDirectory(this, tr("Choose an image directory to load"),
fileCamA, QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
if((cdir.isEmpty()) || (cdir.isNull()))
return;
croppedA.setPath(cdir);
croppedFilenamesA = croppedA.entryList(QStringList() << "*.tiff" << "*.TIFF" << "*.tif" << "*.TIF", QDir::Files);
croppedIndexA = 0;
ui->lineEditfolderA->setText(croppedA.path());
}
void MainWindow::on_leftArrowCroppedA_clicked()
{
croppedIndexA--;
if(croppedIndexA < 0)
croppedIndexA = croppedFilenamesA.size()-1;
if(croppedFilenamesA.size() > 0)
{
ui->labelCroppedA->setScaledContents(true);
ui->labelCroppedA->setPixmap(QPixmap::fromImage(QImage(croppedFilenamesA[croppedIndexA])));
ui->labelCroppedA->show();
}
}
void MainWindow::on_rightArrowCroppedA_clicked()
{
croppedIndexA++;
if(croppedIndexA >= croppedFilenamesA.size())
croppedIndexA = 0;
if(croppedFilenamesA.size() > 0)
{
ui->labelCroppedA->setScaledContents(true);
ui->labelCroppedA->setPixmap(QPixmap::fromImage(QImage(croppedFilenamesA[croppedIndexA])));
ui->labelCroppedA->show();
}
}
I have been trying to change the implementation in many different ways but I always am not able to see images. Can anyone shed a little bit of light on this issue?
QImage ctor requires the full path to an image which is read. You can store a result of calling getExistingDirectory in data member cdir. When you call entryList all files in the passed directory are listed. While creating QImage you need to concatenate dir name with file name from this dir. So you can call:
ui->labelCroppedA->setPixmap(
QPixmap::fromImage(QImage(cdir + "/" + croppedFilenamesA[croppedIndexA])));
^ add directory separator

How to capture parent directory from lambda

In my code I list subdirectories name(only child dir) and if i click this subdirectory , the images inside this subdir will be displayed. In my case I cant capture parent directory from inside lambda. How can I do that?
Dir directory = QFileDialog::getExistingDirectory(this, tr("Open Directory"),"/home",
QFileDialog::ShowDirsOnly| QFileDialog::DontResolveSymlinks);
for (const QFileInfo &finfo: directory.entryInfoList()) {
QDir dir(finfo.absoluteFilePath());
ui->listWidget_dirs->addItem(dir.dirName());
}
//QStringList files = directory.entryList(QDir::Dirs);
//ui->listWidget_dirs->addItems(files);
auto listWidget_images = new QListWidget();//set listwidget to display images
listWidget_images->setMinimumSize(1200,400);
listWidget_images->setViewMode(QListWidget::IconMode);
listWidget_images->setIconSize(QSize(320,240));
listWidget_images->setResizeMode(QListWidget::Adjust);
connect(ui->listWidget_dirs, & QListWidget::itemClicked,[listWidget_images,this](QListWidgetItem *item)
{
listWidget_images->show();
listWidget_images->clear();
/*this is where it is wrong*/ QDir path(directory + '/' + item->text());
path.setNameFilters({"*.png", "*.jpg"});
for(const QFileInfo & finfo: path.entryInfoList()){
QListWidgetItem *item = new QListWidgetItem(QIcon(finfo.absoluteFilePath()), finfo.fileName());
listWidget_images->addItem(item);
}
});
I just wanted to leave a note. Be sure that your objects alive during your lamda exist, especially listWidget_images. If you delete it elsewhere, your lamda will crash. I would recommment to change the connect line a little:
from
connect(ui->listWidget_dirs, & QListWidget::itemClicked,[directory,listWidget_images, this](QListWidgetItem *item)
to
connect(ui->listWidget_dirs, & QListWidget::itemClicked, listWidget_images, [directory,listWidget_images, this](QListWidgetItem *item)
If you put the pointer listWidget_images before the capture list [] the lamda will be destroyed, when the object from listWidget_images is destroyed. Otherwise your application will crash in the first line of your lamda if the object doesn't exist anymore.
Well I get how to do it, this is the code.
QDir directory = QFileDialog::getExistingDirectory(this, tr("Open Directory"),"/home",
QFileDialog::ShowDirsOnly| QFileDialog::DontResolveSymlinks);
for (const QFileInfo &finfo: directory.entryInfoList()) {
QDir dir(finfo.absoluteFilePath());
ui->listWidget_dirs->addItem(dir.dirName());
}
auto listWidget_images = new QListWidget();//set listwidget to display images
listWidget_images->setMinimumSize(1200,400);
listWidget_images->setViewMode(QListWidget::IconMode);
listWidget_images->setIconSize(QSize(320,240));
listWidget_images->setResizeMode(QListWidget::Adjust);
connect(ui->listWidget_dirs, & QListWidget::itemClicked,[directory,listWidget_images, this](QListWidgetItem *item)
{
listWidget_images->show();
listWidget_images->clear();
// QDir path(item->text());
QDir dir = directory.absolutePath() + '/' + item->text();
dir.setNameFilters({"*.png", "*.jpg"});
for(const QFileInfo & finfo: dir.entryInfoList()){
QListWidgetItem *item = new QListWidgetItem(QIcon(finfo.absoluteFilePath()), finfo.fileName());
listWidget_images->addItem(item);
}
});

Getting full path from QListWidget

I have 2 listwidgets, lets call them listwidgetinput and listwidgetoutput. I have alot of files(only file name) on listwidgetinput. And i trim the file name before adding it to listwidgetinput like this it.fileName(). and i transfer the selected files to listdigetoutput like:
QList <QListWidgetItem*> items=ui->listWidgetinput->selectedItems();
for(int j=0;j<items.count();j++)
{
list= items.at(j)->text();
ui->listWidgetOutput->insertItem(j,list);
After i transfer the file can i get the path for all the files?. If Yes, how?
edit: code where whole path is available.
QString Dir, Type;
QStringList Files;
Qlistwidget wid
if (index==0)
{
Dir.append(C:\desktop....);
type.append(".txt")
wid = ui->listwidgetinput_txt;
}
if (index ==1)
{
Dir.append(C:\desktop....);
type.append(".doc")
wid = ui->listwidgetinput_doc
}
QDirIterator it(Dir, QStringList() << Type, QDir::Files, QDirIterator::Subdirectories);
while (it.hasNext())
{
it.next();
Files.append(it.fileName());
}
wid->additems(Files);
Use QListWidgetItem::setData() to pass additional "invisible" properties like the full path when creating the item:
auto item = new QListWidgetItem;
item->setText(fileInfo.fileName());
item->setData(Qt::UserRole, fileInfo.absoluteFilePath());
...
Later you can retrieve it via QListWidgetItem::data():
const auto fullPath = item->data(Qt::UserRole).toString();

qt pixmap.save not working

In my screenshot taking project the QPixmap.save() function returns false meaning fails every time. However when I copy the example project from Qt page http://qt-project.org/doc/qt-5/qtwidgets-desktop-screenshot-example.html, it works. Thus it rules out Windows 7 issue with file permissions.
So I wonder why it fails?
widget.h file:
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private slots:
void updateTimer();
void startScreenshots();
void stopScreenshots();
void takeScreenshot();
private:
Ui::Widget *ui;
QString initialPath;
QPixmap currentScreenshot;
QSpinBox * delaySpinBox;
QPushButton * startButton;
QPushButton * stopButton;
QHBoxLayout * hboxLayout;
QGroupBox * groupBox;
QTimer * timer;
void setInitialPath();
void addStuff();
};
widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
setInitialPath();
addStuff();
}
Widget::~Widget()
{
delete ui;
}
void Widget::updateTimer()
{
timer->stop();
int milisecs = delaySpinBox->value() *1000;
timer->start( milisecs );
}
void Widget::startScreenshots()
{
timer->start( delaySpinBox->value() * 1000 );
}
void Widget::stopScreenshots()
{
timer->stop();
}
void Widget::takeScreenshot()
{
//take screenshot
currentScreenshot = QPixmap::grabWindow(QApplication::desktop()->winId());
//save screenshot
QString format = "png";
QDateTime local( QDateTime::currentDateTime() );
QString date = local.toString();
QString fileName = initialPath + date;
if(!currentScreenshot.save(fileName, format.toLatin1().constData()) )
{
qDebug() << "didnt save\n";
QMessageBox::information(this,"failed to save","failed to save");
}
}
void Widget::setInitialPath()
{
initialPath = QFileDialog::getExistingDirectory(this, tr("Open Directory"),
"/home",
QFileDialog::ShowDirsOnly
| QFileDialog::DontResolveSymlinks);
}
void Widget::addStuff()
{
timer = new QTimer(this);
connect(timer,SIGNAL(timeout()),this,SLOT(takeScreenshot()) );
delaySpinBox = new QSpinBox(this);
delaySpinBox->setValue(60);
delaySpinBox->setSuffix(tr(" s"));
connect( delaySpinBox,SIGNAL(valueChanged(int)),this,SLOT(updateTimer()) );
startButton = new QPushButton(this);
startButton->setText("start");
connect(startButton,SIGNAL(clicked()),this,SLOT(startScreenshots()) );
stopButton = new QPushButton(this);
stopButton->setText("stop");
connect(stopButton,SIGNAL(clicked()),this,SLOT(stopScreenshots()) );
hboxLayout = new QHBoxLayout(this);
hboxLayout->addWidget(startButton);
hboxLayout->addWidget(stopButton);
hboxLayout->addWidget(delaySpinBox);
groupBox = new QGroupBox(tr("Options"));
groupBox->setLayout(hboxLayout);
setLayout(hboxLayout);
}
QDateTime local( QDateTime::currentDateTime() )
probably contains symbols which Windows doesn't allow. (there are few symbols). That's why you cannot save it.
Solution: fist of all, try to remove dateTime from filename and see is it work.
If you want use dateTime try format it without forbidden symbols
Forbidden symbols in Windows for example:
< (less than)
> (greater than)
: (colon)
" (double quote)
/ (forward slash)
\ (backslash)
| (vertical bar or pipe)
? (question mark)
* (asterisk)
QDateTime always return string which contains colon, but it is forbidden and you can't use it, you should replace it.
It just works under linux. In addition to #Chernobyl's answer, AFAIK QPixmap::save doesn't add the suffix automatically, so you need to change
QString fileName = initialPath + date;
to
QString fileName = initialPath + date.replace(":", "-") + ".png";
(The .replace(":", "-") part is for escaping forbidden ":" symbol in the file name)
When you are constructing the filename and its path as follows:
QString fileName = initialPath + date;
You will have a path similar to
C:/Users/YourName/Pictures
for your initial path.
While your date will be in the format of
Sun Sep 7 11:35:46 2014
So during your concatenation, you would end up with something like
C:/Users/YourName/PicturesSun Sep 7 11:35:46 2014
If you look closely, there are quite a few problems here:
You are missing an "/" at the end of your initial path
Your date String contains characters that Windows does not allow for file names
Once saved, the file will be missing its extension, this will need to be added to the end of your filename String as well.
Fixes required:
You need change the date format to something acceptable for Windows by using
QString date = local.toString("A Valid QDateTime format here for windows")
QString fileName = initialPath + "/" + date + "." + format;

Copying Directory and sub directory

Hi im try to copy a directory and all its contents. I thought a good start is to list all the sub directories so i can get an idea and copy the sub structure to my destination folder.
but i need to get the path from the QDir onwards not the path from the root of the machine,
how do i do this so i get sweetassurfwear/folder/folder instaed of /usr/local/websites/sweetassurfwear/folder/folder
here is my code so far
QDir targetDir1("/home/brett/sweetback");
targetDir1.mkdir("sweetassurfwear");
QDir targetDir("/usr/local/websites/sweetassurfwear");
targetDir.setFilter(QDir::NoDotAndDotDot| QDir::Dirs | QDir::Files);
QDirIterator it(targetDir, QDirIterator::Subdirectories);
while (it.hasNext()) {
QFileInfo Info(it.next().relativePath());
QString testName = QString(Info.fileName());
QString testPath = QString(Info.absoluteFilePath());
if(Info.isDir()){
// QString cpd = "/home/brett/sweetback/sweetassurfwear/";
// targetDir.mkdir(cpd+testName);
QString dirname = Info.filePath();
ui.textEdit->append(QString("Copy Files " + dirname));
}
}
The problem is that as I wrote in the comment, you are constructing the file info with the full path as opposed to relative path as you wish to have.
There are two approaches, both presented below, to solve the issue:
1) The work around would be to remove the "prefix" manually by string manipulation.
2) The nicer solution would be to get the relative path out of the absolute and root paths by using the corresponding QDir convenience method.
main.cpp
#include <QDir>
#include <QDirIterator>
#include <QString>
#include <QFileInfo>
#include <QDebug>
int main()
{
QDir targetDir("/usr/local");
targetDir.setFilter(QDir::NoDotAndDotDot| QDir::Dirs | QDir::Files);
QDirIterator it(targetDir, QDirIterator::Subdirectories);
while (it.hasNext()) {
it.next();
QFileInfo Info = it.fileInfo();
QString testName = Info.fileName();
// Work around
// QString testPath = Info.absoluteFilePath().mid(it.path().size()+1);
// Real solution
QString testPath = QString(targetDir.relativeFilePath(Info.absoluteFilePath()));
qDebug() << "TEST:" << testPath;
if(Info.isDir()) {
// QString cpd = "/home/brett/sweetback/sweetassurfwear/";
// targetDir.mkdir(cpd+testName);
QString dirname = Info.filePath();
}
}
return 0;
}
main.pro
TEMPLATE = app
TARGET = main
QT = core
SOURCES += main.cpp
Build and run
qmake && make && ./main
Note that, you do not really need to build a file info based on the path since the QDirIterator instance can return that directly.
Why not just use relativePath method of this class ? Thanks to this method you can get relative path from your path -> link