Struggling emitting a signal from main/QML thread to another thread with a QList< QStringList > parameter. Variations I'v tried:
Q_DECLARATIVE_METATYPE in and out
Using EventExport vs. const EventExport& in signal and slot profiles
Sending empty EventExport in prepareExport() so emit has no/low data amount
Checking connect statement (always returns true)
Having qDebug() in prepareExport() and signal always appears to be emitted
Calling emit right after connect as a test (Works! Think you're going to tell me the main thread or cryoUtility objects don't exist but they do!)
Tried qRegisterMetaType with () and ("EventExport")...some say use text for typedef types
Any thoughts much appreciated!
sqlquery_model.h (not certain I need Q_DECLARATIVE_METATYPE but tried with and without...no change)
typedef QList<QStringList> EventExport;
Q_DECLARE_METATYPE(EventExport);
Q_INVOKABLE void prepareExport();
signals:
void updateEventListDataSig(const EventExport&);
sqlquery_model.cpp (this is connected to a qml page using TableView model...this emit does not seem to work)
void SqlQueryModel::prepareExport() {
if (this->rowCount() > 0) {
EventExport eventsList;
for(int i=0; i<this->rowCount(); ++i) {
QStringList eventInfo;
eventInfo.append(this->record().value(0).toString());
eventInfo.append(this->record().value(1).toString());
eventInfo.append(this->record().value(2).toString());
eventInfo.append(this->record().value(3).toString());
eventInfo.append(this->record().value(4).toString());
eventsList.append(eventInfo);
}
emit updateEventListDataSig(eventsList);
qDebug() << "Emit updatedEventListData" << eventsList.count();
}
}
main.cpp (includes sqlquery_model.h, need this as cryoUtility is a separate thread using Qt::QueuedConnection)
// Use string if using typedef method
qRegisterMetaType<EventExport>("EventExport");
mediator.h
void updateEventListDataSig(const EventExport&);
mediator.cpp (connects mainly live here, this test event works)
bool ret = connect(this, SIGNAL(updateEventListDataSig(const EventExport&)), cryoUtility, SLOT(updateEventListData(const EventExport&)), Qt::QueuedConnection);
EventExport ed;
emit updateEventListDataSig(ed);
qDebug() << "Event list CONN: " << ret;
utilities.h
void updateEventListData(const EventExport&);
utilities.cpp (this is the slot, trigger once on test call)
void Utilities::updateEventListData(const EventExport& el) {
qDebug() << "Load event list: ";// << el.count();
//eventList = el;
}
So, after more study, sqlmodelquery connection would have to occur in its constructor as it isn't active yet until its QML page loads.
I am trying to use the following code to open the existing file to append data at it's end:
void AddPharmacyForm::addInsertToFile(QString insert)
{
QFile inserts(":/new/prefix1/insertstatements.txt");
if(!inserts.exists())
qDebug() << "File does not exist";
if(inserts.isOpen())
qDebug() << "file is open";
if(inserts.open(QFile::ReadWrite | QFile::Text))
{
// Another workaround- could not open file with append flag
qDebug() << "im here!";
QString currentInserts;
QTextStream out(&inserts);
out >> currentInserts;
out << endl << insert;
inserts.close();
}
else
{
QMessageBox::information(this, tr("Error"), tr("Cannot add new pharmacy! "
"Please contact program designer."
));
qDebug() << "error code: " + QString::number(inserts.error());
return;
}
}
The output of this code is the QMessageBox with the error and in qDebug it produces following line:
"error code: 5"
It does not give notice about file not existing and file being open. I have also tried opening file with different flags: QFile::ReadWrite, QFile::append, QFile::WriteOnly and the same modes within QIODevice. The error code is still the same. When I am opening the file from another class, the file opens without errors (it is not an access error).
What might be causing this problem?
There's no support for writing into the resource system, whether implemented using Qt's resource system or native to the platform. Your application typically has no right to modify its own executable, or the application bundle, or its installation location - it'd be a security risk if it did since bugs in networking code could be easily exploited to infect your user's system. So what you're trying to do is just a bad idea.
Instead, store the modified resources in your application's data folder, and revert to reading from the resource if the file doesn't exist. It is also probably not very wise to append to a file if the file is small: such appends are not atomic and can partially fail, leaving the file corrupted. Using a QSaveFile is guaranteed to either completely succeed or to fail without modifying any data.
An example implementation follows. The src.close() is not necessary to close the file, as QFile will automatically close upon destruction, as it is a proper resource-managing C++ class. By closing it earlier we ensure minimal use of the file descriptor - a finite system resource.
// https://github.com/KubaO/stackoverflown/tree/master/questions/resource-bypass-43044268
#include <QtCore>
const char kInsertsFile[] = ":/insertstatements.txt";
QString toWritableName(const QString & qrcFileName) {
Q_ASSERT (qrcFileName.startsWith(":/"));
QFileInfo info(qrcFileName);
return
QStandardPaths::writableLocation(QStandardPaths::DataLocation)
+ info.path().mid(1) + '/' + info.fileName();
}
QString toReadableName(const QString & qrcFileName) {
Q_ASSERT (qrcFileName.startsWith(":/"));
auto writable = toWritableName(qrcFileName);
return QFileInfo(writable).exists() ? writable : qrcFileName;
}
bool setupWritableFile(QSaveFile & dst, QIODevice::OpenMode mode = {}) {
Q_ASSERT (dst.fileName().startsWith(":/"));
Q_ASSERT (mode == QIODevice::OpenMode{} || mode == QIODevice::Text);
QFile src(toReadableName(dst.fileName()));
dst.setFileName(toWritableName(dst.fileName()));
if (!src.open(QIODevice::ReadOnly | mode))
return false;
auto data = src.readAll();
src.close(); // Don't keep the file descriptor tied up any longer.
QFileInfo dstInfo(dst.fileName());
if (!dstInfo.dir().exists() && !QDir().mkpath(dstInfo.path()))
return false;
if (!dst.open(QIODevice::WriteOnly | mode))
return false;
return dst.write(data) == data.size();
}
bool addInsertToFile(const QString & insert) {
QSaveFile file(kInsertsFile);
if (!setupWritableFile(file, QIODevice::Text))
return false;
if (true) {
// Alternative 1
QTextStream s(&file);
s << insert << '\n';
} else {
// Alternative 2
file.write((insert + '\n').toLocal8Bit());
}
return file.commit();
}
QStringList readInserts() {
QFile file(toReadableName(kInsertsFile));
if (!file.open(QIODevice::ReadOnly))
return {};
return QString::fromLocal8Bit(file.readAll()).split('\n', QString::SkipEmptyParts);
}
int main(int argc, char ** argv) {
QCoreApplication app{argc, argv};
app.setApplicationName("resource-bypass-42044268");
qDebug() << "Original Inserts:" << readInserts();
auto rc = addInsertToFile("NewInsert");
qDebug() << "Modification status:" << rc;
qDebug() << "Current Inserts:" << readInserts();
}
When you use the Qt Resource System (qrc files) to add files for your project, they are compiled directly into the binary of your application, so are therefore readonly. As the documentation states: -
Resource data can either be compiled into the binary and thus accessed immediately in application code, or a binary resource can be created and at a later point in application code registered with the resource system.
And...
Currently, Qt always stores the data directly in the executable, even on Windows, macOS, and iOS, where the operating system provides native support for resources. This might change in a future Qt release.
I want to save an image of a frame from a QMediaPlayer. After reading the documentation, I understood that I should use QVideoProbe. I am using the following code :
QMediaPlayer *player = new QMediaPlayer();
QVideoProbe *probe = new QVideoProbe;
connect(probe, SIGNAL(videoFrameProbed(QVideoFrame)), this, SLOT(processFrame(QVideoFrame)));
qDebug()<<probe->setSource(player); // Returns true, hopefully.
player->setVideoOutput(myVideoSurface);
player->setMedia(QUrl::fromLocalFile("observation.mp4"));
player->play(); // Start receving frames as they get presented to myVideoSurface
But unfortunately, probe->setSource(player) always returns false for me, and thus my slot processFrame is not triggered.
What am I doing wrong ? Does anybody have a working example of QVideoProbe ?
You're not doing anything wrong. As #DYangu pointed out, your media object instance does not support monitoring video. I had the same problem (and same for QAudioProbe but it doesn't interest us here). I found a solution by looking at this answer and this one.
The main idea is to subclass QAbstractVideoSurface. Once you've done that, it will call the method QAbstractVideoSurface::present(const QVideoFrame & frame) of your implementation of QAbstractVideoSurface and you will be able to process the frames of your video.
As it is said here, usually you will just need to reimplement two methods :
supportedPixelFormats so that the producer can select an appropriate format for the QVideoFrame
present which allows to display the frame
But at the time, I searched in the Qt source code and happily found this piece of code which helped me to do a full implementation. So, here is the full code for using a "video frame grabber".
VideoFrameGrabber.cpp :
#include "VideoFrameGrabber.h"
#include <QtWidgets>
#include <qabstractvideosurface.h>
#include <qvideosurfaceformat.h>
VideoFrameGrabber::VideoFrameGrabber(QWidget *widget, QObject *parent)
: QAbstractVideoSurface(parent)
, widget(widget)
, imageFormat(QImage::Format_Invalid)
{
}
QList<QVideoFrame::PixelFormat> VideoFrameGrabber::supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const
{
Q_UNUSED(handleType);
return QList<QVideoFrame::PixelFormat>()
<< QVideoFrame::Format_ARGB32
<< QVideoFrame::Format_ARGB32_Premultiplied
<< QVideoFrame::Format_RGB32
<< QVideoFrame::Format_RGB24
<< QVideoFrame::Format_RGB565
<< QVideoFrame::Format_RGB555
<< QVideoFrame::Format_ARGB8565_Premultiplied
<< QVideoFrame::Format_BGRA32
<< QVideoFrame::Format_BGRA32_Premultiplied
<< QVideoFrame::Format_BGR32
<< QVideoFrame::Format_BGR24
<< QVideoFrame::Format_BGR565
<< QVideoFrame::Format_BGR555
<< QVideoFrame::Format_BGRA5658_Premultiplied
<< QVideoFrame::Format_AYUV444
<< QVideoFrame::Format_AYUV444_Premultiplied
<< QVideoFrame::Format_YUV444
<< QVideoFrame::Format_YUV420P
<< QVideoFrame::Format_YV12
<< QVideoFrame::Format_UYVY
<< QVideoFrame::Format_YUYV
<< QVideoFrame::Format_NV12
<< QVideoFrame::Format_NV21
<< QVideoFrame::Format_IMC1
<< QVideoFrame::Format_IMC2
<< QVideoFrame::Format_IMC3
<< QVideoFrame::Format_IMC4
<< QVideoFrame::Format_Y8
<< QVideoFrame::Format_Y16
<< QVideoFrame::Format_Jpeg
<< QVideoFrame::Format_CameraRaw
<< QVideoFrame::Format_AdobeDng;
}
bool VideoFrameGrabber::isFormatSupported(const QVideoSurfaceFormat &format) const
{
const QImage::Format imageFormat = QVideoFrame::imageFormatFromPixelFormat(format.pixelFormat());
const QSize size = format.frameSize();
return imageFormat != QImage::Format_Invalid
&& !size.isEmpty()
&& format.handleType() == QAbstractVideoBuffer::NoHandle;
}
bool VideoFrameGrabber::start(const QVideoSurfaceFormat &format)
{
const QImage::Format imageFormat = QVideoFrame::imageFormatFromPixelFormat(format.pixelFormat());
const QSize size = format.frameSize();
if (imageFormat != QImage::Format_Invalid && !size.isEmpty()) {
this->imageFormat = imageFormat;
imageSize = size;
sourceRect = format.viewport();
QAbstractVideoSurface::start(format);
widget->updateGeometry();
updateVideoRect();
return true;
} else {
return false;
}
}
void VideoFrameGrabber::stop()
{
currentFrame = QVideoFrame();
targetRect = QRect();
QAbstractVideoSurface::stop();
widget->update();
}
bool VideoFrameGrabber::present(const QVideoFrame &frame)
{
if (frame.isValid())
{
QVideoFrame cloneFrame(frame);
cloneFrame.map(QAbstractVideoBuffer::ReadOnly);
const QImage image(cloneFrame.bits(),
cloneFrame.width(),
cloneFrame.height(),
QVideoFrame::imageFormatFromPixelFormat(cloneFrame .pixelFormat()));
emit frameAvailable(image); // this is very important
cloneFrame.unmap();
}
if (surfaceFormat().pixelFormat() != frame.pixelFormat()
|| surfaceFormat().frameSize() != frame.size()) {
setError(IncorrectFormatError);
stop();
return false;
} else {
currentFrame = frame;
widget->repaint(targetRect);
return true;
}
}
void VideoFrameGrabber::updateVideoRect()
{
QSize size = surfaceFormat().sizeHint();
size.scale(widget->size().boundedTo(size), Qt::KeepAspectRatio);
targetRect = QRect(QPoint(0, 0), size);
targetRect.moveCenter(widget->rect().center());
}
void VideoFrameGrabber::paint(QPainter *painter)
{
if (currentFrame.map(QAbstractVideoBuffer::ReadOnly)) {
const QTransform oldTransform = painter->transform();
if (surfaceFormat().scanLineDirection() == QVideoSurfaceFormat::BottomToTop) {
painter->scale(1, -1);
painter->translate(0, -widget->height());
}
QImage image(
currentFrame.bits(),
currentFrame.width(),
currentFrame.height(),
currentFrame.bytesPerLine(),
imageFormat);
painter->drawImage(targetRect, image, sourceRect);
painter->setTransform(oldTransform);
currentFrame.unmap();
}
}
VideoFrameGrabber.h
#ifndef VIDEOFRAMEGRABBER_H
#define VIDEOFRAMEGRABBER_H
#include <QtWidgets>
class VideoFrameGrabber : public QAbstractVideoSurface
{
Q_OBJECT
public:
VideoFrameGrabber(QWidget *widget, QObject *parent = 0);
QList<QVideoFrame::PixelFormat> supportedPixelFormats(
QAbstractVideoBuffer::HandleType handleType = QAbstractVideoBuffer::NoHandle) const;
bool isFormatSupported(const QVideoSurfaceFormat &format) const;
bool start(const QVideoSurfaceFormat &format);
void stop();
bool present(const QVideoFrame &frame);
QRect videoRect() const { return targetRect; }
void updateVideoRect();
void paint(QPainter *painter);
private:
QWidget *widget;
QImage::Format imageFormat;
QRect targetRect;
QSize imageSize;
QRect sourceRect;
QVideoFrame currentFrame;
signals:
void frameAvailable(QImage frame);
};
#endif //VIDEOFRAMEGRABBER_H
Note : in the .h, you will see I added a signal taking an image as a parameter. This will allow you to process your frame anywhere in your code. At the time, this signal took a QImage as a parameter, but you can of course take a QVideoFrame if you want to.
Now, we are ready to use this video frame grabber:
QMediaPlayer* player = new QMediaPlayer(this);
// no more QVideoProbe
VideoFrameGrabber* grabber = new VideoFrameGrabber(this);
player->setVideoOutput(grabber);
connect(grabber, SIGNAL(frameAvailable(QImage)), this, SLOT(processFrame(QImage)));
Now you just have to declare a slot named processFrame(QImage image) and you will receive a QImage each time you will enter the method present of your VideoFrameGrabber.
I hope that this will help you!
After Qt QVideoProbe documentation:
bool QVideoProbe::setSource(QMediaObject *mediaObject)
Starts monitoring the given mediaObject.
If there is no media object associated with mediaObject, or if it is
zero, this probe will be deactivated and this function wil return
true.
If the media object instance does not support monitoring video, this
function will return false.
Any previously monitored objects will no longer be monitored. Passing
in the same object will be ignored, but monitoring will continue.
So it seems your "media object instance does not support monitoring video"
TL;DR: https://gist.github.com/JC3/a7bab65acbd7659d1e57103d2b0021ba (only file)
I had a similar issue (5.15.2; although in my case I was on Windows, was definitely using the DirectShow back-end, the probe attachment was returning true, the sample grabber was in the graph, but the callback wasn't firing).
I never figured it out but needed to get something working so I kludged one out of a QAbstractVideoSurface, and it's been working well so far. It's a bit simpler than some of the other implementations in this post, and it's all in one file.
Note that Qt 5.15 or higher is required if you intend to both process frames and play them back with this, since the multi-surface QMediaPlayer::setVideoOutput wasn't added until 5.15. If all you want to do is process video you can still use the code below as a template for pre-5.15, just gut the formatSource_ parts.
Code:
VideoProbeSurface.h (the only file; link is to Gist)
#ifndef VIDEOPROBESURFACE_H
#define VIDEOPROBESURFACE_H
#include <QAbstractVideoSurface>
#include <QVideoSurfaceFormat>
class VideoProbeSurface : public QAbstractVideoSurface {
Q_OBJECT
public:
VideoProbeSurface (QObject *parent = nullptr)
: QAbstractVideoSurface(parent)
, formatSource_(nullptr)
{
}
void setFormatSource (QAbstractVideoSurface *source) {
formatSource_ = source;
}
QList<QVideoFrame::PixelFormat> supportedPixelFormats (QAbstractVideoBuffer::HandleType type) const override {
return formatSource_ ? formatSource_->supportedPixelFormats(type)
: QList<QVideoFrame::PixelFormat>();
}
QVideoSurfaceFormat nearestFormat (const QVideoSurfaceFormat &format) const override {
return formatSource_ ? formatSource_->nearestFormat(format)
: QAbstractVideoSurface::nearestFormat(format);
}
bool present (const QVideoFrame &frame) override {
emit videoFrameProbed(frame);
return true;
}
signals:
void videoFrameProbed (const QVideoFrame &frame);
private:
QAbstractVideoSurface *formatSource_;
};
#endif // VIDEOPROBESURFACE_H
I went for the quickest-to-write implementation possible so it just forwards supported pixel formats from another surface (my intent was to both probe and play back to a QVideoWidget) and you get whatever format you get. I just needed to grab subimages into QImages though, which handles most common formats. But you could modify this to force any formats you want (e.g. you might want to just return formats supported by QImage or filter out source formats not supported by QImage), etc.).
Example set up:
QMediaPlayer *player = ...;
QVideoWidget *widget = ...;
// forward surface formats provided by the video widget:
VideoProbeSurface *probe = new VideoProbeSurface(...);
probe->setFormatSource(widget->videoSurface());
// same signal signature as QVideoProbe's signal:
connect(probe, &VideoProbeSurface::videoFrameProbed, ...);
// the key move is to render to both the widget (for viewing)
// and probe (for processing). fortunately, QMediaPlayer can
// take a list:
player->setVideoOutput({ widget->videoSurface(), probe });
Notes
The only really sketchy thing I had to do was const_cast the QVideoFrame on the receiver side (for read-only access), since QVideoFrame::map() isn't const:
if (const_cast<QVideoFrame&>(frame).map(QAbstractVideoBuffer::ReadOnly)) {
...;
const_cast<QVideoFrame&>(frame).unmap();
}
But the real QVideoProbe would make you do the same thing so, I don't know what's up with that -- it's a strange API. I ran some tests with sw, native hw, and copy-back hw renderers and decoders and map/unmap in read mode seem to be functioning OK, so, whatever.
Performance-wise, the video will bog down if you spend too much time in the callback, so design accordingly. However, I didn't test QueuedConnection, so I don't know if that'd still have the issue (although the fact that the signal parameter is a reference would make me wary of trying it, as well as conceivable issues with the GPU releasing the memory before the slot ends up being called). I don't know how QVideoProbe behaves in this regard, either. I do know that, at least on my machine, I can pack and queue Full HD (1920 x 1080) resolution QImages to a thread pool for processing without slowing down the video.
You probably also want to implement some sort of auto-unmapper utility object for exception safe unmap(), etc. But again, that's not unique to this, same thing you'd have to do with QVideoProbe.
So hopefully that helps somebody else.
Example QImage Use
PS, example of packing arbitrarily-formatted QVideoFrames into a QImage in:
void MyVideoProcessor::onFrameProbed(const QVideoFrame &frame) {
if (const_cast<QVideoFrame&>(frame).map(QAbstractVideoBuffer::ReadOnly)) {
auto imageFormat = QVideoFrame::imageFormatFromPixelFormat(frame.pixelFormat());
QImage image(frame.bits(), frame.width(), frame.height(), frame.bytesPerLine(), imageFormat);
// *if* you want to use this elsewhere you must force detach:
image = image.copy();
// but if you don't need to use it past unmap(), you can just
// use the original image instead of a copy.
// <---- now do whatever with the image, e.g. save() it.
// if you *haven't* copied the image, then, before unmapping,
// kill any internal data pointers just to be safe:
image = QImage();
const_cast<QVideoFrame&>(frame).unmap();
}
}
Notes about that:
Constructing a QImage directly from the data is fast and essentially free: no copies are done.
The data buffers are only technically valid between map and unmap, so if you intend to use the QImage outside of that scope, you'll want to use copy() (or anything else that forces a detach) to force a deep copy.
You also probably want to ensure that the original not-copied QImage is destructed before calling unmap. It's unlikely to cause problems but it's always a good idea to minimize how many invalid pointers are hanging around at any given time, and also the QImage docs say "The buffer must remain valid throughout the life of the QImage and all copies that have not been modified or otherwise detached from the original buffer". Best to be strict about it.
I have a QT4 application which wraps libssh2 in order to communicate with the embedded devices on the network. The following snippet is making me crazy for a while.
SSHClient ssh_client(host, 22);
try {
ssh_client.connect_to_host(USERNAME, PASSWORD);
ssh_client.receive_file(file_name, file_path);
} catch(const SSHException &excp) {
qDebug() << "test";
emit console(excp.what());
return;
}
I put breakpoints to qDebug() << "test"; and emit console(excp.what()); in debugger, however, they never get hit even though an SSHException is thrown. If i put a breakpoint to the return; statement, it stops with no problem.
The output is also strange. qDebug() << "test" does what it is supposed to do, but, emit console(excp.what()); is not emitting a signal. And when the control reaches to the return; statement, it returns, again with no problem.
Can anyone help me read the output of qprocess after write and loop until all task is done?
I have this code
wifi->write("scan\n");
wifi->closeWriteChannel();
wifi->waitForBytesWritten(100);
wifi->waitForReadyRead(100);
wifi->waitForFinished(100);
qDebug() << "read output" << wifi->readAllStandardOutput();
wifi->write("scan\n");
wifi->closeWriteChannel();
wifi->waitForBytesWritten(100);
wifi->waitForReadyRead(100);
wifi->waitForFinished(100);
qDebug() << "read output" << wifi->readAllStandardOutput();
the expected output must be
"OK"
"scan results"
but the ouput is
"OK"
""
thanks.
Your multiple waits are not useful for anything. All you care about is when the process finishes, so have a single waitForFinished call with a much longer timeout (those scans don't happen in ~100ms, a few seconds is a good minimum).
You should not be using the blocking waitForXxx methods. They trip up everyone and are a source of unending grief. Forget that they exist. Use process's signals to react to events as they happen.
Qt 5 + C++11
This is the way forward. This is why you should insist on using a modern development environment, if you can. It's less typing and easier to understand.
void MyObject::startWifi() {
auto process = new QProcess(this);
process->start("program", QStringList() << "argument");
connect(process, &QProcess::started, [process]{
process->write("scan\n");
process->closeWriteChannel();
});
connect(process, &QProcess::finished, [process]{
qDebug() << process->readAllStandardOutput();
process->deleteLater();
});
}
Qt 4
class MyObject : public QObject {
Q_OBJECT
QProcess m_wifi;
Q_SLOT void onStarted() {
m_wifi.write("scan\n");
m_wifi.closeWriteChannel();
}
Q_SLOT void onFinished() {
qDebug() << m_wifi.readAllStandardOutput();
}
public:
MyObject(QObject * parent = 0) : QObject(parent) {
connect(&m_wifi, SIGNAL(started()), SLOT(onStarted()));
connect(&m_wifi, SIGNAL(finished(int,QProcess::ExitStatus)),
SLOT(onFinished()));
}
Q_SLOT void start() {
m_wifi.start("program", QStringList() << "argument");
}
};
Then invoke the start method/slot on an instance of this object. That's all.