Display images from source directory selected by user - c++

I have a Qt application where user selects source directory which has images (.pgm files) (any number). In the UI,
I have created a display area (QGroupBox) which has 2 QGraphicsview which displays 2 images at a time.
UI image:
Going ahead,
I want to create QGraphicsViews Widget for all the images and display 2 of them at a time. There will be a scroll bar to the right.
When user drags the scroll bar down other QGraphicsView Widgets
should be visible to the user with different images. How can i do this using Qt in C++?
PS: I am able to display two images in the display area using QGraphicsView Widget when the load images button is clicked.
#include "ui_DataViewerPage.h"
#include <iostream>
#include <QGraphicsScene>
#include <QPixmap>
#include <QThread>
#include <QSettings>
#include "DataWorker.h"
namespace Ui
{
class DataViewerPage;
}
// This is the main UI thread header and cpp file
class DataViewerPage : public QWidget
{
Q_OBJECT
public:
DataViewerPage(QWidget* parent = nullptr);
virtual ~DataViewerPage();
public slots:
void on_loadButton_clicked();
void startWorker();
void stopWorker();
void displayImage(QString fileName);
signals:
void startLoadingImages();
private:
Ui::DataViewerPage* ui;
DataWorker* worker;
QSettings settings;
// Worker thread handles data processing away from ui thread
QThread workerThread;
QSharedPointer<QGraphicsScene> ptr_scene;
QSharedPointer<QGraphicsScene> ptr_scene2;
int currentDisplayNum;
};
#include "DataViewerPage.h"
DataViewerPage::DataViewerPage(QWidget* parent)
: QWidget(parent), worker(nullptr), ui(new Ui::DataViewerPage)
{
ui->setupUi(this);
ui->Display->setVisible(true);
ptr_scene = QSharedPointer<QGraphicsScene>(new QGraphicsScene(this));
ptr_scene2 = QSharedPointer<QGraphicsScene>(new QGraphicsScene(this));
}
DataViewerPage::~DataViewerPage()
{
workerThread.quit();
workerThread.wait();
workerThread.terminate();
if (worker)
{
delete worker;
}
delete ui;
}
void DataViewerPage::on_loadButton_clicked()
{
ui->Display->setVisible(true);
ui->loadButton->setDisabled(true);
emit startLoadingImages();
}
void DataViewerPage::stopWorker()
{
workerThread.quit();
workerThread.wait();
workerThread.terminate();
if (worker)
{
delete worker;
worker = nullptr;
}
}
void DataViewerPage::startWorker()
{
stopWorker();
worker = new DataWorker();
worker->moveToThread(&workerThread);
connect(this, &DataViewerPage::startLoadingImages, worker, &DataWorker::startLoadingImages);
connect(worker, &DataWorker::displayImage, this, &DataViewerPage::displayImage);
workerThread.start();
currentDisplayNum = 0;
}
void DataViewerPage::displayImage(QString fileName)
{
QPixmap pixelMap(fileName.toStdString().c_str()); // <- path to image file
switch (currentDisplayNum)
{
case 0:
ptr_scene->addPixmap(pixelMap);
ui->img1->setScene(ptr_scene.get());
break;
case 1:
ptr_scene2->addPixmap(pixelMap);
ui->img2->setScene(ptr_scene2.get());
break;
default:
break;
}
currentDisplayNum++;
}
// This is the worker thread header and cpp file.
#pragma once
#include <QThread>
#include <QSettings>
#include <filesystem>
class DataWorker : public QObject
{
Q_OBJECT
public:
DataWorker();
~DataWorker();
public slots:
void startLoadingImages();
signals:
void displayImage(QString fileName);
private:
QSettings settings;
std::string path;
};
#include "DataWorker.h"
#include <iostream>
#include <QImage>
DataWorker::DataWorker()
{
path = settings.value("dataDir").toString().toStdString();
}
DataWorker::~DataWorker()
{
}
void DataWorker::startLoadingImages()
{
std::cout << "startloadingImages called..." << std::endl;
int iCount = 0;
for (const auto& entry : std::filesystem::directory_iterator(path))
{
QString imgPath = QString::fromStdString(entry.path().string());
emit displayImage(imgPath);
iCount++;
if(iCount > 1)
break; // Right now do it for two only.......
}
}

Related

How? I show a message, while the task is running

I have the following code.
A method compress (), where I do the task of compressing an entire directory, I call this method from my button, with a QtConcurrent, as you can see. so far so good.
My question is, how can I display a message while compressing the folder is running.
I am using QuaZIP, for the compression process.
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void on_pushButton_5_clicked();
private:
Ui::Widget *ui;
void compress();
public:
};
#endif // WIDGET_H
#include "widget.h"
#include "ui_widget.h"
#include <QMessageBox>
#include <QDebug>
#include <JlCompress.h>
#include <QtConcurrent/QtConcurrent>
#include <QFuture>
Widget::Widget(QWidget *parent)
: QWidget(parent), ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_5_clicked()
{
QGuiApplication::setOverrideCursor(Qt::WaitCursor);
QFuture<void> future=QtConcurrent::run(this,&Widget::compress);
future.waitForFinished();
QGuiApplication::restoreOverrideCursor();
QMessageBox::information(this,qApp->applicationName(),"The folder was compressed successfully.");
}
void Widget::compress()
{
if(!JlCompress::compressDir("C:/Users/Mypc/Desktop/iconos.zip", "D:/images/iconos")){
QMessageBox::warning(this,qApp->applicationName(),"Error compressing folder.");
return;
}
}
I did this, but now when the backup finishes, the entire application closes.
void Widget::on_pushButton_5_clicked()
{
QMessageBox box;
// box->setWindowFlags(Qt::Dialog | Qt::SplashScreen);
box.setStandardButtons(QMessageBox::NoButton);
box.setText("Realizando una tarea!");
box.setWindowTitle(qApp->applicationName());
box.setIcon(QMessageBox::Information);
box.setAttribute(Qt::WA_DeleteOnClose);
QFutureWatcher<void> watcher;
QFuture<void> future=QtConcurrent::run(this,&Widget::compress);
watcher.setFuture(future);
QGuiApplication::setOverrideCursor(Qt::WaitCursor);
QObject::connect(&watcher,&QFutureWatcher<void>::finished,[&](){
box.close();
box.deleteLater();
QGuiApplication::restoreOverrideCursor();
});
box.exec();
QMessageBox::information(this,qApp->applicationName(), "The folder was compressed successfully.");
}
Also try a ProgressDialog and QFutureWatcher as follows, but it doesn't compile, I get this error.
void Widget::on_pushButton_5_clicked()
{
QStringList files;
QDir dir("D:/images/iconos/uigenericicon");
files=dir.entryList(QStringList() << "*.jpg" << "*.JPG"<<"*.PNG",QDir::Files);
qInfo()<<files;
QProgressDialog pDialog;
pDialog.setLabelText("Porecessing!!");
QFutureWatcher<void> watcher;
QObject::connect(&pDialog,SIGNAL(canceled()),&watcher,SLOT(cancel()));
QObject::connect(&watcher,SIGNAL(finished()),&pDialog,SLOT(reset()));
QObject::connect(&watcher,SIGNAL(progressRangeChanged(int,int)),&pDialog,SLOT(setRange(int,int)));
QObject::connect(&watcher,SIGNAL(progressValueChanged(int)),&pDialog,SLOT(setValue(int)));
watcher.setFuture(QtConcurrent::map(files,&Widget::compress));
pDialog.exec();
if(watcher.isCanceled()){
QMessageBox::information(this,qApp->applicationName(),"You canceled.");
}else{
QMessageBox::information(this,qApp->applicationName(),"All done.");
}
}
void Widget::compress(QStringList &files)
{
if(!JlCompress::compressFiles("C:/Users/Lincoln/Desktop/iconos.zip",files)){
QMessageBox::warning(this,qApp->applicationName(),"Error al comprimir la carpeta.");
return;
}
}

menu bar functionalities aren't working in Qt

so i am writing a simple video player program and i did the same steps as the lesson i am taking but when i run the program and click on functionalities like end (which is close()) and open (open file) they dont work, i used the slot triggering as per the lesson although i saw different ways of using the menubar here but i must follow this format, here is my code:
header:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QPushButton>
#include <QVideoWidget>
#include <QStyle>
#include <QMediaPlayer>
#include <QFileDialog >
namespace Ui {
class videoWidget;
}
class videoWidget : public QMainWindow
{
Q_OBJECT
QMediaPlayer *meinPlayer;
QPushButton *playButton;
QPushButton *stopButton;
public:
explicit videoWidget(QWidget *parent = 0);
~videoWidget();
private slots:
void listeUndTitelAktualisieren();
void on_action_End_triggered();
void on_action_ffnen_triggered();
void on_action_Stop_triggered();
void on_action_PlayBack_triggered();
void on_action_Pause_triggered();
private:
Ui::videoWidget *ui;
};
#endif // MAINWINDOW_H
cpp:
#include "mainwindow.h"
#include "ui_mainwindow.h"
videoWidget::videoWidget(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::videoWidget)
{
ui->setupUi(this);
meinPlayer = new QMediaPlayer(this);
meinPlayer->setMedia(QUrl::fromLocalFile("/beispiele/topologien.wmv"));
meinPlayer->play();
}
void videoWidget::listeUndTitelAktualisieren()
{
QString titel = meinPlayer->media().canonicalUrl().toLocalFile();
ui->listWidget->addItem(titel);
this->setWindowTitle("Multimedia-Player – " + titel);
connect(meinPlayer, SIGNAL(mediaChanged(QMediaContent)), this, SLOT(listeUndTitelAktualisieren()));
}
void videoWidget::on_action_End_triggered()
{
this->close();
}
void videoWidget::on_action_ffnen_triggered()
{
QFileDialog *meinDialog = new QFileDialog(this);
meinDialog->setAcceptMode(QFileDialog::AcceptOpen);
meinDialog->setWindowTitle("Datei öffnen");
meinDialog->setNameFilters(QStringList() << "Videos (*.mp4 *.wmv)" << "Audios (*.mp3)" << "Alle Dateien (*.*)");
meinDialog->setDirectory(QDir::currentPath());
meinDialog->setFileMode(QFileDialog::ExistingFile);
if (meinDialog->exec() == QDialog::Accepted) {
QString datei = meinDialog->selectedFiles().first();
meinPlayer->setMedia(QUrl::fromLocalFile(datei));
/*QString fileName = QFileDialog::getOpenFileName(this,
tr("Open Image"), "/home/jana", tr("Video & Audio Files (*.mp3 *.mp4 *.wmv)"));
*/
meinPlayer->play();
}
}
void videoWidget::on_action_Stop_triggered()
{
meinPlayer->pause();
}
void videoWidget::on_action_PlayBack_triggered()
{
meinPlayer->play();
}
void videoWidget::on_action_Pause_triggered()
{
meinPlayer->pause();
}
am pretty sure this instruction here:
connect(meinPlayer, SIGNAL(mediaChanged(QMediaContent)), this, SLOT(listeUndTitelAktualisieren()));
is not located where it should coz you are connecting the signal toa slot INSIDE of the SLOT implementation....
try moving that to the constructor of dein videoWidget

How to display QLabel inside qwidget from active QMdiAreaSubwindow in statusbar?

I have a app that uses QMdiArea.
I want the text in the statusbar to update when another QMdiAreaSubwindow becomes active.
So the text in the statusbar should become the same as the Qlabel text inside the QWidget which is been displayed inside the QMdiAreaSubwindow.
But i can't find a way to do this. Right now the statusbar only shows the text from latest created QMdiAreaSubwindow. But it won't update the text in the statusbar(With qlabel from the qwidget) when another QMdiAreaSubwindow is selected.
As you can see in the screenshot, the text in the statusbar keeps saying "test2", but I want it to change to "text" from the active QMdiAreaSubwindow.
mainwindow.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QMdiArea>
#include <QMdiSubWindow>
#include <newwindow.h>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
void NewSubWindow(QString name);
void createStatusBar(QString name);
private slots:
void on_actionNew_triggered();
void on_mdiArea_subWindowActivated(QMdiSubWindow *arg1);
private:
Ui::MainWindow *ui;
NewWindow *nDialog;
};
#endif // MAINWINDOW_H
mainwindow.cpp:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "mdisubwidget.h"
#include "newwindow.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
nDialog = new NewWindow();
connect(nDialog,&NewWindow::transmit,this,&MainWindow::NewSubWindow);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::NewSubWindow(QString name) {
// new Widget to add to mdiareasubwindow
mdisubwidget *mdiwidget = new mdisubwidget();
mdiwidget->addName(name);
mdiwidget->setWindowTitle(name);
// Create new mdiAreaSubWindow
ui->mdiArea->addSubWindow(mdiwidget);
// Show mdiArea
mdiwidget->show();
}
void MainWindow::on_actionNew_triggered()
{
nDialog->show();
}
void MainWindow::on_mdiArea_subWindowActivated(QMdiSubWindow *arg1)
{
mdisubwidget *mdiwidget = new mdisubwidget(arg1->widget());
qDebug() << "name" << mdiwidget->returnName();
createStatusBar(mdiwidget->returnName());
}
void MainWindow::createStatusBar(QString name)
{
statusBar()->showMessage("chart = "+name);
}
mdisubwidget.h
#ifndef MDISUBWIDGET_H
#define MDISUBWIDGET_H
#include <QWidget>
namespace Ui {
class mdisubwidget;
}
class mdisubwidget : public QWidget
{
Q_OBJECT
public:
explicit mdisubwidget(QWidget *parent = nullptr);
void addName(QString name);
QString returnName();
~mdisubwidget();
private:
Ui::mdisubwidget *ui;
};
#endif // MDISUBWIDGET_H
mdisubwidget.cpp
#include "mdisubwidget.h"
#include "ui_mdisubwidget.h"
#include "mainwindow.h"
QString currentName;
mdisubwidget::mdisubwidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::mdisubwidget)
{
ui->setupUi(this);
}
void mdisubwidget::addName(QString name) {
ui->label_2->setText(name);
currentName = name;
}
QString mdisubwidget::returnName() {
return currentName;
}
mdisubwidget::~mdisubwidget()
{
delete ui;
}
NewWindow.h:
#ifndef NEWWINDOW_H
#define NEWWINDOW_H
#include <QWidget>
namespace Ui {
class NewWindow;
}
class NewWindow : public QWidget
{
Q_OBJECT
public:
explicit NewWindow(QWidget *parent = nullptr);
~NewWindow();
signals:
void transmit(QString name);
private slots:
void on_pushButton_clicked();
private:
Ui::NewWindow *ui;
};
#endif // NEWWINDOW_H
NewWindow.cpp:
#include "newwindow.h"
#include "ui_newwindow.h"
#include "mainwindow.h"
NewWindow::NewWindow(QWidget *parent) :
QWidget(parent),
ui(new Ui::NewWindow)
{
ui->setupUi(this);
}
NewWindow::~NewWindow()
{
delete ui;
}
void NewWindow::on_pushButton_clicked()
{
QString name = ui->lineEdit->text();
emit transmit(name);
}
ok you're using Qt Designer to connect the signal of subWindowActivated to the slot of on_mdiArea_subWindowActivated of your MainWindow, double check with qDebug in your on_mdiArea_subWindowActivated function if the name of your selected sub window appears on the console as you tried to change your current mdi sub window so follow my code snippets to find your way:
connect(ui->mdiArea, &QMdiArea::subWindowActivated, this, &DesignerWindow::activeViewChanged);
activeViewChanged():
void DesignerWindow::activeViewChanged(QMdiSubWindow *activeSubWindow)
{
// checks if there is no active sub window defined or the number of subwindows
// are zero then return
if (!activeSubWindow)
return;
if (ui->mdiArea->subWindowList().count() == 0) {
ui->itemsTree->clear();
return;
}
// defines the current Midi, View and graphical Scene when current sub window changes
currentMidi = reinterpret_cast<MidiWindow*>(activeSubWindow->widget());
currentView = reinterpret_cast<HMIView*>(currentMidi->internalView());
currentScene = reinterpret_cast<HMIScene*>(currentMidi->internalScene());
ItemsToolBar::ItemType currentType = currentScene->itemType();
itemsToolBar->selectItemType(currentType);
// updates the widgets and labels in status bar related to current midi sub window
updateScale(currentView->zoomFactor() * 100);
updateSelected();
updateItemsTree();
updateRendererType();
}
for example for updating the label in the status bar that holds the zooming factor of the current mdiSubWindow I wrote the updateScale procedure as below:
void DesignerWindow::updateScale(double _scale)
{
scale = static_cast<int>(_scale);
scaleLbl->setText(QString("%1%").arg(scale));
}
and finally I've noticed that your creating a label in status bar every time that you try to update the text in it, please avoid such a procedure and create a QLabel object and add it to your status bar as a permanent widget like below:
scaleLbl = new QLabel(this);
scaleLbl->setFrameStyle(QFrame::Sunken | QFrame::Panel);
scaleLbl->setMinimumWidth(50);
statusBar()->addPermanentWidget(scaleLbl);

Using Thread in GUI application

I am making a multiple game. I need to take command from client. This means I have GUI and TcpServer. So I need to work them simultaneously. I used Thread but it doesnt work. Could you please help me to find the problem?
Summarizing: Firstly player press the "online" button. Then Oyun() Gui function runs and button connected with connectPressed() function. In this function there is a thread in order to run read the client commands when Gui is working.
Firstly I used QTimer in order to take command from Client in every 1 second. But My GUI freezed. And Then I used QThread but according to my research, QThread is not proper for GUI app. So I found Qtconcurrent, QFutureWatcher and QFuture. But my thread is still not working. I should have made a mistake somewhere.
#include <QApplication>
#include <anaclass.h>
#include <player2.h>
#include <tcpserver.h>
#include <QThread>
#include <QObject>
//#include <worker.h>
AnaClass *anaclass;
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
anaclass = new AnaClass();
anaclass->show();
anaclass->Giris(); //button selection page
return a.exec();
}
#include "anaclass.h"
Puan *puanlama1;
Puan *puanlama2;
player2 *yilan2;
AnaClass::AnaClass() : QGraphicsView()
{
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
setFixedSize(800,600);
scene = new QGraphicsScene;
scene->setSceneRect(0,0,800,600);
setScene(scene);
}
void AnaClass::Giris()
{
connectButton = new Button("Online");
double cxPos = this->width()/2 - connectButton->boundingRect().width()/2;
double cyPos= 425;
connectButton->setPos(cxPos, cyPos);
connect(connectButton, SIGNAL(clicked()), this, SLOT(connectPressed()));
scene->addItem(connectButton);
}
void AnaClass::Oyun()
{
scene->clear();
puanlama1 = new Puan();
puanlama1->setDefaultTextColor(Qt::blue);
puanlama1->setPos(5, 2);
scene->addItem(puanlama1);
yilan = new Yilan();
yilan->setRect(0,0,19,19);
scene->addItem(yilan);
yilan->setFlags(QGraphicsItem::ItemIsFocusable);
yilan->setFocus();
QBrush brush;
brush.setStyle(Qt::SolidPattern);
brush.setColor(Qt::blue);
yilan->setBrush(brush);
if(stringButtonName == "Player2" || stringButtonName == "Online")
{
yilan->setPos(scene->width()/2 + 60, scene->height()/2);
}
else
{
yilan->setPos(scene->width()/2, scene->height()/2);
}
if(stringButtonName == "Player2" || stringButtonName == "Online")
{
yilan->playerNumber=1;
puanlama2 = new Puan();
puanlama2->setDefaultTextColor(Qt::green);
puanlama2->setPos(700, 2);
scene->addItem(puanlama2);
yilan2 = new player2();
yilan2->setRect(0,0,19,19);
scene->addItem(yilan2);
yilan2->setFlags(QGraphicsItem::ItemIsFocusable);
yilan2->setFocus();
QBrush brush2;
brush2.setStyle(Qt::SolidPattern);
brush2.setColor(Qt::green);
yilan2->setBrush(brush2);
yilan2->setPos(scene->width()/2 - 60,scene->height()/2);
}
emit emitTcp();
}
void AnaClass::connectPressed()
{
qDebug()<<"connect basildi";
server = new TCPServer();
server->Test();
stringButtonName = connectButton->buttonName;
qDebug()<<"Gelen Veri " + server->OkunanBilgi;
QFutureWatcher<void> watcher;
connect(&watcher, SIGNAL(finished()), server, SLOT(daimaOku()), Qt::QueuedConnection);
QFuture<void> deneme = QtConcurrent::run(this, &AnaClass::emitTcp);
watcher.setFuture(deneme);
Oyun();
}
}
#ifndef ANACLASS_H
#define ANACLASS_H
#include <QGraphicsScene>
#include <QGraphicsView>
#include <yilan.h>
#include <Meyve.h>
#include <QBrush>
#include <Puan.h>
#include <player2.h>
#include <QThread>
#include <label.h>
#include <QKeyEvent>
#include <button.h>
#include <QDebug>
#include <tcpserver.h>
#include <QTime>
#include <QTimer>
#include <QMutex>
//#include <worker.h>
#include <QFuture>
#include <QtConcurrent>
#include <QFutureWatcher>
class AnaClass : public QGraphicsView
{
Q_OBJECT
public:
AnaClass();
void Giris();
void Oyun();
void timerEvent(QTimerEvent *event);
void keyPressEvent(QKeyEvent *event2);
public:
Yilan *yilan;
//QThread *thread;
QGraphicsScene *scene;
Label *label1;
Button* player1Button;
Button* player2Button;
Button* connectButton;
TCPServer *server;
QTimer *timerOnline;
public:
int k=0;
int t=0;
QString stringButtonName;
signals:
void emitTcp();
public slots:
void connectPressed();
void player1Pressed();
void player2Pressed();
};
#endif // ANACLASS_H
#define TCPSERVER_H
#include <QTcpServer>
#include <QTcpSocket>
#include <QDebug>
#include <QObject>
#include <QTimer>
class TCPServer : public QObject
{
Q_OBJECT
public:
TCPServer(QObject* parent = nullptr);
void Test();
signals:
//void emitTcp();
public slots:
void newConnection();
void daimaOku(); // always read as english
public:
QTcpServer *server;
QTcpSocket *socket;
QTimer *timerTcp;
QString OkunanBilgi;
};
#endif // TCPSERVER_H
#include "tcpserver.h"
TCPServer::TCPServer(QObject * parent) : QObject()
{
}
void TCPServer::Test()
{
server = new QTcpServer();
connect(server, SIGNAL(newConnection()), this, SLOT(newConnection()));
if(!server->listen(QHostAddress::Any, 1234))
{
qDebug()<<"server baslamadi";
}
else
{
qDebug()<<"server basladi";
}
//timerTcp = new QTimer();
//connect(timerTcp, SIGNAL(timeout()), this, SLOT(daimaOku()));
//emit emitTcp();
}
void TCPServer::newConnection()
{
qDebug()<<"newconnection";
socket = server->nextPendingConnection();
socket->write("Merhaba Client");
socket->flush();
socket->waitForBytesWritten(5000);
timerTcp->start(50);
}
void TCPServer::daimaOku() //alwaysread() as english
{
qDebug()<<"always read function is working";
// if(socket->state() == QAbstractSocket::ConnectedState)
// {
// qDebug()<<"Daima oku fonsiyonu soket bagli";
// socket->waitForReadyRead();
// OkunanBilgi = socket->readAll();
// qDebug()<<"Tcp daima oku :" + OkunanBilgi;
// }
}
Thank you for your comments. I solved the problem by deleting QFuture and adding connect() like below.
timerOnline = new QTimer();
connect(timerOnline, SIGNAL(timeout()), server, SLOT(daimaOku()));
timerOnline->start(500);
But I have another problem. When Client connects the server, my Gui app freezes. You can find the revised code below.
void TCPServer::daimaOku()
{
qDebug()<<"Function is running";
if(socket->state() == QAbstractSocket::UnconnectedState)
{
qDebug()<<"Socket is not connected";
}
else
{
qDebug()<<"Socket connected";
socket->waitForReadyRead();
OkunanBilgi = socket->readAll();
qDebug()<<"Tcp always read :" + OkunanBilgi;
}
}
When client is not connected, the output is:
Function is running
Socket is not connected
Socket is not connected ...
I can play the game but when client is connected, game freezes. I don't understand why.

Is there a Qt widget that looks like a label to display text but can also be edited if double-clicked?

I want a widget in Qt that will act like a spreadsheet cell does. It can display text, then when the user double-clicks on it, it becomes editable. Once the user is done with editing and presses Enter, the text gets saved and the control is not editable anymore. If the user hits Escape while editing, then the control returns to its previous value.
One possible solution is sub-classing QWidget, QLabel and QLineEdit. Are there any other solutions available in Qt?
The following version also implements the same functionalities of your answer but instead of subclassing the QLineEdit and the QLabel only use eventFilter() and instead of managing the visibility manually let QStackedWidget do it.
#include <QApplication>
#include <QFormLayout>
#include <QKeyEvent>
#include <QLabel>
#include <QLineEdit>
#include <QStackedWidget>
#include <QVBoxLayout>
class MyEditableLabel: public QWidget{
Q_OBJECT
Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
public:
MyEditableLabel(QWidget *parent=nullptr):
QWidget(parent),
mLabel(new QLabel),
mLineEdit(new QLineEdit)
{
setLayout(new QVBoxLayout);
layout()->setMargin(0);
layout()->setSpacing(0);
layout()->addWidget(&stacked);
stacked.addWidget(mLabel);
stacked.addWidget(mLineEdit);
mLabel->installEventFilter(this);
mLineEdit->installEventFilter(this);
setSizePolicy(mLineEdit->sizePolicy());
connect(mLineEdit, &QLineEdit::textChanged, this, &MyEditableLabel::setText);
}
bool eventFilter(QObject *watched, QEvent *event){
if (watched == mLineEdit) {
if(event->type() == QEvent::KeyPress){
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if(keyEvent->key() == Qt::Key_Return ||
keyEvent->key() == Qt::Key_Escape ||
keyEvent->key() == Qt::Key_Enter)
{
mLabel->setText(mLineEdit->text());
stacked.setCurrentIndex(0);
}
}
else if (event->type() == QEvent::FocusOut) {
mLabel->setText(mLineEdit->text());
stacked.setCurrentIndex(0);
}
}
else if (watched == mLabel) {
if(event->type() == QEvent::MouseButtonDblClick){
stacked.setCurrentIndex(1);
mLineEdit->setText(mLabel->text());
mLineEdit->setFocus();
}
}
return QWidget::eventFilter(watched, event);
}
QString text() const{
return mText;
}
void setText(const QString &text){
if(text == mText)
return;
mText == text;
emit textChanged(mText);
}
signals:
void textChanged(const QString & text);
private:
QLabel *mLabel;
QLineEdit *mLineEdit;
QStackedWidget stacked;
QString mText;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget w;
QFormLayout *lay = new QFormLayout(&w);
MyEditableLabel el;
lay->addRow("MyEditableLabel: ", &el);
lay->addRow("QLineEdit: ", new QLineEdit);
w.show();
return a.exec();
}
#include "main.moc"
this solution is not as sexy but probably one of the more performant solutions available to you is to use a QInputdialog to change QLabel and override the mouseDoubleClickEvent to trigger the input dialog. I as some here have learned that there is no means to Pull edited text from a QLabel. Not without changing QLabels internal code. Here's an example using a QInputDialog as means.
//intrlbl.h
#ifndef INTRLBL_H
#define INTRLBL_H
#include <QWidget>
#include <QLabel>
#include <QMouseEvent>
class intrLbl: public QLabel
{
Q_OBJECT
public:
intrLbl(QWidget *parent);
void mouseDoubleClickEvent(QMouseEvent *event) override;
QString text;
};
#endif // INTRLBL_H
//intrlbl.cpp file
#include "intrlbl.h"
#include <QDebug>
#include <QInputDialog>
intrLbl::intrLbl(QWidget *parent)
{
this->setText("Text Changeable Via Double Click QInput Dialog");
this->setFocusPolicy(Qt::ClickFocus);
this->setWordWrap(false);
}
void intrLbl::mouseDoubleClickEvent(QMouseEvent *event)
{
QString title
= QInputDialog::getText(this,
tr("Enter your Idea Title:"),
tr("Title:"), QLineEdit::Normal,
tr("enter your title here"));
if(!title.isEmpty())
{
qDebug() << "Title set to:" << title;
this->setText(title);
}
else
{
title = "Title";
this->setText(title);
}
}
One of the solutions is to have a QLineEdit and set it to read-only and style it in a way that it will look like a label. I personally do not like this solution, because it's more of a hacking approach. I have come up with something that in my opinion is pretty cool, which includes sub-classing QWidget, QLabel and QLineEdit:
Let's first introduce a model, which will be created in the sub-classed version of our QWidget and this model will be passed to its child widgets, the sub-classed versions of QLabel and QLineEdit:
Model header - mymodel.h:
#ifndef MYMODEL_H
#define MYMODEL_H
#include <QObject>
class MyModel : public QObject {
Q_OBJECT
Q_PROPERTY(Mode mode READ getMode WRITE setMode NOTIFY modeChanged)
Q_PROPERTY(QString text READ getText WRITE setText NOTIFY textChanged)
public:
enum class Mode {
ReadOnly = 0,
Edit = 1,
};
explicit MyModel(QObject* parent = nullptr);
Mode getMode() const {
return _mode;
}
const QString& getText() const {
return _text;
}
signals:
void modeChanged(Mode mode);
void textChanged(const QString& text);
public slots:
void setMode(Mode mode);
void setText(const QString& text);
private:
Mode _mode;
QString _text;
};
#endif // MYMODEL_H
Model implementation - mymodel.cpp
#include "mymodel.h"
MyModel::MyModel(QObject *parent)
: QObject(parent)
, _mode(MyModel::Mode::ReadOnly)
, _text(QString()) {
}
void MyModel::setMode(MyModel::Mode mode) {
if (_mode != mode) {
_mode = mode;
emit modeChanged(_mode);
}
}
void MyModel::setText(const QString &text) {
if (_text != text) {
_text = text;
emit textChanged(text);
}
}
As we see the model has the text, which is common for both the QLabel and the QLineEdit, and it has a mode, which can be either read only or edit mode.
The label implementation is a sub-class of Label.
Header - mylabel.h:
#ifndef MYLABEL_H
#define MYLABEL_H
#include <QLabel>
#include <QSharedPointer>
#include "mymodel.h"
class MyLabel : public QLabel {
Q_OBJECT
public:
explicit MyLabel(QWidget *parent = 0);
void setModel(QSharedPointer<MyModel> model);
protected:
void mouseDoubleClickEvent(QMouseEvent *) override;
private:
QSharedPointer<MyModel> _model;
};
#endif // MYLABEL_H
Implementation - mylabel.cpp:
#include "mylabel.h"
#include <QMouseEvent>
MyLabel::MyLabel(QWidget *parent)
: QLabel(parent) {
}
void MyLabel::setModel(QSharedPointer<MyModel> model) {
_model = model;
}
void MyLabel::mouseDoubleClickEvent(QMouseEvent *) {
_model->setText(text());
_model->setMode(MyModel::Mode::Edit);
}
As we our class MyLabel has a setModel() method, which will take the model from its parent. We are overriding the mouseDoubleClickEvent(), though which we are setting the text of the model to whatever text there is in the label, and setting the mode to edit, because when double-clicking we want to edit the text.
Now let's take a look at the QLineEdit. Our version of QLineEdit, called MyLineEdit, is listening to keyboard events and when Enter and Esc keys are pressed it either saves the text to the model, or discards it. Then it changes the mode to read-only.
MyLineEdit.h:
#ifndef MYLINEEDIT_H
#define MYLINEEDIT_H
#include <QLineEdit>
#include <QSharedPointer>
#include "mymodel.h"
class MyLineEdit : public QLineEdit {
Q_OBJECT
public:
MyLineEdit(QWidget* parent = nullptr);
void setModel(QSharedPointer<MyModel> model);
protected:
void keyPressEvent(QKeyEvent* event) override;
void focusOutEvent(QFocusEvent*);
private:
QSharedPointer<MyModel> _model;
};
#endif // MYLINEEDIT_H
And here's the implementation - MyLineEdit.cpp:
#include "mylineedit.h"
#include <QKeyEvent>
MyLineEdit::MyLineEdit(QWidget *parent)
: QLineEdit(parent) {
}
void MyLineEdit::setModel(QSharedPointer<MyModel> model) {
_model = model;
}
void MyLineEdit::keyPressEvent(QKeyEvent *event) {
if (event->key() == Qt::Key_Enter) {
_model->setText(text());
_model->setMode(MyModel::Mode::ReadOnly);
} else if (event->key() == Qt::Key_Escape) {
_model->setMode(MyModel::Mode::ReadOnly);
} else {
QLineEdit::keyPressEvent(event);
}
}
void MyLineEdit::focusOutEvent(QFocusEvent *) {
_model->setText(text());
_model->setMode(MyModel::Mode::ReadOnly);
}
So now we have the model, we have our version of QLabel and our version of QLineEdit. What we want now is a parent widget that will contain both of them, listen to signals from the model and change its appearance based on the signals. That class is derived from QWidget and is called MyEditableLabel:
MyEditableLabel.h:
#ifndef MYEDITABLELABEL_H
#define MYEDITABLELABEL_H
#include <QSharedPointer>
#include <QWidget>
#include "mylabel.h"
#include "mylineedit.h"
class MyEditableLabel : public QWidget {
Q_OBJECT
public:
explicit MyEditableLabel(QWidget *parent = nullptr);
QString getText() const {return _text;}
private:
MyLabel *_label;
MyLineEdit *_lineEdit;
QSharedPointer<MyModel> _model;
private slots:
void onModeChanged(MyModel::Mode mode);
void onTextChanged(const QString &text);
private:
QString _text;
};
#endif // MYEDITABLELABEL_H
MyEditableLabel.cpp:
#include "myeditablelabel.h"
#include <QHBoxLayout>
MyEditableLabel::MyEditableLabel(QWidget *parent)
: QWidget(parent) {
_model = QSharedPointer<MyModel>(new MyModel());
_model->setText("Click me!");
_label = new MyLabel(this);
_label->setModel(_model);
_lineEdit = new MyLineEdit(this);
_lineEdit->setModel(_model);
_lineEdit->setReadOnly(false);
QHBoxLayout *mainLayout = new QHBoxLayout();
mainLayout->setMargin(0);
mainLayout->setSpacing(0);
mainLayout->addWidget(_label);
mainLayout->addWidget(_lineEdit);
setLayout(mainLayout);
connect(_model.data(), &MyModel::modeChanged, this, &MyEditableLabel::onModeChanged);
onModeChanged(_model->getMode());
connect(_model.data(), &MyModel::textChanged, this, &MyEditableLabel::onTextChanged);
onTextChanged(_model->getText());
}
void MyEditableLabel::onModeChanged(MyModel::Mode mode) {
_lineEdit->setVisible(mode == MyModel::Mode::Edit);
_lineEdit->selectAll();
_label->setVisible(mode == MyModel::Mode::ReadOnly);
}
void MyEditableLabel::onTextChanged(const QString &text) {
_lineEdit->setText(text);
_label->setText(text);
_text = text;
}
Usage:
Using this is pretty straightforward. If you're using the Qt Creator designer, then you want to draw a QWidget and the right click on it and promote it to MyEditableLabel and you're done. If you're not using the Qt Creator designer then you just have to create and instance of MyEditableLabel and you're in business.
Improvements:
It probably is a better idea to not create the model in the constructor of MyEditableLabel, but outside of it and have a setModel method in MyEditableLabel.