I'm new to QT and I'm trying to create a program that shows different images depending on input from a serial interface.
I realize that there are a number of ways to achieve this and I'm looking for input from someone with experience in QT.
My idea was to send new data to a compare function that returns an integer to the main function, this integer will determine what picture will be shown. However, using a while loop results in the picture being re-drawn and not static.
My question is, should i start another thread for the image viewer, or use a different class for it?
Or is this approach hideous and should i start over?
Thankful for any input!
if(!serial.open(QIODevice::ReadOnly))
qDebug() << serial.errorString();
QObject::connect(&serial, &QSerialPort::readyRead, [&]
{
int comp=0;
int landscape =1;
int total_data = serial.bytesAvailable();
qDebug() << "New data Available: " << serial.bytesAvailable();
QByteArray datas = serial.readAll();
comp= compare(total_data,datas);
while(comp == landscape){
qDebug() << "I Picture";
QLabel label("<img src='landscape.jpg' /> ");
label.show();
}
qDebug() << datas;
});
This is the compare function that reads data from serial interface
int compare(int x, QByteArray y)
{
int r=0;
for(int i = 0; i <= x ; i++){
if (strncmp (y, "\x00",1) ==0)
{
//picture();
r=1;
return r;
}
}
return r;
}
By doing this:
while(comp == landscape){
qDebug() << "I Picture";
QLabel label("<img src='landscape.jpg' /> ");
label.show();
}
You are creating a local QLabel on the stack. It will be destroyed at each iteration.
Qt uses its own mechanism to update its objects (the event loop) and you just have to change the picture in your QLabel when needed.
So, what you can do, it's creating a QLabel in your widget and change the image in your slot:
class Window: public QWidget
{
Q_OBJECT
public:
enum ImageType {
landscape = 1,
};
Window(QObject* parent=nullptr): QWidget(parent),
myLabel(new QLabel(this))
{
if(!serial.open(QIODevice::ReadOnly))
qDebug() << serial.errorString();
connect(&serial, &QSerialPort::readyRead, this, &Window::updateImage);
}
public slots:
void updateImage()
{
int total_data = serial.bytesAvailable();
qDebug() << "New data Available: " << serial.bytesAvailable();
QByteArray datas = serial.readAll();
int const comp = compare(total_data,datas);
if (comp == Window::landscape)
myLabel->setPixmap("landscape.png");
else
myLabel->setPixmap("anotherImg.png");
qDebug() << datas;
}
private:
QLabel* myLabel;
QSerialPort serial;
};
You can use signals/slots and a thread (only if you want to do more actions during the program execution). It is very typical when using the serial line, as it is an asynchronus protocol that has to be pulled to know if there exist new data.
To do so, emit a signal every time you receive data from serial line and create a slot that process it. In this way you will use similar than an event driven system.
Related
I wrote this code:
void StartToSendCommand(QString fileName, QPlainTextEdit *textEdit)
{
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
return;
QTimer * inputTimer=new QTimer(textEdit);
QTextStream in(&file);
QString line;
while (!in.atEnd()) {
line = in.readLine();
if (line==""||line[0]=="#")
continue;
qDebug()<<line;
//TO DO: make also a waiting time between letters.
inputTimer->start(GWaitingTimeBetweenCommand);
QApplication::processEvents();
QThread::sleep(2);`
}
inputTimer->deleteLater();
SendCommandByUsb(fileName, line);
}
and I want to do that when its read from the file its also make a wait for 1 second between any letter.
how can I make it?
If you want to wait for individual letters, you can also do this with a QTimer interval. Once the condition is met, you can submit the file.
Here would be a small example, oriented towards a typewriter but has the same effect:
.h
....
private slots:
void typeWriter();
private:
QString line;
QTimer *timer;
....
.cpp
//constructor
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
line = "C:/.../readme.txt";
timer = new QTimer(this);
connect(timer,&QTimer::timeout, this, &MainWindow::typeWriter);
timer->start(1000);
}
void MainWindow::typeWriter()
{
static int counter = 0;
if(counter < line.length())
{
counter ++;
ui->label->setText(line.mid(0, counter));
//SendCommandByUsb(fileName, line.mid(0, counter));
timer->setInterval(1000);
qDebug() << counter << " : " << line.length() << " : " << line.mid(0, counter);
}
if(counter == line.length())
{
qDebug() << "end";
timer->stop();
}
}
the length of the string is compared with the counter. it is counted until it reaches the length of the string. the string method QString::mid can be thought of as a slice. it is sliced from index 0 (i.e. the beginning of the string) to the end, the counter takes care of that.
The timer interval defines the interval between the next call.
Just for info (because that's how the question read):
If you want to open a file and always want to wait a second for a letter to be read, you have to consider that you don't have a valid file path. Well that wouldn't make sense because you have to commit the file before anything is read at all.
I have a qt and cpp application that creates threads on create button and runs them with a certain start time specified by the user. It list the thread object created into a QListWidget in the application with some random id generated. I can select and delete the QListWidget item as shown below.
void MainWindow::on_stopPushButton_clicked()
{
qDebug() << mythread->threadIdGenerator();
QList<QListWidgetItem*> items = ui->threadWithId->selectedItems();
foreach(QListWidgetItem* item, items){
ui->threadWithId->removeItemWidget(item);
delete item; // Qt documentation warnings you to destroy item to effectively remove it from QListWidget.
}
}
This delete operation just deletes the entry from the UI (QListWidget) of the application but the thread deleted from QListWidget is still running in the application.
My question is how can I get terminate this thread with deletion from the QListWidget from the UI of the application?
Or is there any way to get this thread object and delete it after being selected from the UI.
Update
void MainWindow::on_createNewThreadButton_clicked()
{
qDebug(" in create new thread slot");
mythread = new MyThread(this);
mythread->threadOccuranceTime = ui->insertThreadTimeoutHere->text().toInt();
mythread->start();
myThreadListCreatedObjects.append(mythread);
connect(mythread, SIGNAL(signalForThreadMessage(int)), this, SLOT(displayThreadMessages(int)));
connect(mythread, SIGNAL(sendThreadId(int)), this, SLOT(displayThread(int)));
}
MyThread.cpp
int MyThread::threadIdGenerator()
{
qDebug(" in threadIdGenerator function");
// qDebug() << 1 + (rand() % 100);
srand(time(0));
return 1 + (rand() % 100);
}
void MyThread::run()
{
this->threadId = 0;
qDebug() << this->threadId;
qDebug(" in run function for the thread");
this->threadId = this->threadIdGenerator();
qDebug(" id inside the run method of thread");
qDebug() << threadId;
emit sendThreadId(threadId);
while (1) {
msleep(this->threadOccuranceTime);
emit signalForThreadMessage(this->threadId);
}
}
I used QList to store all the threads created, and when a item is selected in the QLIstWidget I tried to get the thread with that id property, terminate it and then remove from the QListWidget from UI as shown below.
void MainWindow::on_stopPushButton_clicked()
{
QList<QListWidgetItem*> items = ui->threadWithId->selectedItems();
foreach(QListWidgetItem* item, items){
qDebug() << "the item selected to delete";
qDebug() << item->text();
ui->threadWithId->removeItemWidget(item);
QString selected = item->text();
QString subString = selected.mid(0,2);
qDebug() << "subString = ";
qDebug() << subString;
for(MyThread *sp:myThreadListCreatedObjects){
qDebug() << "thread id = ";
qDebug() << sp->threadId;
if(sp->threadId == subString.toInt()){
qDebug() << "subString = ";
qDebug() << subString;
sp->terminate();
delete sp;
}
}
delete item; // Qt documentation warnings you to destroy item to effectively remove it from QListWidget.
}
}
I am not sure this is the correct way of doing the things but it works some how. Any better solution is welcome :).
So i have 2 classes, one named ConsoleInput, which contains member function check4Flood and second named AntiFloodSys, in which connect function for signal-slot system is present, and also its signal (QTimer) and slot.
AntiFloodSys object is in check4Flood member function which scope never ends as inside there is infinite while loop. Thus the object is never destroyed. So when the object anti is created, the constuctor of AntiFloodSys class is called and therefore the connection between signal and slot.
My question at which point of the code the connection timeout signal and mySlot is separated, so the slot is never fired?
ConsoleInput cpp file looks like this:
void ConsoleInput::check4Flood(int& lineCounter)
{
AntiFloodSys anti;
while(1)
{
std::string chatLine[2];
std::cin >> chatLine[0] >> chatLine[1];
anti.input(chatLine[0], chatLine[1]);
}
}
and AntiFloodSys class like this:
AntiFloodSys::AntiFloodSys(QObject *parent) : QObject(parent)
{
timeFrame = 1000 ;
timer = new QTimer;
connect(timer, SIGNAL(timeout()), this, SLOT(mySlot()));
timer->start(timeFrame);
std::cout << "AntiFloodSys constructor - timer starts " << "\n";
}
AntiFloodSys::~AntiFloodSys()
{
std::cout << "AntiFloodSys Destructor" << "\n";
}
void AntiFloodSys::input(std::string nick_, std::string line_)
{
nick = nick_;
line = line_;
std::cout << "nick: " << nick << " line: " << line << " " << "\n";
}
void AntiFloodSys::mySlot()
{
std::cout << "slot" << "\n";
}
The problem is your while(1): the Qt event loop is never processed because your program is blocked in this loop.
You can force the event loop processing calling QCoreApplication::processEvents() but the std::cin is a blocking function. So, it will not completly solve your problem.
You should move your loop in a dedicated thread that will send data to the main thread (e.g. signals/slots system).
You can also use the QSocketNotifier class to create a non blocking stdin access.
A quick example:
class Widget: public QWidget
{
Q_OBJECT
public:
Widget(): QWidget(), input(new QLabel("Edit", this))
{
connect(this, &Widget::displayText, input, &QLabel::setText);
}
private:
QLabel* input;
signals:
void displayText(QString const&);
};
class UserInput: public QObject
{
Q_OBJECT
public:
UserInput(): QObject()
{}
public slots:
void run()
{
while(1) // User Input in an infinite loop
{
std::string line;
std::cin >> line;
emit inputReceived(QString::fromStdString(line));
}
}
signals:
void inputReceived(QString const&);
};
int main(int argc, char** argv) {
QApplication app(argc, argv);
Widget* w = new Widget();
UserInput* input = new UserInput();
QThread* thread = new QThread();
input->moveToThread(thread); // Move the user input in another thread
QObject::connect(thread, &QThread::started, input, &UserInput::run);
QObject::connect(input, &UserInput::inputReceived, w, &Widget::displayText);
thread->start();
w->show();
return app.exec();
}
I am creating an application which prints text through a POS printer.
The prints works fine. But for POS printer there are control commands to do certain functions like : paper cut, cashdraw open etc.. For eg:
Function: Partial cut
Code:
ASCII———-> ESC i
Hex ————-> 1B 69
Decimal—-> 27 105
When I try to send command 27 105 it just prints on paper instead of performing action.. I’m not exactly sure how to send it… Can someone suggest how to write to the socket…
#include "lanprinterui.h"
#include "ui_lanprinterui.h"
LanPrinterUI::LanPrinterUI(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::LanPrinterUI)
{
ui->setupUi(this);
m_TextInput = ui->textEdit;
m_pSocket = new QTcpSocket();
m_pSocket->connectToHost("192.168.1.20", 9100);
m_ConnectStatus = true;
QObject::connect(ui->pushButton, SIGNAL(clicked()), this, SLOT(print()));
}
LanPrinterUI::~LanPrinterUI()
{
delete ui;
delete m_pSocket;
}
void LanPrinterUI::print()
{
const int Timeout = 5 * 1000;
if (!m_ConnectStatus)
{
m_pSocket->connectToHost("192.168.1.20", 9100);
}
if (!m_pSocket->waitForConnected(Timeout))
{
//sent error
qDebug ("error in waitForConnected()");
qDebug (qPrintable(m_pSocket->errorString()));
m_ConnectStatus = false;
return;
}
m_ConnectStatus = true;
QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_0);
out << m_TextInput->toPlainText();
out << '\n';
m_pSocket->write(block);
}
What you want is
out << "\n\x1Bi";
That is, you want, after the '\n' char, to send the '\x1B' char (also known as "escape") and the 'i' char. You could also have written this as:
out << '\n' << "\x1b\x69";
or:
out << '\n' << char(27) << char(105);
or:
out << '\n' << char(0x1b) << char(0x69);
(or any other valid combination)
Another, better idea, would be to put in your class:
class LanPrinterUI {
private:
static const QString PARTIAL_PAPER_CUT = "\x1bi";
static const QString CASHDRAWER_OPEN = "\x1b....";
//...
};
And then you would just:
out << '\n' << PARTIAL_PAPER_CUT;
(which would be much better than hardcoded constants)
Can someone help to clear up the confusion of how to update a gui window without user input.
In other words, I would like to be able to output text to either or both the console our the gui window.
At present I can call the gui window (Window with a label for example) and output the initial text. However, the process doesn't return to my c++ code until the window closes. I'm trying to figure out how to (or where to have my code) for updating the gui screen before the gui window exits.
This is an example:
#include <gtkmm.h>
#include <iostream>
using namespace std;
int main(int argc, char* argv[])
{
Gtk::Main kit(argc, argv);
Gtk::Window window;
Gtk::TextView textview;
Gtk::Label label;
string mylabeltext = "This is the first line of text in my gui window.\n";
window.set_default_size(600, 360);
window.set_title("Gtkmm Programming - C++");
window.set_position(Gtk::WIN_POS_CENTER);
label.show();
window.add(label);
label.set_text(mylabeltext);
mylabeltext += "About to run some routines...\n";
label.set_text(mylabeltext);
cout << "An initial line has been set to the gui window." << endl;
// The Gui Window is displayed
Gtk::Main::run(window);
// Now my main program has performed some functions and wants to update
// the console and the gui window.
cout << "Continuing after various functions and processing..." << endl;
mylabeltext = "Showing the results of the functions and processing.";
label.set_text(mylabeltext);
return 0;
}
The last line of text is never printed to the console until the gui is exited. The last line of the mylabeltext is never printed to the label window.
What I'm trying to describe is how to keep the gtkmm window active while I run other routines in my c++ code and update the output to both the console and the gui window without closing the gui window to continue the c++ routines.
All the examples that I can find uses a button in the code. I have tested and experimented enough that I can update the gui screen after a button is pressed. However, I don't want to have to rely on the user for screen updates. I hope to be able to run disc scans and other functions and periodically update the screen so that the user can see the progress and know that the program is still working and not dead.
Some of the resources that I have studied in my attempts at understanding this include:
https://developer.gnome.org/
https://developer.gnome.org/gtkmm-tutorial/3.2/gtkmm-tutorial.html
http://en.wikipedia.org/wiki/Gtkmm
Like tp1 said in their comment on your question, a timer is going to be the easiest way to do this.
To set a 1.5 second timeout that will call another function, do this (gtkmm 3):
#include <gtkmm.h>
#include <iostream>
using namespace std;
class MyApp : public Gtk::Window{
public:
Gtk::Label label;
bool on_timeout(); //return true to keep the timeout and false to end it
MyApp();
virtual ~MyApp();
};
MyApp::MyApp(){
string mylabeltext = "This is the first line of text in my gui window.\n";
set_default_size(600, 360);
set_title("Gtkmm Programming - C++");
set_position(Gtk::WIN_POS_CENTER);
add(label);
label.set_text(mylabeltext);
mylabeltext += "About to run some routines...\n";
label.set_text(mylabeltext);
cout << "An initial line has been set to the gui window." << endl;
//create slot for timeout signal
int timeout_value = 1500; //in ms (1.5 sec)
sigc::slot<bool>my_slot = sigc::mem_fun(*this, &MyApp::on_timeout);
//connect slot to signal
Glib::signal_timeout().connect(my_slot, timeout_value);
show_all_children();
}
MyApp::~MyApp(){
}
bool MyApp::on_timeout(){
cout << "Continuing after various functions and processing..." << endl;
string temp = label.get_text();
temp += "Showing the results of the functions and processing.\n";
label.set_text(temp);
return true;
}
int main(int argc, char* argv[])
{
Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "com.kaze.test");
MyApp myapp;
// The Gui Window is displayed
return app->run(myapp);
}
More info here: https://developer.gnome.org/gtkmm-tutorial/3.3/sec-timeouts.html.en
This is crude, but this is functional for what I was trying to do:
#include <gtkmm.h>
#include <iostream>
using namespace std;
class myLabel: public Gtk::Window
{
public:
myLabel();
virtual ~myLabel();
protected:
Gtk::Label m_label;
string labeltext;
string newtext;
void myprocess1();
};
myLabel::myLabel() :
m_label()
{
void myprocess1();
set_title("Gtkmm Programming - C++");
add(m_label);
m_label.show();
Glib::Thread::create(sigc::mem_fun(*this, &myLabel::myprocess1), true);
}
myLabel::~myLabel()
{
}
void myLabel::myprocess1()
{
labeltext = "About to preform a number of processes.\n";
labeltext += "Each process may take up to three hours.\n";
labeltext += "Please carry your daily chores and wait.\n";
cout << labeltext;
cout.flush();
m_label.set_text(labeltext);
sleep(10); // Back from a three hour function
newtext = "Back from a three hour function\n";
labeltext += newtext;
m_label.set_text(labeltext);
cout << newtext;
cout.flush();
sleep(10); // Back from a three hour function
newtext = "Back from another three hour function\n";
labeltext += newtext;
m_label.set_text(labeltext);
cout << newtext;
cout.flush();
newtext = "Exiting in 1 minute...\n";
labeltext += newtext;
m_label.set_text(labeltext);
cout << newtext;
cout.flush();
sleep(60);
exit(0);
}
int main(int argc, char* argv[])
{
if (Glib::thread_supported())
Glib::thread_init();
else
{
cerr << "Threads aren't supported!" << endl;
exit(1);
}
Gtk::Main kit(argc, argv);
myLabel mylabel;
Gtk::Main::run(mylabel);
return 0;
}
Hope the example can help anyone else that wants to output to the gtkmm gui with updates, similar to updating info to the console.