ia: Benvenite! In mi blog io scribe in interlingua, italiano e anglese.

it: Benvenuti! Nel mio blog scrivo in interlingua, italiano e inglese.

en: Welcome! In my blog I write in Interlingua, Italian and English.

Crescere, ovvero abbandonare Facebook

Ho finalmente deciso di cancellare il mio profilo Facebook. È una decisione che ho iniziato a considerare già alcuni anni fa, ma alla quale ho fino ad ora preferito quella di limitare al minimo il mio uso della piattaforma, usandola solo in modalità di sola scrittura, ovvero soltanto per diffondere propaganda politica e in generale per renderlo un posto orribile anche per tutti gli altri. Il mio ragionamento era il seguente: se fossi un personaggio famoso, allora sì che abbandonerei la piattaforma senza indugi, generando un gran chiasso e un impatto su chi mi segue; ma siccome sono connesso con poche persone, l'unica maniera per avere un impatto (per quanto minimo) è quella di restare e “salvare” i miei amici convincendoli delle mie opinioni e/o invogliandoli col mio comportamento ad abbandonare la piattaforma prima di me.

C'è vita anche là fuori

Tuttavia col passare del tempo si sono rafforzati i miei dubbi sulla bontà di questa idea. Quello a cui miro, in fondo, è che la gente si liberi di Facebook; ma finché resto in Facebook come fanno tutti, per quanto io possa protestare o comportarmi in maniera distruttiva, sarò comunque parte del business di Facebook e non costituirò in alcun modo una sfida allo status quo. Non abbiamo nessuna possibilità di convincere nessuno a migrare a piattaforme alternative (o ad abbandonare del tutto le reti sociali) finché noi stessi continuiamo ad esserne parte. Inoltre, so fin troppo bene che è praticamente impossibile convincere qualcuno delle proprie idee su una rete sociale, e anche il mio piano malvagio di rendere orribile questo posto per tutti non può funzionare: se anche qualcuno perdesse la pazienza, mi bloccherà e via.

D'altra parte, lasciando definitivamente Facebook c'è sì un'alta probabilità che la mia dipartita non importi a nessuno, ma al meno io me ne sono andato. Non sarò più una risorsa per Facebook. Nella grandezza dello schema questo è molto poco, una piccola goccia nel mare, ma ritengo che sia l'unica via d'uscita sensata. Lo rimpiangerò? Certamente no: è già da un paio d'anni che praticamente non leggo gli aggiornamenti dei miei amici in Facebook. Certo, è senz'altro possibile che il numero delle visite al mio blog o al canale YouTube (di quest'altro mostro parleremo un'altra volta) ne soffrirà: di questi tempi non molte persone sanno come usare i segnalibri del browser (per non parlare dei flussi RSS) ma, onestamente, non c'è molto che io possa fare per rimediare alla situazione.

I giganti tecnologici della rete stanno certamente rendendo le nostre vite più facili: è diventato più facile tenersi in contatto, trovare informazioni, consolidare l'opinione sul mainstream. È tutto talmente conveniente che ci ha resi non solo più pigri, ma anche più incapaci, al punto che senza i loro servizi ci sentiremmo persi e senza risorse. Non si parla più di una semplice dipendenza psicologica: stanno diventendo una necessità pratica. Questo, a sua volta, ci rende deboli, facili da manipolare e, in ultima istanza, schiavi.

Io credo in internet come una inter net (inter-rete): una rete distribuita, interconnessa, senza un padrone. Il mio fornitore di spazio web decide di censurarmi e chiudermi il sito? Bene, ne apro un altro con un altro fornitore. Ma se Facebook ti chiude l'accesso, immediatamente perdi le connessioni ai tuoi amici, ai gruppi in cui partecipavi, il mercatino, gli eventi locali, le notizie del giorno, il tuo profilo, la tua storia.

Io, se permettete, vorrei essere il padrone della mia vita.

How to be an awesome member of the Free Software community

No, this is not a tutorial, unfortunately. The main reason being that, while I strive to do my best, I don't consider myself to be an excellent member of the Free Software community, let alone be able to teach others about it. But this morning I got an email from the FSF about a campaign for St. Valentine's day, which reminded me of something I've been planning to do since a long time ago, but never got to it.

Love

I want to publicly send huge thanks to Robin Mills from the Exiv2 project, and not only because I've been fruitfully using his work in three (!) projects of mine (PhotoTeleport, Mappero Geotagger and Imaginario), but also, and especially, for being an extremely pleasant interlocutor. On the web, yes. Whereas most people tend to be more thorny and touchy in their interactions over the internet, Robin has always been friendly and coversational, trying to form a bond with some complete foreigner who just happened to report a bug on Exiv2.

Just adding some bits of information about one's personal events (such as travels) can make an enormous difference on how one's attitude is perceived. Mentioning that you visited the place that is familiar to the bug reporter almost makes one forget of being sitting in front of a computer on the internet, because your mind flies to that place. Considering that even on this personal blog of mine I'm kind of reticent about speaking of my private life, I cannot help appreciating the friendly attitude that Robin reserves for people writing on a bug tracker.

You are a wonderful netizen, Robin. A happy Valentine Day to you and your family. Thank you.

Invoking a C++ function from QML, asynchronously

In the Imaginario code I had written a C++ method to find all files present in a directory tree (for some reason this code is always encoding file paths as QUrl, but feel free to ignore that):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
QList<QUrl> Utils::findFiles(const QUrl &dirUrl, bool recursive) const
{
    QList<QUrl> files;

    QDir dir(dirUrl.toLocalFile());
    if (Q_UNLIKELY(!dir.exists())) return files;

    auto list = dir.entryInfoList(QDir::NoDotAndDotDot |
                                  QDir::Dirs | QDir::Files,
                                  QDir::Name);
    for (const QFileInfo &info: list) {
        if (info.isDir()) {
            if (recursive) {
                files += findFiles(QUrl::fromLocalFile(info.filePath()), true);
            }
        } else {
            files.append(QUrl::fromLocalFile(info.filePath()));
        }
    }

    return files;
}

The Utils mentioned in this snipped is a QObject-derived class which is registered to QML as

1
2
3
4
5
6
7
8
static QObject *utilsProvider(QQmlEngine *, QJSEngine *)                                               
{                                                                                                      
    return new Utils;                                                                                  
}

...
// In some other part of the code, before entering the main loop:
qmlRegisterSingletonType<Utils>("Imaginario", 1, 0, "Utils", utilsProvider);

This allows me to call the C++ findFiles() method from QML, like this:

1
2
3
4
import Imaginario 1.0

...
    onClicked: importer.addFiles(Utils.findFiles(folder, true))

So far, so good. However, I couldn't help noticing that when the selected folder contains a large number of files, the whole UI freezes until the findUtils() method has returned. So, how can I invoke my C++ method without blocking the UI?

QML offers a WorkerScript element which seems to do exactly what we need, but unfortunately the possibility of invoking C++ code is only there since Qt 5.12 (before that version, worker script could not use import statements), and in any case the requirement to store the script into a separate Javascript file makes the code less readable.

A better option, in my opinion, is to write a C++ method that performs the lengthy task in a thread and invokes a Javascript callback once the job execution is completed. This sounds pretty complex at first, but fortunately Qt's nice APIs make this an almost trivial task. Without modifying our previous findFiles() method, we write a second one which will execute it in a thread, using QtConcurrent::run():

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
void Utils::findFiles(const QUrl &dirUrl,
                      bool recursive,
                      const QJSValue &callback) const
{
    auto *watcher = new QFutureWatcher<QList<QUrl>>(this);
    QObject::connect(watcher, &QFutureWatcher<QList<QUrl>>::finished,
                     this, [this,watcher,callback]() {
        QList<QUrl> files = watcher->result();
        QJSValue cbCopy(callback); // needed as callback is captured as const
        QJSEngine *engine = qjsEngine(this);
        cbCopy.call(QJSValueList { engine->toScriptValue(files) });
        watcher->deleteLater();
    }); 
    watcher->setFuture(QtConcurrent::run(this, &Utils::findFiles, 
                                         dirUrl, recursive));
}

You might be surprised, but this is all what is needed in the C++ side. The changes to the QML side are equally trivial:

1
2
3
4
5
6
7
8
import Imaginario 1.0

...
    onClicked: {
        Utils.findFiles(folder, true, function(files) {
            importer.addFiles(files)
        })
    }

That's it! Line 6 in this last snippet will be invoked once the findFiles() method has completed its execution, and you'll be glad to see that the UI will be responsive throughout the duration of the operation.

QtConcurrent alternatives

QtConcurrent::run() is very simple to use, but it's not without its shortcomings, the biggest of which is that it doesn't support cancelling. This is something I can live with in this particular case, but, if you can't, don't despair: there are other options. The second simplest one is probably the static QThread::create() method, which behaves similarly to QtConcurrent::run() but returns a QThread object which can be requested to terminate, for example with QThread::requestInterruption(). This method exists only since Qt 5.10, and that's why I didn't list it as my first choice; but if you are using such a recent version of Qt, then it's probably the best option, because one doesn't even need to subclass QThread (and there's no need to depend on QtConcurrent). Of course, subclassing QThread is also an option, but this involves a little more typing.

Beware of threading issues

The most tempting solution (and I confess, my first approach to the problem was exactly this) is to invoke the Javascript callback directly from the thread:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
void Utils::findFiles(const QUrl &dirUrl,
                      bool recursive,
                      const QJSValue &callback) const
{
    QtConcurrent::run([=]() {
        const QList<QUrl> files = findFiles(dirUrl, recursive);
        QJSValue cbCopy(callback);
        QJSEngine *engine = qjsEngine(this);
        cbCopy.call(QJSValueList { engine->toScriptValue(files) });
    });
}

However, this will work 95% of the times only, because QJSValue::call() is not thread-safe, and therefor your application will be victim of random crashes. So, we need to write a couple of lines more and invoke the callback from the main thread, like I'm doing before.