QChecbkBox events into QGraphicsScene - c++

I have a problem on the behavior of QCheckBox. I am under Qt5.3.2/MinGW
I create a QGraphicsScene where i add severals QCheckBox via QGraphicsProxyWidget.
To be able to do drag and drop, zoom in / out, I reimplemented the virtual method eventFilter(QObject, QEvent) into QMainWindows and I apply it to my scene:
scene->installEventFilter(this);
But I can’t catch QCheckBox signal with QObject::connect :
connect(checkBox, &QCheckBox::clicked, [=](bool value){
qDebug() << "checkBox->objectName() : " << checkBox->objectName();
qDebug() << "value : " << value; });
If I don’t apply event filter on my scene, my QCheckBox work perfectly.
If now I apply event filter on my QCheckbox :
checkBox->installEventFilter(this);
I receive the event of the first one QCheckBox selected. But if I click on another QCheckBox it is the event of the first QCheckBox selected who is received. QCheckBox status are not modified too and I need to change status into QMainWindows::eventfilter :
if(checkbox->isChecked()) checkbox->setChecked(false);
else if(!checkbox->isChecked()) checkbox->setChecked(true);
MainWindow.cpp :
void MainWindow::Display()
{
scene = new QGraphicsScene();
scene->setObjectName("scene");
scene->installEventFilter(this);
for(int i=;i<10;++i){
QCheckBox *checkBox = new QCheckBox("ID"+QString::number(i));
checkBox->setObjectName("checkBox"+QString::number(i));
checkBox->installEventFilter(this);
checkBox->setChecked(true);
QString style = "QCheckBox {background : white;}";
checkBox->setStyleSheet(style);
// connect(checkBox, &QCheckBox::clicked, [=](bool value){
// qDebug() << "checkBox->objectName() : " << checkBox->objectName();
// qDebug() << "value : " << value; });
QGraphicsProxyWidget* proxyWidget = scene->addWidget(checkBox);
proxyWidget->setObjectName("proxyWidget"+QString::number(i));
proxyWidget->setScale(2);
proxyWidget->setPos(QPointF(i*50, 24));
proxyWidget->setFlag(QGraphicsItem::ItemIsSelectable);
proxyWidget->setZValue(2.0);
}
scene->setSceneRect(QRect(0, 0, 500, 200));
ui->graphicsView->setScene(scene);
ui->graphicsView->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
}
bool MainWindow::eventFilter(QObject *object, QEvent *event)
{
qDebug() << "object->objectName() : " << object->objectName();
qDebug() << "event->type() : " << event->type();
if (event->type() == QEvent::MouseButtonDblClick)
{
if(object->objectName().contains("checkBox",Qt::CaseInsensitive))
{
QCheckBox *checkbox = dynamic_cast<QCheckBox *>(object);
if(checkbox != nullptr)
{
event->setAccepted(true);
if(checkbox->isChecked()) checkbox->setChecked(false);
else if(!checkbox->isChecked()) checkbox->setChecked(true);
checkbox->update();
return true;
}
else return false;
}
else return false;
}
else
{
qDebug() << "default return";
// standard event processing
return QObject::eventFilter(object, event);
}
}
What is it that I'm doing wrong? Thank you.

Related

How to optimize resource usage when drawing image in a QWidget?

I'm subclassing the scrollAreaWidgetContents (which is literally a QWidget) of a QScrollArea and drawing an image on its background.
While resizing the widget, it was using too much CPU (tested with the QScrollArea containing no other widgets) so I tried to find a way to 'optimize' the drawing of the image, and I have written this class:
class ScrollChild : public QWidget
{
Q_OBJECT
public:
QTimer* m_resizeTimer;
int width;
int height;
QPixmap pixmap = QPixmap("...");
QPixmap pixmapScaled;
int pixWidth, prevpixWidth;
int pixHeight, prevpixHeight;
int pixmapPosX;
int pixmapPosY;
// getPos: a dynamic property set in Qt Designer to
// the widget that will be used as base to where the
// pixmap should be drawn.
QWidget* getPos;
QWidget* widget;
ScrollChild(QWidget* parent = 0) : QWidget(parent)
{
initTimer();
};
void resizeEvent(QResizeEvent* event)
{
QWidget::resizeEvent(event);
width = event->size().width();
height = event->size().height();
m_resizeTimer->start();
}
bool runtime = true;
void showEvent(QShowEvent* event)
{
if (runtime)
{
runtime = false;
// Looking for the property inside showEvent because
// when searched in the constructor its not found.
for (auto wdgt : this->findChildren<QWidget*>())
{
if (!wdgt->property("getPos").isNull())
{
getPos = wdgt;
break;
}
}
}
}
void initTimer()
{
m_resizeTimer = new QTimer(this);
m_resizeTimer->setInterval(100);
m_resizeTimer->setSingleShot(true);
connect(m_resizeTimer, &QTimer::timeout, this, &ScrollChild::resizePicture);
}
void resizePicture()
{
pixWidth = width;
pixHeight = height;
if (pixWidth > 600)
pixWidth = 600;
if (pixHeight > 400)
pixHeight = 400;
if ( (pixWidth != prevpixWidth) && (pixHeight != prevpixHeight) )
pixmapScaled = pixmap.scaled(pixWidth, pixHeight, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
prevpixWidth = pixWidth;
prevpixHeight = pixHeight;
//qDebug() << "w: " << width << " h: " << height;
//qDebug() << "pixmap:\nw: " << pixmap.width() << "h: " << pixmap.height() << "\n";
repaint();
}
void paintEvent(QPaintEvent *e)
{
if (pixmapScaled.isNull())
return;
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.setRenderHint(QPainter::SmoothPixmapTransform);
widget = getPos;
pixmapPosX = 0;
pixmapPosY = 0;
// Search the position of the widget marked in
// the Qt Designer.
// As the widget is inside a container we must
// iterate trought her parent adding the x pos
// relative to the scrollAreaWidgetContent.
while (widget != this)
{
pixmapPosX += widget->pos().x();
pixmapPosY += widget->pos().y();
widget = widget->parentWidget();
}
painter.drawPixmap(pixmapPosX, pixmapPosY,
pixmapScaled.width(), pixmapScaled.height(),
pixmapScaled);
}
};
It reduced the CPU usage by 60%~ while keeping the same looking/quality.
Its possible to improve on something else? or write it in a most efficient way?

Looking for the correct Qt5 mechanism to create an identical sized QGridLayout using a custom widget in the grid

So,I'm trying to build a small app that watches my MQTT server for messages and presents a widget with the MQTT JSON content for every message it sees. The logic works well, but I'm having a lot of trouble with adding a QScrollView to contain the grid of widgets. No matter what I do, the eventual widget size is never completely correct.
jsonwidget.h
#pragma once
#include <QtCore/QtCore>
#include <QtWidgets/QtWidgets>
class JsonWidget : public QWidget
{
Q_OBJECT
public:
explicit JsonWidget(QString topic, QWidget *parent = nullptr);
~JsonWidget() override;
void addJson(QJsonDocument &json);
QString topic() { return m_topicString; }
QSize minimumSizeHint() const override;
QSize sizeHint() const override;
protected slots:
void showEvent(QShowEvent *e) override;
void paintEvent(QPaintEvent *e) override;
private:
void populateNewWidget(int localX, QJsonObject obj);
void updateWidget(int localX, int i, QJsonObject obj);
QLabel *m_topic;
QString m_topicString;
uint32_t m_y;
uint32_t m_width;
uint32_t m_origin;
bool m_populated;
};
jsonwidget.cpp
#include "jsonwidget.h"
JsonWidget::JsonWidget(QString topic, QWidget *parent) : QWidget(parent), m_topicString(topic), m_y(0), m_width(0), m_populated(false)
{
m_topic = new QLabel(topic, this);
m_topic->setStyleSheet("QLabel { color: blue; font: 16pt 'Roboto'; }");
m_topic->move(5, m_y + 10);
m_origin = m_topic->height();
m_topic->show();
m_y = m_topic->height() + 10;
QPalette pal = palette();
pal.setColor(QPalette::Window, Qt::white);
setAutoFillBackground(true);
setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
setPalette(pal);
}
JsonWidget::~JsonWidget() = default;
QSize JsonWidget::minimumSizeHint() const
{
if (parentWidget())
return QSize(parentWidget()->width() / 2, m_y);
else
return QSize(20, 20);
}
QSize JsonWidget::sizeHint() const
{
if (parentWidget())
return QSize(parentWidget()->width() / 2, m_y);
else
return QSize(20, 20);
}
void JsonWidget::paintEvent(QPaintEvent* e)
{
QPainter painter(this);
painter.drawRoundedRect(0, 0, width(), height(), 0, 0);
QWidget::paintEvent(e);
}
void JsonWidget::showEvent(QShowEvent *e)
{
QWidget::showEvent(e);
m_topic->adjustSize();
}
void JsonWidget::populateNewWidget(int localX, QJsonObject obj)
{
QFont f("Roboto", 14);
localX += 10;
foreach(const QString& key, obj.keys()) {
QJsonValue value = obj.value(key);
QLabel *label;
switch (value.type()) {
case QJsonValue::Bool:
label = new QLabel(QString("%1 : %2").arg(key).arg(value.toBool()), this);
label->move(localX, m_y);
label->setObjectName(key);
m_y += label->height();
break;
case QJsonValue::Double:
label = new QLabel(QString("%1 : %2").arg(key).arg(value.toDouble()), this);
label->move(localX, m_y);
label->setObjectName(key);
m_y += label->height();
break;
case QJsonValue::String:
label = new QLabel(QString("%1 : %2").arg(key).arg(value.toString()), this);
label->move(localX, m_y);
label->setObjectName(key);
m_y += label->height();
break;
case QJsonValue::Array:
label = new QLabel(QString("%1 : %2").arg(key).arg("NOT DONE YET"), this);
label->move(localX, m_y);
m_y += label->height();
label->setObjectName(key);
break;
case QJsonValue::Object:
label = new QLabel(QString("%1").arg(key), this);
label->move(localX, m_y);
m_y += label->height();
label->setObjectName("object");
populateNewWidget(localX, value.toObject());
break;
case QJsonValue::Undefined:
case QJsonValue::Null:
label = new QLabel(QString("%1 : %2").arg(key).arg("UNDEFINED"), this);
label->move(localX, m_y);
m_y += label->height();
label->setObjectName(key);
break;
}
label->setFont(f);
uint32_t newWidth = label->width() + localX;
if (newWidth > m_width)
m_width = newWidth;
}
}
void JsonWidget::updateWidget(int localX, int i, QJsonObject obj)
{
QList<QLabel*> labels = findChildren<QLabel*>();
bool moveon = false;
int index = i + 1;
localX += 10;
foreach(const QString &key, obj.keys()) {
QJsonValue value = obj.value(key);
for (auto label : labels) {
moveon = false;
switch (value.type()) {
case QJsonValue::Bool:
if (label->objectName() == key) {
label->setText(QString("%1 : %2").arg(key).arg(value.toBool()));
moveon = true;
}
break;
case QJsonValue::Double:
if (label->objectName() == key) {
label->setText(QString("%1 : %2").arg(key).arg(value.toDouble()));
moveon = true;
}
break;
case QJsonValue::String:
if (label->objectName() == key) {
label->setText(QString("%1 : %2").arg(key).arg(value.toString()));
moveon = true;
}
break;
case QJsonValue::Array:
if (label->objectName() == key) {
label->setText("ARRAY UNFINISHED");
moveon = true;
}
break;
case QJsonValue::Object:
updateWidget(localX, index, value.toObject());
moveon = true;
break;
case QJsonValue::Undefined:
case QJsonValue::Null:
if (label->objectName() == key) {
label->setText("UNDEFINED");
moveon = true;
}
break;
}
if (moveon)
break;
}
}
}
void JsonWidget::addJson(QJsonDocument& doc)
{
if (doc.isEmpty() || doc.isNull()) {
qWarning() << __PRETTY_FUNCTION__ << ": Bad JSON document passed in";
return;
}
// qDebug() << __PRETTY_FUNCTION__ << "Topic label height" << m_topic->height();
QJsonObject json = doc.object();
if (m_populated) {
updateWidget(5, 0, json);
}
else {
populateNewWidget(5, json);
m_populated = true;
}
QDateTime now = QDateTime::currentDateTime();
m_topic->setText(QString("Topic: %1 [%2]").arg(m_topicString).arg(now.toString("dd-MM-yyyy h:mm:ss ap")));
}
Note, some of the sizing is just the result of trying things to see what works and what doesn't. The else 20,20 is so I know when parentWidget() isn't valid.
tabwidget.h
#include <QtCore/QtCore>
#include <QtWidgets/QtWidgets>
#include "jsonwidget.h"
/**
* #todo write docs
*/
class TabWidget : public QWidget
{
Q_OBJECT
public:
TabWidget(QWidget *parent = nullptr);
~TabWidget() override;
bool addJson(QString topic, QJsonDocument doc);
private:
bool addNewWidget(int row, int col, QString topic, QJsonDocument doc);
QGridLayout *m_layout;
};
tabwidget.cpp
#include "tabwidget.h"
TabWidget::TabWidget(QWidget *parent) : QWidget(parent)
{
m_layout = new QGridLayout();
setLayout(m_layout);
QPalette pal = palette();
pal.setColor(QPalette::Window, Qt::white);
setAutoFillBackground(true);
setPalette(pal);
}
TabWidget::~TabWidget() = default;
bool TabWidget::addNewWidget(int row, int col, QString topic, QJsonDocument doc)
{
JsonWidget *widget = new JsonWidget(topic);
widget->addJson(doc);
widget->setFixedWidth(parentWidget()->width() / 2);
m_layout->addWidget(widget, row, col);
m_layout->setSizeConstraint(QLayout::SetMinimumSize);
return true;
}
bool TabWidget::addJson(QString topic, QJsonDocument doc)
{
for (int i = 0; i <= m_layout->rowCount(); i++) {
for (int j = 0; j < 2; j++) {
QLayoutItem *item = m_layout->itemAtPosition(i, j);
if (item == nullptr) {
// qDebug() << __PRETTY_FUNCTION__ << "No widget at row" << i << ", column" << j << ", creating a new topic [" << topic << "]";
return addNewWidget(i, j, topic, doc);
}
JsonWidget *jw = static_cast<JsonWidget*>(item->widget());
if (jw->topic() == topic) {
// qDebug() << __PRETTY_FUNCTION__ << "Updating existing [" << topic << "] at row" << i << ", column" << j;
jw->addJson(doc);
return false;
}
}
}
// qDebug() << __PRETTY_FUNCTION__ << "Did not find [" << topic << "] in the widget set";
return false;
}
Finally, because I'll cute the QMainWindow down a bit, I'll put the function I use to create the tab
void MQTTSnoopWindow::newTab(QString topic, QJsonDocument json)
{
QMutexLocker locker(&m_newTabMutex);
QString parentTopic = topic.left(topic.indexOf("/"));
QScrollArea *sa = new QScrollArea(m_mainWidget);
sa->setBackgroundRole(QPalette::Light);
TabWidget *tab = new TabWidget(sa);
tab->addJson(topic, json);
sa->setWidget(tab);
m_mainWidget->addTab(sa, parentTopic);
m_topics++;
// qDebug() << __PRETTY_FUNCTION__ << "Created a new tab";
}
So, my question is, what's the correct way to do this kind of embedding? The goal is two columns of equal sized widgets in a grid. I know I'm doing the sizing wrong, but I'm not sure how. Also, if I had to guess, my parent/child on the objects isn't correct either. So I'm roughly guessing and reading all the layout and widget docs plus google searches for similar questions. This all works great if I don't use the scrollview too. But then I get an ever expanding window which is not what I want.
OK, finally figured it out. Turns out, you have to use layouts across the board, and it works. I was not aware of this. So, with some indirection, my TabWidget and JsonWidget both work correctly, and I needed to embed those in the QScrollArea which was embedded in a layout which is embedded in a widget which is set on the tab.
So, I created a single entity QHBoxLayout, and stick that in an empty parent QWidget as the layout. Then create the QScrollArea, and add that as the only widget in the layout. Finally, create my custom TabWidget and add that as the only element to the QScrollArea.
QWidget *parentWidget = new QWidget(m_mainWidget);
QHBoxLayout *parentLayout = new QHBoxLayout();
parentWidget->setLayout(parentLayout);
QScrollArea *parentScroll = new QScrollArea();
parentLayout->addWidget(parentScroll);
TabWidget *tab = new TabWidget();
parentScroll->setWidgetResizable(true);
parentScroll->setWidget(tab);
tab->addJson(topic, json);
m_mainWidget->addTab(parentWidget, parentTopic);
Then, to get my custom widget back out, you have to unwind the boxes.
QWidget *top = static_cast<QWidget*>(m_mainWidget->widget(i));
QHBoxLayout *topLayout = static_cast<QHBoxLayout*>(top->layout());
QScrollArea *scroller = static_cast<QScrollArea*>(topLayout->itemAt(0)->widget());
TabWidget *widget = static_cast<TabWidget*>(scroller->widget());
I'm going to rename a few entities here now that the layering makes the old names somewhat confusing. I left them to reference the original post. Hoping this helps someone.

QAudioInput stops send data in tabWidged when tab changed

All!
I have some application with 3 tabs, one tab initialized QWidget from other tab. I start QAudioInput in constructor(read to memory, I don't need file) - it starts works. But when I change tab to my widget, data stops and audioInput has status isActive still.
I have changed and minimized sources. It is a cross-compilation for ARM Linux platform. Now it is works usiallu, but few changes active tab crash stream.
int main(int argc, char *argv[])
{
QApplication myapp(argc, argv);
Widget wid;
wid.show();
return myapp.exec();
}
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
// Default widget in new tab
wMetod = new Vnimi(this);//this
ui->tabWidget->insertTab(1, wMetod, "Empty"); // Set Tab's name
ui->pButton_Vnimi->setChecked(true);
/*QButtonGroup **/
grMetod = new QButtonGroup(this);
grMetod->addButton(ui->pButton_Vnimi );
grMetod->addButton(ui->pButton_ExpVibro);
grMetod->setExclusive(true);
connect(grMetod, static_cast<void(QButtonGroup::*)(QAbstractButton *)>(&QButtonGroup::buttonPressed),
[=](QAbstractButton *button){ metChanged(button); });
ui->tabWidget->setCurrentIndex(0);
}
Widget::~Widget()
{
delete ui;
}
void Widget::metChanged(QAbstractButton *button)
{
/* switch for strings */
if( button->objectName() =="pButton_Empty" ) {
ui->tabWidget->removeTab(1);
wMetod->disconnect(); delete wMetod;
wMetod = new Vnimi(this);//this
ui->tabWidget->insertTab(1, wMetod, "Empty"); // Set Tab's name
}else if( button->objectName() =="pButton_Input" ) {
ui->tabWidget->removeTab(1);
wMetod->disconnect(); delete wMetod;
wMetod = new VibrExpr(this);//this
ui->tabWidget->insertTab(1, wMetod, "Input"); // Set Tab's name
}else {
qDebug() << "Unknow method!";
}
qDebug() << button->objectName() << " pressed";
}
VibrExpr::VibrExpr(QWidget *parent) :
QWidget(parent),
ui(new Ui::VibrExpr)
{
ui->setupUi(this);
ui->lb_level->setText( QString().number(minLevel) );
QAudioFormat frmt;
frmt.setCodec( "audio/pcm"), frmt.setChannelCount( 2);
frmt.setSampleRate( 44100), frmt.setSampleType( QAudioFormat::SignedInt);
frmt.setSampleSize( 16);
rawSrc = new QAudioInput(QAudioDeviceInfo::defaultInputDevice(), frmt, this);
rawSrc->setNotifyInterval(250);
connect(rawSrc, SIGNAL(notify()), this, SLOT(slot()));
connect(rawSrc, SIGNAL(stateChanged(QAudio::State)), this, SLOT(iStateChanged(QAudio::State)));
buff = new QBuffer(this);
buff->open(QBuffer::ReadWrite);
connect(buff, SIGNAL(readyRead()), this, SLOT(newData()) );
rawSrc->start(buff);
timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(timSlot()) );
timer->start(1000);
qDebug() << "Created";
}
void VibrExpr::timSlot()
{
int sbVal = ui->lb_level->text().toInt();
if(sbVal < -6 ) {
sbVal += 1;
}
if(buff != nullptr) \
if(buff->isOpen() && sbVal >= -10) {
qDebug() << "Sz " << buff->size();
rawSrc->stop();
buff->close();
qDebug() << " Input closed";
}
ui->lb_level->setText( QString().number(sbVal));
if(rawSrc != nullptr) qDebug() << "iStat " << rawSrc->state();
}
void VibrExpr::newData(void)
{
int k = buff->size();
QByteArray qba = buff->read(k);
buff->seek(0);
buff->buffer().remove(0, k);
qDebug() << "n=" << k;
}
void VibrExpr::slot(void)
{
qDebug() << "USec:" << rawSrc->processedUSecs() ;
}
void VibrExpr::iStateChanged(QAudio::State state)
{
qDebug() << "Now state: " << state;
}

Animation on qwidget is not working

I want a widget to animate its opacity when it is shown/hidden. I used the below code, but it does not work.
If I animate the property "maximumHeight", it gets animated in show(), but not in hide(). Could someone tell me where I am going wrong?
Header file
byeform.h
#include <QWidget>
#include <QPropertyAnimation>
namespace Ui {
class ByeForm;
}
class ByeForm : public QWidget
{
Q_OBJECT
public:
explicit ByeForm(QWidget *parent = 0);
~ByeForm();
private:
Ui::ByeForm *ui;
QPropertyAnimation *mpTransition;
protected:
bool eventFilter(QObject *obj, QEvent *event);
};
Source file
byeform.cpp
#include "byeform.h"
#include "ui_byeform.h"
#include <QDebug>
ByeForm::ByeForm(QWidget *parent) :
QWidget(parent),
ui(new Ui::ByeForm)
{
ui->setupUi(this);
this->installEventFilter(this);
mpTransition = new QPropertyAnimation(this, "windowOpacity");
mpTransition->setDuration(1000);
mpTransition->setStartValue(0.00);
mpTransition->setEndValue(1.00);
}
ByeForm::~ByeForm()
{
delete ui;
}
bool ByeForm::eventFilter(QObject *obj, QEvent *event)
{
if (this == obj && QEvent::Show == event->type())
{
qDebug() << Q_FUNC_INFO << "in show";
mpTransition->setDirection(QAbstractAnimation::Forward);
mpTransition->start();
}
else if (this == obj && (QEvent::Hide == event->type() ||
QEvent::Close == event->type()))
{
mpTransition->setDirection(QAbstractAnimation::Backward);
mpTransition->start();
}
return false;
}
Does this fix it?
bool ByeForm::eventFilter(QObject *obj, QEvent *event)
{
if (this == obj && QEvent::Show == event->type())
{
qDebug() << Q_FUNC_INFO << "in show";
mpTransition->setDirection(QAbstractAnimation::Forward);
mpTransition->start();
return true; // you might want to remove this line
}
else if (this == obj && (QEvent::Hide == event->type() ||
QEvent::Close == event->type()))
{
mpTransition->setDirection(QAbstractAnimation::Backward);
mpTransition->start();
return true; // you might want to remove this line
}
return QWidget::eventFilter(obj, event);
}
Ofcourse, it doesn't work because it's already hidden when you start the animation. You need to prolong the visibility until your animation has finished.
Like this, maybe:
void ByeForm::setVisible(bool visible)
{
if(isVisible() && !visible) // transition to hide
{
// m_bHideCalled = true;
mpTransition->setDirection(QAbstractAnimation::Backward);
mpTransition->start();
QTimer::singleShot(1000, this, SLOT(hide());
}
if(!isVisible() && visible) // transition to show
{
mpTransition->setDirection(QAbstractAnimation::Forward);
mpTransition->start();
show();
}
// if(m_bHideCalled)
// {
// m_bHideCalled = false;
// hide();
// }
}
Note that you MIGHT need the m_bHideCalled. Set it to false in the constructor. The name could be better though.
It works, but I think that it not good way to do this (I think that you should be do animate before close event will coming)
bool ByeForm::eventFilter(QObject *obj, QEvent *event)
{
if (this == obj && QEvent::Show == event->type())
{
qDebug() << Q_FUNC_INFO << "in show";
mpTransition->setDirection(QAbstractAnimation::Forward);
mpTransition->start();
}
else if (this == obj && (QEvent::Hide == event->type() ||
QEvent::Close == event->type()))
{
mpTransition->setDirection(QAbstractAnimation::Backward);
mpTransition->start();
while (mpTransition->state() == QAbstractAnimation::Running)
{
QApplication::processEvents();
}
}
return false;
}
Other method
Also you can override closeEvent method like this:
void MainWindow::closeEvent(QCloseEvent* e)
{
mpTransition->setDirection(QAbstractAnimation::Backward);
mpTransition->start();
e->ignore();
}
but in this case you should do something after animate will finished (for example connect on signal finished and call some method for closing window/application/etc).
Also you should be check, would manually call close event or by user operations.

How to send a file in Qt?

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?