Given a pointer to a method of a QObject derived class: Is there a way of getting the QMetaMethod of the method the pointer is pointing to? I am basically looking for a function like QMetaMethod::fromSignal, but for slots.
Note:
I tried getting the index through static_metacall with QMetaObject::IndexOfMethod and using that for QMetaObject::method:
void(Class::*method)() = &Class::method;
int methodIndex = -1;
void *metaArgs[] = {&methodIndex, reinterpret_cast<void **>(&method)};
const QMetaObject mo = Class::staticMetaObject;
mo.static_metacall(QMetaObject::IndexOfMethod, 0, metaArgs);
qDebug() << methodIndex;
// QMetaMethod mm = mo.method(methodIndex)
The output is always -1.
At the moment, the only solution is to modify moc. The patch is rather trivial, though:
diff --git a/src/tools/moc/generator.cpp b/src/tools/moc/generator.cpp
index d831edf..7dcefcc 100644
--- a/src/tools/moc/generator.cpp
+++ b/src/tools/moc/generator.cpp
## -1311,15 +1311,12 ## void Generator::generateStaticMetacall()
isUsed_a = true;
}
- }
- if (!cdef->signalList.isEmpty()) {
- Q_ASSERT(needElse); // if there is signal, there was method.
fprintf(out, " else if (_c == QMetaObject::IndexOfMethod) {\n");
fprintf(out, " int *result = reinterpret_cast<int *>(_a[0]);\n");
fprintf(out, " void **func = reinterpret_cast<void **>(_a[1]);\n");
bool anythingUsed = false;
- for (int methodindex = 0; methodindex < cdef->signalList.size(); ++methodindex) {
- const FunctionDef &f = cdef->signalList.at(methodindex);
+ for (int methodindex = 0; methodindex < methodList.size(); ++methodindex) {
+ const FunctionDef &f = methodList.at(methodindex);
if (f.wasCloned || !f.inPrivateClass.isEmpty() || f.isStatic)
continue;
anythingUsed = true;
The following then works as expected:
// https://github.com/KubaO/stackoverflown/tree/master/questions/metamethod-lookup-24577095
#include <QtCore>
class MyObject : public QObject {
Q_OBJECT
public:
Q_SLOT void aSlot() {}
Q_SLOT void aSlot2(int) {}
Q_SLOT int aSlot3(int) { return 0; }
Q_SIGNAL void aSignal();
Q_SIGNAL void aSignal2(int);
};
template <typename Func> int indexOfMethod(Func method)
{
using FuncType = QtPrivate::FunctionPointer<Func>;
int methodIndex = -1;
void *metaArgs[] = {&methodIndex, reinterpret_cast<void **>(&method)};
auto mo = FuncType::Object::staticMetaObject;
mo.static_metacall(QMetaObject::IndexOfMethod, 0, metaArgs);
return methodIndex;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug() << indexOfMethod(&MyObject::aSlot)
<< indexOfMethod(&MyObject::aSlot3) << indexOfMethod(&MyObject::aSignal2);
return 0;
}
#include "main.moc"
Related
When calling QMetaMethod::invoke() on a method that contains default arguments, the invoke fails.
class MyClass : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE MyClass() : QObject(nullptr){}
public slots:
int MyMethod(int a = 0)
{
return a*2;
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MyClass* object = new MyClass();
QMetaObject *metaObject = object->metaObject();
for(int i=metaObject->methodOffset(); i<metaObject->methodCount(); i++)
{
if(metaObject->method(i).name() == "MyMethod")
{
int returnVal;
//returns false
metaObject->method(i).invoke(object,
Qt::DirectConnection,
Q_RETURN_ARG(int, returnVal));
break;
}
}
return a.exec();
}
If I pass an int as the first argument, then it runs fine. Is there any way to retrieve the default values of the arguments for the method so that way I can pass those instead of passing nothing?
I was about to manually store the defaults within the class for each method, but this is an ugly hack.
Thanks for your time.
If you review the generated .moc you see the following:
void MyClass::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
if (_c == QMetaObject::InvokeMetaMethod) {
MyClass *_t = static_cast<MyClass *>(_o);
Q_UNUSED(_t)
switch (_id) {
case 0: { int _r = _t->MyMethod((*reinterpret_cast< int(*)>(_a[1])));
if (_a[0]) *reinterpret_cast< int*>(_a[0]) = std::move(_r); } break;
case 1: { int _r = _t->MyMethod();
if (_a[0]) *reinterpret_cast< int*>(_a[0]) = std::move(_r); } break;
default: ;
}
}
}
As you can see there are 2 methods generated and that can be verified by printing the methods with that name:
#include <QCoreApplication>
#include <QMetaMethod>
#include <QDebug>
class MyClass: public QObject
{
Q_OBJECT
public:
using QObject::QObject;
public slots:
int MyMethod(int a = 0){ return a*2;}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MyClass object;
const QMetaObject *metaObject = object.metaObject();
for(int i=metaObject->methodOffset(); i<metaObject->methodCount(); i++)
{
QMetaMethod method = metaObject->method(i);
if(method.name() == QByteArray("MyMethod"))
qDebug()<<i<<method.name();
};
return 0;
}
#include "main.moc"
Output:
5 "MyMethod"
6 "MyMethod"
So what sets them apart? The number of parameters, so you must add a filter that is the parameterCount().
#include <QCoreApplication>
#include <QMetaMethod>
#include <QDebug>
class MyClass: public QObject
{
Q_OBJECT
public:
using QObject::QObject;
public slots:
int MyMethod(int a = 0){ return a*2;}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MyClass object;
const QMetaObject *metaObject = object.metaObject();
for(int i=metaObject->methodOffset(); i<metaObject->methodCount(); i++)
{
QMetaMethod method = metaObject->method(i);
if(method.name() == QByteArray("MyMethod") && method.parameterCount() == 0)
{
int returnVal;
bool status = method.invoke(&object,
Qt::DirectConnection,
Q_RETURN_ARG(int, returnVal));
Q_ASSERT(status);
qDebug()<<returnVal;
}
};
return 0;
}
#include "main.moc"
Output:
0
On the other hand if you want to avoid this kind of problems you can use QMetaObject::invokeMethod() that makes that verification:
MyClass object;
int returnVal;
bool status = QMetaObject::invokeMethod(&object,
"MyMethod",
Qt::DirectConnection,
Q_RETURN_ARG(int, returnVal));
Q_ASSERT(status);
qDebug()<<returnVal;
I've been trying to figure out how to pass a QStringList pointer around between my member functions. For some reason I'm getting an error that says the following:
mainwindow.cpp:497: error: invalid use of member 'MainWindow::emails' in static member function
// global definition
QStringList* MainWindow::emails;
mainwindow.h
class MainWindow : public QMainWindow{
...
private:
static QStringList * emails;
}
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
size_t MainWindow::WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
{
QString filteredNewLine = plainText.replace("\n"," ");
QRegularExpression re("[a-z0-9]+[_a-z0-9.-]*[a-z0-9]+#[a-z0-9-]+(.[a-z0-9-]+)");
QRegularExpressionMatchIterator i = re.globalMatch(filteredNewLine);
QStringList words;
while (i.hasNext()) {
QRegularExpressionMatch match = i.next();
QString word = match.captured(0);
words << word;
MainWindow::emails = &words;
qDebug() << MainWindow::emails
}
}
MainWindow::~MainWindow()
{
delete emails;
}
You seem to be using curl in your project. There is a C++ binding for curl, but we can make one that leverages Qt.
First of all, let's wrap the global initialization and cleanup in a RAII wrapper:
// https://github.com/KubaO/stackoverflown/tree/master/questions/curl-50975613
#include <QtWidgets>
#include <curl/curl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <cstdio>
#include <limits>
class CurlGlobal {
Q_DISABLE_COPY(CurlGlobal)
CURLcode rc;
public:
CurlGlobal() { rc = curl_global_init(CURL_GLOBAL_ALL); }
~CurlGlobal() { curl_global_cleanup(); }
CURLcode code() const { return rc; }
explicit operator bool() const { return rc == CURLE_OK; }
};
We'll also need a RAII wrapper around curl_slist:
class CurlStringList {
struct curl_slist *list = {};
public:
CurlStringList() = default;
explicit CurlStringList(const QStringList &strings) {
for (auto &s : strings)
list = curl_slist_append(list, s.toLatin1().constData());
}
CurlStringList &operator=(CurlStringList &&o) {
std::swap(o.list, list);
return *this;
}
~CurlStringList() {
curl_slist_free_all(list);
}
struct curl_slist *curl() const { return list; }
};
The "easy" handle, CURL*, can be wrapped in a QObject, to leverage its flexibility. This is where we see how to properly implement callbacks using CURLOPT_xxxDATA, without any global/static variables:
class CurlEasy : public QObject {
Q_OBJECT
friend class CurlMulti;
CURL *const d = curl_easy_init();
QAtomicInteger<bool> inMulti = false;
CURLcode rc = d ? CURLE_OK : (CURLcode)-1;
char err[CURL_ERROR_SIZE];
CurlStringList headers;
bool addToMulti();
void removeFromMulti();
static size_t write_callback(const char *ptr, size_t size, size_t nmemb, void *data) {
Q_ASSERT(data);
return static_cast<CurlEasy*>(data)->writeCallback(ptr, size * nmemb);
}
protected:
virtual qint64 writeCallback(const char *, qint64) {
return -1;
}
void setWriteCallback() {
curl_easy_setopt(d, CURLOPT_WRITEDATA, (void*)this);
curl_easy_setopt(d, CURLOPT_WRITEFUNCTION, (void*)write_callback);
}
virtual void clear() {}
The clear method is used in derived classes to prepare the state before a request gets underway.
The remaining methods wrap and expose the various options, and error handling:
public:
CurlEasy(QObject *parent = {}) : QObject(parent)
{
curl_easy_setopt(d, CURLOPT_ERRORBUFFER, err);
}
~CurlEasy() override {
removeFromMulti();
curl_easy_cleanup(d);
}
CURL *c() const { return d; }
operator CURL*() const { return d; }
bool ok() const { return rc == CURLE_OK; }
QString errorString() const { return QString::fromUtf8(err); }
bool setUrl(const QUrl& url) {
rc = curl_easy_setopt(d, CURLOPT_URL, url.toEncoded().constData());
return ok();
}
void setMaxRedirects(int count) {
curl_easy_setopt(d, CURLOPT_FOLLOWLOCATION, count ? 1L : 0L);
curl_easy_setopt(d, CURLOPT_MAXREDIRS, (long)count);
}
void setVerbose(bool v) {
curl_easy_setopt(d, CURLOPT_VERBOSE, v ? 1L : 0L);
}
bool get() {
if (inMulti.loadAcquire())
return false;
clear();
curl_easy_setopt(d, CURLOPT_HTTPGET, 1L);
if (addToMulti())
return true;
rc = curl_easy_perform(d);
if (!ok())
qDebug() << errorString();
return ok();
}
void setHeaders(const QStringList &h) {
headers = CurlStringList(h);
curl_easy_setopt(d, CURLOPT_HTTPHEADER, headers.curl());
}
void setWriteTo(FILE *);
void setReadFrom(FILE *);
void setWriteTo(QIODevice *dev);
void setReadFrom(QIODevice *dev);
};
The details of setWriteTo and setReadFrom methods can be skipped: they are used to make the CurlEasy object interoperate with C files and Qt I/O devices. See the repository for full code.
Now, we need a line parser: a class that uses the writeCallback to decode individual lines:
class CurlLineParser : public CurlEasy {
Q_OBJECT
QByteArray m_buf;
protected:
qint64 writeCallback(const char *ptr, qint64 count) override {
const char *start = ptr;
const char *const end = ptr + count;
for (; ptr != end; ptr++) {
if (*ptr == '\n') {
if (!m_buf.isEmpty())
m_buf.append(start, ptr-start);
else
m_buf = QByteArray::fromRawData(start, ptr-start);
emit hasLine(QString::fromUtf8(m_buf));
m_buf.clear();
start = ptr + 1;
}
}
// keep partial line
m_buf = QByteArray::fromRawData(start, ptr-start);
return count;
}
void clear() override {
m_buf.clear();
}
public:
CurlLineParser(QObject *parent = {}) : CurlEasy(parent) {
setWriteCallback();
}
Q_SIGNAL void hasLine(const QString &);
};
The application-specific filtering of lines can be implemented by listening to the hasLine signal.
At this point, we have everything needed to run the synchronous (blocking) requests. Let's postpone the examination of the CurlMulti class for a moment, and see how the user interface / demo looks:
class Ui : public QWidget {
Q_OBJECT
CurlLineParser m_curl{this};
QRegularExpression m_re{"[a-z0-9]+[_a-z0-9.-]*[a-z0-9]+#[a-z0-9-]+(.[a-z0-9-]+)"};
QStringList m_emails;
QGridLayout m_layout{this};
QPlainTextEdit m_view;
QLineEdit m_url;
QCheckBox m_async{"Async"};
QPushButton m_get{"Get"};
public:
Ui() {
m_url.setPlaceholderText("Url");
m_view.setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
m_url.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
m_layout.addWidget(&m_view, 0, 0, 1, 3);
m_layout.addWidget(&m_url, 1, 0);
m_layout.addWidget(&m_async, 1, 1);
m_layout.addWidget(&m_get, 1, 2);
connect(&m_get, &QPushButton::clicked, this, [this]{
m_emails.clear();
emit requestGet(m_url.text());
});
connect(&m_async, &QCheckBox::toggled, this, &Ui::requestAsync);
}
Q_SIGNAL void requestGet(const QString &);
Q_SIGNAL void requestAsync(bool);
void setUrl(const QString &url) { m_url.setText(url); }
void setAsync(bool async) { m_async.setChecked(async); }
void processLine(const QString &line) {
auto it = m_re.globalMatch(line);
while (it.hasNext()) {
auto email = it.next().captured(0);
m_emails.push_back(email);
m_view.appendPlainText(email);
}
}
};
The Ui has a method that can process lines from any source, and filter them using the regular expression. This class is completely decoupled from the specifics of curl machinery.
Connections between the curl "stack" and the Ui are done from main:
int main(int argc, char* argv[]) {
CurlGlobal curlGlobal;
QApplication app(argc, argv);
QThread netThread;
CurlMulti multi;
CurlLineParser curl;
multi.moveToThread(&netThread);
Ui ui;
QObject::connect(&ui, &Ui::requestGet, [&](const QString &url){
curl.setUrl(url);
curl.get();
});
QObject::connect(&ui, &Ui::requestAsync, [&](bool async){
QMetaObject::invokeMethod(&curl, [&curl, &multi, async]{
if (async) curl.moveToThread(multi.thread());
curl.setParent(async ? &multi : nullptr);
if (!async) curl.moveToThread(qApp->thread());
});
});
QObject::connect(&curl, &CurlLineParser::hasLine, &ui, &Ui::processLine);
curl.setMaxRedirects(2);
curl.setVerbose(false);
curl.setHeaders({"Accept: text/plain; charset=utf-8"});
ui.setUrl("https://gist.github.com/retronym/f9419787089149ad3a59835c2e1ab81a/"
"raw/8cd88ab3645508ae1243f3aa4ec7c012af4fae7b/emails.txt");
ui.show();
netThread.start();
int rc = app.exec();
QMetaObject::invokeMethod(&multi, [&]{
if (!curl.parent())
curl.moveToThread(app.thread());
multi.moveToThread(app.thread());
netThread.quit();
});
netThread.wait();
return rc;
}
#include "main.moc"
When the asynchronous option is selected, the CurlLineParser object is moved to a worker thread, and made a child of the CurlMulti object. The thread wrangling is necessary only because it's OK to flip-flop the configuration between blocking, same-thread implementation and the async, separate-thread implementation. It is of course also possible to the async, same-thread configuration - simply remove all of the moveToThread calls.
The CurlMulti wraps around CURLM* and uses Qt's socket notifiers to interface curl with the event loop. The per-socket notifiers are stored as the per-socket pointer, using curl_multi_assign.
class CurlMulti : public QObject {
Q_OBJECT
friend class CurlEasy;
struct Notifiers {
QScopedPointer<QSocketNotifier> read, write, exception;
};
CURLM *m = curl_multi_init();
QBasicTimer m_timer;
void timerEvent(QTimerEvent *ev) override {
int running;
if (ev->timerId() == m_timer.timerId()) {
m_timer.stop();
curl_multi_socket_action(m, CURL_SOCKET_TIMEOUT, 0, &running);
processInfo();
}
}
static int socket_callback(CURL *, curl_socket_t s, int what, void *userp, void *socketp) {
Q_ASSERT(userp);
return static_cast<CurlMulti*>(userp)->
socketCallback(s, what, static_cast<Notifiers*>(socketp));
}
static int timer_callback(CURLM *, long ms, void *userp) {
Q_ASSERT(userp);
auto *q = static_cast<CurlMulti*>(userp);
if (ms == -1)
q->m_timer.stop();
else if (ms >= 0)
q->m_timer.start(ms, q);
return 0;
}
int socketCallback(curl_socket_t s, int what, Notifiers* n) {
if (what == CURL_POLL_REMOVE) {
delete n;
curl_multi_assign(m, s, nullptr);
processInfo();
return 0;
}
if (!n) {
n = new Notifiers;
curl_multi_assign(m, s, n);
n->exception.reset(new QSocketNotifier(s, QSocketNotifier::Exception, this));
connect(&*n->exception, &QSocketNotifier::activated, [this, s]{
int running;
curl_multi_socket_action(m, s, CURL_CSELECT_ERR, &running);
});
}
if ((what & CURL_POLL_IN) && !n->read) {
n->read.reset(new QSocketNotifier(s, QSocketNotifier::Read, this));
connect(&*n->read, &QSocketNotifier::activated, [this, s]{
int running;
curl_multi_socket_action(m, s, CURL_CSELECT_IN, &running);
});
}
if ((what & CURL_POLL_OUT) && !n->write) {
n->write.reset(new QSocketNotifier(s, QSocketNotifier::Write, this));
connect(&*n->write, &QSocketNotifier::activated, [this, s]{
int running;
curl_multi_socket_action(m, s, CURL_CSELECT_OUT, &running);
});
}
n->exception->setEnabled(what & CURL_POLL_INOUT);
if (n->read)
n->read->setEnabled(what & CURL_POLL_IN);
if (n->write)
n->write->setEnabled(what & CURL_POLL_OUT);
processInfo();
return 0;
}
void processInfo() {
int msgq;
while (const auto *info = curl_multi_info_read(m, &msgq)) {
if (info->msg == CURLMSG_DONE)
for (auto *c : children())
if (auto *b = qobject_cast<CurlEasy*>(c))
if (b->d == info->easy_handle && b->inMulti)
b->removeFromMulti();
}
}
public:
CurlMulti(QObject *parent = {}) : QObject(parent) {
curl_multi_setopt(m, CURLMOPT_SOCKETDATA, (void*)this);
curl_multi_setopt(m, CURLMOPT_SOCKETFUNCTION, (void*)socket_callback);
curl_multi_setopt(m, CURLMOPT_TIMERDATA, (void*)this);
curl_multi_setopt(m, CURLMOPT_TIMERFUNCTION, (void*)timer_callback);
int running;
curl_multi_socket_action(m, CURL_SOCKET_TIMEOUT, 0, &running);
}
Q_SIGNAL void finished(CurlEasy *);
~CurlMulti() override {
for (auto *c : children())
if (auto *b = qobject_cast<CurlEasy*>(c))
b->removeFromMulti();
curl_multi_cleanup(m);
}
};
Finally, the two separately-defined internal methods of CurlEasy that handle its additions and removals from the multi object:
bool CurlEasy::addToMulti() {
auto *m = qobject_cast<CurlMulti*>(parent());
if (d && m && inMulti.testAndSetOrdered(false, true)) {
QMetaObject::invokeMethod(m, [m, this]{
curl_multi_add_handle(m->m, d);
});
}
return inMulti;
}
void CurlEasy::removeFromMulti() {
auto *m = qobject_cast<CurlMulti*>(parent());
if (d && m && inMulti.testAndSetOrdered(true, false)) {
curl_multi_remove_handle(m->m, d);
emit m->finished(this);
}
}
See also:
https://github.com/tarasvb/qtcurl
https://github.com/pavlonion/qtcurl
I have to manage movie and audio file and I need to render a wave of the sound like in audacity. But I just find example for realtime render.
I want to render all the file without playing it.
Expected result:
My actual result:
With Qt I tried to use QAudioDecoder to open my file and get a QAudioBuffer but I don't find an algorithm to transform all the data into wave. I also try to see with the Qt Spectrum Example but it's not trivial to understand and it's still in realtime.
My track.h:
#ifndef TRACK_H
#define TRACK_H
#include <QWidget>
#include <QAudioBuffer>
class QAudioDecoder;
class Track : public QWidget
{
Q_OBJECT
public:
Track(QWidget *parent = Q_NULLPTR);
~Track();
void setSource(const QString &fileName);
public slots:
void setBuffer();
protected:
void paintEvent(QPaintEvent *e) override;
private:
int pointDistance(const QPoint& a, const QPoint& b);
QAudioDecoder *decoder;
QAudioBuffer buffer;
QByteArray byteArr;
};
#endif // TRACK_H
My track.cpp:
#include "track.h"
#include <QPaintEvent>
#include <QPainter>
#include <QAudioDecoder>
Track::Track(QWidget *parent)
: QWidget(parent)
, decoder(new QAudioDecoder(this))
{
setMinimumHeight(50);
connect(decoder, SIGNAL(bufferReady()), this, SLOT(setBuffer()));
connect(decoder, SIGNAL(finished()), this, SLOT(update()));
}
Track::~Track()
{
delete decoder;
}
void Track::setSource(const QString &fileName)
{
byteArr.clear();
decoder->setSourceFilename(fileName);
decoder->start();
}
void Track::setBuffer()
{
buffer = decoder->read();
byteArr.append(buffer.constData<char>(), buffer.byteCount());
}
void Track::paintEvent(QPaintEvent *e)
{
QWidget::paintEvent(e);
int w = width(), h = height();
QBrush backgroundBrush(Qt::white);
QPainter painter(this);
painter.fillRect(0, 0, w, h, backgroundBrush);
painter.drawLine(0, h/2, w, h/2);
if (!byteArr.isEmpty()){
QPen pen(QColor(Qt::blue));
painter.setPen(pen);
int length = byteArr.size();
int samplesPerPixel = length/w;
int idx=0;
for (int i=0; i<w; i++){
QLine line;
int higher = 0;
for (int j=0; j<samplesPerPixel && idx+1<length; j++){
const QPoint a(i, byteArr.at(idx)+(h/2));
const QPoint b(i, byteArr.at(idx+1)+(h/2));
if (higher < pointDistance(a, b))
line = QLine(a, b);
idx++;
}
painter.drawLine(line);
}
}
}
int Track::pointDistance(const QPoint &a, const QPoint &b)
{
int ret = 0;
ret = sqrt(pow(b.x()-a.x(), 2) + pow(b.y()-a.y(), 2));
return ret;
}
I finally found a solution by using QCustomPlot widget (by reading this post):
My result:
My track.h:
#ifndef TRACK_H
#define TRACK_H
#include "qcustomplot.h"
#include <QAudioBuffer>
class QAudioDecoder;
class Track : public QCustomPlot
{
Q_OBJECT
public:
Track(TrackType type, QWidget *parent = Q_NULLPTR);
~Track();
void setSource(const QString &fileName);
public slots:
void setBuffer();
void plot();
private:
qreal getPeakValue(const QAudioFormat& format);
QAudioDecoder *decoder;
QAudioBuffer buffer;
QVector<double> samples;
QCPGraph *wavePlot;
};
#endif // TRACK_H
My track.cpp:
#include "track.h"
#include <QAudioDecoder>
Track::Track(Track::TrackType type, QWidget *parent)
: QCustomPlot(parent)
, decoder(new QAudioDecoder(this))
{
this->type = type;
wavePlot = addGraph();
setMinimumHeight(50);
connect(decoder, SIGNAL(bufferReady()), this, SLOT(setBuffer()));
connect(decoder, SIGNAL(finished()), this, SLOT(plot()));
}
Track::~Track()
{
delete decoder;
// wavePlot delete auto ?
}
void Track::setSource(const QString &fileName)
{
samples.clear();
decoder->setSourceFilename(fileName);
decoder->start();
}
void Track::setBuffer()
{
buffer = decoder->read();
qreal peak = getPeakValue(buffer.format());
const qint16 *data = buffer.constData<qint16>();
int count = buffer.sampleCount() / 2;
for (int i=0; i<count; i++){
double val = data[i]/peak;
samples.append(val);
}
}
void Track::plot()
{
QVector<double> x(samples.size());
for (int i=0; i<x.size(); i++)
x[i] = i;
wavePlot->addData(x, samples);
yAxis->setRange(QCPRange(-1, 1));
xAxis->setRange(QCPRange(0, samples.size()));
replot();
}
/**
* https://stackoverflow.com/questions/46947668/draw-waveform-from-raw-data-using-qaudioprobe
* #brief Track::getPeakValue
* #param format
* #return The peak value
*/
qreal Track::getPeakValue(const QAudioFormat &format)
{
qreal ret(0);
if (format.isValid()){
switch (format.sampleType()) {
case QAudioFormat::Unknown:
break;
case QAudioFormat::Float:
if (format.sampleSize() != 32) // other sample formats are not supported
ret = 0;
else
ret = 1.00003;
break;
case QAudioFormat::SignedInt:
if (format.sampleSize() == 32)
#ifdef Q_OS_WIN
ret = INT_MAX;
#endif
#ifdef Q_OS_UNIX
ret = SHRT_MAX;
#endif
else if (format.sampleSize() == 16)
ret = SHRT_MAX;
else if (format.sampleSize() == 8)
ret = CHAR_MAX;
break;
case QAudioFormat::UnSignedInt:
if (format.sampleSize() == 32)
ret = UINT_MAX;
else if (format.sampleSize() == 16)
ret = USHRT_MAX;
else if (format.sampleSize() == 8)
ret = UCHAR_MAX;
break;
default:
break;
}
}
return ret;
}
I get this error message:
C:\Users\Rick\Documents\Qt-Übung\testVisualizer\main.cpp:12: error: passing 'const ChartData' as 'this' argument of 'int ChartData::getHour()' discards qualifiers [-fpermissive]
qDebug() << myVis.getChartDataVector().at(0).getHour();
^
for testing purposes I remove all const. But this error remains.
With all getter as const methodes I get over 60 errors for each calling of a setter.
I dont know where it come from.
main.cpp
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Visualize myVis;
myVis.handleDataFromString("C:\\Users\\Rick\\Google Drive\\FH Sachen\\5. Semester\\EMS\\ricks code\\maitime-20151026.json");
qDebug() << myVis.getChartDataVector().at(0).getHour();
/*
for(int i = 0; i < myVis.getChartDataVector().size(); i++){
qDebug() << "--------Whitelist--------";
for(int j = 0; j < myVis.getChartDataVector().at(i).getWhitelist().size(); j++){
qDebug() << "Stunde: " << myVis.getChartDataVector().at(i).getHour() << "Name: " << myVis.getChartDataVector().at(i).getWhitelist().at(j).getName() << "Zeit: " << myVis.getChartDataVector().at(i).getWhitelist().at(j).getTotalTime();
}
qDebug() << "--------Blacklist--------";
for(int j = 0; myVis.getChartDataVector().at(i).getBlacklist().size(); j++){
qDebug() << "Stunde: " << myVis.getChartDataVector().at(i).getHour() << "Name: " << myVis.getChartDataVector().at(i).getBlacklist().at(j).getName() << "Zeit: " << myVis.getChartDataVector().at(i).getBlacklist().at(j).getTotalTime();
}
}
*/
return a.exec();
}
visualize.h
#ifndef VISUALIZE_H
#define VISUALIZE_H
#include <QObject>
#include <QVector>
#include "applicationdata.h"
#include "chartdata.h"
class Visualize
{
QVector<ChartData> _chartDataObjects;
int _lastChangeChart, _lastChangeApp, _chartRef, _appRef;
bool _lastChange;
public:
Visualize();
QVector<ChartData> &getChartDataVector();
void handleDataFromString(QString filepath);
private:
void findPosition(QString name, bool onWhitelist, int hour, int &chartRef, int &appRef, QString url = "");
signals:
public slots:
};
#endif // VISUALIZE_H
visualize.cpp
#include "visualize.h"
#include "applicationdata.h"
#include "chartdata.h"
#include <QFile>
#include <QJsonDocument>
#include <QJsonObject>
#include <QDebug>
long ApplicationData::_totalTimeAll(0);
Visualize::Visualize()
{
}
QVector<ChartData> &Visualize::getChartDataVector()
{
return _chartDataObjects;
}
void Visualize::handleDataFromString(QString filepath)
{
QString line;
QFile file(filepath);
QJsonDocument d;
QJsonObject o;
ChartData tempChart;
ApplicationData tempApp;
QString tempName, tempUrl, tempTimeString;
bool tempOnWhitelist;
QTime tempTime, fullHour;
int tempHour;
_lastChangeChart = -1, _lastChangeApp = -1, _chartRef = -1, _appRef = -1;
file.open(QIODevice::ReadOnly | QIODevice::Text);
if(!file.exists()){
qDebug() << "Error read File!";
}
while(!file.atEnd()){
line = file.readLine();
d = QJsonDocument::fromJson(line.toUtf8());
o = d.object();
tempName = o.value(QString("process")).toString();
tempUrl = o.value(QString("url")).toString();
tempTimeString = o.value(QString("time")).toString();
tempOnWhitelist = o.value(QString("whitelisted")).toBool();
tempTime = QTime::fromString(tempTimeString, "hh:mm:ss");
tempHour = tempTime.hour();
//existing object is searched in vector
findPosition(tempName, tempOnWhitelist, tempHour, _chartRef, _appRef, tempUrl);
//if instance with actual hour not exist
if(_chartRef == -1){
tempChart.setHour(tempHour);
_chartDataObjects.append(tempChart);
_chartRef = _chartDataObjects.size()-1;
//_lastChangeChart = _chartRef;
}
//if an old object exist
if(_lastChangeApp != -1){
if(_chartDataObjects[_lastChangeChart].getHour() == tempHour){
if(_lastChange){
_chartDataObjects[_lastChangeChart].getWhitelist().at(_lastChangeApp).setEndTime(tempTime);
_chartDataObjects[_lastChangeChart].getWhitelist().at(_lastChangeApp).calcTimes();
} else {
_chartDataObjects[_lastChangeChart].getBlacklist().at(_lastChangeApp).setEndTime(tempTime);
_chartDataObjects[_lastChangeChart].getBlacklist().at(_lastChangeApp).calcTimes();
}
} else {
if(_lastChange){
fullHour.setHMS(tempHour, 0, 0);
_chartDataObjects[_lastChangeChart].getWhitelist().at(_lastChangeApp).setEndTime(fullHour);
_chartDataObjects[_lastChangeChart].getWhitelist().at(_lastChangeApp).calcTimes();
tempApp.setName(_chartDataObjects[_lastChangeChart].getWhitelist().at(_lastChangeApp).getName());
tempApp.setUrl(_chartDataObjects[_lastChangeChart].getWhitelist().at(_lastChangeApp).getUrl());
tempApp.setStartTime(fullHour);
tempApp.setEndTime(tempTime);
_chartDataObjects[_chartRef].getWhitelist().append(tempApp);
_chartDataObjects[_chartRef].getWhitelist().at(_chartDataObjects[_chartRef].getWhitelist().size()-1).calcTimes();
} else {
fullHour.setHMS(tempHour, 0, 0);
_chartDataObjects[_lastChangeChart].getBlacklist().at(_lastChangeApp).setEndTime(fullHour);
_chartDataObjects[_lastChangeChart].getBlacklist().at(_lastChangeApp).calcTimes();
tempApp.setName(_chartDataObjects[_lastChangeChart].getBlacklist().at(_lastChangeApp).getName());
tempApp.setUrl(_chartDataObjects[_lastChangeChart].getBlacklist().at(_lastChangeApp).getUrl());
tempApp.setStartTime(fullHour);
tempApp.setEndTime(tempTime);
_chartDataObjects[_chartRef].getBlacklist().append(tempApp);
_chartDataObjects[_chartRef].getBlacklist().at(_chartDataObjects[_chartRef].getBlacklist().size()-1).calcTimes();
}
}
}
if(tempName == "stopped"){
_lastChangeApp = -1;
_lastChangeChart = -1;
qDebug() << "Log gestoppet";
} else if(_appRef != -1){
if(tempOnWhitelist){
_chartDataObjects[_chartRef].getWhitelist().at(_appRef).setStartTime(tempTime);
_lastChangeApp = _appRef;
} else {
_chartDataObjects[_chartRef].getBlacklist().at(_appRef).setStartTime(tempTime);
_lastChangeApp = _appRef;
}
} else {
tempApp.setName(tempName);
tempApp.setStartTime(tempTime);
tempApp.setUrl(tempUrl);
if(tempOnWhitelist){
_chartDataObjects[_chartRef].getWhitelist().append(tempApp);
_lastChangeApp = _chartDataObjects[_chartRef].getWhitelist().size()-1;
_lastChange = true;
} else {
_chartDataObjects[_chartRef].getBlacklist().append(tempApp);
_lastChangeApp = _chartDataObjects[_chartRef].getBlacklist().size()-1;
_lastChange = false;
}
}
}
file.close();
}
void Visualize::findPosition(QString name, bool onWhitelist, int hour, int &chartRef, int &appRef, QString url)
{
if(onWhitelist){
for(int i = 0; i < _chartDataObjects.size(); i++){
if(hour == _chartDataObjects[i].getHour()){
for(int j = 0; j < _chartDataObjects[i].getWhitelist().size(); j++){
if(name == _chartDataObjects[i].getWhitelist().at(j).getName() && url == _chartDataObjects[i].getWhitelist().at(j).getUrl()){
chartRef = i;
appRef = j;
} else {
chartRef = i;
appRef = -1;
}
}
} else {
chartRef = -1;
appRef = -1;
}
}
} else {
for(int i = 0; i < _chartDataObjects.size(); i++){
if(hour == _chartDataObjects[i].getHour()){
for(int j = 0; j < _chartDataObjects[i].getBlacklist().size(); j++){
if(name == _chartDataObjects[i].getBlacklist().at(j).getName() && url == _chartDataObjects[i].getBlacklist().at(j).getUrl()){
chartRef = i;
appRef = j;
} else {
chartRef = i;
appRef = -1;
}
}
} else {
chartRef = -1;
appRef = -1;
}
}
}
}
chartdata.h
#ifndef CHARTDATA_H
#define CHARTDATA_H
#include "applicationdata.h"
class ChartData
{
int _hour;
QVector<ApplicationData> _whitelist;
QVector<ApplicationData> _blacklist;
public:
ChartData();
void setHour(int hour);
int getHour();
QVector<ApplicationData> &getWhitelist();
QVector<ApplicationData> &getBlacklist();
signals:
public slots:
};
#endif // CHARTDATA_H
chartdata.cpp
#include "chartdata.h"
ChartData::ChartData()
{
}
void ChartData::setHour(int hour)
{
_hour = hour;
}
int ChartData::getHour()
{
return _hour;
}
QVector<ApplicationData> &ChartData::getWhitelist()
{
return _whitelist;
}
QVector<ApplicationData> &ChartData::getBlacklist()
{
return _blacklist;
}
chartdata.cpp
#include "chartdata.h"
ChartData::ChartData()
{
}
void ChartData::setHour(int hour)
{
_hour = hour;
}
int ChartData::getHour()
{
return _hour;
}
QVector<ApplicationData> &ChartData::getWhitelist()
{
return _whitelist;
}
QVector<ApplicationData> &ChartData::getBlacklist()
{
return _blacklist;
}
applicationdata.h
#ifndef APPLICATIONDATA_H
#define APPLICATIONDATA_H
#include <QObject>
#include <QString>
#include <QTime>
#include <QDebug>
#include <QVector>
class ApplicationData
{
QString _name, _url;
QTime _startTime, _endTime;
long _totalTime;
static long _totalTimeAll;
public:
ApplicationData(QString name = "", QString url = ""):
_name(name), _url(url), _startTime(0,0), _endTime(0,0), _totalTime(0){}
//Setter
void setName(QString name);
void setUrl(QString url);
void setStartTime(QTime start);
void setEndTime(QTime end);
void setTotalTime(long totalTime);
//Getter
QString getName();
QString getUrl();
QTime getStartTime();
QTime getEndTime();
long getTotalTime();
static long getTotalTimeAll();
//Methodes
void calcTimes();
signals:
public slots:
};
#endif // APPLICATIONDATA_H
applicationdata.cpp
#include "applicationdata.h"
//Setter-------------------------------------------------------------------------
void ApplicationData::setName(QString name)
{
_name = name;
}
void ApplicationData::setUrl(QString url)
{
_url = url;
}
void ApplicationData::setStartTime(QTime start)
{
_startTime = start;
}
void ApplicationData::setEndTime(QTime end)
{
_endTime = end;
}
void ApplicationData::setTotalTime(long totalTime)
{
_totalTime = totalTime;
}
//Getter--------------------------------------------------------------------------
QString ApplicationData::getName()
{
return _name;
}
QString ApplicationData::getUrl()
{
return _url;
}
QTime ApplicationData::getStartTime()
{
return _startTime;
}
QTime ApplicationData::getEndTime()
{
return _endTime;
}
long ApplicationData::getTotalTime()
{
return _totalTime;
}
long ApplicationData::getTotalTimeAll()
{
return _totalTimeAll;
}
//Methoden--------------------------------------------------------------------------
void ApplicationData::calcTimes()
{
long tempTime;
tempTime = _startTime.secsTo(_endTime);
_totalTime += tempTime;
_totalTimeAll += tempTime;
}
Having your ChartData::getData() prototype changed like this should fix it:
int getHour() const;
You need that because at returns a const reference and you're calling a non-const method on it.
I get a Solution. I cant work with the at() methode from the QVector class. Now i use [].
Example:
_chartDataObjects[_lastChangeChart].getWhitelist()[_lastChangeApp].calcTimes();
In Firefox/Chrome/InternetExplorer/Safari/Opera pop-ups from the combobox expand as the content, see Firefox picture:
QComboBox pop-up does not expand the content. Pop-ups are limited by the size of QComboBox, see QWebView picture:
So I implemented the QComboBox::showPopup:
void newQComboBox::showPopup() {
int width = this->width();
this->view()->setTextElideMode( Qt::ElideNone );
const int iconSize = this->iconSize().width();
const QFontMetrics fontMetrics = this->fontMetrics();
const int j = this->count();
for( int i=0; i < j; ++i ) {
const int textWidth = fontMetrics.width( this->itemText(i) + "WWW" );
if (this->itemIcon(i).isNull()) {
width = qMax(width, textWidth);
} else {
width = qMax(width, textWidth + iconSize);
}
}
QStyleOptionComboBox opt;
this->initStyleOption(&opt);
QSize size(width, 0);
size = this->style()->sizeFromContents(QStyle::CT_ComboBox, &opt, size, this);
this->view()->setFixedWidth( width );
QComboBox::showPopup();
}
Is there any way to modify (reimplement) the QComboBox::showPopup of QWebViews (QtWebkit)?
Qt-BUG (suggestion): https://bugreports.qt.io/browse/QTBUG-35771
I solved using QProxyStyle class, example:
#include <QProxyStyle>
#include <QAbstractItemView>
#include <QComboBox>
class myProxyStyle : public QProxyStyle
{
public:
int styleHint(StyleHint hint, const QStyleOption *option, const QWidget *widget, QStyleHintReturn *returnData) const
{
if(hint == QStyle::SH_ComboBox_Popup) {
const QComboBox *combo = (QComboBox *) widget;//cast to QComboBox
const QObjectList a = combo->children();
const int j = a.count();
QAbstractItemView *view = 0;
QString className = "";
bool hasView = false;
/*
at this point I have not used combo->view() because he "crash" the application without explanation
so I had to make a loop to find the "view"
*/
for (int i = 0; i < j; ++i) {
const QObjectList b = a.at(i)->children();
const int y = b.count();
for (int x = 0; x < y; ++x) {
className = b.at(x)->metaObject()->className();
if (className == "QComboBoxListView") {
view = (QAbstractItemView *) b.at(x);
hasView = true;
break;
}
}
if (hasView) {
break;
}
}
if (hasView) {
const int iconSize = combo->iconSize().width();
const QFontMetrics fontMetrics1 = view->fontMetrics();
const QFontMetrics fontMetrics2 = combo->fontMetrics();
const int j = combo->count();
int width = combo->width(); //default width
for (int i = 0; i < j; ++i) {
const int textWidth = qMax(
fontMetrics1.width(combo->itemText(i) + "WW"),
fontMetrics2.width(combo->itemText(i) + "WW")
);
if(combo->itemIcon(i).isNull()) {
width = qMax(width, textWidth);
} else {
width = qMax(width, textWidth + iconSize);
}
}
view->setFixedWidth(width);
}
}
return QProxyStyle::styleHint(hint, option, widget, returnData);
}
};
how to use:
You must apply in QApplication (usually file main.cpp):
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
a.setStyle(new myProxyStyle);//set ProxyStyle
return a.exec();
}
#peppe thanks to your comment I got the solution
Please try to make use of the setMinimumWidth function and pass the max width you have calculated. it should work
ui.comboBox->view()->setMinimumWidth(width);
for more detail please go through https://qt-project.org/forums/viewthread/26388 link