Children aren’t worried about the future. Young people aren’t worried about the future; they’re worried about us: us leading them into the future we envision
Jack Ma — Oct 2017, keynote speech at Alibaba Cloud’s Computing Conference in Hangzhou
Category: controversial
Post Iceland holiday
I’m filled up with new inspiration.
Going to Iceland for the holidays is truly an amazing experience. With its stunning landscapes, natural wonders like geysers and waterfalls, and the opportunity to witness the breathtaking Northern Lights, Iceland offers a unique and unforgettable holiday destination. From exploring the vibrant capital city of Reykjavik to venturing into the rugged and pristine wilderness of the countryside, there are endless adventures and memories to be made in this beautiful country.
When traveling, being able to purchase children’s clothing online can be a significant advantage. It eliminates the need to carry extra luggage, saves time and effort spent searching for stores in an unfamiliar location, and allows for convenient delivery right to your accommodation. Additionally, buy childrens clothing here provides access to a wide range of options and makes it easier to compare prices and find unique items that may not be available locally.
The RelayCommand in Qt
A few days ago I explained how we can do MVVM techniques like ICommand in Qt.
Today I’ll explain how to make and use a simple version of the, in the XAML MVVM world quite famous, RelayCommand. In the Microsoft Prism4 & 5 world this is DelegateCommand. Both are equivalent. I will only show a non-templated RelayCommand, so no RelayCommand<T> for now. Perhaps I’ll add a templated one to that mvvm project some other day.
What people call a delegate in C# is what C++ people call a Functor. Obviously we will use functors, then. Note that for people actually reading all those links: in C# the Action<T> and Func<T,G> are basically also C# delegates (or, functors, if you fancy C++’s names for this more).
Here is the RelayCommand.h:
#include <functional> #include <QSharedPointer> #include <MVVM/Commands/AbstractCommand.h> class RelayCommand : public AbstractCommand { Q_OBJECT public: RelayCommand(std::function<void()> executeDelegatep, std::function<bool()> canExecuteDelegatep, QObject *parent = 0) : AbstractCommand(parent) , executeDelegate(executeDelegatep) , canExecuteDelegate(canExecuteDelegatep) {} void execute() Q_DECL_OVERRIDE; bool canExecute() const Q_DECL_OVERRIDE; public slots: void evaluateCanExecute(); private: std::function<void()> executeDelegate; std::function<bool()> canExecuteDelegate; };
The implementation is too simple to be true:
#include "RelayCommand.h" bool RelayCommand::canExecute() const { return canExecuteDelegate(); } void RelayCommand::evaluateCanExecute() { emit canExecuteChanged( canExecute() ); } void RelayCommand::execute() { executeDelegate(); }
Okay, so how do we use this? First we make a ViewModel. Because in this case we will define the command in C++. That probably means you want a ViewModel.
I added a CompositeCommand in the mix. For a Q_PROPERTY isn’t a CommandProxy really needed, as ownership stays in C++ (when for example you pass this as parent). For a Q_INVOKABLE you would need it to wrap the QSharedPointer<AbstractCommand>.
Note. I already hear you think: wait a minute, you are not passing this to the QObject’s constructor, it’s not a QScopedPointer and you have a new but no delete. That’s because CommandProxy converts the ownership rules to QQmlEngine::setObjectOwnership (this, QQmlEngine::JavaScriptOwnership) for itself. I don’t necessarily recommend its usage here (for it’s not immediately clear), but at the same time this is just a demo. You can try printing a warning in the destructor and you’ll see that the QML garbage collector takes care of it.
#include <QObject> #include <QScopedPointer> #include <MVVM/Commands/CommandProxy.h> #include <MVVM/Commands/CompositeCommand.h> #include <MVVM/Commands/RelayCommand.h> #include <MVVM/Models/CommandListModel.h> class ViewModel: public QObject { Q_OBJECT Q_PROPERTY(CommandProxy* helloCommand READ helloCommand CONSTANT) public: ViewModel(QObject *parent=0):QObject(parent), helloCmd(new CompositeCommand()){ QSharedPointer<CompositeCommand> cCmd = helloCmd.dynamicCast<CompositeCommand>(); cCmd->add( new RelayCommand ([=] { qWarning() << "Hello1 from C++ RelayCommand"; }, [=]{ return true; })); cCmd->add( new RelayCommand ([=] { qWarning() << "Hello2 from C++ RelayCommand"; }, [=]{ return true; })); proxyCmd = new CommandProxy (helloCmd); } CommandProxy* helloCommand() { return proxyCmd; } private: QSharedPointer<AbstractCommand> helloCmd; CommandProxy *proxyCmd; };
Let’s also make a very simple View.qml that uses the ViewModel
import QtQuick 2.3 import QtQuick.Window 2.0 import QtQuick.Controls 1.2 import Example 1.0 Item { property ViewModel viewModel: ViewModel {} Button { enabled: viewModel.helloCommand.canExecute onClicked: viewModel.helloCommand.execute() } }
AbstractCommand Model View ViewModel techniques
In the .NET XAML world, you have the ICommand, the CompositeCommand and the DelegateCommand. You use these commands to in a declarative way bind them as properties to XAML components like menu items and buttons. You can find an excellent book on this titled Prism 5.0 for WPF.
The ICommand defines two things: a canExecute property and an execute() method. The CompositeCommand allows you to combine multiple commands together, the DelegateCommand makes it possible to pass two delegates (functors or lambda’s); one for the canExecute evaluation and one for the execute() method.
The idea here is that you want to make it possible to put said commands in a ViewModel and then data bind them to your View (so in QML that’s with Q_INVOKABLE and Q_PROPERTY). Meaning that the action of the component in the view results in execute() being called, and the component in the view being enabled or not is bound to the canExecute bool property.
In QML that of course corresponds to a ViewModel.cpp for a View.qml. Meanwhile you also want to make it possible to in a declarative way use certain commands in the View.qml without involving the ViewModel.cpp.
So I tried making exactly that. I’ve placed it on github in a project I plan to use more often to collect MVVM techniques I come up with. And in this article I’ll explain how and what. I’ll stick to the header files and the QML file.
We start with defining a AbstractCommand interface. This is very much like .NET’s ICommand, of course:
#include <QObject> class AbstractCommand : public QObject { Q_OBJECT Q_PROPERTY(bool canExecute READ canExecute NOTIFY canExecuteChanged) public: AbstractCommand(QObject *parent = 0):QObject(parent){} Q_INVOKABLE virtual void execute() = 0; virtual bool canExecute() const = 0; signals: void canExecuteChanged(bool canExecute); };
We will also make a command that is very easy to use in QML, the EmitCommand:
#include <MVVM/Commands/AbstractCommand.h> class EmitCommand : public AbstractCommand { Q_OBJECT Q_PROPERTY(bool canExecute READ canExecute WRITE setCanExecute NOTIFY privateCanExecuteChanged) public: EmitCommand(QObject *parent=0):AbstractCommand(parent){} void execute() Q_DECL_OVERRIDE; bool canExecute() const Q_DECL_OVERRIDE; public slots: void setCanExecute(bool canExecute); signals: void executes(); void privateCanExecuteChanged(); private: bool canExe = false; };
We make a command that allows us to combine multiple commands together as one. This is the equivalent of .NET’s CompositeCommand, here you have our own:
#include <QSharedPointer> #include <QQmlListProperty> #include <MVVM/Commands/AbstractCommand.h> #include <MVVM/Commands/ListCommand.h> class CompositeCommand : public AbstractCommand { Q_OBJECT Q_PROPERTY(QQmlListProperty<AbstractCommand> commands READ commands NOTIFY commandsChanged ) Q_CLASSINFO("DefaultProperty", "commands") public: CompositeCommand(QObject *parent = 0):AbstractCommand (parent) {} CompositeCommand(QList<QSharedPointer<AbstractCommand> > cmds, QObject *parent=0); ~CompositeCommand(); void execute() Q_DECL_OVERRIDE; bool canExecute() const Q_DECL_OVERRIDE; void remove(const QSharedPointer<AbstractCommand> &cmd); void add(const QSharedPointer<AbstractCommand> &cmd); void add(AbstractCommand *cmd); void clearCommands(); QQmlListProperty<AbstractCommand> commands(); signals: void commandsChanged(); private slots: void onCanExecuteChanged(bool canExecute); private: QList<QSharedPointer<AbstractCommand> > cmds; static void appendCommand(QQmlListProperty<AbstractCommand> *lst, AbstractCommand *cmd); static AbstractCommand* command(QQmlListProperty<AbstractCommand> *lst, int idx); static void clearCommands(QQmlListProperty<AbstractCommand> *lst); static int commandCount(QQmlListProperty<AbstractCommand> *lst); };
We also make a command that looks a lot like ListElement in QML’s ListModel:
#include <MVVM/Commands/AbstractCommand.h> class ListCommand : public AbstractCommand { Q_OBJECT Q_PROPERTY(AbstractCommand *command READ command WRITE setCommand NOTIFY commandChanged) Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) public: ListCommand(QObject *parent = 0):AbstractCommand(parent){} void execute() Q_DECL_OVERRIDE; bool canExecute() const Q_DECL_OVERRIDE; AbstractCommand* command() const; void setCommand(AbstractCommand *newCommand); void setCommand(const QSharedPointer<AbstractCommand> &newCommand); QString text() const; void setText(const QString &newValue); signals: void commandChanged(); void textChanged(); private: QSharedPointer<AbstractCommand> cmd; QString txt; };
Let’s now also make the equivalent for QML’s ListModel, CommandListModel:
#include <QObject> #include <QQmlListProperty> #include <MVVM/Commands/ListCommand.h> class CommandListModel:public QObject { Q_OBJECT Q_PROPERTY(QQmlListProperty<ListCommand> commands READ commands NOTIFY commandsChanged ) Q_CLASSINFO("DefaultProperty", "commands") public: CommandListModel(QObject *parent = 0):QObject(parent){} void clearCommands(); int commandCount() const; QQmlListProperty<ListCommand> commands(); void appendCommand(ListCommand *command); ListCommand* command(int idx) const; signals: void commandsChanged(); private: static void appendCommand(QQmlListProperty<ListCommand> *lst, ListCommand *cmd); static ListCommand* command(QQmlListProperty<ListCommand> *lst, int idx); static void clearCommands(QQmlListProperty<ListCommand> *lst); static int commandCount(QQmlListProperty<ListCommand> *lst); QList<ListCommand* > cmds; };
Okay, let’s now put all this together in a simple example QML:
import QtQuick 2.3 import QtQuick.Window 2.0 import QtQuick.Controls 1.2 import be.codeminded.mvvm 1.0 import Example 1.0 as A Window { width: 360 height: 360 visible: true ListView { id: listView anchors.fill: parent delegate: Item { height: 20 width: listView.width MouseArea { anchors.fill: parent onClicked: if (modelData.canExecute) modelData.execute() } Text { anchors.fill: parent text: modelData.text color: modelData.canExecute ? "black" : "grey" } } model: comsModel.commands property bool combineCanExecute: false CommandListModel { id: comsModel ListCommand { text: "C++ Lambda command" command: A.LambdaCommand } ListCommand { text: "Enable combined" command: EmitCommand { onExecutes: { console.warn( "Hello1"); listView.combineCanExecute=true; } canExecute: true } } ListCommand { text: "Disable combined" command: EmitCommand { onExecutes: { console.warn( "Hello2"); listView.combineCanExecute=false; } canExecute: true } } ListCommand { text: "Combined emit commands" command: CompositeCommand { EmitCommand { onExecutes: console.warn( "Emit command 1"); canExecute: listView.combineCanExecute } EmitCommand { onExecutes: console.warn( "Emit command 2"); canExecute: listView.combineCanExecute } } } } } }
I made a task-bug for this on Qt, here.
Comité I deelt informatie met De Tijd. Waarom niet met de burger?
Onze inlichtingendiensten en Comité I geven dit jaar enkele cijfers over het speurwerk van staatsveiligheid, blijkbaar ‘vertrouwelijk’, aan een Belgische krant.
Het is spijtig dat gewone burgers dit niet zelf (eenvoudig) kunnen vinden op de website van Comité I. Wel kon ik een ouder verslag vinden van 2014 – 2015.
Misschien moet men bijzondere opsporingsmethoden gebruiken om officiële informatie vrijgegeven door Comité I te vinden? Laat ik dat maar niet doen. Ik heb er eigenlijk geen idee van waarom we dan maar De Tijd moeten vertrouwen, en waarom we als burger niet meteen zelf het originele verslag kunnen lezen?
Ik heb altijd gevonden dat het vrijgeven van zulke informatie perfect kan zonder één en ander te onthullen wat het criminelen gemakkelijker maakt. Deze vrijgave van cijfers bewijst dat, volgens mij. Ons land blijkt vrij uniek te zijn met het vrijgeven van dit soort gegevens. Zulke openbaarheid van bestuur van onze overheid is iets waar wij burgers dan ook trots op mogen zijn, vind ik. Het siert de medewerkers van de inlichtingendiensten, en Comité I, dat dit in ons land mogelijk is.
Hoewel er een forse toename is in terrorisme dossiers, wat te verwachten was na wat er gebeurd is in Zaventem, blijkt er geen sprake te zijn van grootschalig aftappen of van grote inbreuken, of zo iets. Slechts één procent van de operaties moest door Comité I stopgezet worden. Dat is één procent te veel, maar dat is eerlijk gezegd ook vrij weinig. Een vraag is hoe Comité I er voor zorgt dat dit a) zo weinig blijft en b) steeds minder wordt? Worden er cursussen gegeven aan de medewerkers van de diensten? Wordt men desnoods gesanctioneerd bij (herhaaldelijke) inbreuken?
Er wordt meer gebruik gemaakt van het inbreken op computersystemen, en ook dat lijkt me logisch: heel wat criminaliteit verhuist de dag van vandaag naar de digitale wereld. Ik vraag me daar wel bij af of onze inlichtingendiensten voldoende scholing en recruitering van specialisten ter zake doet. Het is ook eenvoudiger inbreuken tegen de wet te verbergen dan bij gebruik van meer conventionele bijzondere inlichtingendiensten. Snel een tap afzetten en de logs en data verwijderen is eenvoudiger dan officiële verslagen te vernietigen. Toch moet ook dit allemaal volgens de wet gebeuren. Uiteraard.
Een vraag blijft voor mij wel welk percentage van de operaties Comité I heeft gemonitord? Want, indien er slechts één procent van de operaties door Comité I moest stopgezet worden, maar het Comité I onderzoekt maar twee procent van alle gevoerde operaties, dan zou dat willen zeggen dat de helft van alle operaties foutief uitgevoerd worden. Vermoedelijk onderzoekt men veel meer. Maar hoe weet de burger dat? Ik zie ook niet hoe het onthullen van dat percentage het criminelen eenvoudiger maakt. Misschien, vond ik ook deze informatie gewoon nog niet?
Leuk dat Comité I meldt dat er vorig jaar geen enkele journalist, arts of advocaat een doelwit is geweest. Dat zal journalist Lars van De Tijd heel leuk vinden. Het is vermoedelijk een reactie op dit nieuwsfeit? Onze wet is hier, terecht, heel specifiek in. Ze moet, uiteraard, gevolgd worden. Éénieder in dit land, ook criminelen, moeten in vertrouwen medische verzorging kunnen genieten. Het afluisteren van een arts kan slechts in zeer uitzonderlijke gevallen (bv. wanneer de arts zelf betrokken is bij de criminele activiteiten van zijn of haar patiënt). Journalisten en advocaten moeten voorts ook altijd hun werk onafhankelijk kunnen doen.
Ik zou willen herhalen wat Peter Buysrogge, N-VA-Kamerlid, ook al liet weten: Het stelt ons gerust dat de inlichtingendiensten hun bevoegdheden, blijkbaar, correct toepassen. Maar voor mij mag hieraan toegevoegd worden dat het ons land zou sieren, en haar burgers nog meer geruststellen, moest er meer informatie over de werking van controleorgaan Comité I zelf gepubliceerd worden.
Colleague tells me I write blogs in chats while I explain how to write a producer-consumer
I’m at home now. I don’t do non-public unpaid work. So let’s blog the example I’m making for him.
workplace.h
#ifndef Workplace_H #define Workplace_H #include <QObject> #include <QFuture> #include <QWaitCondition> #include <QMutex> #include <QStack> #include <QList> #include <QThread> #include <QFutureWatcher> class Workplace; typedef enum { WT_INSERTS, WT_QUERY } WorkplaceWorkType; typedef struct { WorkplaceWorkType type; QList<int> values; QString query; QFutureInterface<bool> insertIface; QFutureInterface<QList<QStringList> > queryIface; } WorkplaceWork; class WorkplaceWorker: public QThread { Q_OBJECT public: WorkplaceWorker(QObject *parent = NULL) : QThread(parent), m_running(false) { } void run() Q_DECL_OVERRIDE; void pushWork(WorkplaceWork *a_work); private: QStack<WorkplaceWork*> m_ongoing; QMutex m_mutex; QWaitCondition m_waitCondition; bool m_running; }; class Workplace: public QObject { Q_OBJECT public: explicit Workplace(QObject *a_parent=0) : QObject (a_parent) {} bool insert(QList<int> a_values); QList<QStringList> query(const QString &a_param); QFuture<bool> insertAsync(QList<int> a_values); QFuture<QList<QStringList> > queryAsync(const QString &a_param); private: WorkplaceWorker m_worker; }; class App: public QObject { Q_OBJECT public slots: void perform(); void onFinished(); private: Workplace m_workplace; }; #endif// Workplace_H
workplace.cpp
#include "workplace.h" void App::onFinished() { QFutureWatcher<bool> *watcher = static_cast<QFutureWatcher<bool>* > ( sender() ); delete watcher; } void App::perform() { for (int i=0; i<10; i++) { QList<int> vals; vals.append(1); vals.append(2); QFutureWatcher<bool> *watcher = new QFutureWatcher<bool>; connect (watcher, &QFutureWatcher<bool>::finished, this, &App::onFinished); watcher->setFuture( m_workplace.insertAsync( vals ) ); } for (int i=0; i<10; i++) { QList<int> vals; vals.append(1); vals.append(2); qWarning() << m_workplace.insert( vals ); qWarning() << m_workplace.query("test"); } } void WorkplaceWorker::pushWork(WorkplaceWork *a_work) { if (!m_running) { start(); } m_mutex.lock(); switch (a_work->type) { case WT_QUERY: m_ongoing.push_front( a_work ); break; default: m_ongoing.push_back( a_work ); } m_waitCondition.wakeAll(); m_mutex.unlock(); } void WorkplaceWorker::run() { m_mutex.lock(); m_running = true; while ( m_running ) { m_mutex.unlock(); m_mutex.lock(); if ( m_ongoing.isEmpty() ) { m_waitCondition.wait(&m_mutex); } WorkplaceWork *work = m_ongoing.pop(); m_mutex.unlock(); // Do work here and report progress sleep(1); switch (work->type) { case WT_QUERY: { // Report result here QList<QStringList> result; QStringList row; row.append("abc"); row.append("def"); result.append(row); work->queryIface.reportFinished( &result ); } break; case WT_INSERTS: default: { // Report result here bool result = true; work->insertIface.reportFinished( &result ); } break; } m_mutex.lock(); delete work; } m_mutex.unlock(); } bool Workplace::insert(QList<int> a_values) { WorkplaceWork *work = new WorkplaceWork;; QFutureWatcher<bool> watcher; work->type = WT_INSERTS; work->values = a_values; work->insertIface.reportStarted(); watcher.setFuture ( work->insertIface.future() ); m_worker.pushWork( work ); watcher.waitForFinished(); return watcher.result(); } QList<QStringList> Workplace::query(const QString &a_param) { WorkplaceWork *work = new WorkplaceWork; QFutureWatcher<QList<QStringList> > watcher; work->type = WT_QUERY; work->query = a_param; work->queryIface.reportStarted(); watcher.setFuture ( work->queryIface.future() ); m_worker.pushWork( work ); watcher.waitForFinished(); return watcher.result(); } QFuture<bool> Workplace::insertAsync(QList<int> a_values) { WorkplaceWork *work = new WorkplaceWork; work->type = WT_INSERTS; work->values = a_values; work->insertIface.reportStarted(); QFuture<bool> future = work->insertIface.future(); m_worker.pushWork( work ); return future; } QFuture<QList<QStringList> > Workplace::queryAsync(const QString &a_param) { WorkplaceWork *work = new WorkplaceWork; work->type = WT_QUERY; work->query = a_param; work->queryIface.reportStarted(); QFuture<QList<QStringList> > future = work->queryIface.future(); m_worker.pushWork( work ); return future; }
How do they do it? Asynchronous undo and redo editors
Imagine we want an editor that has undo and redo capability. But the operations on the editor are all asynchronous. This implies that also undo and redo are asynchronous operations.
We want all this to be available in QML, we want to use QFuture for the asynchronous stuff and we want to use QUndoCommand for the undo and redo capability.
But how do they do it?
First of all we will make a status object, to put the status of the asynchronous operations in (asyncundoable.h).
class AbstractAsyncStatus: public QObject { Q_OBJECT Q_PROPERTY(bool success READ success CONSTANT) Q_PROPERTY(int extra READ extra CONSTANT) public: AbstractAsyncStatus(QObject *parent):QObject (parent) {} virtual bool success() = 0; virtual int extra() = 0; };
We will be passing it around as a QSharedPointer, so that lifetime management becomes easy. But typing that out is going to give us long APIs. So let’s make a typedef for that (asyncundoable.h).
typedef QSharedPointer<AbstractAsyncStatus> AsyncStatusPointer;
Now let’s make ourselves an undo command that allows us to wait for asynchronous undo and asynchronous redo. We’re combining QUndoCommand and QFutureInterface here (asyncundoable.h).
class AbstractAsyncUndoable: public QUndoCommand { public: AbstractAsyncUndoable( QUndoCommand *parent = nullptr ) : QUndoCommand ( parent ) , m_undoFuture ( new QFutureInterface<AsyncStatusPointer>() ) , m_redoFuture ( new QFutureInterface<AsyncStatusPointer>() ) {} QFuture<AsyncStatusPointer> undoFuture() { return m_undoFuture->future(); } QFuture<AsyncStatusPointer> redoFuture() { return m_redoFuture->future(); } protected: QScopedPointer<QFutureInterface<AsyncStatusPointer> > m_undoFuture; QScopedPointer<QFutureInterface<AsyncStatusPointer> > m_redoFuture; };
Okay, let’s implement these with an example operation. First the concrete status object (asyncexample1command.h).
class AsyncExample1Status: public AbstractAsyncStatus { Q_OBJECT Q_PROPERTY(bool example1 READ example1 CONSTANT) public: AsyncExample1Status ( bool success, int extra, bool example1, QObject *parent = nullptr ) : AbstractAsyncStatus(parent) , m_example1 ( example1 ) , m_success ( success ) , m_extra ( extra ) {} bool example1() { return m_example1; } bool success() Q_DECL_OVERRIDE { return m_success; } int extra() Q_DECL_OVERRIDE { return m_extra; } private: bool m_example1 = false; bool m_success = false; int m_extra = -1; };
Let’s make a QUndoCommand that uses a timer to simulate asynchronous behavior. We could also use QtConcurrent’s run function to use a QThreadPool and QRunnable instances that also implement QFutureInterface, of course. Seasoned Qt developers know what I mean. For the sake of example, I wanted to illustrate that QFuture can also be used for asynchronous things that aren’t threads. We’ll use the lambda because QUndoCommand isn’t a QObject, so no easy slots. That’s the only reason (asyncexample1command.h).
class AsyncExample1Command: public AbstractAsyncUndoable { public: AsyncExample1Command(bool example1, QUndoCommand *parent = nullptr) : AbstractAsyncUndoable ( parent ), m_example1(example1) {} void undo() Q_DECL_OVERRIDE { m_undoFuture->reportStarted(); QTimer *timer = new QTimer(); timer->setSingleShot(true); QObject::connect(timer, &QTimer::timeout, [=]() { QSharedPointer<AbstractAsyncStatus> result; result.reset(new AsyncExample1Status ( true, 1, m_example1 )); m_undoFuture->reportFinished(&result); timer->deleteLater(); } ); timer->start(1000); } void redo() Q_DECL_OVERRIDE { m_redoFuture->reportStarted(); QTimer *timer = new QTimer(); timer->setSingleShot(true); QObject::connect(timer, &QTimer::timeout, [=]() { QSharedPointer<AbstractAsyncStatus> result; result.reset(new AsyncExample1Status ( true, 2, m_example1 )); m_redoFuture->reportFinished(&result); timer->deleteLater(); } ); timer->start(1000); } private: QTimer m_timer; bool m_example1; };
Let’s now define something we get from the strategy design pattern; a editor behavior. Implementations provide an editor all its editing behaviors (abtracteditorbehavior.h).
class AbstractEditorBehavior : public QObject { Q_OBJECT public: AbstractEditorBehavior( QObject *parent) : QObject (parent) {} virtual QFuture<AsyncStatusPointer> performExample1( bool example1 ) = 0; virtual QFuture<AsyncStatusPointer> performUndo() = 0; virtual QFuture<AsyncStatusPointer> performRedo() = 0; virtual bool canRedo() = 0; virtual bool canUndo() = 0; };
So far so good, so let’s make an implementation that has a QUndoStack and that therefor is undoable (undoableeditorbehavior.h).
class UndoableEditorBehavior: public AbstractEditorBehavior { public: UndoableEditorBehavior(QObject *parent = nullptr) : AbstractEditorBehavior (parent) , m_undoStack ( new QUndoStack ){} QFuture<AsyncStatusPointer> performExample1( bool example1 ) Q_DECL_OVERRIDE { AsyncExample1Command *command = new AsyncExample1Command ( example1 ); m_undoStack->push(command); return command->redoFuture(); } QFuture<AsyncStatusPointer> performUndo() { const AbstractAsyncUndoable *undoable = dynamic_cast<const AbstractAsyncUndoable *>( m_undoStack->command( m_undoStack->index() - 1)); m_undoStack->undo(); return const_cast<AbstractAsyncUndoable*>(undoable)->undoFuture(); } QFuture<AsyncStatusPointer> performRedo() { const AbstractAsyncUndoable *undoable = dynamic_cast<const AbstractAsyncUndoable *>( m_undoStack->command( m_undoStack->index() )); m_undoStack->redo(); return const_cast<AbstractAsyncUndoable*>(undoable)->redoFuture(); } bool canRedo() Q_DECL_OVERRIDE { return m_undoStack->canRedo(); } bool canUndo() Q_DECL_OVERRIDE { return m_undoStack->canUndo(); } private: QScopedPointer<QUndoStack> m_undoStack; };
Now we only need an editor, right (editor.h)?
class Editor: public QObject { Q_OBJECT Q_PROPERTY(AbstractEditorBehavior* editorBehavior READ editorBehavior CONSTANT) public: Editor(QObject *parent=nullptr) : QObject(parent) , m_editorBehavior ( new UndoableEditorBehavior ) { } AbstractEditorBehavior* editorBehavior() { return m_editorBehavior.data(); } Q_INVOKABLE void example1Async(bool example1) { QFutureWatcher<AsyncStatusPointer> *watcher = new QFutureWatcher<AsyncStatusPointer>(this); connect(watcher, &QFutureWatcher<AsyncStatusPointer>::finished, this, &Editor::onExample1Finished); watcher->setFuture ( m_editorBehavior->performExample1(example1) ); } Q_INVOKABLE void undoAsync() { if (m_editorBehavior->canUndo()) { QFutureWatcher<AsyncStatusPointer> *watcher = new QFutureWatcher<AsyncStatusPointer>(this); connect(watcher, &QFutureWatcher<AsyncStatusPointer>::finished, this, &Editor::onUndoFinished); watcher->setFuture ( m_editorBehavior->performUndo() ); } } Q_INVOKABLE void redoAsync() { if (m_editorBehavior->canRedo()) { QFutureWatcher<AsyncStatusPointer> *watcher = new QFutureWatcher<AsyncStatusPointer>(this); connect(watcher, &QFutureWatcher<AsyncStatusPointer>::finished, this, &Editor::onRedoFinished); watcher->setFuture ( m_editorBehavior->performRedo() ); } } signals: void example1Finished( AsyncExample1Status *status ); void undoFinished( AbstractAsyncStatus *status ); void redoFinished( AbstractAsyncStatus *status ); private slots: void onExample1Finished() { QFutureWatcher<AsyncStatusPointer> *watcher = dynamic_cast<QFutureWatcher<AsyncStatusPointer>*> (sender()); emit example1Finished( watcher->result().objectCast<AsyncExample1Status>().data() ); watcher->deleteLater(); } void onUndoFinished() { QFutureWatcher<AsyncStatusPointer> *watcher = dynamic_cast<QFutureWatcher<AsyncStatusPointer>*> (sender()); emit undoFinished( watcher->result().objectCast<AbstractAsyncStatus>().data() ); watcher->deleteLater(); } void onRedoFinished() { QFutureWatcher<AsyncStatusPointer> *watcher = dynamic_cast<QFutureWatcher<AsyncStatusPointer>*> (sender()); emit redoFinished( watcher->result().objectCast<AbstractAsyncStatus>().data() ); watcher->deleteLater(); } private: QScopedPointer<AbstractEditorBehavior> m_editorBehavior; };
Okay, let’s register this up to make it known in QML and make ourselves a main function (main.cpp).
#include <QtQml> #include <QGuiApplication> #include <QQmlApplicationEngine> #include <editor.h> int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); QQmlApplicationEngine engine; qmlRegisterType<Editor>("be.codeminded.asyncundo", 1, 0, "Editor"); engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); return app.exec(); }
Now, let’s make ourselves a simple QML UI to use this with (main.qml).
import QtQuick 2.3 import QtQuick.Window 2.2 import QtQuick.Controls 1.2 import be.codeminded.asyncundo 1.0 Window { visible: true width: 360 height: 360 Editor { id: editor onUndoFinished: text.text = "undo" onRedoFinished: text.text = "redo" onExample1Finished: text.text = "whoohoo " + status.example1 } Text { id: text text: qsTr("Hello World") anchors.centerIn: parent } Action { shortcut: "Ctrl+z" onTriggered: editor.undoAsync() } Action { shortcut: "Ctrl+y" onTriggered: editor.redoAsync() } Button { onClicked: editor.example1Async(99); } }
You can find the sources of this complete example at github. Enjoy!
RE: Bye Facebook
Wim made a stir in the land of the web. Good for Wim that he rid himself of the shackles of social media.
But how will we bring a generation of people, who are now more or less addicted to social media, to a new platform? And what should that platform look like?
I’m not a anthropologist, but I believe human nature of organizing around new concepts and techniques is that we, humans, start central and monolithic. Then we fine-tune it. We figure out that the central organization and monolithic implementation of it becomes a limiting factor. Then we decentralize it.
The next step for all those existing and potential so-called ‘online services’ is to become fully decentralized.
Every family or home should have its own IMAP and SMTP server. Should that be JMAP instead? Probably. But that ain’t the point. The fact that every family or home will have its own, is. For chat, XMPP’s s2s is like SMTP. Postfix is an implementation of SMTP like ejabberd is for XMPP’s s2s. We have Cyrus, Dovecot and others for IMAP, which is the c2s of course. And soon we’ll probably have JMAP, too. Addressability? IPv6.
Why not something like this for social media? For the next online appliance, too? Augmented reality worlds can be negotiated in a distributed fashion. Why must Second Life necessarily be centralized? Surely we can run Linden Lab’s server software, locally.
Simple, because money is not interested in anything non-centralized. Not yet.
In the other news, the Internet stopped working truly well ever since money became its driving factor.
ps. The surest way to corrupt a youth is to instruct him to hold in higher esteem those who think alike than those who think different. Quote by Friedrich Nietzsche.
Asynchronous undoable and redoable APIs
Combining QFuture with QUndoCommand made a lot of sense for us. The undo and the redo methods of the QUndoCommand can also be asynchronous, of course. We wanted to use QFuture without involving threads, because our asynchronosity is done through a process and IPC, and not a thread. It’s the design mistake of QtConcurrent‘s run method, in my opinion. That meant using QFutureInterface instead (which is undocumented, but luckily public – so it’ll remain with us until at least Qt’s 6.y.z releases).
So how do we make a QUndoCommand that has a undo, and that has a redo method that returns a asynchronous QFuture<ResultType>?
We just did that, today. I’m very satisfied with the resulting API and design. It might have helped if QUndoStack would be a QUndoStack<T> and QUndoCommand would have been a QUndoCommand<T> with undo and redo’s return type being T. Just an idea for the Qt 6.y.z developers.
The undoable editor that can open > 4 GB text files
We are making an editor for industrial uses at Heidenhain. This is to make big Klartext programs, editable. I’m sure other industries could also use that.
Nowadays these programs often come out of a conversion from a CAD-CAM format. Before you can mill and turn your pesky military secrets on one of the machines controlled by a Heidenhain set, you’ll have to tweak the program that you converted from your CAD-CAM product. We are making the editor for that.
I wrote on this blog how we will instantaneously open those >4GB files, ready for editing. It looks a lot like how I made the E-mail client modest open the headers instantaneously on the N900. Basically, having a partition or index table that gets mmapped.
We’re also making the overlaying (the changes made by the user) undoable. The APIs for that kinda look like this. All examples on my blog are amateur extracts of the real thing, of course.
I feel like it’s actually going to work out. Architecturally and organizationally the other developers in our team are getting at the right level of expertise and sense of wanting this.
That is most important for anything to make it happen.
It feels a bit like how Nokia was: I’m learning a lot about myself from techleading: how to propose a design, concept or idea; how to convince deeply technical people; how to push others to go further than what they can already do. How to make a team quit competing and start sharing a common goal. The infrastructure for that was provided to me by Nokia. At Heidenhain, I feel like having played a small role in it.
Making something that is ‘undoable editable’ with Qt
Among the problems we’ll face is that we want asynchronous APIs that are undoable and that we want to switch to read only, undoable editing, non-undoable editing and that QML doesn’t really work well with QFuture. At least not yet. We want an interface that is easy to talk with from QML. Yet we want to switch between complicated behaviors.
We will also want synchronous mode and asynchronous mode. Because I just invented that requirement out of thin air.
Ok, first the “design”. We see a lot of behaviors, for something that can do something. The behaviors will perform for that something, the actions it can do. That is the strategy design pattern, then. It’s the one about ducks and wing fly behavior and rocket propelled fly behavior and the ostrich that has a can’t fly behavior. For undo and redo, we have the command pattern. We have this neat thing in Qt for that. We’ll use it. We don’t reinvent the wheel. Reinventing the wheel is stupid.
Let’s create the duck. I mean, the thing-editor as I will use “Thing” for the thing that is being edited. We want copy (sync is sufficient), paste (must be aysnc), and edit (must be async). We could also have insert and delete, but those APIs would be just like edit. Paste is usually similar to insert, of course. Except that it can be a combined delete and insert when overwriting content. The command pattern allows you to make such combinations. Not the purpose of this example, though.
Enough explanation. Let’s start! The ThingEditor, is like the flying Duck in strategy. This is going to be more or less the API that we will present to the QML world. It could be your ViewModel, for example (ie. you could let your ThingViewModel subclass ThingEditor).
class ThingEditor : public QObject { Q_OBJECT Q_PROPERTY ( ThingEditingBehavior* editingBehavior READ editingBehavior WRITE setEditingBehavior NOTIFY editingBehaviorChanged ) Q_PROPERTY ( Thing* thing READ thing WRITE setThing NOTIFY thingChanged ) public: explicit ThingEditor( QSharedPointer<Thing> &a_thing, ThingEditingBehavior *a_editBehavior, QObject *a_parent = nullptr ); explicit ThingEditor( QObject *a_parent = nullptr ); Thing* thing() const { return m_thing.data(); } virtual void setThing( QSharedPointer<Thing> &a_thing ); virtual void setThing( Thing *a_thing ); ThingEditingBehavior* editingBehavior() const { return m_editingBehavior.data(); } virtual void setEditingBehavior ( ThingEditingBehavior *a_editingBehavior ); Q_INVOKABLE virtual void copyCurrentToClipboard ( ); Q_INVOKABLE virtual void editCurrentAsync( const QString &a_value ); Q_INVOKABLE virtual void pasteCurrentFromClipboardAsync( ); signals: void editingBehaviorChanged (); void thingChanged(); void editCurrentFinished( EditCurrentCommand *a_command ); void pasteCurrentFromClipboardFinished( EditCurrentCommand *a_command ); private slots: void onEditCurrentFinished(); void onPasteCurrentFromClipboardFinished(); private: QScopedPointer<ThingEditingBehavior> m_editingBehavior; QSharedPointer<Thing> m_thing; QList<QFutureWatcher<EditCurrentCommand*> *> m_editCurrentFutureWatchers; QList<QFutureWatcher<EditCurrentCommand*> *> m_pasteCurrentFromClipboardFutureWatchers; };
For the implementation of this class, I’ll only provide the non-obvious pieces. I’m sure you can do that setThing, setEditingBehavior and the constructor yourself. I’m also providing it only once, and also only for the EditCurrentCommand. The one about paste is going to be exactly the same.
void ThingEditor::copyCurrentToClipboard ( ) { m_editingBehavior->copyCurrentToClipboard( ); } void ThingEditor::onEditCurrentFinished( ) { QFutureWatcher<EditCurrentCommand*> *resultWatcher = static_cast<QFutureWatcher<EditCurrentCommand*>*> ( sender() ); emit editCurrentFinished ( resultWatcher->result() ); if (m_editCurrentFutureWatchers.contains( resultWatcher )) { m_editCurrentFutureWatchers.removeAll( resultWatcher ); } delete resultWatcher; } void ThingEditor::editCurrentAsync( const QString &a_value ) { QFutureWatcher<EditCurrentCommand*> *resultWatcher = new QFutureWatcher<EditCurrentCommand*>(); connect ( resultWatcher, &QFutureWatcher<EditCurrentCommand*>::finished, this, &ThingEditor::onEditCurrentFinished, Qt::QueuedConnection ); resultWatcher->setFuture ( m_editingBehavior->editCurrentAsync( a_value ) ); m_editCurrentFutureWatchers.append ( resultWatcher ); }
For QUndo we’ll need a QUndoCommand. For each undoable action we indeed need to make such a command. You could add more state and pass it to the constructor. It’s common, for example, to pass Thing, or the ThingEditor or the behavior (this is why I used QSharedPointer for those: as long as your command lives in the stack, you’ll need it to hold a reference to that state).
class EditCurrentCommand: public QUndoCommand { public: explicit EditCurrentCommand( const QString &a_value, QUndoCommand *a_parent = nullptr ) : QUndoCommand ( a_parent ) , m_value ( a_value ) { } void redo() Q_DECL_OVERRIDE { // Perform action goes here } void undo() Q_DECL_OVERRIDE { // Undo what got performed goes here } private: const QString &m_value; };
You can (and probably should) also make this one abstract (and/or a so called pure interface), as you’ll usually want many implementations of this one (one for every kind of editing behavior). Note that it leaks the QUndoCommand instances unless you handle them (ie. storing them in a QUndoStack). That in itself is a good reason to keep it abstract.
class ThingEditingBehavior : public QObject { Q_OBJECT Q_PROPERTY ( ThingEditor* editor READ editor WRITE setEditor NOTIFY editorChanged ) Q_PROPERTY ( Thing* thing READ thing NOTIFY thingChanged ) public: explicit ThingEditingBehavior( ThingEditor *a_editor, QObject *a_parent = nullptr ) : QObject ( a_parent ) , m_editor ( a_editor ) { } explicit ThingEditingBehavior( QObject *a_parent = nullptr ) : QObject ( a_parent ) { } ThingEditor* editor() const { return m_editor.data(); } virtual void setEditor( ThingEditor *a_editor ); Thing* thing() const; virtual void copyCurrentToClipboard ( ); virtual QFuture<EditCurrentCommand*> editCurrentAsync( const QString &a_value, bool a_exec = true ); virtual QFuture<EditCurrentCommand*> pasteCurrentFromClipboardAsync( bool a_exec = true ); protected: virtual EditCurrentCommand* editCurrentSync( const QString &a_value, bool a_exec = true ); virtual EditCurrentCommand* pasteCurrentFromClipboardSync( bool a_exec = true ); signals: void editorChanged(); void thingChanged(); private: QPointer<ThingEditor> m_editor; bool m_synchronous = true; };
That setEditor, the constructor, etc: these are too obvious to write here. Here are the non-obvious ones:
void ThingEditingBehavior::copyToClipboard ( ) { } EditCurrentCommand* ThingEditingBehavior::editCurrentSync( const QString &a_value, bool a_exec ) { EditCurrentCommand *ret = new EditCurrentCommand ( a_value ); if ( a_exec ) ret->redo(); return ret; } QFuture<EditCurrentCommand*> ThingEditingBehavior::editCurrentAsync( const QString &a_value, bool a_exec ) { QFuture<EditCurrentCommand*> resultFuture = QtConcurrent::run( QThreadPool::globalInstance(), this, &ThingEditingBehavior::editCurrentSync, a_value, a_exec ); if (m_synchronous) resultFuture.waitForFinished(); return resultFuture; }
And now we can make the whole thing undoable by making a undoable editing behavior. I’ll leave a non-undoable editing behavior as an exercise to the reader (ie. just perform redo() on the QUndoCommand, don’t store it in the QUndoStack and immediately delete or cmd->deleteLater() the instance).
Note that if m_synchronous is false, that (all access to) m_undoStack, and the undo and redo methods of your QUndoCommands, must be (made) thread-safe. The thread-safety is not the purpose of this example, though.
class UndoableThingEditingBehavior : public ThingEditingBehavior { Q_OBJECT public: explicit UndoableThingEditingBehavior( ThingEditor *a_editor, QObject *a_parent = nullptr ); protected: EditCellCommand* editCurrentSync( const QString &a_value, bool a_exec = true ) Q_DECL_OVERRIDE; EditCurrentCommand* pasteCurrentFromClipboardSync( bool a_exec = true ) Q_DECL_OVERRIDE; private: QScopedPointer<QUndoStack> m_undoStack; }; EditCellCommand* UndoableThingEditingBehavior::editCurrentSync( const QString &a_value, bool a_exec ) { Q_UNUSED(a_exec) EditCellCommand *undoable = ThingEditingBehavior::editCurrentSync( a_value, false ); m_undoStack->push( undoable ); return undoable; } EditCellCommand* UndoableThingEditingBehavior::pasteCurrentFromClipboardSync( bool a_exec ) { Q_UNUSED(a_exec) EditCellCommand *undoable = ThingEditingBehavior::pasteCurrentFromClipboardSync( false ); m_undoStack->push( undoable ); return undoable; }
Perfection
Perfection has been reached not when there is nothing left to add, but when there is nothing left to take away.
Merkel bashing
It seems to be the new sport of nitwit moronic world leaders like Trump and Erdogan to bash Frau Merkel.
It makes me respect her more.
Duck typing
Imagine you have a duck. Imagine you have a wall. Now imagine you throw the duck with a lot of force against a wall. Duck typing means that the duck hitting the wall quacks like a duck would.
ps. Replace wall with API and duck with ugly stupid script written by an idiot. You can leave quacks.
Binaries in git, release numbering, Git-Flow and Scrum at the CIA
Funny how even the software developers at the CIA have problems with idiots who want to put binaries in git. They also know about Git-Flow, my preferred git branching workflow. I kind of wonder how come, if they know about Git-Flow, we see so few leaked NSA and CIA tools with correct semver versioning. Sometimes it’s somewhat okayish, like you can see here. But v1.0-RC3 is not really semver if you see how they got there here. To start with, your alpha versions start with 0.0.x. So where are all those versions under 0.0.x that happened before release candidate 3? 1.0, 1.1-beta, 1.0-phase2, 1.0-beta1-, 1.0-beta-7. WTF guys. That’s not a good versioning scheme. Just call it 0.0.1, 0.0.2, 0.1.0, 0.1.1, 0.1.2 for the betas. And when the thing sees first usage, start calling it 1.0.0, 1.0.1, 1.0.2, 1.1.0, 1.1.1, 1.2.0 etc. What’s wrong with that? And how are all these words like alha-1, beta-2, phase2, etc any better? Maybe just fire your release maintainer! Admittedly for that 2.0.x series they at least tried to get it right.
The point is that packaging tools can be configured to let other packages depend on these version numbers. In x.y.z the x number has implications on API incompatibility whereas the y number can be used for compatible feature additions.
I can imagine that different malwares, exploits, rootkits, intrusion tools they develop would pose incompatibilities with each other, and that for example the NSA and CIA want to share libraries and link without having to recompile or repackage. So versioning to indicate ABI and API compatibility wouldn’t be a bad idea. Let’s hope in the next round of massive leaks we see them having learned good software development processes and practices.
They are doing Scrum sprints and do retrospectives, though. That’s not so bad.
How to expose a QList<MyListClass> in a ViewModel to QML
MyPlugin/MyPlugin.cpp:
#include <ViewModels/MyListClass.h> #include <ViewModels/DisplayViewModel.h> qmlRegisterUncreatableType<MyListClass>( a_uri, 1, 0, "MyListClass", "Use access via DisplayViewModel instead"); qmlRegisterType<DisplayViewModel>( a_uri, 1, 0, "DisplayViewModel");
Utils/MyQMLListUtils.h
#define MY_DECLARE_QML_LIST(type, name, owner, prop) \ QQmlListProperty<type> name(){ \ return QQmlListProperty<type>( \ this, 0,&owner::count ## type ## For ## name ## List, \ &owner::at ## type ## For ## name ## List); \ } \ static int count ## type ## For ## name ## List(QQmlListProperty<type>*property){ \ owner *m = qobject_cast<owner *>(property->object); \ return m->prop.size(); \ } \ static type *at ## type ## For ## name ## List( \ QQmlListProperty<type>*property, int index){ \ owner *m = qobject_cast<owner *>(property->object); \ return m->prop[index]; \ }
ViewModels/DisplayViewModel.h
#ifndef DISPLAYVIEWMODEL_H #define DISPLAYVIEWMODEL_H #include <QObject> #include <QtQml> #include <ViewModels/MyListClass.h> #include <Utils/MyQMLListUtils.h> class DisplayViewModel : public QObject { Q_OBJECT Q_PROPERTY(constQString title READ title WRITE setTitle NOTIFY titleChanged ) Q_PROPERTY(constQList<MyListClass*> objects READ objects NOTIFY objectsChanged ) Q_PROPERTY( QQmlListProperty<MyListClass> objectList READ objectList NOTIFY objectsChanged ) public: explicit DisplayViewModel( QObject *a_parent = nullptr ); explicit DisplayViewModel( const QString &a_title, QList<MyListClass*> a_objects, QObject *a_parent = nullptr ); const QString title() { return m_title; } void setTitle( const QString &a_title ); const QList<MyListClass*> objects () { return m_objects; } Q_INVOKABLE void appendObject( MyListClass *a_object); Q_INVOKABLE void deleteObject( MyListClass *a_object); Q_INVOKABLE void reset( ); protected: MY_DECLARE_QML_LIST(MyListClass, objectList, DisplayViewModel, m_objects) signals: void titleChanged(); void objectsChanged(); private: QString m_title; QList<MyListObject*> m_objects; }; #endif// DISPLAYVIEWMODEL_H
DisplayViewModel.cpp
#include "DisplayViewModel.h" DisplayViewModel::DisplayViewModel( const QString &a_title, QList<MyListClass*> a_objects, QObject *a_parent ) : QObject ( a_parent ) , m_title ( a_title ) , m_objects ( a_objects ) { foreach (MyListClass* mobject, m_objects) { mobject->setParent (this); } } void DisplayViewModel::setTitle (const QString &a_title ) { if ( m_title != a_title ) { m_title = a_title; emit titleChanged(); } } void DisplayViewModel::reset( ) { foreach ( MyListClass *mobject, m_objects ) { mobject->deleteLater(); } m_objects.clear(); emit objectsChanged(); } void DisplayViewModel::appendObject( MyListClass *a_object ) { a_object->setParent( this ); m_objects.append( a_object ); emit objectsChanged(); } void DisplayViewModel::deleteObject( MyListClass *a_object ) { if (m_objects.contains( a_object )) { m_objects.removeOne( a_object ); a_object->deleteLater(); emit objectsChanged(); } }
Tester.cpp
#include <ViewModels/DisplayViewModel.h> #include <ViewModels/MyListClass.h> QList<MyListClass*> objectList; for( int i = 0; i < 100 ; ++i ) { objectList.append ( new MyListClass (i) ); } DisplayViewModel *viewModel = new DisplayViewModel (objectList); viewModel->appendObject ( new MyListClass (101) );
Display.qml
import QtQuick 2.5 import MyPlugin 1.0 Repeater { property DisplayViewModel viewModel: DisplayViewModel { } model: viewModel.objectList delegate: Item { property MyListClass object: modelData Text { text: object.property } } }
Beste VRT, over PGP
PGP, Pretty Good Privacy, is niet enkel te koop op Het Internet; het is zowel gratis als dat het open source is. Dat wil zeggen dat iedereen op deze planeet de broncode van PGP kan downloaden, compileren en aanpassen en dat iedereen het op een telefoon kan zetten.
Velen gebruiken dan ook zulke encryptiesoftware voor allerlei redenen. Vaak zonder dat ze het door hebben (https websites, de diensten van je bank, online aankopen, Whatsapp berichten, en zo verder). Soms hebben ze het wel door (dat zijn dan vaak techneuten).
Enkelingen doen dat om hun communicatie over criminele plannen te versleutelen. Maar velen anderen doen dat om hun bedrijfsgegevens, privacy, persoonlijke data en zo verder te beveiligen. Er zouden ook overheden zijn, naar het schijnt, zoals de onze, bijvoorbeeld, die het veelvuldig gebruiken. Volgens geruchten gebruiken alle militairen het. Ik ben er van overtuigd dat jullie werkgever, de VRT, het ook heel veel gebruikt en zelfs oplegt. Een beetje serieus journalist gebruikt het tegenwoordig ook voor, laat ik hopen, alles.
PGP is niet illegaal. Het versleutelen van berichten is niet illegaal. Dat de politie het daarom niet meer kan lezen, is op zichzelf niet illegaal. Er staat in onze Belgische wetgeving helemaal niets over het illegaal zijn van het versleutelen van berichten. Er staat in onze Belgische wetgeving helemaal niets over het illegaal zijn van bv. software zoals PGP.
Versleutelen van berichten bestaat ook al eeuwen. Nee, millennia. Misschien zelfs wel al tienduizenden jaren. Kinderen doen het en leren het. Misschien niet op al te professionele manier. Maar sommige kinderlijk eenvoudige encryptietechnieken zijn theoretisch onkraakbaar. Zoals bv. de one time pad
Sterker nog, onze Belgische universiteiten staan over de hele wereld bekend om haar wiskundigen die zich met cryptografie bezig houden. De wereldwijd belangrijkste encryptiestandaard, Rijndael, werd door Leuvense studenten ontwikkeld. Die PGP software gebruikt die encryptiestandaard, ook. Het staat tegenwoordig bekend als de Advanced Encryption Standard, AES, en wordt oa. militair vrijwel overal ingezet.
Dat een aantal mensen van het gerecht en de politie het lastig vinden dat een aantal criminelen gebruik maken van software zoals PGP, betekent op geen enkele manier dat het gebruik van en het voorzien van telefoons met PGP, op zichzelf, illegaal is. Helemaal niet. Het is dus vrij onnozel om in jullie journaal te laten uitschijnen als zou dat wel het geval zijn. Dat is het niet. Zeker in extreme tijden zoals nu is het gevaarlijk wanneer de media zoiets doet uitschijnen. Straks maakt men de gewone burger weer bang over allerlei dingen, en dan gaat de politiek domme eisen stellen zoals het verbieden van encryptie.
Het versleutelen van berichten is een erg belangrijk onderdeel, in de technologie, om die technologie veilig en betrouwbaar te laten werken. Het ontbreken van de mogelijkheid om berichten en gegevens te versleutelen zou werkelijk catastrofale gevolgen hebben.
Het zou ook geen enkele crimineel weerhouden toch één en ander te encrypteren. Encryptie verbieden zou letterlijk alleen maar de onschuldige burger schaden. De crimineel zou er net voordeel uit halen; de crimineel wordt het daardoor plots veel eenvoudiger gemaakt om bij onschuldige burgers (die geen encryptie meer kunnen gebruiken) digitaal in te breken.
Denk in deze extreme tijden goed na wat je in jullie mediakanaal, het nationale nieuws, doet uitschijnen. Als techneut vind ik het bijzonder onverantwoordelijk van jullie. OOK en vooral wanneer de bedoeling was om het nieuws zo dom mogelijk te brengen. Alsof onze burgers het niet zouden aankunnen om correct geïnformeerd te worden over deze materie.
Scrum is (best done) like a soccer team
As a freelancer I saw many companies, many situations and worked with many Project Managers, Product Owners, Scrum Masters and god knows what names the HR department came up with.
What is most important, in my experience, is that the people leading the team try to focus the people in the group on as few common goals as possible during one sprint. The more stories or goals the team has to finish, the more individualism and the fewer things will get done (with done being defined by your definition of done).
Differently put you should try to make your team work like how a soccer team plays. You try to make three, four or five goals per sprint. But you do this as a team.
Example: When a story isn’t finished at the end of the sprint; it’s the team’s fault. Not the fault of the one guy that worked on it. The team has to find a solution. If it’s always the same guy being lazy, that’s not the project’s or the team’s results. You or HR deals with that guy later. Doing so is outside of Scrum’s scope. It’s not for your retrospective discussion. But do sanction the team for not delivering.
Another example is not to put too much stories on the task board and yet to keep stories as small and/or well defined as possible. Too much stories or too large stories will result in every individual picking a different story (or subtask of the larger story) to be responsible for. At the end of the sprint none of these will be really finished. And the developers in the team won’t care about the other features being developed during the sprint. So they will dislike having to review other people’s features. They’ll have difficulty finding a reviewer. They won’t communicate with each other. They’ll become careless and dispassionate.
Your planning caused that. That makes you a bad team player. The coach is part of the team.
Onlife, Hoe de digitale wereld je leven bepaalt
Moraal filosofe Katleen Gabriels presenteerde gisteren haar boek ‘Onlife, Hoe de digitale wereld je leven bepaalt’. Ik ben dus maar eens gaan kijken. Ik was onder de indruk.
Haar uiteenzetting was gebalanceerd; ze klonk geïnformeerd. Het debat met oa. Sven Gatz, Pedro De Bruyckere en Karel Verhoeven was eigenlijk ook wel cava. Ik ben dus erg benieuwd naar het boek.
Na een Spinoza-kenner hebben we dus nu ook een moraal filosofe die zich met oa. Internet of Things dingen zal gaan bezig houden. Ik vind het dus wel goed dat de filosofie van’t land zich eindelijk eens komt moeien. De consument gelooft ons, techneuten, toch niet dat al die rommel die ze gekocht hebben dikke rotzooi is. Dus misschien dat ze wat gaan luisteren naar s’lands filosofen? Ik denk het niet. Maar slechter zal de situatie er ook niet van worden, hé?
Enfin. Medeneurdjes contacteer die Katleen en vertel over wat je zoal hebt meegemaakt wat onethisch is. Wie weet verwerkt ze je verhaal in een uiteenzetting of volgend boek? Je kan dat nooit weten he. Je moest trouwens toch eens van die zolder afkomen.
De afspraak
Den NV-A vandaag in De Afspraak (onze eigen dezinformatsiya):
Het kinderrechtenverdrag versus de vrijheid van meningsuiting van de prinses van België. Dat, om de liefde voor het koningshuis van een liberaal, Herman De Croo, te pareren.
Het vrije debat, het is toch iets mooi.
Niet waar?