I apologize in advance am very new to QT (and fairly new to C++)
I am trying setup a program that will execute a function anytime a specific file is changed. Despite hours on google and reading the docs provided on QT I cant find a way to get myfunction() to execute when SSOpen_Log is edited
currently my code looks something like this (SOpen_Log is a QString declared in .h):
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
QFileSystemWatcher watcher;
watcher.addPath(SOpen_Log);
MainWindow::connect(&watcher, SIGNAL(fileChanged(QString)), this, SLOT(myfunction()));
}
MainWindow::myfunction()
{
//mycode executes here
}
You allocated your instance on the stack and it thus get destructed when the constructor ends. I would suggest that you make a class member for the instance so that it remains valid throughout the class.
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
m_watcher.addPath(SOpen_Log);
MainWindow::connect(&m_watcher, SIGNAL(fileChanged(QString)), this, SLOT(myfunction()));
}
There is no need to complicate this by putting it on the heap, effectively using a pointer, although that is also an option to consider.
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
QFileSystemWatcher *watcher = new QFileSystemWatcher(this);
watcher->addPath(SOpen_Log);
MainWindow::connect(watcher, SIGNAL(fileChanged(QString)), this, SLOT(myfunction()));
}
While we are at it, I would also like to mention to use the new signal-slot syntax style rather than the old. The old one is checked at runtime and the new one is checked at compilation time. So, you catch any issues with it during compilation rather than at runtime.
A complete and simple example:
Dirwatcher.h
class DirWatcher : public QObject
{
Q_OBJECT
public:
static const int sMaxCount = 5;
explicit DirWatcher(const QString& root, QObject* parent = nullptr);
virtual ~DirWatcher();
private slots:
void hDirChanged(const QString&);
void hFileChanged(const QString&);
private:
quint64 m_rbSize;
QByteArray m_ringBuffer[10];
QFileSystemWatcher m_watcher;
QDir m_root;
QSet<QString> m_directories;
QMap<QString, QSet<QString>> m_abspaths;
QString m_lastKey;
};
and Dirwatcher.cpp
namespace
{
void split(const char* str, const char* delim, std::vector<std::string>& out)
{
const char* begin = str;
const char* it = strstr(str, delim);
if (it != NULL)
{
std::string data{begin, it};
out.push_back(data);
it++;
split(it, delim, out);
} else {
std::string data{str};
out.push_back(data);
}
}
}
DirWatcher::DirWatcher(const QString &root, QObject* parent):
QObject(parent), m_root(root), m_rbSize{10}
{
m_watcher.addPath(root);
QObject::connect(&m_watcher, SIGNAL(directoryChanged(const QString&)),
this, SLOT(hDirChanged(const QString&)));
QObject::connect(&m_watcher, SIGNAL(fileChanged(const QString&)),
this, SLOT(hFileChanged(const QString&)));
}
DirWatcher::~DirWatcher()
{
}
void DirWatcher::hDirChanged(const QString &path)
{
QDirIterator it(m_root.path());
while (it.hasNext()) {
QString d = it.next();
if (d.at(0) == '.' || d.at(d.size()-1) == '.') {
} else {
if (!m_directories.contains(d)){
m_directories << d;
m_lastKey = d;
}
}
}
#if 0 //delete directories when count reaches...
if (m_directories.count() > DirWatcher::sMaxCount) {
QSetIterator<QString> setit(m_directories);
while (setit.hasNext()) {
QString toDel = setit.next();
if (toDel != m_lastKey) {
QDir rem (toDel);
rem.removeRecursively();
}
}
m_directories.clear();
m_directories << m_lastKey;
}
#endif
std::cout << "##############################################\r\n";
QSetIterator<QString> setit(m_directories);
while (setit.hasNext()) {
QString d = setit.next();
if (d.contains(".tlf")) {
m_watcher.addPath(d);
}
std::cout << d.toStdString() << std::endl;
}
}
void DirWatcher::hFileChanged(const QString& file)
{
static size_t cnt = 0;
QFile f{file};
if (f.open(QFile::ReadOnly)) {
quint64 s = f.size();
f.seek(f.size()-f.size()/2);
while (!f.atEnd()) {
QByteArray data = f.readLine();
m_ringBuffer[cnt++ % m_rbSize]=data;
}
}
std::cout << "----------------- B E G I N ----------------------\r\n";
for(auto i : m_ringBuffer) {
std::cout << "~~~~~~\r\n";
std::cout << i.toStdString().c_str() << "\r\n";
}
}
It's a real background monitoring job process for deleting files on a given time or count, but you will understand what is missing in your code if you debug it.
Related
I have a "cell" class that describes some kind of smart device. I've got plenty of them. Each of these devices must implement a connection to the server. I have written methods in the "cell" class.
cell.cpp
#include "cell.h"
#include <QDateTime>
#include <QMessageBox>
cell::cell(QObject *parent) : QObject(parent)
{
}
void cell::Connect(){
m_client = new QMqttClient(this);
m_client->setHostname("192.111.11.111");
m_client->setPort(1883);
m_client->connectToHost();
QObject::connect(m_client, &QMqttClient::stateChanged, this, &cell::updateLogStateChange);
QObject::connect(m_client, &QMqttClient::disconnected, this, &cell::brokerDisconnected);
}
void cell::MsgReceive(){
connect(m_client, &QMqttClient::messageReceived, this, [this](const QByteArray &message, const QMqttTopicName &topic) {
const QString content =
QLatin1String(" Received Topic: ")
+ topic.name()
+ QLatin1String(" Message: ")
+ message
+ QLatin1Char('\n');
setMsg(content);
});
}
void cell::Publish()
{
QString topic = "topic/";
QString msg = "HELLO";
m_client->publish(topic, msg.toUtf8());
}
void cell::Subscribe()
{
QString topic = "topic/";
auto subscription = m_client->subscribe(topic);
if (!subscription) {
return;
}
}
void cell::updateLogStateChange()
{
const QString content = QDateTime::currentDateTime().toString()
+ QLatin1String(": State Change")
+ QString::number(m_client->state())
+ QLatin1Char('\n');
setLogState(content);
}
void cell::brokerDisconnected()
{
}
In the main window, I create objects through a loop and call the connection methods as an example, just to get a message in the ListWidget.
But it doesn't show up, what's the problem? I would be very grateful if you help me. I just started learning c++.
MainWindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDateTime>
#include <QMessageBox>
#include <modelcell.h>
#include <cell.h>
#include <QThread>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
QVector <cell *> listcell;
QString val;
QFile file;
file.setFileName("C:\\Users\\User\\Desktop\\cell.json");
file.open(QIODevice::ReadOnly | QIODevice::Text);
val = file.readAll();
file.close();
QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(val.toUtf8(), &error);
if (doc.isObject())
{
QJsonObject json = doc.object();
QJsonArray jsonArray = json["cell"].toArray();
foreach (const QJsonValue & value, jsonArray)
{
if (value.isObject())
{
QJsonObject obj = value.toObject();
QString id = obj["ID"].toString();
QString name = obj["Name"].toString();
QString IP = obj["IP"].toString();
QString status = obj["status"].toString();
cell * cell_obj = new cell;
cell_obj->setName(name);
cell_obj->setID(id);
cell_obj->setIP(IP);
cell_obj->setStatus(status);
cell_obj->Connect();
cell_obj->MsgReceive();
cell_obj->Subscribe();
cell_obj->Publish();
listcell.append(cell_obj);
}
}
}
for(int i = 0; i < listcell.size(); i++){
ui->listWidget->addItem(listcell[i]->getName() + " | " + listcell[i]->getID() + " | " + listcell[i]->getIP()+ " | " + listcell[i]->getStatus());
ui->listWidget->addItem(listcell[i]->getMsg());
}
}
MainWindow::~MainWindow()
{
delete ui;
}
I want the listWidget to display a "hello" message from each created object. But it doesn't happen. Where is the mistake?
I subclassed a QTreeWidget in order to reimplement the protected functionalities of drag and drop in order to drag drop parents and children of a QTreeWidget.
It almost works and the problem is that as soon as I try to drag and drop either children or parents, they are erased as soon as I drop them.
Source code can be found here in case you need to see what is going on.
Also if I try to drag-drop the parent, it unfortunately does not move and nothing happens.
See below the strange effect for the children:
I drag the child "Original" to drop it right under "Sample" and this procedure seems to work correctly:
After dropping "Original" as you can see the index seems to be there but it seems to be partially erased:
I am not sure exactly of what is going on and why the children seems to not completely be dropped correctly and why the parents are literally not moving.
Below the code of the minimal verifiable example:
mainwindow.h
#include <QMainWindow>
class QTreeWidget;
class QTreeWidgetItem;
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
QTreeWidgetItem *createItem(const QString &name, const QString &iconPath);
private:
Ui::MainWindow *ui;
QTreeWidget *widget;
QString m_Name;
QString m_Path;
};
#endif // MAINWINDOW_H
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->treeWidget->setSelectionMode(QAbstractItemView::SingleSelection);
ui->treeWidget->setDragEnabled(true);
ui->treeWidget->viewport()->setAcceptDrops(true);
ui->treeWidget->setDropIndicatorShown(true);
ui->treeWidget->setDragDropMode(QAbstractItemView::InternalMove);
auto *top1 = createItem("Images", "/home/ui/qrc/laserscan.png");
auto *top2 = createItem("Path", "/home/ui/qrc/laserscan.png");
top1->addChild(createItem("Original", "/home/ui/qrc/laserscan.png"));
top1->addChild(createItem("Sample", "/home/ui/qrc/laserscan.png"));
top2->addChild(createItem("Left Side", "/home/ui/qrc/laserscan.png"));
top2->addChild(createItem("Right Side", "/home/ui/qrc/laserscan.png"));
ui->treeWidget->addTopLevelItems({ top1, top2 });
// Below I am assigning to each parent/children a checkbox
const int n_tops = ui->treeWidget->topLevelItemCount();
for(int a = 0; a<n_tops; ++a) {
const int n_children = ui->treeWidget->topLevelItem(a)->childCount();
qtreewidgetitem_assign_qcheckbox(ui->treeWidget, ui->treeWidget->topLevelItem(a));
for(int b = 0; b<n_children; ++b) {
qtreewidgetitem_assign_qcheckbox(ui->treeWidget, ui->treeWidget->topLevelItem(a)->child(b));
}
}
}
MainWindow::~MainWindow()
{
delete ui;
}
QTreeWidgetItem *MainWindow::createItem(const QString &name, const QString &iconPath)
{
auto *item = new QTreeWidgetItem(QStringList{name});
item->setIcon(0, QIcon(iconPath));
return item;
}
maphelpers.h
#include <QTreeWidget>
#include <QTreeWidgetItem>
void qtreewidgetitem_assign_qcheckbox(QTreeWidget *tree_widget, QTreeWidgetItem *tree_item);
#endif // MAPHELPERS_H
maphelpers.cpp
#include <QTreeWidget>
void qtreewidgetitem_assign_qcheckbox(QTreeWidget *tree_widget, QTreeWidgetItem *tree_item)
{
MyCheckBoxMap *myCheckBoxMap = new MyCheckBoxMap(tree_item);
tree_widget->setItemWidget(tree_item, MAP_COLUMN, myCheckBoxMap);
}
Below how I subclassed QTreeWidget:
customtreewidget.h
#include <QTreeWidget>
class CustomTreeWidget : public QTreeWidget
{
public:
CustomTreeWidget(QWidget *parent = Q_NULLPTR);
protected:
void dragEnterEvent(QDragEnterEvent *event) override;
void dropEvent(QDropEvent *event) override;
private:
QTreeWidgetItem *draggedItem;
};
#endif // CUSTOMTREEWIDGET_H
customtreewidget.cpp
CustomTreeWidget::CustomTreeWidget(QWidget *parent)
{
QTreeWidgetItem* parentItem = new QTreeWidgetItem();
parentItem->setText(0, "Test");
parentItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDropEnabled);
int num_rows = topLevelItemCount();
for(int i = 0; i < num_rows; ++i)
{
int nChildren = topLevelItem(i)->childCount();
QTreeWidgetItem* pItem = new QTreeWidgetItem(parentItem);
for(int j = 0; j < nChildren; ++j) {
pItem->setText(0, QString("Number %1").arg(i) );
pItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled);
pItem->addChild(pItem);
topLevelItem(i)->child(j);
}
}
}
void CustomTreeWidget::dragEnterEvent(QDragEnterEvent *event)
{
draggedItem = currentItem();
QTreeWidget::dragEnterEvent(event);
}
void CustomTreeWidget::dropEvent(QDropEvent *event)
{
QModelIndex droppedIndex = indexAt(event->pos());
if(!droppedIndex.isValid()) {
return;
}
if(draggedItem) {
QTreeWidgetItem *mParent = draggedItem->parent();
if(mParent) {
if(itemFromIndex(droppedIndex.parent()) != mParent)
return;
mParent->removeChild(draggedItem);
mParent->insertChild(droppedIndex.row(), draggedItem);
}
}
}
What I have done so far and what I have tried:
1) According to official documentation it is possible to use QTreeWidgetItemIterator Class and i tried to go that way and in fact you can see below my initial implementation idea, but this didn't really bring me anywhere:
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
auto *top1 = createItem("Images", "/home/ui/qrc/laserscan.png");
auto *top2 = createItem("Path", "/home/ui/qrc/laserscan.png");
top1->addChild(createItem("Original", "/home/ui/qrc/laserscan.png"));
top1->addChild(createItem("Sample", "/home/ui/qrc/laserscan.png"));
top2->addChild(createItem("Left Side", "/home/ui/qrc/laserscan.png"));
top2->addChild(createItem("Right Side", "/home/ui/qrc/laserscan.png"));
ui->treeWidget->addTopLevelItems({ top1, top2 });
const int n_tops = ui->treeWidget->topLevelItemCount();
// Below I am assigning to each parent/children a checkbox
for(int a = 0; a<n_tops; ++a) {
const int n_children = ui->treeWidget->topLevelItem(a)->childCount();
qtreewidgetitem_assign_qcheckbox(ui->treeWidget, ui->treeWidget->topLevelItem(a));
for(int b = 0; b<n_children; ++b) {
qtreewidgetitem_assign_qcheckbox(ui->treeWidget, ui->treeWidget->topLevelItem(a)->child(b));
}
}
QTreeWidgetItem *parentItem = Q_NULLPTR;
QTreeWidgetItem *childItem = Q_NULLPTR;
QTreeWidgetItemIterator it(ui->treeWidget);
while (*it) {
parentItem = new QTreeWidgetItem(ui->treeWidget);
//parentItem->setText(0, it.key());
foreach (const auto &str, it) {
childItem = new QTreeWidgetItem;
childItem->setText(0, str);
parentItem->addChild(childItem);
}
}
}
2) After further research in the official documentation I decided to approach the problem applying the QMapIterator Class I wanted to keep the constructor as I structured initially and figured that passing through a QMap meant to rewrite the constructor in a different way, basically the implementation idea below, but didn't want to pass that way:
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
auto *top1 = createItem("Images", "/home/ui/qrc/laserscan.png");
auto *top2 = createItem("Path", "/home/ui/qrc/laserscan.png");
top1->addChild(createItem("Original", "/home/ui/qrc/laserscan.png"));
top1->addChild(createItem("Sample", "/home/ui/qrc/laserscan.png"));
top2->addChild(createItem("Left Side", "/home/ui/qrc/laserscan.png"));
top2->addChild(createItem("Right Side", "/home/ui/qrc/laserscan.png"));
ui->treeWidget->addTopLevelItems({ top1, top2 });
ui->treeWidget->addTopLevelItems({ top1, top2 });
const int n_tops = ui->treeWidget->topLevelItemCount();
// Below I am assigning to each parent/children a checkbox
for(int a = 0; a<n_tops; ++a) {
const int n_children = ui->treeWidget->topLevelItem(a)->childCount();
qtreewidgetitem_assign_qcheckbox(ui->treeWidget, ui->treeWidget->topLevelItem(a));
for(int b = 0; b<n_children; ++b) {
qtreewidgetitem_assign_qcheckbox(ui->treeWidget, ui->treeWidget->topLevelItem(a)->child(b));
}
}
QTreeWidgetItem *parentItem = Q_NULLPTR;
QTreeWidgetItem *childItem = Q_NULLPTR;
QMapIterator<QString, QStringList> i(treeMap);
while (i.hasNext()) {
i.next();
parentItem = new QTreeWidgetItem(widget);
parentItem->setText(0, i.key());
foreach (const auto &str, i.value()) {
childItem = new QTreeWidgetItem;
childItem->setText(0, str);
parentItem->addChild(childItem);
}
}
}
3) After more research I cam across this source, and this other source which was useful (in particular the last post) as guidance to the implementation of the subclassing of the QTreeWidget.
Now I am stuck as I am trying to figure out why, despite all the things I tried, I achieved a 90% working application and, therefore, what is missing about it?
Thanks for providing guidance on how to solve this problem.
I think the checkboxes are handling the mouse events and don't let the events go through to the item. To have the desired behavior, you should remove this part:
const int n_tops = ui->treeWidget->topLevelItemCount();
for(int a = 0; a<n_tops; ++a) {
const int n_children = ui->treeWidget->topLevelItem(a)->childCount();
qtreewidgetitem_assign_qcheckbox(ui->treeWidget, ui->treeWidget->topLevelItem(a));
for(int b = 0; b<n_children; ++b) {
qtreewidgetitem_assign_qcheckbox(ui->treeWidget, ui->treeWidget->topLevelItem(a)->child(b));
}
}
And in the createItem() function:
QTreeWidgetItem *MainWindow::createItem(const QString &name, const QString &iconPath)
{
auto *item = new QTreeWidgetItem(QStringList{name});
item->setCheckState(0, Qt::Unchecked);
item->setIcon(0, QIcon(iconPath));
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
return item;
}
I am new in c++ programming , so i need help with the logic (code would be awesome). I want to make QTextEdit to work as a terminal like widget.
I am trying to achieve that with QProcess like:
void QPConsole::command(QString cmd){
QProcess* process = new QProcess();
connect(process, &QProcess::readyReadStandardOutput, [=](){ print(process->readAllStandardOutput()); });
connect(process, &QProcess::readyReadStandardError, [=](){ print(process->readAllStandardError()); });
connect(process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),[=](int exitCode, QProcess::ExitStatus exitStatus){ prepareCommandLine(); return; });
process->setProgram("/bin/bash");
process->start();
process->write(cmd.toStdString().c_str());
// process->write("\n\r");
process->closeWriteChannel();
}
The problem is next:
If i don't close write channel (process->closeWriteChannel) than i am stuked in infinite loop. And if i do close it, than i cannot remember state (kinda makes new session) for example if i do "pwd" i get result, but if i do next "cd .." and then "pwd" the result is same as the first output of "pwd"
So, my question is is it possible to achieve some kind of real time output + having previous state remembered (like in real terminal) with QProcess or i have to do in some other way. In python i did it with subprocess calls with pipes, but i don't know what is the equilavent for c++.
An code example would be great. I have QTextEdit part (which is sending QString param to function), i need part with interaction with console.
The idea is simple: Keep an instance of QProcess and write commands to it followed by \n!
Here is a full example, with a QMainWindow and a Bash class, put it in main.cpp:
#include <QtCore>
#include <QtGui>
#include <QtWidgets>
class Bash : public QObject
{
Q_OBJECT
public:
explicit Bash(QObject *parent = nullptr) : QObject(parent)
{
}
~Bash()
{
closePrivate();
}
signals:
void readyRead(QString);
public slots:
bool open(int timeout = -1)
{
if (m_bash_process)
return false;
m_timeout = timeout;
return openPrivate();
}
void close()
{
closePrivate();
}
bool write(const QString &cmd)
{
return writePrivate(cmd);
}
void SIGINT()
{
SIGINTPrivate();
}
private:
//Functions
bool openPrivate()
{
if (m_bash_process)
return false;
m_bash_process = new QProcess();
m_bash_process->setProgram("/bin/bash");
//Merge stdout and stderr in stdout channel
m_bash_process->setProcessChannelMode(QProcess::MergedChannels);
connect(m_bash_process, &QProcess::readyRead, this, &Bash::readyReadPrivate);
connect(m_bash_process, QOverload<int>::of(&QProcess::finished), this, &Bash::closePrivate);
m_bash_process->start();
bool started = m_bash_process->waitForStarted(m_timeout);
if (!started)
m_bash_process->deleteLater();
return started;
}
void closePrivate()
{
if (!m_bash_process)
return;
m_bash_process->closeWriteChannel();
m_bash_process->waitForFinished(m_timeout);
m_bash_process->terminate();
m_bash_process->deleteLater();
}
void readyReadPrivate()
{
if (!m_bash_process)
return;
while (m_bash_process->bytesAvailable() > 0)
{
QString str = QLatin1String(m_bash_process->readAll());
emit readyRead(str);
}
}
bool writePrivate(const QString &cmd)
{
if (!m_bash_process)
return false;
if (runningPrivate())
return false;
m_bash_process->write(cmd.toLatin1());
m_bash_process->write("\n");
return m_bash_process->waitForBytesWritten(m_timeout);
}
void SIGINTPrivate()
{
if (!m_bash_process)
return;
QProcess::startDetached("pkill", QStringList() << "-SIGINT" << "-P" << QString::number(m_bash_process->processId()));
}
bool runningPrivate()
{
if (!m_bash_process)
return false;
QProcess process;
process.setProgram("pgrep");
process.setArguments(QStringList() << "-P" << QString::number(m_bash_process->processId()));
process.setProcessChannelMode(QProcess::MergedChannels);
process.start();
process.waitForFinished(m_timeout);
QString pids = QLatin1String(process.readAll());
QStringList pid_list = pids.split(QRegularExpression("\n"), QString::SkipEmptyParts);
return (pid_list.size() > 0);
}
//Variables
QPointer<QProcess> m_bash_process;
int m_timeout;
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr) : QMainWindow(parent)
{
connect(&m_bash, &Bash::readyRead, this, &MainWindow::readyRead);
QWidget *central_widget = new QWidget(this);
QVBoxLayout *layout = new QVBoxLayout(central_widget);
layout->addWidget(&m_message_board);
layout->addWidget(&m_cmd_edit);
layout->addWidget(&m_sigint_btn);
m_message_board.setReadOnly(true);
m_cmd_edit.setEnabled(false);
m_sigint_btn.setText("Send SIGINT(CTRL+C)");
connect(&m_cmd_edit, &QLineEdit::returnPressed, this, &MainWindow::writeToBash);
connect(&m_sigint_btn, &QPushButton::clicked, this, &MainWindow::SIGINT);
setCentralWidget(central_widget);
resize(640, 480);
QTimer::singleShot(0, this, &MainWindow::startBash);
}
~MainWindow()
{
m_bash.close(); //Not mandatory, called by destructor
}
private slots:
void startBash()
{
//Open and give to each operation a maximum of 1 second to complete, use -1 to unlimited
if (m_bash.open(1000))
{
m_cmd_edit.setEnabled(true);
m_cmd_edit.setFocus();
}
else
{
QMessageBox::critical(this, "Error", "Failed to open bash");
}
}
void writeToBash()
{
QString cmd = m_cmd_edit.text();
m_cmd_edit.clear();
if (!m_bash.write(cmd))
{
QMessageBox::critical(this, "Error", "Failed to write to bash");
}
}
void readyRead(const QString &str)
{
m_message_board.appendPlainText(str);
}
void SIGINT()
{
m_bash.SIGINT();
}
private:
Bash m_bash;
QPlainTextEdit m_message_board;
QLineEdit m_cmd_edit;
QPushButton m_sigint_btn;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
#include "main.moc"
I'm trying to send a file from client to server. But it sends only a part of file. Seems like it happens when the size of file is more than 2Mb. What can be the problem? Sorry if it's a stupid question but I can't find an answer in Google.
This is client cpp:
#include "widget.h"
Widget::Widget(QWidget *parent) :
QWidget(parent)
{
progressBar = new QProgressBar(this);
tcpSocket = new QTcpSocket(this);
fileLabel = new QLabel(this);
progressLabel = new QLabel(this);
fileBtn = new QPushButton(this);
fileBtn->setText("Open");
sendBtn = new QPushButton(this);
sendBtn->setText("Send");
layout = new QGridLayout;
layout->addWidget(fileBtn, 0, 0);
layout->addWidget(sendBtn, 0, 1);
layout->addWidget(fileLabel, 1, 0);
layout->addWidget(progressBar, 2, 0);
connect(fileBtn, &QPushButton::clicked, this, &Widget::fileOpened);
connect(sendBtn, &QPushButton::clicked, this, &Widget::onSend);
setLayout(layout);
}
Widget::~Widget()
{
}
void Widget::fileOpened()
{
fileName = QFileDialog::getOpenFileName(this, tr("Open file"));
QFileInfo fileInfo(fileName);
fileLabel->setText(fileInfo.fileName() + " : " + QString::number(fileInfo.size()));
qDebug() << fileName;
}
void Widget::onSend()
{
tcpSocket->connectToHost("127.0.0.1", 33333);
QFile file(fileName);
QDataStream out(tcpSocket);
int size = 0;
if (file.open(QIODevice::ReadOnly))
{
QFileInfo fileInfo(file);
QString fileName(fileInfo.fileName());
out << fileName;
qDebug() << fileName;
out << QString::number(fileInfo.size());
qDebug() << fileInfo.size();
progressBar->setMaximum(fileInfo.size());
while (!file.atEnd())
{
QByteArray rawFile;
rawFile = file.read(5000);
//false size inc
QFileInfo rawFileInfo(rawFile);
size += rawFileInfo.size();
out << rawFile;
progressBar->setValue(rawFile.size());
qDebug() << QString::number(fileInfo.size());
qDebug() << "ToSend:"<< rawFile.size();
}
out << "#END";
}
}
This is a server one:
#include "myserver.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
startBtn = new QPushButton(this);
startBtn->setText("Connect");
progressBar = new QProgressBar(this);
layout = new QGridLayout;
layout->addWidget(startBtn, 0, 0);
layout->addWidget(progressBar, 1, 0);
connect(startBtn, &QPushButton::clicked, this, &MainWindow::on_starting_clicked);
setCentralWidget (new QWidget (this));
centralWidget()->setLayout(layout);
}
MainWindow::~MainWindow()
{
server_status=0;
}
void MainWindow::on_starting_clicked()
{
startBtn->setText("Connecting...");
tcpServer = new QTcpServer(this);
connect(tcpServer, SIGNAL(newConnection()), this, SLOT(acceptConnection()));
if (!tcpServer->listen(QHostAddress::Any, 33333) && server_status==0)
{
qDebug() << QObject::tr("Unable to start the server: %1.").arg(tcpServer->errorString());
}
else
{
server_status=1;
qDebug() << QString::fromUtf8("Сервер запущен!");
startBtn->setText("Running");
}
}
void MainWindow::acceptConnection()
{
qDebug() << QString::fromUtf8("У нас новое соединение!");
tcpServerConnection = tcpServer->nextPendingConnection();
connect(tcpServerConnection,SIGNAL(readyRead()),this, SLOT(slotReadClient()));
// tcpServer->close();
QDir::setCurrent("/Users/vlad/Desktop/");
QString fileName;
QString fileSize;
}
void MainWindow::slotReadClient()
{
QDataStream in(tcpServerConnection);
QByteArray z;
if (!isInfoGot)
{
isInfoGot = true;
in >> fileName;
qDebug() << fileName;
in >> fileSize;
qDebug() << fileSize;
}
QFile loadedFile(fileName);
if (loadedFile.open(QIODevice::Append))
{
while (tcpServerConnection->bytesAvailable())
{
qDebug() << "bytesAvailable:" << tcpServerConnection->bytesAvailable();
in >> z;
qDebug() << z;
loadedFile.write(z);
}
loadedFile.close();
}
}
Not so far i faced the same problem. So i find some solution. Test it on files about ~200Mb, no problems i see.
Sender part:
void FileSender::send()
{
QTcpSocket *socket = new QTcpSocket;
connect(socket, SIGNAL(disconnected()), socket, SLOT(deleteLater()));
// specified m_host and m_port to yours
socket->connectToHost(m_host, m_port);
socket->waitForConnected();
if ( (socket->state() != QAbstractSocket::ConnectedState) || (!m_file->open(QIODevice::ReadOnly)) ) {
qDebug() << "Socket can't connect or can't open file for transfer";
delete socket;
return;
}
QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_5_4);
// This part i need to send not only file, but file name too
// Erase it if you needn't it
out << (quint32)0 << m_file->fileName();
QByteArray q = m_file->readAll();
block.append(q);
m_file->close();
out.device()->seek(0);
// This difference appear because of we send file name
out << (quint32)(block.size() - sizeof(quint32));
qint64 x = 0;
while (x < block.size()) {
qint64 y = socket->write(block);
x += y;
//qDebug() << x; // summary size you send, so you can check recieved and replied sizes
}
}
Server part:
I specified my server as :
class Server : public QTcpServer
{
Q_OBJECT
public:
explicit Server(QHostAddress host = QHostAddress::Any,
quint16 port = Constants::Server::DEFAULT_PORT,
QObject *parent = 0);
~Server();
public slots:
void start();
protected:
void incomingConnection(qintptr handle) Q_DECL_OVERRIDE;
private:
QHostAddress m_host;
quint16 m_port;
};
And realization:
Server::Server(QHostAddress host, quint16 port, QObject *parent)
: QTcpServer(parent),
m_host(host),
m_port(port)
{
...
// your settings init there
}
void Server::start()
{
if ( this->listen(m_host, m_port) )
qDebug() << "Server started at " << m_host.toString() << ":" << m_port;
else
qDebug() << "Can't start server";
}
void Server::incomingConnection(qintptr handle)
{
qDebug() << "incomingConnection = " << handle;
SocketThread *thread = new SocketThread(handle);
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
}
As you can see i create new class SocketThread for receiving as multitheading server i need.
class SocketThread : public QThread
{
Q_OBJECT
public:
SocketThread(qintptr descriptor, QObject *parent = 0);
~SocketThread();
protected:
void run() Q_DECL_OVERRIDE;
signals:
void onFinishRecieved();
private slots:
void onReadyRead();
void onDisconnected();
private:
qintptr m_socketDescriptor;
QTcpSocket *m_socket;
qint32 m_blockSize;
};
SocketThread::SocketThread(qintptr descriptor, QObject *parent)
: QThread(parent),
m_socketDescriptor(descriptor),
m_blockSize(0)
{
}
SocketThread::~SocketThread()
{
delete m_socket;
}
void SocketThread::run()
{
m_socket = new QTcpSocket;
m_socket->setSocketDescriptor(m_socketDescriptor);
connect(m_socket, SIGNAL(readyRead()), this, SLOT(onReadyRead()), Qt::DirectConnection);
connect(m_socket, SIGNAL(disconnected()), this, SLOT(onDisconnected()), Qt::DirectConnection);
exec();
}
void SocketThread::onReadyRead()
{
QDataStream in(m_socket);
in.setVersion(QDataStream::Qt_5_4);
if (m_blockSize == 0) {
if (m_socket->bytesAvailable() < sizeof(quint32))
return;
in >> m_blockSize;
}
if (m_socket->bytesAvailable() < m_blockSize)
return;
QString fileName;
// get sending file name
in >> fileName;
QByteArray line = m_socket->readAll();
QString filePath = "YOUR"; // your file path for receiving
fileName = fileName.section("/", -1);
QFile target(filePath + "/" + fileName);
if (!target.open(QIODevice::WriteOnly)) {
qDebug() << "Can't open file for written";
return;
}
target.write(line);
target.close();
emit onFinishRecieved();
m_socket->disconnectFromHost();
}
void SocketThread::onDisconnected()
{
m_socket->close();
// leave event loop
quit();
}
I hope you will be able to adapt my code to your project. Best regards
Vlad, I'd suggest you to look at a Qt Example, like this one: http://doc.qt.io/qt-5/qtbluetooth-btfiletransfer-example.html
Just ignore the BT specific stuff, and see what it do.
I think I can help you more if I had a stand-alone code, which I could compile... ie, you didn't posted the headers, main files, and so.
Make a zip, up it somewhere, and I can try looking what is wrong with it when I come back from my late report delivery! =]
I was looking for the same solution. In the end, I am able to transfer the file upto 635MB without QDataStream.
I simply used below code.
for the client.
void MyClient::establishConnection(QString ip, quint16 port){
this->ip = ip;
this->port = port;
socket = new QTcpSocket();
socket->connectToHost(ip, port);
socket->waitForConnected(3000);
QFile file("D:/dummy.txt"); //file path
file.open(QIODevice::ReadOnly);
QByteArray q = file.readAll();
socket->write(q);
}
for the server
void MyThread::readyRead(){
QByteArray line = socket->readAll();
QFile target;
target.setFileName("D:/new1.txt");
if (!target.open(QIODevice::WriteOnly | QIODevice::Append)) {
qDebug() << "Can't open file for written";
return;
}
target.write(line);
target.close();
qDebug() << "file size: " << target.size();
qDebug() << "Finished!";
}
Now, My Question is what will be the effect if I use QDataStream?
This error;
C:\Users\Seb\Desktop\SDIcw2\widget.cpp:6: error: uninitialized reference member 'Widget::handler' [-fpermissive]
Widget::Widget(QWidget *parent) :
^
has occurs after I added an event handler for my combination box. Does anyone know why? The widget ran fine before so im not sure as to why!
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QString>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::getList(SDI::shipHandler& shipHandler)
{
handler = shipHandler;
}
void Widget::populateCombo()
{
ui->comboBox->addItem("Select Vessel...");
for(std::vector<SDI::navalVessels*>::const_iterator i = handler.ships.begin(); i != handler.ships.end(); ++i)
{
SDI::navalVessels* ship = *i;
QString qstr = QString::fromStdString(ship->name);
ui->comboBox->addItem(qstr);
}
}
void Widget::on_comboBox_currentIndexChanged(int index)
{
for(std::vector<SDI::navalVessels*>::const_iterator i = handler.ships.begin(); i != handler.ships.end(); ++i)
{
SDI::navalVessels* ship = *i;
QString qstr = QString::fromStdString(ship->name);
QString str = ui->comboBox->currentText();
if (str == qstr)
{
//do something here
}
}
}
trying to change the values of boxes depending on whats selected in the combo box.
You haven't shown widget.h, but presumably you have a private section that looks something like:
private:
Ui::Widget *ui;
ShipHandler *handler;
Your compiler is reminding you that uninitialized pointers can be dangerous, so you need to make sure you change your constructor to initialize them:
Widget::Widget(QWidget *parent)
: QWidget(parent),
ui(new Ui::Widget),
handler(0)
{
ui->setupUi(this);
}