qt run as Administrator drop is invalid why? - qt

qt run as Administrator drop is invalid,
I used the win32 api ChangeWindowMessageFilterEx in qt but is not work,
I used the win32 api ChangeWindowMessageFilterEx in MFC is work ok.
Can not receive WM_DROPFILES message in qt when i run as Administrator.
This is in qt
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
DropTest w;
w.show();
CHANGEFILTERSTRUCT chfit1 = { sizeof(CHANGEFILTERSTRUCT) };
CHANGEFILTERSTRUCT chfit2 = { sizeof(CHANGEFILTERSTRUCT) };
CHANGEFILTERSTRUCT chfit3 = { sizeof(CHANGEFILTERSTRUCT) };
HWND hwnd = (HWND)w.winId();;
DragAcceptFiles(hwnd, TRUE);
DropTest::ChangeWndMessageFilterOk(hwnd, WM_DROPFILES, MSGFLT_ALLOW, &chfit1);
DropTest::ChangeWndMessageFilterOk(hwnd, WM_COPYDATA, MSGFLT_ALLOW, &chfit2);
DropTest::ChangeWndMessageFilterOk(hwnd, 0x0049, MSGFLT_ALLOW, &chfit3); // 0x0049 == WM_COPYGLOBALDATA
//MyXcbEventFilter nativeEventFilterZ;
//a.installNativeEventFilter(&nativeEventFilterZ);
return a.exec();
}
BOOL DropTest::ChangeWndMessageFilterOk(HWND hWnd, UINT nMessage, DWORD dwAction, PCHANGEFILTERSTRUCT chfit)
{
//typedef BOOL (WINAPI * ChangeWindowMessageFilterOkFn)(__in HWND hWnd, __in UINT message, __in DWORD action, __inout_opt PCHANGEFILTERSTRUCT pChangeFilterStruct);
typedef BOOL (WINAPI *ChangeWindowMessageFilterOkFn)(HWND, UINT, DWORD, PCHANGEFILTERSTRUCT);
HMODULE hModUser32 = NULL;
hModUser32 = LoadLibrary(L"user32.dll");
if (hModUser32 == NULL) {
return FALSE;
}
ChangeWindowMessageFilterOkFn pfnChangeWindowMessageFilter = (ChangeWindowMessageFilterOkFn) GetProcAddress(hModUser32, "ChangeWindowMessageFilterEx");
if (pfnChangeWindowMessageFilter == NULL)
{
FreeLibrary(hModUser32);
return FALSE;
}
FreeLibrary(hModUser32);
BOOL ret = pfnChangeWindowMessageFilter(hWnd, nMessage, dwAction, chfit);
QString strOut = QString("%1 ret = [%2] ExtStatus=[%3]\n").arg(__FUNCTION__, QString::number(ret), QString::number(chfit->ExtStatus));
OutputDebugString(strOut.toStdWString().c_str());
return ret;
}

I find the reseason,
use RevokeDragDrop is ok!
This is the qt source
void QWindowsWindow::setDropSiteEnabled(bool dropEnabled)
{
if (isDropSiteEnabled() == dropEnabled)
return;
qCDebug(lcQpaMime) << __FUNCTION__ << window() << dropEnabled;
#if !defined(QT_NO_CLIPBOARD) && !defined(QT_NO_DRAGANDDROP)
if (dropEnabled) {
Q_ASSERT(m_data.hwnd);
m_dropTarget = new QWindowsOleDropTarget(window());
RegisterDragDrop(m_data.hwnd, m_dropTarget);
CoLockObjectExternal(m_dropTarget, true, true);
} else {
CoLockObjectExternal(m_dropTarget, false, true);
m_dropTarget->Release();
RevokeDragDrop(m_data.hwnd);
m_dropTarget = 0;
}
#endif // !QT_NO_CLIPBOARD && !QT_NO_DRAGANDDROP
}

I tried changewindowmessagefilter, is invalid, and I also didn't find any abnormal from qt source, currently only need administrator privileges function alone to make a program, other I haven't found a better solution

Related

Testing Qt application with Qt Test

I have looked at the 5 Qt testing examples including the one about GUI events, but these examples are way too simple.
I want to test my program by launching it, simulating some clicks, and checking the value of instance variables that have been changed by those clicks.
I assume that this test below is illogical: a.exec() blocks the thread until the program is closed, and when the program is closed w has been deleted I think (or will be deleted later?).
So how to write system/GUI tests?
My test:
void LaunchProgramTest::LaunchProgramTestFunction() {
QApplication a(argc, argv);
MainWindow *w = new MainWindow();
w->show();
a.exec();
int testResult = w->myTestFunction();
qDebug() << testResult; //Prints big numbers like "-17891602" or "1753770528" as if testResult was not initialized
QVERIFY2(testResult == 3, "Incorrectly changed");
}
In mainWindow.h I declared a variable:
int testValue;
Mainwindow.cpp is the class for the main GUI of the program. In the constructor I added
testValue = 2;
Then in a function that is executed upon events I wrote
void MainWindow::on_actionTest_clicked() {
testValue = 3;
}
enter code hereSo)) you need to add QTest, add .pro
QT += testlib
and
#include <QTest>
I will show an example of my implementation for MousePress, the rest you can do yourself))
struct EventMK
{
int type;
QString m_widPath;
int _timer;
int width;
int height;
QPoint p;
QPoint g;
int button;
int buttons;
int modifiers;
int _key;
QString text;
void print(){
qDebug()<<"{ \n"
<<"type "<< type<<","<<"\n"
<<"Widget_Path "<< m_widPath<<","<<"\n"
<<"Timer "<< _timer<<","<<"\n"
<<"Width "<< width<<","<<"\n"
<<"Height "<< height<<","<<"\n"
<<"Pos_x "<< p.x()<<","<<"\n"
<<"Pos_y "<< p.y()<<","<<"\n"
<<"Global_x "<< g.x()<<","<<"\n"
<<"Global_y "<< g.y()<<","<<"\n"
<<"Button "<< button<<","<<"\n"
<<"Buttons "<< buttons<<","<<"\n"
<<"Modifiers "<< modifiers<<","<<"\n"
<<"Key "<< _key<<","<<"\n"
<<"Text "<< text<<"\n"
<<"}\n";
}
};
QWidget * _getWidget(EventMK ev)
{
QString wname = ev.m_widPath;
QStringList wpath = wname.split ( "/" );
return QWidgetUtils::getAWidget(&wpath);
}
void _postExecution(EventMK ev, QWidget *widget)
{
if (widget){
//set focus
QWidgetUtils::setFocusOnWidget(widget);
//end simulation
widget->setUpdatesEnabled ( true );
widget->update();
}
}
QPoint adaptedPosition(EventMK ev, QWidget *w)
{
if (w == nullptr)
return QPoint(ev.p.x(), ev.p.y());
int orig_w = ev.width;
int orig_h = ev.height;
int curr_w = w->width();
int curr_h = w->height();
int new_x = ev.p.x() * curr_w / orig_w;
int new_y = ev.p.y() * curr_h / orig_h;
return QPoint(new_x, new_y);
}
and function implementation
void executeMousePressEvent(EventMK ev)
{
QWidget* widget = _getWidget(ev);
if ( widget == nullptr )
{
qDebug()<<"error: "<<__LINE__<<__FUNCTION__;
return;
}
// _preExecutionWithMouseMove(ev, widget);
if (widget){
QTest::mousePress ( widget, (Qt::MouseButton)ev.button ,
(Qt::KeyboardModifier) ev.modifiers,
adaptedPosition(ev,widget));
}
_postExecution(ev, widget);
}
now left to fill struct EventMK , you need to populate it from MouseButtonPress events.
Here is my example
bool eventFilter(QObject *obj, QEvent *event)
{
///
/// process control
///
//window events
if (event->type() == QEvent::KeyPress)
{
handleKeyPressEvent(obj, event);
}
//mouse events
else if (event->type() == QEvent::MouseButtonPress)
{
handleMousePressEvent(obj, event);
}
else if (event->type() == QEvent::MouseButtonRelease)
{
handleMouseReleaseEvent(obj, event);
}
else if (event->type() == QEvent::MouseButtonDblClick)
{
handleMouseDoubleEvent(obj, event);
}
else if (event->type() == QEvent::Wheel)
{
handleWheelEvent(obj, event);
}
//keyboard events
else if (event->type() == QEvent::Close)
{
handleCloseEvent(obj, event);
}
///the event should continue to reach its goal...
return false;
}
and
void handleMousePressEvent(QObject *obj, QEvent *event)
{
QWidget *widget = isValidWidget(obj);
if (!widget){
return;
}
QMouseEvent *me = dynamic_cast< QMouseEvent*> ( event );
//create the event
if (widget != nullptr){
EventMK evkm;
evkm.type = QOE_MOUSE_PRESS; // set your type
evkm._timer = _timer.restart(); // add your QElapsedTimer
evkm.m_widPath = QWidgetUtils::getWidgetPath(widget);
evkm. width = widget->width();
evkm. height = widget->height();
QPoint p ( me->pos() );
QPoint g = widget->mapToGlobal ( p );
evkm. p = p;
evkm. g = g;
evkm. button = me->button();
evkm. buttons = me->buttons();
evkm. modifiers = me->modifiers();
evkm.print();
}
//send event if EventMK is valid
}
so, it turns out we can write a scenario and run what you wanted, thanks

Where is sync() of linux is defined. Data writting to QSettings is not persisted once the application is closed

I am working in Ubuntu and want to store data in QSettings. The during the run, data is persistent, once the application restarts the data is no longer read. I am reading that an additional sync call of linux is required, but I am unable to find where this is defined.
During a run of program, I have successfully written the data to QSettings object and read the value from stored QSettings object.
I have a user class which can read and write from/to QJSonObject
class User
{
public:
User() {}
User(const QString& ruser_name, const QImage& rdisplay_image)
{
user_name = ruser_name;
display_image = rdisplay_image;
}
QString get_user_name() const { return user_name; }
void write(QJsonObject &json) const
{
json["user_name"] = user_name;
json["display_image"] = getImageInJsonValueFormat(display_image);
}
void read(const QJsonObject &json)
{
if (json.contains("user_name") && json["user_name"].isString())
user_name = json["user_name"].toString();
if (json.contains("display_image"))
display_image = getImageFromJsonValueFormat(json["display_image"]);
}
private:
QString user_name;
QImage display_image;
};
There are two global functions to work with image and Json
QJsonValue getImageInJsonValueFormat(const QImage& image)
{
QByteArray image_ByteArray;
if (image.isNull() != true)
{
QBuffer image_buffer(&image_ByteArray);
image_buffer.open(QIODevice::WriteOnly);
image.save(&image_buffer, "png");
auto const encoded = image_buffer.data().toBase64();
return {QLatin1String(encoded)};
}
return QLatin1String("");
}
QImage getImageFromJsonValueFormat(const QJsonValue& imageJsonValue)
{
QImage image;
auto const encoded = imageJsonValue.toString().toLatin1();
image.loadFromData(QByteArray::fromBase64(encoded), "png");
return image;
}
Also, three functions, but read the list from QSettings, to create default list and to store list to QSettings.
QMap<QString, User> Build_user_list()
{
QMap<QString, User> user_list;
User User1("baur",QImage("/home/Downloads/bechambaur.png"));
user_list["baur"] = User1;
User User2("Muller",QImage("/home/Downloads/GerdMuller.png"));
user_list["Muller"] = User2;
return user_list;
}
void StoreUserList(const QMap<QString, User> user_list)
{
QJsonObject userListObject;
QJsonArray userListArray;
QMapIterator<QString,User> iterator(user_list);
while (iterator.hasNext())
{
iterator.next();
QJsonObject userJsonObject;
User userObj = iterator.value();
userObj.write(userJsonObject);
userListArray.append(userJsonObject);
}
userListObject["user_list"] = userListArray;
QJsonDocument userListDoc(userListObject);
QSettings user_settings;
user_settings.setValue("QSettingsApplication", userListDoc);
user_settings.sync();
}
QMap<QString, User> GetStoredList()
{
QMap<QString, User> user_list;
QSettings user_settings;
QJsonDocument userListDocRead = user_settings.value("QSettingsApplication", "").toJsonDocument();
QJsonObject userListObjectRead = userListDocRead.object();//["userlist"];
if (userListObjectRead.contains("user_list"))
{
if(userListObjectRead["user_list"].isArray())
{
QJsonArray userlistarray = userListObjectRead["user_list"].toArray();
user_list.clear();
for(int index = 0; index < userlistarray.size(); ++index)
{
QJsonObject json_user_object = userlistarray[index].toObject();
User userObject;
userObject.read(json_user_object);
user_list[userObject.get_user_name()] = userObject;
}
}
}
return user_list;
}
With this application ran for the subsequent times is not reading the value from QSettings which it reads successfully during the run
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// Why below list is empty after first successful run?
QMap<QString, User> user_listRead = GetStoredList();
if (user_listRead.size() == 0)
{
QMap<QString, User> user_list = Build_user_list();
StoreUserList(user_list);
QMap<QString, User> user_listRead = GetStoredList();
user_listRead = GetStoredList();
// This is is not empty, it reads contents successfully. What's wrong?
}
MainWindow w;
w.show();
return a.exec();
}
I expect data to be persistent. Where it is going wrong?
Entire file contents are
#include "mainwindow.h"
#include <QApplication>
#include <QString>
#include <QImage>
#include <QJsonObject>
#include <QBuffer>
#include <QSettings>
#include <QJsonArray>
#include <QJsonDocument>
QJsonValue getImageInJsonValueFormat(const QImage& image)
{
QByteArray image_ByteArray;
if (image.isNull() != true)
{
QBuffer image_buffer(&image_ByteArray);
image_buffer.open(QIODevice::WriteOnly);
image.save(&image_buffer, "png");
auto const encoded = image_buffer.data().toBase64();
return {QLatin1String(encoded)};
}
return QLatin1String("");
}
QImage getImageFromJsonValueFormat(const QJsonValue& imageJsonValue)
{
QImage image;
auto const encoded = imageJsonValue.toString().toLatin1();
image.loadFromData(QByteArray::fromBase64(encoded), "png");
return image;
}
class User
{
public:
User() {}
User(const QString& ruser_name, const QImage& rdisplay_image)
{
user_name = ruser_name;
display_image = rdisplay_image;
}
QString get_user_name() const { return user_name; }
void write(QJsonObject &json) const
{
json["user_name"] = user_name;
json["display_image"] = getImageInJsonValueFormat(display_image);
}
void read(const QJsonObject &json)
{
if (json.contains("user_name") && json["user_name"].isString())
user_name = json["user_name"].toString();
if (json.contains("display_image"))
display_image = getImageFromJsonValueFormat(json["display_image"]);
}
private:
QString user_name;
QImage display_image;
};
QMap<QString, User> Build_user_list()
{
QMap<QString, User> user_list;
User User1("baur",QImage("/home/Downloads/bechambaur.png"));
user_list["baur"] = User1;
User User2("Muller",QImage("/home/Downloads/GerdMuller.png"));
user_list["Muller"] = User2;
return user_list;
}
void StoreUserList(const QMap<QString, User> user_list)
{
QJsonObject userListObject;
QJsonArray userListArray;
QMapIterator<QString,User> iterator(user_list);
while (iterator.hasNext())
{
iterator.next();
QJsonObject userJsonObject;
User userObj = iterator.value();
userObj.write(userJsonObject);
userListArray.append(userJsonObject);
}
userListObject["user_list"] = userListArray;
QJsonDocument userListDoc(userListObject);
QSettings user_settings("userlist.conf", QSettings::NativeFormat);//(QSettings::IniFormat,QSettings::UserScope,"A","B");
user_settings.setValue("QSettingsApplication", userListDoc.toVariant());
user_settings.sync();
}
QMap<QString, User> GetStoredList()
{
QMap<QString, User> user_list;
QSettings user_settings("userlist.conf", QSettings::NativeFormat);
QVariant qvarval = user_settings.value("QSettingsApplication");
QJsonDocument userListDocRead = user_settings.value("QSettingsApplication").toJsonDocument();
QJsonObject userListObjectRead = userListDocRead.object();//["userlist"];
if (userListObjectRead.contains("user_list"))
{
if(userListObjectRead["user_list"].isArray())
{
QJsonArray userlistarray = userListObjectRead["user_list"].toArray();
user_list.clear();
for(int index = 0; index < userlistarray.size(); ++index)
{
QJsonObject json_user_object = userlistarray[index].toObject();
User userObject;
userObject.read(json_user_object);
user_list[userObject.get_user_name()] = userObject;
}
}
}
return user_list;
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QMap<QString, User> user_listreRead;
// Why below list is empty after first successful run?
QMap<QString, User> user_listRead = GetStoredList();
if (user_listRead.size() == 0)
{
QMap<QString, User> user_list = Build_user_list();
StoreUserList(user_list);
user_listreRead = GetStoredList();
// This is is not empty, it reads contents successfully. What's wrong?
}
MainWindow w;
w.show();
return a.exec();
}
Contents of userlist.conf are
[General]
QSettingsApplication=#Variant(\0\0\0\b\0\0\0\x1\0\0\0\x12\0u\0s\0\x65\0r\0_\0l\0i\0s\0t\0\0\0\t\0\0\0\x2\0\0\0\b\0\0\0\x2\0\0\0\x12\0u\0s\0\x65\0r\0_\0n\0\x61\0m\0\x65\0\0\0\n\0\0\0\f\0M\0u\0l\0l\0\x65\0r\0\0\0\x1a\0\x64\0i\0s\0p\0l\0\x61\0y\0_\0i\0m\0\x61\0g\0\x65\0\0\0\n\0\0\0\0\0\0\0\b\0\0\0\x2\0\0\0\x12\0u\0s\0\x65\0r\0_\0n\0\x61\0m\0\x65\0\0\0\n\0\0\0\b\0\x62\0\x61\0u\0r\0\0\0\x1a\0\x64\0i\0s\0p\0l\0\x61\0y\0_\0i\0m\0\x61\0g\0\x65\0\0\0\n\0\0\0\0)

How to use the callback function in vc++ dll to respond to the click event in the widget UI dll based on Qt?

When a click event happens on the Qt based UI dll, how to call the processing function in a VC++ .dll or .exe? (qtwinmigrate` is used in my project). In short, When pushButton is clicked how to call function elementsExampleCreateShapeActive().
This is my QT dll code.
//define the callback function pointer
typedef wchar_t const* WCharCP;
typedef void(CALLBACK *FunCallBack)(WCharCP);
//define the callback function
FunCallBack OnEvent = NULL;
//define the callback function var
int par;
WCharCP unparsed;
extern "C" __declspec(dllexport) void SetFunCallBack(FunCallBack fun, WCharCP var)
{
OnEvent =fun;
unparsed = var;
}
void CDialog::on_pushButton_clicked()
{
OnEvent(unparsed);
}
And Below is the code for pulgin dll in VC++.
typedef void (CALLBACK *FunCallBack)(WCharCP);
void CALLBACK handleEvent(WCharCP unparsed)
{
elementsExampleCreateShapeActive(unparsed);
}
void attachLibrary()
{
char* dllName = "qtdialog.dll";
HMODULE hDLL = LoadLibrary(dllName);
if (hDLL != NULL)
{
typedef int(*pMain)(int, char *[]);
pMain dialog2 = pMain(GetProcAddress(hDLL, "main"));
if (dialog2 != NULL)
{
dialog2(0, 0);
}
typedef void (CALLBACK *PFunCallBack)(FunCallBack);
PFunCallBack SetFunCallBack = (PFunCallBack)GetProcAddress(hDLL, "SetFunCallBack");
if (SetFunCallBack)
{
SetFunCallBack(handleEvent);
}
FreeLibrary(hDLL);
}
else
{
cout << "Cannot find" << dllName << endl;
}
}

QAudioOutput buffer underflow

Getting a message "Got a buffer underflow!" after each write in this simple program.
Beep.hpp:
#pragma once
#include <QTimer>
#include <QAudioOutput>
class Beep: public QObject
{
Q_OBJECT
public:
explicit Beep();
virtual ~Beep();
void onTimer();
private:
QAudioOutput m_out;
QIODevice *m_outDev;
QTimer m_timer;
};
Beep.cpp:
#include "Beep.hpp"
int ms = 100;
const QAudioFormat defaultAudioFormat = []()
{
QAudioFormat format;
format.setSampleRate(8000);
format.setChannelCount(1);
format.setSampleSize(16);
format.setCodec("audio/pcm");
format.setByteOrder(QAudioFormat::LittleEndian);
format.setSampleType(QAudioFormat::SignedInt);
return format;
}();
Beep::Beep() :
m_out(defaultAudioFormat),
m_outDev()
{
m_out.setBufferSize(16 * ms);
m_outDev = m_out.start();
QObject::connect(&m_timer, &QTimer::timeout, this, &Beep::onTimer);
m_timer.setSingleShot(false);
m_timer.start(ms);
}
Beep::~Beep()
{
}
void Beep::onTimer()
{
std::vector<uint8_t> samples(16 * ms);
m_outDev->write((char*) &samples.front(), samples.size());
}
main.cpp:
#include <QCoreApplication>
#include "Beep.hpp"
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
Beep beep;
return app.exec();
}
This test program is just writing buffers with zeros. With real data there are cracking sounds.
Writing more data or changing timings makes it worse. What's wrong with this code?
Using a Timer is the wrong way to do it.
Use the notify() signal
void AudioManager::init_audio(AudioManager *mgr) {
if (mgr->stream_id == -1) return;
mgr->audio_format.setSampleRate(mgr->context->time_base.den);
mgr->audio_format.setSampleSize(16);
mgr->audio_format.setChannelCount(2);
mgr->audio_format.setCodec("audio/pcm");
mgr->audio_format.setSampleType(QAudioFormat::SignedInt);
QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());
if (!info.isFormatSupported(mgr->audio_format)) {
mgr->audio_format = info.nearestFormat(mgr->audio_format);
}
mgr->audio_out = new QAudioOutput(mgr->audio_format, nullptr);
mgr->audio_out->setNotifyInterval(15);
mgr->audio_out->setBufferSize(mgr->context->time_base.den * 4); // 1 second worth of stereo data
connect(mgr->audio_out, SIGNAL(notify()), mgr, SLOT(audio_out_notify()));
connect(mgr->audio_out, SIGNAL(stateChanged(QAudio::State)), mgr, SLOT(audio_out_state_changed(QAudio::State)));
qreal volume_out = (qreal)parent->volume / 100.0f;
mgr->audio_out->setVolume(volume_out);
mgr->audio_out_device = mgr->audio_out->start();
}
This will be called when the audio playback requires more data
void AudioManager::audio_out_notify() {
qDebug() << "Audio notify";
check_audio_playback();
}
Most of the below code will be irrelevant but it is also called is audio has stopped playing.
void AudioManager::check_audio_playback() {
if (stream_id == -1) return;
pthread_mutex_lock(&audio_mutex);
if (!audio_out->state() == QAudio::State::IdleState) {
pthread_mutex_unlock(&audio_mutex);
return;
}
if (parent->pts_start_time < 0.0) {
if (parent->Video.stream_id == -1 && decode_pos > 65) { // start playback
parent->pts_start_time = buffers[0].frame_time;
parent->sys_start_time = (double)parent->timer.elapsed() / 1000.0;
qDebug() << "Audio playback started";
} else {
pthread_mutex_unlock(&audio_mutex);
return;
}
}
if (playback_pos == decode_pos) {
pthread_mutex_unlock(&audio_mutex);
return;
}
AudioBuffer *buffer = nullptr;
double current_sys_time = ((double)parent->timer.elapsed() / 1000.0) - parent->sys_start_time;
bool bounds = false;
int skipped = 0;
while (!bounds) {
if (playback_pos == decode_pos) bounds = true;
else {
AudioBuffer *temp_buffer = &buffers[playback_pos];
double temp_time = temp_buffer->frame_time - parent->pts_start_time;
if (temp_time < current_sys_time ) {
if (buffer) {
buffer->used = false;
skipped++;
}
buffer = temp_buffer;
playback_pos++; playback_pos %= MAX_AUD_BUFFERS;
} else {
bounds = true;
}
}
}
if (skipped > 0) qDebug("Skipped %d audio buffers on playback", skipped);
if (buffer) {
audio_out_device->write((const char *)buffer->data, buffer->buffer_size);
buffer->used = false;
}
pthread_mutex_unlock(&audio_mutex);
}
The example on the Qt website wasn't that obvious http://qt.apidoc.info/5.1.1/qtmultimedia/audiooutput.html at first but when I put it in to test it wasn't too bad.
The reason was that the source of audio data wasn't a "production-quality module" (it's a dummy testing class): the timer was drifting because its real interval was 10ms plus the processing time.
Other observations:
make QAudioOutput::setBufferSize() bigger
do QAudioInput::read() and QAudioOutput::write() in chunks with size that matches QAudioInput::periodSize() and QAudioOutput::periodSize()

Qt: QSqlDatabase object in class (how to declare?)

I am trying to create a class which should handle all the data from and to a sqlite database. However, I am pretty new to QT and C++ and wondering about the declaration of the database object in the class. I could need some tips on what I am doing right and wrong and how it normally should or could be done. My goal was, to create a single QSqlDatabase for the class and use it for every function within the class.
At the moment, I have the following code:
main.cpp
#include "mainwindow.h"
#include "database.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Database db;
MainWindow w;
if(db.createStructure())
{
w.show();
}
return a.exec();
}
database.h
#ifndef DATABASE_H
#define DATABASE_H
#include <QObject>
#include <QSqlDatabase>
class Database : public QObject
{
Q_OBJECT
public:
explicit Database(QObject *parent = 0);
// FUNCTIONS
bool createStructure();
signals:
public slots:
private:
// VARIABLES
QSqlDatabase m_db;
// FUNCTIONS
bool open();
void close();
bool transaction();
bool commit();
};
#endif // DATABASE_H
database.cpp
#include "database.h"
#include <QCoreApplication>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
#include <QList>
Database::Database(QObject *parent) :
QObject(parent)
{
m_db = QSqlDatabase::addDatabase("QSQLITE");
m_db.setHostName("localhost");
m_db.setDatabaseName(QCoreApplication::applicationDirPath() + "/events.db");
}
// PRIVATE
bool Database::open()
{
return m_db.open();
}
void Database::close()
{
return m_db.close();
}
bool Database::transaction()
{
return m_db.transaction();
}
bool Database::commit()
{
return m_db.commit();
}
// PUBLIC
bool Database::createStructure()
{
bool prepared;
QList<QString> commands;
commands.append("CREATE TABLE...;");
commands.append("CREATE TABLE...;");
commands.append("CREATE TABLE...;");
if (!Database::open())
{
return false;
}
else
{
if (!Database::transaction())
{
Database::close();
return false;
}
else
{
foreach(QString command, commands)
{
QSqlQuery query;
prepared = query.prepare(command);
if(!prepared)
{
if (!Database::commit())
{
Database::close();
return false;
}
else
{
Database::close();
return false;
}
}
else
{
if(!query.exec())
{
if (!Database::commit())
{
Database::close();
return false;
}
else
{
Database::close();
return false;
}
}
}
}
if (!Database::commit())
{
Database::close();
return false;
}
else
{
Database::close();
return true;
}
}
}
}
This code is working.
However, the QSQLITE database is not added a single time to the m_db object, but every time a function in the class is called, because the...
Database::Database(QObject *parent) :
QObject(parent)
{
m_db = QSqlDatabase::addDatabase("QSQLITE");
m_db.setHostName("localhost");
m_db.setDatabaseName(QCoreApplication::applicationDirPath() + "/events.db");
}
...codeblock is executed every time.
The current default connection is just replaced and since the new one is the same, that doesn’t have any effect on the program, but it doesn’t look like a neat solution.
So I tried to replace this codeblock with a declare-function I can call from main.cpp once...
main.cpp
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Database db;
MainWindow w;
db.declare(“QSQLITE”, “localhost”, QCoreApplication::applicationDirPath() + "/events.db");
if(db.createStructure())
{
w.show();
}
return a.exec();
}
database.cpp
void Database::declare(QString driver, QString host, QString path)
{
m_db = QSqlDatabase::addDatabase(driver);
m_db.setHostName(host);
m_db.setDatabaseName(path);
}
...but the values for the m_db object are of course only available within the declare-function and not for the other functions I call afterwards.
My best guess for a solution would be to declare the QSqlDatabase in main.cpp and give it to the function it should call:
main.cpp
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QSqlDatabase qdb = QSqlDatabase::addDatabase("QSQLITE");
qdb.setHostName("localhost");
qdb.setDatabaseName(QCoreApplication::applicationDirPath() + "/events.db");
Database db;
MainWindow w;
if(db.createStructure(qdb))
{
w.show();
}
return a.exec();
}
database.cpp
bool Database::open(QSqlDatabase qdb)
{
return qdb.open();
}
void Database::close(QSqlDatabase qdb)
{
return qdb.close();
}
bool Database::transaction(QSqlDatabase qdb)
{
return qdb.transaction();
}
bool Database::commit(QSqlDatabase qdb)
{
return qdb.commit();
}
bool Database::createStructure(QSqlDatabase qdb)
{
bool prepared;
QList<QString> commands;
commands.append("CREATE TABLE...;");
commands.append("CREATE TABLE...;");
commands.append("CREATE TABLE...;");
if (!Database::open(qdb))
{
return false;
}
else
{
if (!Database::transaction(qdb))
{
Database::close(qdb);
return false;
}
else
{
foreach(QString command, commands)
{
QSqlQuery query;
prepared = query.prepare(command);
if(!prepared)
{
if (!Database::commit(qdb))
{
Database::close(qdb);
return false;
}
else
{
Database::close(qdb);
return false;
}
}
else
{
if(!query.exec())
{
if (!Database::commit(qdb))
{
Database::close(qdb);
return false;
}
else
{
Database::close(qdb);
return false;
}
}
}
}
if (!Database::commit(qdb))
{
Database::close(qdb);
return false;
}
else
{
Database::close(qdb);
return true;
}
}
}
}
Is it possible to somehow store a reusable QSqlDatabase object in a class? If so, how?
Really appreciate your help!
EDIT 1
Some code created from the designer I am using a function in.
mainwindows.cpp
void MainWindow::on_pushButton_24_clicked()
{
Database db;
bool b = db.createStructure();
QMessageBox::information(this, "test", QString(b));
}
I'll stick to your original code for the explanations.
Disclaimer: I didn't compile any of my suggestions, forgive me if there are syntax errors.
First of all, what you are probably looking for is the Singleton Pattern (which I don't really like that much anymore, but for your purpose one could argue that it can be considered appropriate):
You have to have the following in your class definition:
class Database : public QObject
{
Q_OBJECT
public:
static Database* instance();
private:
static Database* m_instance;
Database();
~Database() {}; // it can be necessary to have this public in some cases, if
// you ever get a linker error related to deletion, this is
// probably the reason.
public:
// FUNCTIONS
...
};
And the following in your .cpp file:
// init singleton pointer to NULL
Database* Database::m_instance = NULL;
Database* Database::instance()
{
if( !m_instance )
{
m_instance = new Database();
}
return m_instance;
}
You can then access that singleton using e.g.
if( Database::instance()->createStructure() )
{
w.show();
}
What does this do? At the start of the program, the line
Database* Database::m_instance = NULL;
initialises your m_instance variable to NULL. The first time you call Database::instance() it realizes that m_instance is still NULL and creates a new object and makes m_instance point to that object. From that point on, the pointer to that object will always be returned, but no more Database object will be created.
In your createStructure() function you commit() your database even when there is an error. The usual procedure is to commit() upon success and rollback() upon failure.
Before fixing that, be sure to read the next point though:
The third thing I would recommend is getting used to being suspicious whenever you see multiple occurrences of the same lines a lot. That usually cries for a sub function.
I'm talking about
Database::close();
return false;
Take a look at how I rewrote your createStructure() method by introducing another method and leaving out else{ } where it was not necessary:
bool Database::createStructure()
{
QStringList commands;
commands.append("CREATE TABLE...;");
commands.append("CREATE TABLE...;");
commands.append("CREATE TABLE...;");
if (!Database::open()) return false;
// at this point you can be sure the database is open
if (!Database::transaction())
{
Database::close();
return false;
}
// at this point you can be sure the database is open and a transaction was started
if (!Database::executeCommands(commands))
{
// an error occurred - we need to rollback what we did so far
Database::rollback();
Database::close();
return false;
}
// everything was executed properly, but the transaction is still active
// => commit the changes we've made
bool committed = Database::commit();
// no matter if the commit was successful or not, close the database,
// then return the result we've stored
Database::close();
return committed;
}
bool Database::executeCommands(const QStringList& commands)
{
// This method simply executes the queries and is relieved from
// transaction-related code.
foreach(QString command, commands)
{
QSqlQuery query;
bool prepared = query.prepare(command);
if(!prepared) return false;
if(!query.exec()) return false;
}
return true;
}
This could be further refactored, it is just an example of making your code easier to follow and thus usually less error-prone.

Resources