QtSql: Capturing Event Notification for Changes in the SQLite DataBase - qt

I have a SQLite database. I am trying to capture events on changes in the database in a Qt program that uses QtSql module, e.g., when a new record is inserted in a table from outside the Qt program. QSqlDatabase class offers a function to subscribe to the notifications, however there is no example in the Qt framework that illustrates how to setup and capture the events. I adopted (after some search on Internet) the following approach, but it is not working and I could not find why.
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName("file.db");
if (!db.isOpen()) {
qFatal("Failed to open database");
}
if (db.driver()->hasFeature(QSqlDriver::EventNotifications)) {
db.driver()->subscribeToNotification("IenState"); //"IenState" is table name
QObject::connect(db.driver(), SIGNAL(notification(const QString &)),
this, SLOT(procEvents(const QString &)));
}
else {
qFatal("Driver does NOT support database event notifications");
}
//slot function
void DbSqlite::procEvents(const QString &name)
{
qWarning("%s: %s", __func__, qPrintable(name));
if (name.compare("IenState", Qt::CaseSensitive) == 0) {
qWarning("%s: from IenState table", __func__);
}
}
The procEvents slot function is never get called. It looks like the notification signal is never fired from the db.driver. The above code is a snippet from the program based on the Qt Widget Application.

Related

Close previous slot if still running

I got a searchfield that triggers a database search.
connect(searchField,SIGNAL(textChanged(QString)),this,SLOT(searchSong(QString)));
But the database search takes longer then typing the next search condition.
SLOTS are queued but I don't want this. When a new signal is send the previous SLOT needs to be cancelled and restarted.
How can I do this?
You are going the wrong way. What you have to do is create a flag that indicates if there is a pending request, and if there is when searchSong is called then cancel it and launch a new request:
*.h
private:
bool is_busy;
*.cpp
// constructor
FooClass::FooClass(foo_paramenters):
BaseClass(args), is_busy(false)
{
// some code
connect(searchField, &QLineEdit::textChanged, this, &FooClass::searchSong);
}
void FooClass::searchSong(const QString & text){
if(is_busy)
cancel_request();
is_busy = true;
send_request(text);
}
void FooClass::receive_answer_from_request(){
// some code
is_busy = false;
}

JavaFx Turn-based Multiplayer Application with Google Service

Now I am using Gluon plugin and it is very helpful to start working with JavaFXPorts. I have my application ready and I can use it on computers and phones. I tested on phone with system android. Application is working good but only with my server.
I take care of the different resolutions and I think that it is good for now.
I want to have Turn-based Multiplayer Application but I have still big problem with using Google Play Service. Tutorial which show how to use this services for turn-based multiplayer application is written in pure android and use Activity. My question is maybe very simple but If I have my application view from "fxml" how to use it as an tutorial Activity?
I want to do auto-matching for my application and next I want override method takeTurn() to suit it to my application.
For example, how can I change thing like that (code below) to application in JavaFX?
I must use google services from my JavaFX(src/main/java folder) class in addition AndroidPlatformProvider.java and all methods must be in src/android/java folder. I know that I must use PlatformService and PlatformProvider. I did it as in the examples: HelloPlatform and SMSTracker .
I use methods from my interface PlatformProvider but application still crashes. :(
I only use Provider from my code of JavaFX and I don't have android Activity. I don't know how to use these method without Activity or View:
- public void onActivityResult(int request, int response, Intent data)
- public void playTurn(View view)
Can I call from google service methods to methods from my controller for view (fxml).
I don't know how these methods should working with JavaFX.
public class TbmpGameActivity extends Activity {
...
#Override
public void onActivityResult(int request, int response, Intent data) {
super.onActivityResult(request, response, data);
...
if (request == RC_SELECT_PLAYERS) {
if (response != Activity.RESULT_OK) {
// user canceled
return;
}
// Get the invitee list.
final ArrayList<String> invitees =
data.getStringArrayListExtra(Games.EXTRA_PLAYER_IDS);
// Get auto-match criteria.
Bundle autoMatchCriteria = null;
int minAutoMatchPlayers = data.getIntExtra(
Multiplayer.EXTRA_MIN_AUTOMATCH_PLAYERS, 0);
int maxAutoMatchPlayers = data.getIntExtra(
Multiplayer.EXTRA_MAX_AUTOMATCH_PLAYERS, 0);
if (minAutoMatchPlayers > 0) {
autoMatchCriteria = RoomConfig.createAutoMatchCriteria(
minAutoMatchPlayers, maxAutoMatchPlayers, 0);
} else {
autoMatchCriteria = null;
}
TurnBasedMatchConfig tbmc = TurnBasedMatchConfig.builder()
.addInvitedPlayers(invitees)
.setAutoMatchCriteria(autoMatchCriteria)
.build();
// Create and start the match.
Games.TurnBasedMultiplayer
.createMatch(mGoogleApiClient, tbmc)
.setResultCallback(new MatchInitiatedCallback());
}
}
}
or something like that:
// Call this method when a player has completed his turn and wants to
// go onto the next player, which may be himself.
public void playTurn(View view) {
// Get the next participant in the game-defined way, possibly round-robin.
String nextParticipantId = getNextParticipantId();
// Get the updated state. In this example, we simply retrieve a
// text string from the view. In your game, there may be more
// complicated state.
mTurnData = mDataView.getText().toString();
// At this point, you might want to show a waiting dialog so that
// the current player does not try to submit turn actions twice.
showSpinner();
// Invoke the next turn. We are converting our data to a byte array.
Games.TurnBasedMultiplayer
.takeTurn(mGoogleApiClient, mMatch.getMatchId(),
mTurnData.getBytes(Charset.forName("UTF-16")),
nextParticipantId)
.setResultCallback(this);
}
Latest version of the jfxmobile plugin (1.0.3) includes recent changes in Dalvik SDK that contains the port of JavaFX 8 to android.
In FXActivity class, it has been added a very convenient method to listen to results of intents: setOnActivityResultHandler().
This means you shouldn't add new activities to your app, and only use FXActivity. You should add an Intent to it, and set a proper result handler to the FXActivity.
Have a look at this recent post at Gluon's site. It explains how to access native services, based on the case of taking a picture with the device's camera, and waiting to get the result of it to resume the app.
Basically, this is what it is required in this case:
public void takePicture() {
// create intent
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
...
// Add result handler
FXActivity.getInstance().setOnActivityResultHandler((requestCode, resultCode, data) -> {
if (requestCode == TAKE_PICTURE && resultCode == RESULT_OK) {
...
}
});
// launch activity
FXActivity.getInstance().startActivityForResult(intent, TAKE_PICTURE);
}
Try this approach with your app.

How to close database in Cursorloader

I'm currently working on ListViews in Fragments. The Listviews are loaded by Cursorloader, but without ContentManager. So the code looks like this and it works:
#Override
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
Log.d("SoapERP", "onCreateLoader");
CursorLoader loader = new CursorLoader(getActivity()) {
final DBHelper dbhelper1= new DBHelper(getActivity());
#Override
public Cursor loadInBackground() {
Cursor c = null;
dbhelper1.open();
c = dbhelper1.fetchAllMatnameswithID();
// dbhelper1.close();
return c;
}
};
return loader;
My problem is that I get an LogCat-Error-Message that the database wasn't closed. But if I use dbhelper.close(); I get the Error "Database is already closed" wich is also understandable because it is just before the return statement. After the return statement code is not reachable and if I declare DBHelper dbhelper1 final the program crashes without any information in logcat. So what is my fail???
Finally I found here the right statement as of Dianne Hackborn from android framework development: "A content provider is created when its hosting process is created, and remains around for as long as the process does, so there is no need to close the database -- it will get closed as part of the kernel cleaning up the process's resources when the process is killed. Dianne Hackborn Android framework engineer hack...#android.com " - so let's use Content Provider.

Detecting missing resources in QT Stylesheets

I am using QCSS stylesheets in QT to skin several buttons with images from the QT resource system:
QFrame#DialogButtonTitle_SaveAsNew
{
background-image: url(images:DialogButtonTitle_SaveAsNew.png);
}
This works great, but I would really like to write a warning to our logs if the image file referenced from the CSS could not be found (and the button is thus naked). Any way to catch such errors?
I believe you can do it like this:
Read about the QAbstractFileEngine and QAbstractFileEngineHandler classes.
Extend Qt's own implementation, QFSFileEngine. I believe it also handles the ":" namespace.
Reimplement the QAbstractFileEngine::open() method.
Register your engine using a custom QAbstractFileEngineHandler. The create() method should check the file name to see if it is being read from a resource file.
Haven't tested, but I think it should work. Code:
bool MyEngine::open(QIODevice::OpenMode mode)
{
bool r = QFSFileEngine::open(mode);
if (!r) {
qWarning() << "Failed to open" << fileName();
}
return r;
}
QAbstractFileEngine *MyEngineHandler::create(const QString &fileName) const
{
return fileName.startsWith("images:") ? new MyEngine(fileName) : 0;
}
Edit.
This will not work. The resource file system, “:”, is handled by a private file engine called QResourceFileEngine, not by QFSFileEngine.
Based on #andref answer, I came up with this, which works for me (TM):
class LoggingEngineHandler : public QAbstractFileEngineHandler
{
public:
LoggingEngineHandler()
: QAbstractFileEngineHandler()
, m_lookUpInProgress(false)
, m_lookUpPaths(QRegExp("^(images|meshes|app|sounds):"))
{
// empty
}
QAbstractFileEngine* create(const QString &fileName) const override
{
if (!fileName.contains(m_lookUpPaths))
return 0;
if (m_lookUpInProgress)
return 0;
m_lookUpInProgress = true;
QFileInfo info = QFileInfo(fileName);
m_lookUpInProgress = false;
if (!info.exists())
{
assert(!Utilities::isRunByUser("designer"));
LOG_WARN("Required resource file does not exist: %1%", QUtil_s(fileName));
}
return 0;
}
protected:
mutable bool m_lookUpInProgress;
QRegExp m_lookUpPaths;
};
It's possible that Qt will call one of their message functions when something like this happens (although I don't know for sure). If it does, you could install a message handler function and append some or all of the messages to your log file. There is some information about doing so in the documentation for qInstallMsgHandler.

Blocking a Qt application during downloading a short file

I'm writing an application using Qt4.
I need to download a very short text file from a given http address.
The file is short and is needed for my app to be able to continue, so I would like to make sure the download is blocking (or will timeout after a few seconds if the file in not found/not available).
I wanted to use QHttp::get(), but this is a non-blocking method.
I thought I could use a thread : my app would start it, and wait for it to finish. The thread would handle the download and quit when the file is downloaded or after a timeout.
But I cannot make it work :
class JSHttpGetterThread : public QThread
{
Q_OBJECT
public:
JSHttpGetterThread(QObject* pParent = NULL);
~JSHttpGetterThread();
virtual void run()
{
m_pHttp = new QHttp(this);
connect(m_pHttp, SIGNAL(requestFinished(int, bool)), this, SLOT(onRequestFinished(int, bool)));
m_pHttp->setHost("127.0.0.1");
m_pHttp->get("Foo.txt", &m_GetBuffer);
exec();
}
const QString& getDownloadedFileContent() const
{
return m_DownloadedFileContent;
}
private:
QHttp* m_pHttp;
QBuffer m_GetBuffer;
QString m_DownloadedFileContent;
private slots:
void onRequestFinished(int Id, bool Error)
{
m_DownloadedFileContent = "";
m_DownloadedFileContent.append(m_GetBuffer.buffer());
}
};
In the method creating the thread to initiate the download, here is what I'm doing :
JSHttpGetterThread* pGetter = new JSHttpGetterThread(this);
pGetter->start();
pGetter->wait();
But that doesn't work and my app keeps waiting. It looks lit the slot 'onRequestFinished' is never called.
Any idea ?
Is there a better way to do what I'm trying to do ?
Instead of using a thread you can just go into a loop which calls processEvents:
while (notFinished) {
qApp->processEvents(QEventLoop::WaitForMore | QEventLoop::ExcludeUserInput);
}
Where notFinished is a flag which can be set from the onRequestFinished slot.
The ExcludeUserInput will ensure that GUI related events are ignored while waiting.
A little late but:
Do not use these wait loops, the correct way is to use the done() signal from QHttp.
The requestFinished signal from what I have seen is just for when your application has finished the request, the data may still be on its way down.
You do not need a new thread, just setup the qhttp:
httpGetFile= new QHttp();
connect(httpGetFile, SIGNAL(done(bool)), this, SLOT(processHttpGetFile(bool)));
Also do not forget to flush the file in processHttpGetFile as it might not all be on the disk.
you have to call QThread::quit() or exit() if you are done - otherwise your thread will run forever...
I chose to implement David's solution, which seemed to be the easiest.
However, I had handle a few more things :
I had to adapt the QEventLoop enum values for Qt4.3.3 (the version I'm using);
I had to track the request Id, to make sure to exit the while loop when the download request is finished, and not when another request is finished;
I added a timeout, to make sure to exit the while loop if there is any problem.
Here is the result as (more or less) pseudo-code :
class BlockingDownloader : public QObject
{
Q_OBJECT
public:
BlockingDownloaderBlockingDownloader()
{
m_pHttp = new QHttp(this);
connect(m_pHttp, SIGNAL(requestFinished(int, bool)), this, SLOT(onRequestFinished(int, bool)));
}
~BlockingDownloader()
{
delete m_pHttp;
}
QString getFileContent()
{
m_pHttp->setHost("www.xxx.com");
m_DownloadId = m_pHttp->get("/myfile.txt", &m_GetBuffer);
QTimer::singleShot(m_TimeOutTime, this, SLOT(onTimeOut()));
while (!m_FileIsDownloaded)
{
qApp->processEvents(QEventLoop::WaitForMoreEvents | QEventLoop::ExcludeUserInputEvents);
}
return m_DownloadedFileContent;
}
private slots:
void BlockingDownloader::onRequestFinished(int Id, bool Error)
{
if (Id == m_DownloadId)
{
m_DownloadedFileContent = "";
m_DownloadedFileContent.append(m_GetBuffer.buffer());
m_FileIsDownloaded = true;
}
}
void BlockingDownloader::onTimeOut()
{
m_FileIsDownloaded = true;
}
private:
QHttp* m_pHttp;
bool m_FileIsDownloaded;
QBuffer m_GetBuffer;
QString m_DownloadedFileContent;
int m_DownloadId;
};
I used QNetworkAccsessManager for same necessity. Because this class managing connections RFC base (6 proccess same time) and non-blocking.
http://qt-project.org/doc/qt-4.8/qnetworkaccessmanager.html
How about giving the GUI some amount of time to wait on the thread and then give up.
Something like:
JSHttpGetterThread* pGetter = new JSHttpGetterThread(this);
pGetter->start();
pGetter->wait(10000); //give the thread 10 seconds to download
Or...
Why does the GUI thread have to wait for the "downloader thread" at all? When the app fires up create the downloader thread, connect the finished() signal to some other object, start the downloader thread, and return. When the thread has finished, it will signal the other object which can resume your process.

Resources