I load all text files from my folder to the MainMenu in my Qt application.
void MainWindow::loadFilesToMainMenu() {
QString pathToDir("/myfiles");
QDirIterator it(pathToDir, QStringList() << "*.txt", QDir::Files, QDirIterator::Subdirectories);
while (it.hasNext()) {
QString curPathName = it.next();
QStringList fileSegments = curPathName.split('/');
QString curFileName = fileSegments.at(fileSegments.size() - 1);
QAction* action = new QAction(tr(curFileName.toStdString().c_str()), this);
action->setStatusTip(tr(curPathName.toStdString().c_str()));
ui->menuFileList->addAction(action);
// if new style selected?
connect(action, SIGNAL(triggered()), this, SLOT(onLoadFile()));
}
}
There I create QActions for all files in my folder 'myfiles' and I connect these each of these ations to the SLOT onLoadfile():
void MainWindow::onLoadFile() {
QAction *action = qobject_cast<QAction *>(sender());
if (action)
{
qDebug() << " onLoadFile " << action->data().toString();
}
}
So each time I select one of those files in my MainMenu, this SLOt is triggered, but my debug message says:
onLoadFile ""
When I for instance select /myfiles/file1.txt
onLoadFile "/myfiles/file1.txt"
Wham am I missing? Thanx in advance
The answer from #m.s. solves my question very well...
You should use QAction::setData() before trying to read the data – m.s
Related
so I wrote a programm for my thesis in Qt and now i am supposed to turn it into a working web assembly, which wasnt a real problem except for the filedownload part. I rewrote my filedownload method from:
QString costumfile::read(QString filename){
QString fileName = QFileDialog::getOpenFileName(nullptr, filename, "", "Text Files (*.txt )");
QFile file(filename);
qDebug()<<filename<<"filename";
if(!file.open(QFile::ReadOnly |
QFile::Text))
{
qDebug() << " Could not open the file for reading";
return "";
}
QTextStream in(&file);
QString myText = in.readAll();
//qDebug() << myText;
file.close();
return myText;
}
To this:
QString costumfile::read(QString filename)
{
QMessageBox msgBox;
QString textUser="Open" + filename;
msgBox.setText(textUser);
msgBox.exec();
QString text="hallo";
qDebug()<<filename;
auto fileContentReady = [&](const QString &fileName, const QString &fileContent) {
if (fileName.isEmpty()) {
msgBox.setText("Error");
msgBox.exec();
} else {
text=fileContent;
qDebug()<<text<<"texstis";
return fileContent;
}
return fileContent;
};
QFileDialog::getOpenFileContent(".txt", fileContentReady);
}
and the problem is that the return doesnt wait for the lambda function because its asynch...
I then tried using eventloops which works fine in the Destop applikation but isnt supported in the webassembly Applikation.
So does someone have a good idea how to wait for the fileContentReady Function?
As far as I know, Qt for WebAssembly currently does not support waiting using event loops (at least Qt 6.2 does not). See Qt wiki:
"Nested event loops are not supported. Applications should not call e.g. QDialog::exec() or create a new QEventLoop object."
https://wiki.qt.io/Qt_for_WebAssembly
So you would have to modify your method to handle the asynchronous call. What I mean is that whatever you want to do with the file, you can write directly into the fileContentReady lambda you have. If this is a generic function, you can let the caller register a done callback to execute when the file is ready. Something like:
QString costumfile::read(QString filename,
const std::function<void(const QString&)>& done)
{
...
auto fileContentReady = [=](const QString &fileName, const QString &fileContent) {
if (fileName.isEmpty()) {
// Report error
} else {
text=fileContent;
qDebug()<<text<<"texstis";
done(text);
}
};
QFileDialog::getOpenFileContent(".txt", fileContentReady);
}
// When calling costumfile::read
read(filename, [=] (const QString& text) {
// Do something with `text`
});
Also, about the usage of QMessageBox exec(). This can also cause problems as it internally creates a nested event loop which is not yet supported in Qt for WebAssembly. Instead use the show() method.
auto msgBox = new QMessageBox();
msgBox->setText(textUser);
connect(msgBox, &QMessageBox::finished, &QMessageBox::deleteLater);
msgBox->show();
I need to connect a QProcess to an error handler, but I'm unsure how to pass the error string to the slot. What's below compiles, but doesn't work.
QString MainWindow::RunProcess(QString cstring)
{
QProcess *process = new QProcess(this);
connect(process,SIGNAL(readyReadStandardError()),this,SLOT( adberror(process::ReadAllStandardError() ) ))
process->start(cstring);
}
void MainWindow::adberror(QString errtxt)
{
qDebug() << "error handler";
qDebug() << errtxt;
}
I can induce a a process error, but adberror() never triggers.
When run, in the Application Output pane I see:
QObject::connect: No such slot MainWindow::adberror(process::ReadAllStandardError() )
QObject::connect: (receiver name: 'MainWindow')
edit: this is Qt 5.6. I did a new qmake/clean.
you have two options
1- wait before reading the output
QString MainWindow::RunProcess(QString cstring)
{
QProcess process;
process.start(cstring);
process.waitForFinished();
QString str = process.readAllStandardOutput();
}
2- make you process a member variable and remove your 1st argument from adberror. So,
in RunProcess
connect(process,SIGNAL(readyReadStandardError()),this,SLOT(adberror()))
then in adberror
QString str = process->readAllStandardOutput();
note that in your code you have a problem since your signal and slot args don't to match .. Also, ReadAllStandardError is not going to be ready anyways !
Edit: more code for the 2nd solution
mainwindow.h
class MainWindow
{
private://methods
void adberror();
private://attributes
QProcess* process;
};
mainwindow.cpp
QString MainWindow::RunProcess(QString cstring)
{
process = new QProcess(this);
connect(process,SIGNAL(readyReadStandardError()),this,SLOT(adberror()));
connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater()));
process->start(cstring);
}
void MainWindow::adberror()
{
QString str = process->readAllStandardOutput();
qDebug() << str;
}
To process the readyReadStandardError() signal you should define the slot as:
private slots:
void adberror();
and use it:
connect(process,SIGNAL(readyReadStandardError()),this,SLOT( adberror() ));
i.e. with no arguments. Keep child process as a field of your MainWindow class to read data when it will be available.
I am working in Qt4.7 on MAC OSx. I want to insert files in QTreewidget using the Drag and Drop events. I want to add multiple files at a time. I am using this:
void MainWindow::dragEnterEvent(QDragEnterEvent * e)
{
if(e->mimeData()->hasUrls())
{
e->acceptProposedAction();
}
}
void MainWindow::dropEvent(QDropEvent * e)
{
QTreeWidgetItem *Items = new QTreeWidgetItem(ui->treeWidget);
foreach(const QUrl &url,e->mimeData()->urls())
{
const QString &filename = url.toLocalFile();
qDebug() << "Dropped file:" << filename;
Items->setText(0,filename);
}
}
Using this, I am able to insert only one file at a time. Is there anyone who can help me out in this issue ? Your help will really appreciate.
Thanks,
Ashish.
The problem is that you create only one tree view item. However you need one per each Url you passed with the mime data:
void MainWindow::dropEvent(QDropEvent *e)
{
foreach(const QUrl &url, e->mimeData()->urls()) {
QString filename = url.toLocalFile();
qDebug() << "Dropped file:" << filename;
QTreeWidgetItem *item = new QTreeWidgetItem(ui->treeWidget);
item->setText(0, filename);
}
}
My problem is to make a menu to load files. Here's my code:
QStringList fileNameList;
fileNameList << "file1" << "file2" << "file3";
QMenuBar *menubar = new QMenuBar();
QMenu *menu = menubar->addMenu("File");
QMenu *load = menu->addMenu("Load");
foreach (QString fileName, fileNameList) {
QAction *loadFile = new QAction(fileName, this);
load->addAction(loadFile);
connect(load,SIGNAL(triggered(QAction*)),this, SLOT(load(QAction*)));
}
And a slot:
void MainWindow::load(QAction* action) {
qDebug() << action->text();
}
After I click any action button, qDebug shows:
"file1"
"file1"
"file1"
But I need to run that action only once! QAction does not have a signal from which I can get
its name. How to solve this? Thank you!
The problem is that you create the same connection tree times in the loop. What you probably need, is just doing it only once:
[..]
foreach (QString fileName, fileNameList) {
QAction *loadFile = new QAction(fileName, this);
load->addAction(loadFile);
}
connect(load, SIGNAL(triggered(QAction *)), this, SLOT(load(QAction *)));
UPDATE
The alternative solution would be:
foreach (QString fileName, fileNameList) {
QAction *loadFile = new QAction(fileName, this);
load->addAction(loadFile);
connect(loadFile, SIGNAL(triggered()), this, SLOT(load()));
}
with the corresponding slot:
void MainWindow::load() {
QAction *action = qobject_cast<QAction *>(sender());
if (action)
qDebug() << action->text();
}
so i have been browsing the previous questions before about this issue, but i could not find a solution for my code.
cpp file of dialog
------------------------------------------------
#include "everesult.h"
#include "ui_everesult.h"
everesult::everesult(QWidget *parent) :
QDialog(parent),
ui1(new Ui::everesult)
{
ui1->setupUi(this);
}
everesult::~everesult()
{
delete ui1;
}
void everesult::setmodel(QStandardItemModel *model)
{
ui1->listView->setModel(model);
}
void everesult::on_buttonBox_clicked(QAbstractButton *button)
{
EveReprocess M_;
QModelIndex Selectedindex = ui1->listView->currentIndex();
QModelIndex StationIdsindex = ui1->listView->model()->index(0, 1);
int typeID = 0;
int stationID = 0;
stationID = ui1->listView->model()->data(StationIdsindex, Qt::DisplayRole).toInt();
typeID = ui1->listView->model()->data(Selectedindex, Qt::DisplayRole).toInt();
M_.GetMaterials(typeID, stationID);
}
--------------------------------------------------
Getmaterial and replyFinished from main window.
--------------------------------------------------
void EveReprocess::GetMaterials(int typeId, int stationid)
{
//get typeid from material list
this->modelMaterial = new QSqlQueryModel();
modelMaterial->setQuery(QString("SELECT tm.quantity, tm.materialTypeID, t.typeName FROM invTypeMaterials tm INNER JOIN invTypes t ON t.TypeID = tm.materialTypeId WHERE tm.TypeID=%1 ").arg(typeId));
if (!modelMaterial->query().exec())
qDebug() << modelMaterial->query().lastError();
//Set eve Central Url with typeids
QUrl url = QUrl("http://api.eve-central.com/api/marketstat?");
QUrlQuery q;
int numRows = modelMaterial->rowCount();
for (int row = 0; row < numRows; ++row)
{
QModelIndex index = modelMaterial->index(row, 1);
q.addQueryItem( QString("typeid"), QString::number(modelMaterial->data(index, Qt::DisplayRole).toInt()));
}
q.addQueryItem( QString("usesystem"), QString::number(stationid));
//set created url and connect
url.setQuery(q);
qDebug() << url;
manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply *)));
manager->get(QNetworkRequest(url) );
}
void EveReprocess::replyFinished(QNetworkReply *reply)
{
qDebug() << "replyFinished called";
if ( reply->error() != QNetworkReply::NoError ) {
qDebug() << "Request failed, " << reply->errorString();
emit replyFinished(false);
return;
}
qDebug() << "Request succeeded";
//process with xmlreader and get values
processSearchResult( reply);
}
some of the code is here, i think it should be somewhere here, as the rest works fine.
this issue showed up after i use a dialog to let user pick a int from a list.
below is the function that calls the dialog that i have made for this. sorry about code format wil clean it up after it is working
void EveReprocess::Search_TypeId(QString ItemName, QString SystemName)
{
QList<int> TypeIdList;
QList<int> StationIdList;
modelIds = new QStandardItemModel(10,2,this);
if !(db.isOpen()) return;
this->queryItem = new QSqlQuery;
queryItem->prepare("SELECT typeID FROM invTypes WHERE invTypes.typeName LIKE ? AND invTypes.groupID NOT IN (268,269,270)AND published= 1");
ItemName.prepend("%");
ItemName.append("%");
queryItem->bindValue(0, ItemName);
this->queryStation = new QSqlQuery;
queryStation->prepare("SELECT solarSystemID FROM mapSolarSystems WHERE mapSolarSystems.solarSystemName LIKE ?");
SystemName.prepend("%");
SystemName.append("%");
queryStation->bindValue(0, SystemName);
if(!queryStation->exec() || !queryItem->exec() )
{
qDebug() << queryItem->lastError().text();
qDebug() << queryItem->lastQuery();
qDebug() << queryStation->lastError().text();
qDebug() << queryStation->lastQuery();
}
while( queryStation->next())
{
StationIdList.append(queryStation->value(0).toInt());
}
while(queryItem->next())
{
TypeIdList.append(queryItem->value(0).toInt());
}
for (int i = 0; i < StationIdList.count(); ++i)
{
modelIds->setItem(i,1,new QStandardItem(QString::number(StationIdList.at(i))));
}
for (int i = 0; i < TypeIdList.count(); ++i)
{
modelIds->setItem(i,0,new QStandardItem(QString::number(TypeIdList.at(i))));
}
//
everesult Dialog;
Dialog.setmodel(modelIds);
Dialog.exec();
}
Before you proceed any further, some of your code is allows SQL injections. Even when it's not a security hole, it'll still lead to bugs. Instead of using string substitution in SQL queries, you should be using bindings.
Your problem is here:
everesult Dialog;
Dialog.setmodel(modelIds);
Dialog.exec();
The exec() is a blocking function - it blocks the main event loop until the dialog is dismissed. Thus the signals from the threaded network access manager never get delivered to your objects.
You should display the dialog box asynchronously, like so:
everesult * dialog = new everesult;
dialog->setModel(modelIds);
dialog->show();
connect(dialog, SIGNAL(accepted()), dialog, SLOT(deleteLater());
connect(dialog, SIGNAL(rejected()), dialog, SLOT(deleteLater());
Note that it's misleading to have type names starting with lower case and variable names starting with upper case. Qt's convention is the opposite, and it's useful to retain it unless you have a good reason to do otherwise.
DO Parameter Binding in SQL Queries
QSqlQuery query("SELECT .. WHERE tm.TypeID=:typeid");
query.bindValue(":typeid", typeId);
QSqlQueryModel model;
model.setQuery(query);
DON'T DO String Substitution in SQL Queries
setQuery(QString("SELECT ... WHERE tm.TypeID=%1 ").arg(typeId));