Skip to content

Instantly share code, notes, and snippets.

@Mononofu
Last active March 24, 2017 01:35
Show Gist options
  • Save Mononofu/5155614 to your computer and use it in GitHub Desktop.
Save Mononofu/5155614 to your computer and use it in GitHub Desktop.
Auto reloader for .qml files. If you change one of your .qml files, the View will automagically refresh to reflect the change. Note: Right now, the watch is not recursive, so only changes in top level files will cause a refresh. For a usage sample, check below.
int main() {
QApplication a(argc, argv);
QMainWindow *window = new QMainWindow();
QDeclarativeView *view = new QDeclarativeView(window);
FileWatcher watcher;
// assuming all .qml files are in ./ui
watcher.addWatch(view, "./ui/ui.qml");
watcher.start("./ui");
view->setResizeMode(QDeclarativeView::SizeRootObjectToView);
view->setSource(QUrl("./ui/ui.qml"));
window->setGeometry(100, 100, 800, 220);
window->setCentralWidget(view);
window->show();
return a.exec();
}
#include "FileWatcher.h"
#include <sys/inotify.h>
#include <iostream>
#include <unistd.h>
#include <QtConcurrentRun>
#include <QtDeclarative/QDeclarativeEngine>
#define EVENT_SIZE ( sizeof (struct inotify_event) )
#define BUF_LEN ( 1024 * ( EVENT_SIZE + 16 ) )
void FileWatcher::watch(string dir) {
qRegisterMetaType<QDeclarativeView*>("QDeclarativeView*");
int fd = inotify_init();
if(fd < 0)
throw WatchException("failed to init inotify");
int wd = inotify_add_watch( fd, dir.c_str(),
IN_MODIFY | IN_CREATE | IN_DELETE );
char buffer[BUF_LEN];
while(running) {
fd_set descriptors;
FD_ZERO( &descriptors);
FD_SET( pipe.receiverFd(), &descriptors); // pipe to signal end of execution
FD_SET( fd, &descriptors); // fd from inotify
int nfds = max(fd, pipe.receiverFd()) + 1;
int retval = select ( nfds, &descriptors, NULL, NULL, NULL);
if(retval == -1) {
throw WatchException("select failed");
} else if(retval) {
if(FD_ISSET ( pipe.receiverFd(), &descriptors)) {
// signal to stop watching
break;
} else if(FD_ISSET ( fd, &descriptors)) {
int length = read( fd, buffer, BUF_LEN );
if(length < 0) {
throw WatchException("failed to read() events");
} else {
// refresh all views
QMapIterator<QString, QDeclarativeView *> i(views);
while(i.hasNext()) {
i.next();
QMetaObject::invokeMethod(this, "refreshView", Qt::QueuedConnection,
Q_ARG(QDeclarativeView*, i.value()),
Q_ARG(QUrl, QUrl(i.key())));
}
}
} else {
throw WatchException("unexpected fd");
}
} else { // timeout
throw WatchException("select should never timeout");
}
}
// cleanup
inotify_rm_watch(fd, wd);
close(fd);
}
void FileWatcher::start(string dir) {
running = true;
future = QtConcurrent::run(this, &FileWatcher::watch, dir);
}
void FileWatcher::stop() {
running = false;
pipe.notify();
future.waitForFinished();
}
void FileWatcher::refreshView(QDeclarativeView* view, const QUrl &url) {
// important: .qml is only reloaded if the cache is cleared first
view->engine()->clearComponentCache();
view->setSource(url);
view->show();
}
#include <string>
#include <QtDeclarative/QDeclarativeView>
#include <QObject>
#include "NotifyPipe.h"
#include <QFuture>
using namespace std;
class FileWatcher : public QObject {
Q_OBJECT
public:
~FileWatcher() {
stop();
}
void addWatch(QDeclarativeView *view, QString layout) {
views[layout] = view;
}
void watch(string dir);
void start(string dir);
void stop();
public slots:
void refreshView(QDeclarativeView* view, const QUrl &url);
private:
QMap<QString, QDeclarativeView*> views;
NotifyPipe pipe;
QFuture<void> future;
bool running;
};
class WatchException : exception {
public:
WatchException(string reason) : reason(reason) { }
const char* what() const throw() {
return reason.c_str();
}
~WatchException() throw() { }
private:
string reason;
};
// from http://stackoverflow.com/questions/2486335/wake-up-thread-blocked-on-accept-cal
#include "NotifyPipe.h"
#include <unistd.h>
#include <assert.h>
#include <fcntl.h>
NotifyPipe::NotifyPipe()
{
int pipefd[2];
int ret = pipe(pipefd);
assert(ret == 0);
m_receiveFd = pipefd[0];
m_sendFd = pipefd[1];
}
NotifyPipe::~NotifyPipe()
{
close(m_sendFd);
close(m_receiveFd);
}
int NotifyPipe::receiverFd()
{
return m_receiveFd;
}
void NotifyPipe::notify()
{
write(m_sendFd,"1",1);
}
// from http://stackoverflow.com/questions/2486335/wake-up-thread-blocked-on-accept-call
#ifndef NOTIFYPIPE_H_INCLUDED
#define NOTIFYPIPE_H_INCLUDED
class NotifyPipe
{
int m_receiveFd;
int m_sendFd;
public:
NotifyPipe();
virtual ~NotifyPipe();
int receiverFd();
void notify();
};
#endif // NOTIFYPIPE_H_INCLUDED
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment