Mardy (Entratas super Qt)http://mardy.it/ia/categories/qt.atom2024-02-02T20:11:01ZAlberto MardeganNikolaWill the internet forget russophobia?http://mardy.it/ia/blog/2023/06/will-the-internet-forget-russophobia.html2023-06-04T10:41:02+03:002023-06-04T10:41:02+03:00Alberto Mardegan<p>I've often wondering what will happen when this horrific war in Europe will
finally be over. I won't be discussing politics here, but what is mostly
interesting to me is how (and if) all the companies who made high proclaims
about not doing business with Russia will justify their getting back into the
Russian market. They will probably count on the fact that the war will be long,
and that people will forget what these companies' stance was. After all, the
world has forget about all the companies who collaborated with the Nazi regime,
so we can expect the same to happen with this war.</p>
<p>But I don't think that's right: if you made a mistake, you should be held
accountable for it. You might be wondering what is the “mistake” I'm talking
about: that's <strong>russophobia</strong>, indeed. To put it simply, and make a concrete
example: if The Qt Company stops doing business with Russian companies and
blocks its downloads page to Russian IP addresses because of the war, <em>without
being forced by the government to do so</em>, but does not take similar measures
against other countries who wage wars which have caused way more deaths and
displacement of individuals, well, that's what I call “russophobia”. Of course,
I'm aware that there's way more than that, and that the hatred for all what is
Russian (including culture and sport competitions) is an even bigger issue, but
in this blog post I'm especially focused on the IT world, so please forgive my
semi-intentional narrow-mindness on this topic.</p>
<p>Now, I'm fully aware that we live in a mediatic bubble that directs our
decisions in a way that is almost automatic, and I'm sure that most people
working for companies who took russophobic decisions are not themselves
russophobic at all (and I'm not dismissing the possibility that even the very
same people who took these decisions might not be russophobic) and that these
decisions were taken on impulse, because “everyone else is doing the same” and
due to the media pressure that if you don't do that, you might get accused of
supporting the “wrong” side of the war.</p>
<p>But that's not an excuse, especially for “smart” people like IT engineers (and
I put the adjective between quotes <a href="http://mardy.it/ia/blog/2022/11/the-idiotism-of-software-developers.html">for a
reason</a>), and especially after
the initial heat has passed and when, after more than one year of war, we
should have been exposed to different point of views and be able to evaluate
the situation more rationally. It has been therefore especially stunning for me
to learn that the Linux Kernel community, and hence The Linux Foundation, has
recently given room to russophobic behaviours, refusing a patch coming from the
Russian company Baikal (a CPU maker). For the record, the incriminated patch
was not related to supporting hardware produced by this company (not that this
would make the deed less serious, but at least one could have argued that there
could be some spot of logic in it):</p>
<div class="code"><pre class="code literal-block">From: Jakub Kicinski <kuba@kernel.org>
To: Serge Semin <Sergey.Semin@baikalelectronics.ru>
[...]
On Tue, 14 Mar 2023 01:42:24 +0300 Serge Semin wrote:
> From: Serge Semin <Sergey.Semin@baikalelectronics.ru>
We don't feel comfortable accepting patches from or relating
to hardware produced by your organization.
Please withhold networking contributions until further notice.
</pre></div>
<p>(<a href="https://lore.kernel.org/all/20230314103316.313e5f61@kernel.org/">here</a> the
link to the original discussion). One week later, someone denounced this as a
violation to the Code of Conduct committee (unfortunately the only link I could
find to this is coming from a <a href="https://www.opennet.ru/openforum/vsluhforumID3/129994.html#529">Russian IT
forum</a>, and any
other references seem to have been removed from DuckDuckGo and Google), only to
receive a reply that it was all fine.</p>
<p>To me this is not fine. The war will end, sooner or later, but it bothers me
that we never learn from the past and repeat the same mistakes over and over.
We apparently know a lot about propaganda, yet we fail to recognize it when it
influences our own mind and actions. My humble contribution is the creation of
a page where I list the companies who have taken russophobic actions, and, on
the opposite side, companies (like Flickr and Zorin OS) who have stood out for
positive messages and helpful actions. My hope is that some of the listed
companies will find the courage to review their actions, and either correct
their stance, or at least clarify their reasons. So, I hereby present</p>
<p style="text-align: center; font-size: 130%">
<a href="https://github.com/mardy/russophobia">Denouncing russophobia</a>
</p>
<p>where you'll find some of the good and some of the bad companies. I'm sure I'm
missing plenty of them: I just started recollecting my memories and searching
online a couple of days ago. I created this as a GitHub project, because indeed
I'm looking forward for contributions, to help me make the lists more complete.
I need to stress that the fact that a company has announced the suspension of
its business in Russia does not automatically make it russophobic: what we need
to look at is the <em>reason</em> for that decision: companies like LEGO and Nintendo,
for example, have suspended their operations citing logistic and financial
reasons; no judgement involved.</p>
<p>Let me repeat it once more, just to make sure there are no misunderstandings:
it's perfectly fine for businesses to take a stance on politics, and sometimes
it might be even praiseworthy; but if a company is international, and does not
apply the same reasoning to other armed conflicts, or seem to care only about
certain human rights violations and not others, then it's a case of double
standards which we need to be aware of, and make the company think twice about
it. And that's also the reason why you won't find any Ukrainian company among
the “bad” ones, because in their case the reaction is perfectly understandable
and they can hardly be accused of adopting double standards (well, technically
speaking, they are adopting double standards, but when you are so directly
impacted I think it does not deserve a blame): if it's your house which burns,
you should definitely scream about it, even if you previously have been silent
about your neighbour house's burning.</p>
<p><strong>I'm especially looking forward for more “good” companies</strong>, who have shown empathy
towards the people affected by the war (and maybe even collected money to help
them) while refraining from taking the judging role and forgetting about all
the injustice and suffering that other wars have caused (including on that very
same piece of land that suddenly appeared on all newspapers' front pages on
February 24th, 2022). I hope that these companies can serve as an example of
positive action, humanity, and love.</p><p>I've often wondering what will happen when this horrific war in Europe will
finally be over. I won't be discussing politics here, but what is mostly
interesting to me is how (and if) all the companies who made high proclaims
about not doing business with Russia will justify their getting back into the
Russian market. They will probably count on the fact that the war will be long,
and that people will forget what these companies' stance was. After all, the
world has forget about all the companies who collaborated with the Nazi regime,
so we can expect the same to happen with this war.</p>
<p>But I don't think that's right: if you made a mistake, you should be held
accountable for it. You might be wondering what is the “mistake” I'm talking
about: that's <strong>russophobia</strong>, indeed. To put it simply, and make a concrete
example: if The Qt Company stops doing business with Russian companies and
blocks its downloads page to Russian IP addresses because of the war, <em>without
being forced by the government to do so</em>, but does not take similar measures
against other countries who wage wars which have caused way more deaths and
displacement of individuals, well, that's what I call “russophobia”. Of course,
I'm aware that there's way more than that, and that the hatred for all what is
Russian (including culture and sport competitions) is an even bigger issue, but
in this blog post I'm especially focused on the IT world, so please forgive my
semi-intentional narrow-mindness on this topic.</p>
<p>Now, I'm fully aware that we live in a mediatic bubble that directs our
decisions in a way that is almost automatic, and I'm sure that most people
working for companies who took russophobic decisions are not themselves
russophobic at all (and I'm not dismissing the possibility that even the very
same people who took these decisions might not be russophobic) and that these
decisions were taken on impulse, because “everyone else is doing the same” and
due to the media pressure that if you don't do that, you might get accused of
supporting the “wrong” side of the war.</p>
<p>But that's not an excuse, especially for “smart” people like IT engineers (and
I put the adjective between quotes <a href="http://mardy.it/ia/blog/2022/11/the-idiotism-of-software-developers.html">for a
reason</a>), and especially after
the initial heat has passed and when, after more than one year of war, we
should have been exposed to different point of views and be able to evaluate
the situation more rationally. It has been therefore especially stunning for me
to learn that the Linux Kernel community, and hence The Linux Foundation, has
recently given room to russophobic behaviours, refusing a patch coming from the
Russian company Baikal (a CPU maker). For the record, the incriminated patch
was not related to supporting hardware produced by this company (not that this
would make the deed less serious, but at least one could have argued that there
could be some spot of logic in it):</p>
<div class="code"><pre class="code literal-block">From: Jakub Kicinski <kuba@kernel.org>
To: Serge Semin <Sergey.Semin@baikalelectronics.ru>
[...]
On Tue, 14 Mar 2023 01:42:24 +0300 Serge Semin wrote:
> From: Serge Semin <Sergey.Semin@baikalelectronics.ru>
We don't feel comfortable accepting patches from or relating
to hardware produced by your organization.
Please withhold networking contributions until further notice.
</pre></div>
<p>(<a href="https://lore.kernel.org/all/20230314103316.313e5f61@kernel.org/">here</a> the
link to the original discussion). One week later, someone denounced this as a
violation to the Code of Conduct committee (unfortunately the only link I could
find to this is coming from a <a href="https://www.opennet.ru/openforum/vsluhforumID3/129994.html#529">Russian IT
forum</a>, and any
other references seem to have been removed from DuckDuckGo and Google), only to
receive a reply that it was all fine.</p>
<p>To me this is not fine. The war will end, sooner or later, but it bothers me
that we never learn from the past and repeat the same mistakes over and over.
We apparently know a lot about propaganda, yet we fail to recognize it when it
influences our own mind and actions. My humble contribution is the creation of
a page where I list the companies who have taken russophobic actions, and, on
the opposite side, companies (like Flickr and Zorin OS) who have stood out for
positive messages and helpful actions. My hope is that some of the listed
companies will find the courage to review their actions, and either correct
their stance, or at least clarify their reasons. So, I hereby present</p>
<p style="text-align: center; font-size: 130%">
<a href="https://github.com/mardy/russophobia">Denouncing russophobia</a>
</p>
<p>where you'll find some of the good and some of the bad companies. I'm sure I'm
missing plenty of them: I just started recollecting my memories and searching
online a couple of days ago. I created this as a GitHub project, because indeed
I'm looking forward for contributions, to help me make the lists more complete.
I need to stress that the fact that a company has announced the suspension of
its business in Russia does not automatically make it russophobic: what we need
to look at is the <em>reason</em> for that decision: companies like LEGO and Nintendo,
for example, have suspended their operations citing logistic and financial
reasons; no judgement involved.</p>
<p>Let me repeat it once more, just to make sure there are no misunderstandings:
it's perfectly fine for businesses to take a stance on politics, and sometimes
it might be even praiseworthy; but if a company is international, and does not
apply the same reasoning to other armed conflicts, or seem to care only about
certain human rights violations and not others, then it's a case of double
standards which we need to be aware of, and make the company think twice about
it. And that's also the reason why you won't find any Ukrainian company among
the “bad” ones, because in their case the reaction is perfectly understandable
and they can hardly be accused of adopting double standards (well, technically
speaking, they are adopting double standards, but when you are so directly
impacted I think it does not deserve a blame): if it's your house which burns,
you should definitely scream about it, even if you previously have been silent
about your neighbour house's burning.</p>
<p><strong>I'm especially looking forward for more “good” companies</strong>, who have shown empathy
towards the people affected by the war (and maybe even collected money to help
them) while refraining from taking the judging role and forgetting about all
the injustice and suffering that other wars have caused (including on that very
same piece of land that suddenly appeared on all newspapers' front pages on
February 24th, 2022). I hope that these companies can serve as an example of
positive action, humanity, and love.</p>Deride, a generator of mock objects for unit testinghttp://mardy.it/ia/blog/2022/11/deride-a-generator-of-mock-objects-for-unit-testing.html2022-11-05T09:51:41+03:002022-11-05T09:51:41+03:00Alberto Mardegan<p>If you have been writing C++ classes for mocking out your C or C++
dependencies, you know how tedious it is. I generally write small classes with
just a handful of methods, so it's generally bearable, but when using
third-party code I'm usually not that lucky. If the dependency is a C library
this becomes especially tricky, both because they might be larger than what you
can handle, and both because the lack of an object-oriented design might not
offer you an easy solution to store the mock object data.</p>
<p>But fear no more, <a href="https://pypi.org/project/deride/">Deride</a> is here!</p>
<p>I won't spend too many words describing it, since you can read its description
from the link above, where you will also find some example code. More examples,
by the way, can be found in the <code>example/</code> folder in the <a href="https://gitlab.com/mardy/deride">code
repository</a>, where you can see how it can be
used to mock both pure C++ and <code>QObject</code>-based classes, and C libraries.</p>
<p>What is most important for me to say now, is that the project is in alpha
state, meaning that I've tried it on a handful of header files only; it's
highly likely that it will not work on many real-life scenarios, and if that
happens I warmly invite you to <a href="https://gitlab.com/mardy/deride/-/issues">inform me by filing a bug
report</a> providing the include file
that was not properly processed.</p>
<p>I leave you with a short example of a unit test, written using Deride. The
class under test is called <code>Stable</code>, and internally it uses objects of type
<code>Horse</code>, that we decided to mock. We used Deride to generate the mocked
implementation and a <code>MockHorse</code> class which can be used to control the mocked
objects. When building the test, we won't link against the original
<code>horse.cpp</code>, but we'll only use the original <code>horse.h</code>; the implementation will
be found in <code>mock_horse.cpp</code>, generated by Deride. And in the corresponding
<code>mock_horse.h</code> file we'll find the <code>MockHorse</code> class with all the
<code>on<method>Called()</code> hooks which we can use to install our callbacks (either to
reimplement the object behaviour, or to just be notified on when its methods
are called).</p>
<div class="code"><pre class="code literal-block"><span class="w"> </span><span class="cm">/* This MockHorse is the object created by Deride</span>
<span class="cm"> * |</span>
<span class="cm"> * |</span>
<span class="cm"> * \|/</span>
<span class="cm"> * V</span>
<span class="cm"> */</span>
<span class="w"> </span><span class="k">using</span><span class="w"> </span><span class="n">Mock</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Animals</span><span class="o">::</span><span class="n">MockHorse</span><span class="p">;</span>
<span class="w"> </span><span class="n">std</span><span class="o">::</span><span class="n">list</span><span class="o"><</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">></span><span class="w"> </span><span class="n">horseNames</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="s">"Tom"</span><span class="p">,</span>
<span class="w"> </span><span class="s">"Dick"</span><span class="p">,</span>
<span class="w"> </span><span class="s">"Harry"</span><span class="p">,</span>
<span class="w"> </span><span class="p">};</span>
<span class="w"> </span><span class="cm">/* We could use a vector, but let's be explicit */</span>
<span class="w"> </span><span class="n">Mock</span><span class="w"> </span><span class="o">*</span><span class="n">mockTom</span><span class="p">;</span>
<span class="w"> </span><span class="n">Mock</span><span class="w"> </span><span class="o">*</span><span class="n">mockDick</span><span class="p">;</span>
<span class="w"> </span><span class="n">Mock</span><span class="w"> </span><span class="o">*</span><span class="n">mockHarry</span><span class="p">;</span>
<span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">createdHorses</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span>
<span class="w"> </span><span class="c1">// onConstructorCalled() is created by Deride and called when the mocked</span>
<span class="w"> </span><span class="c1">// Horse object is created</span>
<span class="w"> </span><span class="n">Animals</span><span class="o">::</span><span class="n">MockHorse</span><span class="o">::</span><span class="n">onConstructorCalled</span><span class="p">([</span><span class="o">&</span><span class="p">](</span><span class="k">const</span><span class="w"> </span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="w"> </span><span class="o">&</span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">std</span><span class="o">::</span><span class="n">cout</span><span class="w"> </span><span class="o"><<</span><span class="w"> </span><span class="s">"Horse instantiated: "</span><span class="w"> </span><span class="o"><<</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="o"><<</span><span class="w"> </span><span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="w"> </span><span class="n">createdHorses</span><span class="o">++</span><span class="p">;</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">name</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="s">"Tom"</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">mockTom</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Mock</span><span class="o">::</span><span class="n">latestInstance</span><span class="p">();</span>
<span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">name</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="s">"Dick"</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">mockDick</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Mock</span><span class="o">::</span><span class="n">latestInstance</span><span class="p">();</span>
<span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">name</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="s">"Harry"</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">mockHarry</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Mock</span><span class="o">::</span><span class="n">latestInstance</span><span class="p">();</span>
<span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">assert</span><span class="p">(</span><span class="nb">false</span><span class="p">);</span><span class="w"> </span><span class="c1">// should not be reached</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="p">});</span>
<span class="w"> </span><span class="n">Stable</span><span class="w"> </span><span class="n">stable</span><span class="p">;</span>
<span class="w"> </span><span class="n">stable</span><span class="p">.</span><span class="n">createHorses</span><span class="p">(</span><span class="n">horseNames</span><span class="p">);</span>
<span class="w"> </span><span class="cm">/* It's at this point that the contructor callbacks we defined above will</span>
<span class="cm"> * have been called. Let's double-check that indeed that's the case.</span>
<span class="cm"> */</span>
<span class="w"> </span><span class="n">assert</span><span class="p">(</span><span class="n">createdHorses</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">3</span><span class="p">);</span>
<span class="w"> </span><span class="n">assert</span><span class="p">(</span><span class="n">stable</span><span class="p">.</span><span class="n">count</span><span class="p">()</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">3</span><span class="p">);</span>
<span class="w"> </span><span class="n">assert</span><span class="p">(</span><span class="n">mockTom</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="k">nullptr</span><span class="p">);</span>
<span class="w"> </span><span class="n">assert</span><span class="p">(</span><span class="n">mockDick</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="k">nullptr</span><span class="p">);</span>
<span class="w"> </span><span class="n">assert</span><span class="p">(</span><span class="n">mockHarry</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="k">nullptr</span><span class="p">);</span>
<span class="w"> </span><span class="cm">/* Prepare for mocking the jump; these methods are generated by Deride and</span>
<span class="cm"> * allow setting the return value for the corresponding jumpHeight() method</span>
<span class="cm"> * from the original Horse class. */</span>
<span class="w"> </span><span class="n">mockTom</span><span class="o">-></span><span class="n">setJumpHeightResult</span><span class="p">(</span><span class="mf">1.5</span><span class="p">);</span>
<span class="w"> </span><span class="n">mockDick</span><span class="o">-></span><span class="n">setJumpHeightResult</span><span class="p">(</span><span class="mf">1.7</span><span class="p">);</span>
<span class="w"> </span><span class="n">mockHarry</span><span class="o">-></span><span class="n">setJumpHeightResult</span><span class="p">(</span><span class="mf">1.3</span><span class="p">);</span>
<span class="w"> </span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="w"> </span><span class="n">highestJumper</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">stable</span><span class="p">.</span><span class="n">findHighestJumper</span><span class="p">();</span>
<span class="w"> </span><span class="n">assert</span><span class="p">(</span><span class="n">highestJumper</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="s">"Dick"</span><span class="p">);</span>
</pre></div>
<p>In my closing words I'd like to thank the <a href="https://clang.llvm.org/">Clang
project</a>, which Deride is using to parse and interpret
the input files, and <a href="https://pypi.org/project/Jinja2/">Jinja2</a>, the templating
engine used to generate the mock code.</p><p>If you have been writing C++ classes for mocking out your C or C++
dependencies, you know how tedious it is. I generally write small classes with
just a handful of methods, so it's generally bearable, but when using
third-party code I'm usually not that lucky. If the dependency is a C library
this becomes especially tricky, both because they might be larger than what you
can handle, and both because the lack of an object-oriented design might not
offer you an easy solution to store the mock object data.</p>
<p>But fear no more, <a href="https://pypi.org/project/deride/">Deride</a> is here!</p>
<p>I won't spend too many words describing it, since you can read its description
from the link above, where you will also find some example code. More examples,
by the way, can be found in the <code>example/</code> folder in the <a href="https://gitlab.com/mardy/deride">code
repository</a>, where you can see how it can be
used to mock both pure C++ and <code>QObject</code>-based classes, and C libraries.</p>
<p>What is most important for me to say now, is that the project is in alpha
state, meaning that I've tried it on a handful of header files only; it's
highly likely that it will not work on many real-life scenarios, and if that
happens I warmly invite you to <a href="https://gitlab.com/mardy/deride/-/issues">inform me by filing a bug
report</a> providing the include file
that was not properly processed.</p>
<p>I leave you with a short example of a unit test, written using Deride. The
class under test is called <code>Stable</code>, and internally it uses objects of type
<code>Horse</code>, that we decided to mock. We used Deride to generate the mocked
implementation and a <code>MockHorse</code> class which can be used to control the mocked
objects. When building the test, we won't link against the original
<code>horse.cpp</code>, but we'll only use the original <code>horse.h</code>; the implementation will
be found in <code>mock_horse.cpp</code>, generated by Deride. And in the corresponding
<code>mock_horse.h</code> file we'll find the <code>MockHorse</code> class with all the
<code>on<method>Called()</code> hooks which we can use to install our callbacks (either to
reimplement the object behaviour, or to just be notified on when its methods
are called).</p>
<div class="code"><pre class="code literal-block"><span class="w"> </span><span class="cm">/* This MockHorse is the object created by Deride</span>
<span class="cm"> * |</span>
<span class="cm"> * |</span>
<span class="cm"> * \|/</span>
<span class="cm"> * V</span>
<span class="cm"> */</span>
<span class="w"> </span><span class="k">using</span><span class="w"> </span><span class="n">Mock</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Animals</span><span class="o">::</span><span class="n">MockHorse</span><span class="p">;</span>
<span class="w"> </span><span class="n">std</span><span class="o">::</span><span class="n">list</span><span class="o"><</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">></span><span class="w"> </span><span class="n">horseNames</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="s">"Tom"</span><span class="p">,</span>
<span class="w"> </span><span class="s">"Dick"</span><span class="p">,</span>
<span class="w"> </span><span class="s">"Harry"</span><span class="p">,</span>
<span class="w"> </span><span class="p">};</span>
<span class="w"> </span><span class="cm">/* We could use a vector, but let's be explicit */</span>
<span class="w"> </span><span class="n">Mock</span><span class="w"> </span><span class="o">*</span><span class="n">mockTom</span><span class="p">;</span>
<span class="w"> </span><span class="n">Mock</span><span class="w"> </span><span class="o">*</span><span class="n">mockDick</span><span class="p">;</span>
<span class="w"> </span><span class="n">Mock</span><span class="w"> </span><span class="o">*</span><span class="n">mockHarry</span><span class="p">;</span>
<span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">createdHorses</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span>
<span class="w"> </span><span class="c1">// onConstructorCalled() is created by Deride and called when the mocked</span>
<span class="w"> </span><span class="c1">// Horse object is created</span>
<span class="w"> </span><span class="n">Animals</span><span class="o">::</span><span class="n">MockHorse</span><span class="o">::</span><span class="n">onConstructorCalled</span><span class="p">([</span><span class="o">&</span><span class="p">](</span><span class="k">const</span><span class="w"> </span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="w"> </span><span class="o">&</span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">std</span><span class="o">::</span><span class="n">cout</span><span class="w"> </span><span class="o"><<</span><span class="w"> </span><span class="s">"Horse instantiated: "</span><span class="w"> </span><span class="o"><<</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="o"><<</span><span class="w"> </span><span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="w"> </span><span class="n">createdHorses</span><span class="o">++</span><span class="p">;</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">name</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="s">"Tom"</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">mockTom</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Mock</span><span class="o">::</span><span class="n">latestInstance</span><span class="p">();</span>
<span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">name</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="s">"Dick"</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">mockDick</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Mock</span><span class="o">::</span><span class="n">latestInstance</span><span class="p">();</span>
<span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">name</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="s">"Harry"</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">mockHarry</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Mock</span><span class="o">::</span><span class="n">latestInstance</span><span class="p">();</span>
<span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">assert</span><span class="p">(</span><span class="nb">false</span><span class="p">);</span><span class="w"> </span><span class="c1">// should not be reached</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="p">});</span>
<span class="w"> </span><span class="n">Stable</span><span class="w"> </span><span class="n">stable</span><span class="p">;</span>
<span class="w"> </span><span class="n">stable</span><span class="p">.</span><span class="n">createHorses</span><span class="p">(</span><span class="n">horseNames</span><span class="p">);</span>
<span class="w"> </span><span class="cm">/* It's at this point that the contructor callbacks we defined above will</span>
<span class="cm"> * have been called. Let's double-check that indeed that's the case.</span>
<span class="cm"> */</span>
<span class="w"> </span><span class="n">assert</span><span class="p">(</span><span class="n">createdHorses</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">3</span><span class="p">);</span>
<span class="w"> </span><span class="n">assert</span><span class="p">(</span><span class="n">stable</span><span class="p">.</span><span class="n">count</span><span class="p">()</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">3</span><span class="p">);</span>
<span class="w"> </span><span class="n">assert</span><span class="p">(</span><span class="n">mockTom</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="k">nullptr</span><span class="p">);</span>
<span class="w"> </span><span class="n">assert</span><span class="p">(</span><span class="n">mockDick</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="k">nullptr</span><span class="p">);</span>
<span class="w"> </span><span class="n">assert</span><span class="p">(</span><span class="n">mockHarry</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="k">nullptr</span><span class="p">);</span>
<span class="w"> </span><span class="cm">/* Prepare for mocking the jump; these methods are generated by Deride and</span>
<span class="cm"> * allow setting the return value for the corresponding jumpHeight() method</span>
<span class="cm"> * from the original Horse class. */</span>
<span class="w"> </span><span class="n">mockTom</span><span class="o">-></span><span class="n">setJumpHeightResult</span><span class="p">(</span><span class="mf">1.5</span><span class="p">);</span>
<span class="w"> </span><span class="n">mockDick</span><span class="o">-></span><span class="n">setJumpHeightResult</span><span class="p">(</span><span class="mf">1.7</span><span class="p">);</span>
<span class="w"> </span><span class="n">mockHarry</span><span class="o">-></span><span class="n">setJumpHeightResult</span><span class="p">(</span><span class="mf">1.3</span><span class="p">);</span>
<span class="w"> </span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="w"> </span><span class="n">highestJumper</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">stable</span><span class="p">.</span><span class="n">findHighestJumper</span><span class="p">();</span>
<span class="w"> </span><span class="n">assert</span><span class="p">(</span><span class="n">highestJumper</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="s">"Dick"</span><span class="p">);</span>
</pre></div>
<p>In my closing words I'd like to thank the <a href="https://clang.llvm.org/">Clang
project</a>, which Deride is using to parse and interpret
the input files, and <a href="https://pypi.org/project/Jinja2/">Jinja2</a>, the templating
engine used to generate the mock code.</p>MiTubo 1.4 adds feed foldershttp://mardy.it/ia/blog/2022/10/mitubo-14-adds-feed-folders.html2022-10-10T17:40:09+03:002022-10-10T17:40:09+03:00Alberto Mardegan<p>Exactly one month has passed since the previous release, just the right time
needed to complete the feafure I've been working on since several weeks and to
fix a few bugfixes introduced with the previous release. So it's time a new
release of <a href="https://gitlab.com/mardy/mitubo">MiTubo</a>:</p>
<p></p><center>
<video id="video" controls preload="metadata" width="100%">
<source src="http://mardy.it/archivos/videos/mitubo-1.4.webm" type="video/webm">
</source></video>
<p></p></center>
<p>I realized that I'm not that good at making release videos, but the point of
the video above is to show that you can organize your feeds into folders. When
clicking on a folder, a page opens with the folder's contents; but you can also
directly click on a feed, as long as its preview is visible in the folder's
delegate, and then the feeds open directly. This means that if you organize the
feeds inside your folders so that the favourite ones are at the top, they'll
also be visible in the folder preview and you'll be able to jump to them in
just one click.</p>
<p>Maybe I'm not that good with textual explanations either, so why don't you
check it out for yourself? ☺ Get it at
<a href="http://www.mardy.it/mitubo">mardy.it/mitubo</a> (builds for Linux, Ubuntu Touch,
Windows and macOS are available)!</p><p>Exactly one month has passed since the previous release, just the right time
needed to complete the feafure I've been working on since several weeks and to
fix a few bugfixes introduced with the previous release. So it's time a new
release of <a href="https://gitlab.com/mardy/mitubo">MiTubo</a>:</p>
<p></p><center>
<video id="video" controls preload="metadata" width="100%">
<source src="http://mardy.it/archivos/videos/mitubo-1.4.webm" type="video/webm">
</source></video>
<p></p></center>
<p>I realized that I'm not that good at making release videos, but the point of
the video above is to show that you can organize your feeds into folders. When
clicking on a folder, a page opens with the folder's contents; but you can also
directly click on a feed, as long as its preview is visible in the folder's
delegate, and then the feeds open directly. This means that if you organize the
feeds inside your folders so that the favourite ones are at the top, they'll
also be visible in the folder preview and you'll be able to jump to them in
just one click.</p>
<p>Maybe I'm not that good with textual explanations either, so why don't you
check it out for yourself? ☺ Get it at
<a href="http://www.mardy.it/mitubo">mardy.it/mitubo</a> (builds for Linux, Ubuntu Touch,
Windows and macOS are available)!</p>MiTubo 1.3: sorting of QML ListView via Drag&Drophttp://mardy.it/ia/blog/2022/09/mitubo-13-sorting-of-qml-listview-via-dragdrop.html2022-09-10T13:38:51+03:002022-09-10T13:38:51+03:00Alberto Mardegan<p>One feature that I've been asked to add to
<a href="https://gitlab.com/mardy/mitubo">MiTubo</a>, and that indeed becomes more and
more important as the number of subscriptions increases, is the ability to
group subscriptions into folders. I've spent a good amount of time implementing
the needed support in the C++ backend, which is now able to handle nested
folders too, but given that building the UI parts was not a quick task and
seeing how much time has passed since the last release, I thought of releasing
a partial implementation of the whole feature, consisting only of the ability
to manually sort the subscriptions via drag&drop (that, is no folder support).
It turns out this is already not a trivial work!</p>
<p>I found a <a href="https://agateau.com/2016/reordering-a-listview-via-dragndrop-3/">nice tutorial on ListView DnD
sorting</a> by
the great Aurélien Gâteau which I found very inspiring, and while I didn't
actually reuse the same code (mostly because I was already halfway through with
my implementation, which I started before finding his tutorial), it was helpful
to have it as a reference. I added a few animations to make it look more
pleasant, and I'm rather satisfied with the result:</p>
<p></p><center>
<video id="video" controls preload="metadata" width="100%">
<source src="http://mardy.it/archivos/videos/mitubo-1.3.webm" type="video/webm">
</source></video>
<p></p></center>
<p>I'm not showing you the code yet (though, indeed, you can find it in the
<code>DraggableListView</code> and <code>DraggableDelegate</code> items <a href="https://gitlab.com/mardy/mitubo/-/tree/master/src/desktop/qml">in the source
repository</a>)
because it's not yet in a shape where it's generally reusable in other
projects, but if I happen to need the same feature elsewhere I'll eventually
try to turn it into a couple fully reusable components.</p>
<p>Anyway, here's what's new in this latest MiTubo release:</p>
<ul>
<li>Subscriptions can be sorted by means of Drag&drop</li>
<li>For systems with python 3.5 or older (such as Ubuntu Touch), use the daily
builds of <a href="https://youtube-dl.org/">youtube-dl</a> instead of the official
releases</li>
<li>Implement <strong>PeerTube search</strong> (this should reserve a post of its own!)</li>
</ul>
<p>You can get it <a href="http://www.mardy.it/mitubo">at the usual place</a>. This time
there are only Linux and Windows builds as I'm a bit lazy to make a macOS
version, but should you need it, don't hesitate to ask!</p><p>One feature that I've been asked to add to
<a href="https://gitlab.com/mardy/mitubo">MiTubo</a>, and that indeed becomes more and
more important as the number of subscriptions increases, is the ability to
group subscriptions into folders. I've spent a good amount of time implementing
the needed support in the C++ backend, which is now able to handle nested
folders too, but given that building the UI parts was not a quick task and
seeing how much time has passed since the last release, I thought of releasing
a partial implementation of the whole feature, consisting only of the ability
to manually sort the subscriptions via drag&drop (that, is no folder support).
It turns out this is already not a trivial work!</p>
<p>I found a <a href="https://agateau.com/2016/reordering-a-listview-via-dragndrop-3/">nice tutorial on ListView DnD
sorting</a> by
the great Aurélien Gâteau which I found very inspiring, and while I didn't
actually reuse the same code (mostly because I was already halfway through with
my implementation, which I started before finding his tutorial), it was helpful
to have it as a reference. I added a few animations to make it look more
pleasant, and I'm rather satisfied with the result:</p>
<p></p><center>
<video id="video" controls preload="metadata" width="100%">
<source src="http://mardy.it/archivos/videos/mitubo-1.3.webm" type="video/webm">
</source></video>
<p></p></center>
<p>I'm not showing you the code yet (though, indeed, you can find it in the
<code>DraggableListView</code> and <code>DraggableDelegate</code> items <a href="https://gitlab.com/mardy/mitubo/-/tree/master/src/desktop/qml">in the source
repository</a>)
because it's not yet in a shape where it's generally reusable in other
projects, but if I happen to need the same feature elsewhere I'll eventually
try to turn it into a couple fully reusable components.</p>
<p>Anyway, here's what's new in this latest MiTubo release:</p>
<ul>
<li>Subscriptions can be sorted by means of Drag&drop</li>
<li>For systems with python 3.5 or older (such as Ubuntu Touch), use the daily
builds of <a href="https://youtube-dl.org/">youtube-dl</a> instead of the official
releases</li>
<li>Implement <strong>PeerTube search</strong> (this should reserve a post of its own!)</li>
</ul>
<p>You can get it <a href="http://www.mardy.it/mitubo">at the usual place</a>. This time
there are only Linux and Windows builds as I'm a bit lazy to make a macOS
version, but should you need it, don't hesitate to ask!</p>MiTubo comes to macOShttp://mardy.it/ia/blog/2022/06/mitubo-comes-to-macos.html2022-06-22T22:44:47+03:002022-06-22T22:44:47+03:00Alberto Mardegan<p>I just released <a href="http://mardy.it/mitubo/#downloads">MiTubo 1.2</a>. New in this version:</p>
<ul>
<li>As suggested by <code>alphas12</code> in the comments, I added the author name in the
YouTube search results.</li>
<li>In the same results list, there's now a clickable link to the channel, which
makes it easier to subscribe to it.</li>
<li>Improve layout of some pages on narrow displays (though there's still much to
be done!).</li>
<li>Skip invoking youtube-dl if the video information is already encoded in the
page <code>HEAD</code> meta properties.</li>
<li>Remember the preferred playback resolution; this can be helpful on low
bandwith connections.</li>
<li>First macOS release!</li>
</ul>
<p>While bringing in the macOS version, I updated the <a href="https://gitlab.com/qt-goodies/qscreensaver">QScreenSaver
library</a> to support inhibiting the
screensaver on macOS too.</p>
<p>I also tested the AppImage on openSUSE, and it seems to work fine there too.
So, fewer and fewer people have valid excuses not to try out MiTubo!</p><p>I just released <a href="http://mardy.it/mitubo/#downloads">MiTubo 1.2</a>. New in this version:</p>
<ul>
<li>As suggested by <code>alphas12</code> in the comments, I added the author name in the
YouTube search results.</li>
<li>In the same results list, there's now a clickable link to the channel, which
makes it easier to subscribe to it.</li>
<li>Improve layout of some pages on narrow displays (though there's still much to
be done!).</li>
<li>Skip invoking youtube-dl if the video information is already encoded in the
page <code>HEAD</code> meta properties.</li>
<li>Remember the preferred playback resolution; this can be helpful on low
bandwith connections.</li>
<li>First macOS release!</li>
</ul>
<p>While bringing in the macOS version, I updated the <a href="https://gitlab.com/qt-goodies/qscreensaver">QScreenSaver
library</a> to support inhibiting the
screensaver on macOS too.</p>
<p>I also tested the AppImage on openSUSE, and it seems to work fine there too.
So, fewer and fewer people have valid excuses not to try out MiTubo!</p>MiTubo 1.1: screensaver inhibitorhttp://mardy.it/ia/blog/2022/06/mitubo-11-screensaver-inhibitor.html2022-06-04T13:46:54+03:002022-06-04T13:46:54+03:00Alberto Mardegan<p>Looks like I'm posting a bit too often about <a href="http://mardy.it/mitubo">MiTubo</a>, but don't
worry, I'll soon find something else to write about.</p>
<p>Version 1.1 is now released, bringing you:</p>
<ul>
<li>A screensaver inhibitor, at last!</li>
<li>AppImage now works in Arch and Manjaro</li>
</ul>
<p>More in detail, this means that your computer won't go to sleep or start the
screen saver while you are watching a video. To achieve this, I wrote a
portable (well, for the time being it only supports Linux and Windows, but
macOS support will eventually arrive too) library for inhibiting the
screensaver: <a href="https://gitlab.com/qt-goodies/qscreensaver">QScreenSaver</a>. It
lives in its own repository, and it's written in a way that should be easy to
integrate with your own project. You are welcome to try it out (and add support
for cmake/qmake/meson/…).</p>
<p>The AppImage support has been improved after a user filed a bug about MiTubo
not working in Arch; I myself could not verify the issue as I've grown too lazy
to install a distribution like Arch, but I did it on Manjaro (which is also
based on Arch), and indeed the MiTubo AppImage contained some unnecessary
libraries (while missing some others) that rendered it non-functional in those
distributions. Now it's all fixed, so there's a good chance that the AppImage
will work on your distribution, too.</p><p>Looks like I'm posting a bit too often about <a href="http://mardy.it/mitubo">MiTubo</a>, but don't
worry, I'll soon find something else to write about.</p>
<p>Version 1.1 is now released, bringing you:</p>
<ul>
<li>A screensaver inhibitor, at last!</li>
<li>AppImage now works in Arch and Manjaro</li>
</ul>
<p>More in detail, this means that your computer won't go to sleep or start the
screen saver while you are watching a video. To achieve this, I wrote a
portable (well, for the time being it only supports Linux and Windows, but
macOS support will eventually arrive too) library for inhibiting the
screensaver: <a href="https://gitlab.com/qt-goodies/qscreensaver">QScreenSaver</a>. It
lives in its own repository, and it's written in a way that should be easy to
integrate with your own project. You are welcome to try it out (and add support
for cmake/qmake/meson/…).</p>
<p>The AppImage support has been improved after a user filed a bug about MiTubo
not working in Arch; I myself could not verify the issue as I've grown too lazy
to install a distribution like Arch, but I did it on Manjaro (which is also
based on Arch), and indeed the MiTubo AppImage contained some unnecessary
libraries (while missing some others) that rendered it non-functional in those
distributions. Now it's all fixed, so there's a good chance that the AppImage
will work on your distribution, too.</p>MiTubo 1.0: playlist support, new “website”http://mardy.it/ia/blog/2022/05/mitubo-10-playlist-support-new-website.html2022-05-21T15:53:56+03:002022-05-21T15:53:56+03:00Alberto Mardegan<p>Some news from the MiTubo world:</p>
<ul>
<li>Version 1.0 has been released!</li>
<li>It's also available for Windows (boo!)</li>
<li>Some basic support for remote playlists</li>
<li>New “Check for updates” dialog</li>
<li>Added support for translations</li>
<li>Added Italian translation, of course</li>
<li>Minor cosmetic changes (like using a different unicode symbol for the “Back” button)</li>
<li>New web page for MiTubo</li>
</ul>
<p>Expanding a bit on the points above, the first thing worth saying is that the
choice of releasing this version as “1.0” does not mean that it's more stable
than the previous ones; it just means that I'm rather satisfied with the
feature set, and that I believe that the program is ready for more widespread
use.</p>
<p>This is also the reason why I decided to prepare a web page for it:
<a href="http://mardy.it/mitubo">mardy.it/mitubo</a>. I didn't go for a completely separate website,
unlike what I previously did for <a href="https://mappero.mardy.it">Mappero Geotagger</a>,
<a href="https://phototeleport.com">PhotoTeleport</a> and
<a href="https://imaginario.mardy.it">Imaginario</a> (which reminds me that I haven't been
working on the latter for a long time! I should try to correct this soon!),
both because this way it's simpler to publish news about it (I'll continue
doing that here, instead of cross-posting in two sites), and because having it
in the same domain might be mutually beneficial for the SEO ranking of the blog
and of MiTubo.</p>
<p>As for the Windows version, I want to thank <a href="http://mardy.it/ia/blog/2020/04/new-website-for-mappero-geotagger.html">once
again</a>
the <a href="http://mxe.cc">MXE project</a> for their fantastic cross-compiling suite. I
find it very cumbersome working in Windows, and being able to build my programs
from Linux makes my life a lot easier (if you want to have more information
about how this works with QBS, have a look at the previous MXE post). I wish
there was something similar for macOS; and that's why the macOs version is
going to take more time to arrive — on the other hand, I haven't received any
requests for it, so I'm not in a hurry to work on that.</p>
<p>Last but not least, translation support means that if you want to help with
translations, now you can. I've myself tried
<a href="https://doc.qt.io/qt-5/linguist-translators.html">QtLinguist</a> for the first
time to write the Italian translation, and I found it to be an extremely
effective tool, once you learn the key bindings by heart.</p><p>Some news from the MiTubo world:</p>
<ul>
<li>Version 1.0 has been released!</li>
<li>It's also available for Windows (boo!)</li>
<li>Some basic support for remote playlists</li>
<li>New “Check for updates” dialog</li>
<li>Added support for translations</li>
<li>Added Italian translation, of course</li>
<li>Minor cosmetic changes (like using a different unicode symbol for the “Back” button)</li>
<li>New web page for MiTubo</li>
</ul>
<p>Expanding a bit on the points above, the first thing worth saying is that the
choice of releasing this version as “1.0” does not mean that it's more stable
than the previous ones; it just means that I'm rather satisfied with the
feature set, and that I believe that the program is ready for more widespread
use.</p>
<p>This is also the reason why I decided to prepare a web page for it:
<a href="http://mardy.it/mitubo">mardy.it/mitubo</a>. I didn't go for a completely separate website,
unlike what I previously did for <a href="https://mappero.mardy.it">Mappero Geotagger</a>,
<a href="https://phototeleport.com">PhotoTeleport</a> and
<a href="https://imaginario.mardy.it">Imaginario</a> (which reminds me that I haven't been
working on the latter for a long time! I should try to correct this soon!),
both because this way it's simpler to publish news about it (I'll continue
doing that here, instead of cross-posting in two sites), and because having it
in the same domain might be mutually beneficial for the SEO ranking of the blog
and of MiTubo.</p>
<p>As for the Windows version, I want to thank <a href="http://mardy.it/ia/blog/2020/04/new-website-for-mappero-geotagger.html">once
again</a>
the <a href="http://mxe.cc">MXE project</a> for their fantastic cross-compiling suite. I
find it very cumbersome working in Windows, and being able to build my programs
from Linux makes my life a lot easier (if you want to have more information
about how this works with QBS, have a look at the previous MXE post). I wish
there was something similar for macOS; and that's why the macOs version is
going to take more time to arrive — on the other hand, I haven't received any
requests for it, so I'm not in a hurry to work on that.</p>
<p>Last but not least, translation support means that if you want to help with
translations, now you can. I've myself tried
<a href="https://doc.qt.io/qt-5/linguist-translators.html">QtLinguist</a> for the first
time to write the Italian translation, and I found it to be an extremely
effective tool, once you learn the key bindings by heart.</p>Mitubo 0.9: multiple concurrent video downloadshttp://mardy.it/ia/blog/2022/05/mitubo-09-multiple-concurrent-video-downloads.html2022-05-05T22:16:40+03:002022-05-05T22:16:40+03:00Alberto Mardegan<p>It will never stop surprising me how easy it is to implement big new features
in a QML application! The assumption here is that the C++ part of the
application should be well-written: objects should not be overloaded with
unrelated functionalities just because it seems faster to code them that way,
but one should rather design classes so that each exposes <em>one</em> functionality,
and then QML and javascript act as the glue which binds all the parts together.</p>
<p>In a way, <strong>QML stands to C++ classes like the POSIX shell stands to
command-line tools</strong>: a simple language which allows concatenating small units
of functionality together to build a powerful program.</p>
<p>Anyway, that was not what I wanted to talk you about today. ☺ Today's post is
about <a href="https://gitlab.com/mardy/mitubo">MiTubo</a>, whose version 0.9 has been
released today:</p>
<p></p><center>
<video id="video" controls preload="metadata" width="100%">
<source src="http://mardy.it/archivos/videos/mitubo-0.9.webm" type="video/webm">
</source></video>
<p></p></center>
<p>The big feature in this release is download of audio/video files: I thought,
since I'm using <a href="https://github.com/yt-dlp/yt-dlp">yt-dlp</a> (or
<a href="https://youtube-dl.org/">youtube-dl</a> on Ubuntu Touch) anyway for
extracting video streams, why not add an option to let users download the media
content? This turned out to be easier than expected, so if you were looking for
a graphical frontend to the YouTube downloader, well, now MiTubo is an option
you could try.</p><p>It will never stop surprising me how easy it is to implement big new features
in a QML application! The assumption here is that the C++ part of the
application should be well-written: objects should not be overloaded with
unrelated functionalities just because it seems faster to code them that way,
but one should rather design classes so that each exposes <em>one</em> functionality,
and then QML and javascript act as the glue which binds all the parts together.</p>
<p>In a way, <strong>QML stands to C++ classes like the POSIX shell stands to
command-line tools</strong>: a simple language which allows concatenating small units
of functionality together to build a powerful program.</p>
<p>Anyway, that was not what I wanted to talk you about today. ☺ Today's post is
about <a href="https://gitlab.com/mardy/mitubo">MiTubo</a>, whose version 0.9 has been
released today:</p>
<p></p><center>
<video id="video" controls preload="metadata" width="100%">
<source src="http://mardy.it/archivos/videos/mitubo-0.9.webm" type="video/webm">
</source></video>
<p></p></center>
<p>The big feature in this release is download of audio/video files: I thought,
since I'm using <a href="https://github.com/yt-dlp/yt-dlp">yt-dlp</a> (or
<a href="https://youtube-dl.org/">youtube-dl</a> on Ubuntu Touch) anyway for
extracting video streams, why not add an option to let users download the media
content? This turned out to be easier than expected, so if you were looking for
a graphical frontend to the YouTube downloader, well, now MiTubo is an option
you could try.</p>Looking for Qt support? Get in touch!http://mardy.it/ia/blog/2022/04/looking-for-qt-support-get-in-touch.html2022-04-13T22:11:43+03:002022-04-13T22:11:43+03:00Alberto Mardegan<p>Following The Qt Company's
<a href="https://forum.qt.io/topic/134724/unlock-qt-in-russia">decision</a> to withdraw
support for the Russian market, KDAB's geolocation block of its website and
Upwork stopping its operations in Russia, there's a likely need of Qt support
in the country.</p>
<p>As a developer living in Russia and loving Qt (I spend a considerable amount of
my free time on Qt-based projects), I would find it very unfortunate if some
companies decided to switch to other technologies just for the lack of support.</p>
<p><strong>That's why, with this post, I want to advertise my willingness to help
companies deliver their Qt-based projects.</strong></p>
<p>I need to be honest and admit that I do have a <a href="https://github.com/snapcore/snapd">fantastic full-time
job</a> and that's unlikely that I will be able
to dedicate more than 10-15 hours per week on this effort, but even this small
amount of time has been so far enough to drive several projects
(<a href="https://github.com/ubports/media-hub/pull/28">here's</a>
<a href="https://gitlab.com/ubports/core/fm-radio-service">a</a>
<a href="https://gitlab.com/mardy/photokinesis">few</a>
<a href="https://gitlab.com/mardy/mitubo">examples</a>); and what's more important, I
might be able to get more coding help.</p>
<p>I do have a <a href="https://codereview.qt-project.org/q/owner:mardy%2540users.sourceforge.net">history of contributions to the Qt
project</a>
starting from 2012, developed for the biggest part in my spare time.</p>
<h4>Get in touch!</h4>
<p>Whether you are a company or a Qt expert with some free time to spare and you'd
like to work with me, you are very welcome to <a href="mailto:info@mardyit">drop me a
line</a>. We can try to find a solution for those companies
who need Qt support, and create a network of professionals who can commit to do
some Qt work.</p>
<p>(While I mentioned Russia above, this idea is not limited to Russian companies
or developers in any way! Everybody is welcome, and I promise we'll stay
politics-free.)</p><p>Following The Qt Company's
<a href="https://forum.qt.io/topic/134724/unlock-qt-in-russia">decision</a> to withdraw
support for the Russian market, KDAB's geolocation block of its website and
Upwork stopping its operations in Russia, there's a likely need of Qt support
in the country.</p>
<p>As a developer living in Russia and loving Qt (I spend a considerable amount of
my free time on Qt-based projects), I would find it very unfortunate if some
companies decided to switch to other technologies just for the lack of support.</p>
<p><strong>That's why, with this post, I want to advertise my willingness to help
companies deliver their Qt-based projects.</strong></p>
<p>I need to be honest and admit that I do have a <a href="https://github.com/snapcore/snapd">fantastic full-time
job</a> and that's unlikely that I will be able
to dedicate more than 10-15 hours per week on this effort, but even this small
amount of time has been so far enough to drive several projects
(<a href="https://github.com/ubports/media-hub/pull/28">here's</a>
<a href="https://gitlab.com/ubports/core/fm-radio-service">a</a>
<a href="https://gitlab.com/mardy/photokinesis">few</a>
<a href="https://gitlab.com/mardy/mitubo">examples</a>); and what's more important, I
might be able to get more coding help.</p>
<p>I do have a <a href="https://codereview.qt-project.org/q/owner:mardy%2540users.sourceforge.net">history of contributions to the Qt
project</a>
starting from 2012, developed for the biggest part in my spare time.</p>
<h4>Get in touch!</h4>
<p>Whether you are a company or a Qt expert with some free time to spare and you'd
like to work with me, you are very welcome to <a href="mailto:info@mardyit">drop me a
line</a>. We can try to find a solution for those companies
who need Qt support, and create a network of professionals who can commit to do
some Qt work.</p>
<p>(While I mentioned Russia above, this idea is not limited to Russian companies
or developers in any way! Everybody is welcome, and I promise we'll stay
politics-free.)</p>MiTubo 0.8: search, channels, watch later queuehttp://mardy.it/ia/blog/2022/04/mitubo-update-search-channels-watch-later-queue.html2022-04-10T19:06:30+03:002022-04-10T19:06:30+03:00Alberto Mardegan<p>It has been a while since I last posted about
<a href="https://gitlab.com/mardy/mitubo">MiTubo</a>, despite releasing a few new versions
in the last months. But now I think that there is enough new stuff that's worth
a mention here.</p>
<p></p><center>
<figure>
<a href="http://mardy.it/archivos/imagines/blog/Mitubo-search.png"><img src="http://mardy.it/archivos/imagines/blog/Mitubo-search.png" width="80%"></a>
<figcaption>Search on YouTube</figcaption>
</figure>
<p></p></center>
<p>Initially MiTubo only came with a search feature that was using Yandex video as
a backend; while that worked generally well, most of the returned results were
not playable due to youtube-dl being unable to extract the video information
from the returned web pages. So, now we have the option to search on YouTube,
via the <a href="https://invidious.io/">Invidious API</a>; furthermore, it's also possible
to search for channels, whose RSS feed can then be added as a subscription.</p>
<p>One other thing that has always bothered me is not being able to enqueue a new
video while watching another one, without having to pause the current one, go
back to the main page, add the new video to a playlist, go back to the current
video, and finally, once that is over, open the playlist and start the new
video.</p>
<p></p><center>
<figure>
<a href="http://mardy.it/archivos/imagines/blog/Mitubo-dnd.png"><img src="http://mardy.it/archivos/imagines/blog/Mitubo-dnd.png" width="80%"></a>
<figcaption>Drag and drop a new URL while watching a video</figcaption>
</figure>
<p></p></center>
<p>So now we have that. One can drag and drop an URL (or even a longer text
containing several URLs) while watching another video, and a popup will appear
with a few choices (see the screenshot above). I've also added a “Next” button
next to the “Play” one, which is enabled if the “Watch later” playlist is not
empty, and does the obvious thing when pressed. Once started, the new video is
automatically removed from the “Watch later” playlist and moved into the
“Continue watching” list, where it will remain until it's being watched till
the end, which will cause it to part that list and be added to the watch
history.</p>
<p>To be fair, I haven't being testing this extensively, and this last one is a
feature I've developed just in a few hours during this weekend, so I wouldn't
be surprised if there are serious bugs in it. But hey, that's part of the
adrenaline which comes with hobby projects, I guess.</p><p>It has been a while since I last posted about
<a href="https://gitlab.com/mardy/mitubo">MiTubo</a>, despite releasing a few new versions
in the last months. But now I think that there is enough new stuff that's worth
a mention here.</p>
<p></p><center>
<figure>
<a href="http://mardy.it/archivos/imagines/blog/Mitubo-search.png"><img src="http://mardy.it/archivos/imagines/blog/Mitubo-search.png" width="80%"></a>
<figcaption>Search on YouTube</figcaption>
</figure>
<p></p></center>
<p>Initially MiTubo only came with a search feature that was using Yandex video as
a backend; while that worked generally well, most of the returned results were
not playable due to youtube-dl being unable to extract the video information
from the returned web pages. So, now we have the option to search on YouTube,
via the <a href="https://invidious.io/">Invidious API</a>; furthermore, it's also possible
to search for channels, whose RSS feed can then be added as a subscription.</p>
<p>One other thing that has always bothered me is not being able to enqueue a new
video while watching another one, without having to pause the current one, go
back to the main page, add the new video to a playlist, go back to the current
video, and finally, once that is over, open the playlist and start the new
video.</p>
<p></p><center>
<figure>
<a href="http://mardy.it/archivos/imagines/blog/Mitubo-dnd.png"><img src="http://mardy.it/archivos/imagines/blog/Mitubo-dnd.png" width="80%"></a>
<figcaption>Drag and drop a new URL while watching a video</figcaption>
</figure>
<p></p></center>
<p>So now we have that. One can drag and drop an URL (or even a longer text
containing several URLs) while watching another video, and a popup will appear
with a few choices (see the screenshot above). I've also added a “Next” button
next to the “Play” one, which is enabled if the “Watch later” playlist is not
empty, and does the obvious thing when pressed. Once started, the new video is
automatically removed from the “Watch later” playlist and moved into the
“Continue watching” list, where it will remain until it's being watched till
the end, which will cause it to part that list and be added to the watch
history.</p>
<p>To be fair, I haven't being testing this extensively, and this last one is a
feature I've developed just in a few hours during this weekend, so I wouldn't
be surprised if there are serious bugs in it. But hey, that's part of the
adrenaline which comes with hobby projects, I guess.</p>