Maho's Bloghttp://maho.pro/2022-11-08T00:00:00+01:00a.k.a. Łukasz MachPycon2022 is alive!2022-11-08T00:00:00+01:002022-11-08T00:00:00+01:00Mahotag:maho.pro,2022-11-08:/pycon2022-alive-pl.html<p>Przedwczoraj zakończył się <a href="https://pl.pycon.org/2022/en/agenda/">PyconPL 2022</a>. Wspaniale
że impreza jeszcze żyje. Co prawda jest to na razie cień tego PyconPL który pamiętam z
roku 2018, ale i tak jest merytorycznie o wiele lepiej niż na fajnym przecież
<a href="https://cz.pycon.org/2020/">PyconCZ</a>. No i dużo lepiej pod każdym względem niż na
<a href="https://www.pycon.fr/2019/">PyconFR 2019</a>. </p>
<p>Szczególnie ciekawa …</p><p>Przedwczoraj zakończył się <a href="https://pl.pycon.org/2022/en/agenda/">PyconPL 2022</a>. Wspaniale
że impreza jeszcze żyje. Co prawda jest to na razie cień tego PyconPL który pamiętam z
roku 2018, ale i tak jest merytorycznie o wiele lepiej niż na fajnym przecież
<a href="https://cz.pycon.org/2020/">PyconCZ</a>. No i dużo lepiej pod każdym względem niż na
<a href="https://www.pycon.fr/2019/">PyconFR 2019</a>. </p>
<p>Szczególnie ciekawa była prezentacja Miro Hrončoka pt <em>Making sudo pip safe again</em>.
Niezła była też Wojtka Rząsy <em>From HTTP to Kafka-based microservices (There and Back
Again?)</em>. Bardzo ciekawa analiza różnych cornercasów asynchroniczności. Należy też
wspomnieć o <em>Development with Apache Spark: Scala vs. PySpark</em>
<a href="https://kolodziejj.info/">Jacka Kołodzieja</a></p>
<p>(Ciekawe kiedy pojawią się te prezentacje na youtube, żeby można było je podlinkować). </p>
<p>Byłem też tam i ja, z moim <a href="http://maho.pro/micropython-vs-dog.html">Szczuciem psa (micro)pythonem</a>.</p>
<p><img alt="ja przy prezentacji" src="http://maho.pro/images/pycon2022/ja-upython.jpg"> foto by <a href="https://kolodziejj.info/">Jacek Kołodziej</a></p>
<p>I ciekawostka - Gliwice, miasto jak wiele innych, odznacza się czymś czego nie rozumiem.
Stan infrastruktury rowerowej jest na poziomie takim jaki pamiętam z okolic roku 2000 z
Lublina. Tj - dwie fajne ścieżki rowerowe na krzyż i tyle. </p>
<p>Może nawet jest gorzej, bo nie ma do czego przypiąć roweru. Nie tylko nie ma
nowoczesnych stojaków na rower. Nie ma też stojaków typu <em>wyrwikółka</em>. Nie ma nawet
ogrodzeń ani barierek do których mógłbym się na dziko przyczepić. Pustynia pod tym
względem. </p>
<p>Np. pod <a href="https://www.polsl.pl/rju2-cek/">Centrum Kongresowym</a> plac wygląda tak: (screen z
Google Maps - moje zdjęcia gdzieś zginęły)</p>
<p><img alt="pusty plac" src="http://maho.pro/images/pycon2022/plac.png"></p>
<p>Jak widać, nie ma tam absolutnie gdzie przypiąć rower. </p>
<p>Musiałem przejść kawał drogi, żeby na granicy placu, za murkiem, znaleźć znak drogowy do którego
się przypiąłem.</p>
<p><img alt="rower przypięty do znaku" src="http://maho.pro/images/pycon2022/rower-przy-znaku.jpg"></p>
<p>Czy to dlatego że nikt tam nie kradnie rowerów? Czy wręcz przeciwnie - tak kradną że nie
kalkuluje się inwestować w przypinanie?</p>Pycon2022 is alive!2022-11-08T00:00:00+01:002022-11-08T00:00:00+01:00Mahotag:maho.pro,2022-11-08:/pycon2022-alive.html<p><a href="https://pl.pycon.org/2022/en/agenda/">PyconPL 2022</a> has finished 2 days ago. It's
terrific that this conference is still alive. It's not as splendid as I remember it at
2018, but in merite meaning it's still way better than <a href="https://cz.pycon.org/2019/">PyconCZ</a>,
which was very fine though. And it's way better in any meaning than <a href="https://www.pycon.fr/2019/">PyconFR 2019 …</a></p><p><a href="https://pl.pycon.org/2022/en/agenda/">PyconPL 2022</a> has finished 2 days ago. It's
terrific that this conference is still alive. It's not as splendid as I remember it at
2018, but in merite meaning it's still way better than <a href="https://cz.pycon.org/2019/">PyconCZ</a>,
which was very fine though. And it's way better in any meaning than <a href="https://www.pycon.fr/2019/">PyconFR 2019</a>. </p>
<p>The talk of Miro Hrončok <em>Making sudo pip safe again</em> was the most interesting. Also
Wojtek Rząsa delivered very good <em>From HTTP to Kafka-based microservices (There and Back
Again?)</em>. Very interesting analysis of various corner-cases of asynchronicity.
The <em>Development with Apache Spark: Scala vs. PySpark</em> of
<a href="https://kolodziejj.info/">Jacka Kołodzieja</a> is also worth mentioning.</p>
<p>I was also there, with my <a href="http://maho.pro/micropython-vs-dog.html">Szczuciem psa (micro)pythonem</a>.</p>
<p><img alt="ja przy prezentacji" src="http://maho.pro/images/pycon2022/ja-upython.jpg"> foto by <a href="https://kolodziejj.info/">Jacek Kołodziej</a></p>
<p>And fun fact - Gliwice is a city like many others. However it has something which I don't
understand. The state of bicycle infrastructure is like I remember from Lublin near year
2000. Which means 2 bicycle lanes crossing in the middle of the city and that's all. </p>
<p>I would say that it's even worse, because you have no way to secure your bike by locking
it to something. It's not that there is no modern bike stand. There is also no oldschool
<em>wheel-puller</em> bike racks. There is also no steel fences no barriers which I could use as
thing to mount my bike. It's locking-bike-desert. </p>
<p>Eg. square behind <a href="https://www.polsl.pl/rju2-cek/">Congress Center</a> looks like that: (screen from
Google Maps - I've lost my photos somehow)</p>
<p><img alt="pusty plac" src="http://maho.pro/images/pycon2022/plac.png"></p>
<p>As you can see, there is absolutely no place to lock a bike.</p>
<p>I had to walk quite a distance, to find a road sign, behind the wall. I have mounted my
bike to it's rod. </p>
<p><img alt="rower przypięty do znaku" src="http://maho.pro/images/pycon2022/rower-przy-znaku.jpg"></p>
<p>I wonder if it's because nobody steals bikes in Gliwice, or in contrary - they steal so
much that there is no point to invest in locking bikes?</p>Micropython vs Dog2022-11-06T00:00:00+01:002022-11-06T00:00:00+01:00Mahotag:maho.pro,2022-11-06:/micropython-vs-dog.html<p>Slides of my <a href="https://pl.pycon.org/2022/en/agenda/">PyconPL 2022</a> talk is
<a href="talks/upython-vs-dog-prez/prez/">here (link)</a></p>IM diversity2021-02-07T00:00:00+01:002021-02-07T00:00:00+01:00Mahotag:maho.pro,2021-02-07:/im-diversity.html<p>10 years ago, we had one IM to rule them all. Now, to be in touch with my friends, I need
to have such kaleidoscope on the desktop. </p>
<p>And I forgot about Viber :)</p>
<p><img alt="im-diversity" src="http://maho.pro/images/im-diversity.png"></p>Proof of Concept2020-07-27T00:00:00+02:002020-07-27T00:00:00+02:00Mahotag:maho.pro,2020-07-27:/proof-of-concept.html<p>Proof of Concept doesn't need to look well. </p>
<p><img alt="POC" src="http://maho.pro/images/ubiquity/POC1.jpg"></p>Is my ESP32 bricked again?2020-07-18T00:00:00+02:002020-07-18T00:00:00+02:00Mahotag:maho.pro,2020-07-18:/is-my-esp32-bricked-again.html<p>I just returned to my IoT project. Pulled esp32 from box, connected with usb cable to
raspberry pi, did <code>minicom -D /dev/ttyUSB0</code> and .... nothing. No reaction to my input,
<code>help()</code> doesn't work. No output. </p>
<p>Have I bricked another ESP32? </p>
<p>I thought so, then I started to play with options …</p><p>I just returned to my IoT project. Pulled esp32 from box, connected with usb cable to
raspberry pi, did <code>minicom -D /dev/ttyUSB0</code> and .... nothing. No reaction to my input,
<code>help()</code> doesn't work. No output. </p>
<p>Have I bricked another ESP32? </p>
<p>I thought so, then I started to play with options in settings and voila!. Here is reason
for such strange reason: hardware control was turned on, and apparently ESP32 doesn't work
well with it. </p>
<p><img alt="wrong setting" src="http://maho.pro/images/minicom/bad.png"></p>
<p>Correct setting:</p>
<p><img alt="good setting" src="http://maho.pro/images/minicom/good.png"></p>
<p>How to get to it?. <code>Ctrl-Alt-Z O</code>, then <code>Serial port settings</code>.</p>
<p><img alt="menu" src="http://maho.pro/images/minicom/menu.png"></p>Self contained Django, or Django like Flask2020-05-14T00:00:00+02:002020-05-14T00:00:00+02:00Mahotag:maho.pro,2020-05-14:/self-contained-django-or-django-like-flask.html<p>Have you thought about spawning Django as easy as Flask?</p>
<p>Imagine that you have a library, which may have a feature that is usable in Django. How to test it?</p>
<p>99% of you would say - make a Django project, put test inside the project, and run <code>django
test</code>. </p>
<p>I thought …</p><p>Have you thought about spawning Django as easy as Flask?</p>
<p>Imagine that you have a library, which may have a feature that is usable in Django. How to test it?</p>
<p>99% of you would say - make a Django project, put test inside the project, and run <code>django
test</code>. </p>
<p>I thought about it, and a voice in my head was screaming: "don't do that. It's messy, it's
a lot of unneccesary files". And the voice was right. </p>
<p>After a bit of thinking, I developed such code (see below).</p>
<p>Look! You can instantiate Django like Flask! Few lines and voila - Django is running without
all those <code>settings.py</code>, <code>urls.py</code>, <code>views.py</code>, <code>model.py</code> :)</p>
<p>Here is the code:</p>
<div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">List</span>
<span class="kn">from</span> <span class="nn">django.http</span> <span class="kn">import</span> <span class="n">HttpResponse</span>
<span class="kn">from</span> <span class="nn">django.urls</span> <span class="kn">import</span> <span class="n">path</span>
<span class="kn">from</span> <span class="nn">django.test</span> <span class="kn">import</span> <span class="n">Client</span>
<span class="kn">import</span> <span class="nn">pytest</span>
<span class="nd">@pytest</span><span class="o">.</span><span class="n">fixture</span><span class="p">(</span><span class="n">scope</span><span class="o">=</span><span class="s1">'function'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">view_mappers</span><span class="p">():</span>
<span class="sd">""" just default, foobar root response """</span>
<span class="k">def</span> <span class="nf">rootview</span><span class="p">(</span><span class="o">*</span><span class="n">_a</span><span class="p">,</span> <span class="o">**</span><span class="n">_kwa</span><span class="p">):</span>
<span class="sd">""" just some rootview """</span>
<span class="k">return</span> <span class="n">HttpResponse</span><span class="p">(</span><span class="s1">'{"foo": "bar"}'</span><span class="p">,</span> <span class="n">content_type</span><span class="o">=</span><span class="s1">'application/json'</span><span class="p">)</span>
<span class="k">return</span> <span class="p">[(</span><span class="s2">""</span><span class="p">,</span> <span class="n">rootview</span><span class="p">)]</span>
<span class="nd">@pytest</span><span class="o">.</span><span class="n">fixture</span><span class="p">(</span><span class="n">scope</span><span class="o">=</span><span class="s1">'function'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">urls</span><span class="p">(</span><span class="n">view_mappers</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">tuple</span><span class="p">]):</span>
<span class="sd">""" fixture which provide urls pseudo module, with urlpatterns and embedded simple view """</span>
<span class="k">class</span> <span class="nc">UrlsPseudoModule</span><span class="p">:</span> <span class="c1"># pylint: disable=too-few-public-methods</span>
<span class="sd">""" class which emulates URLS of django """</span>
<span class="n">urlpatterns</span> <span class="o">=</span> <span class="p">[</span><span class="n">path</span><span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">view_mappers</span><span class="p">]</span>
<span class="k">return</span> <span class="n">UrlsPseudoModule</span>
<span class="nd">@pytest</span><span class="o">.</span><span class="n">fixture</span><span class="p">(</span><span class="n">scope</span><span class="o">=</span><span class="s1">'function'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">configure_django</span><span class="p">(</span><span class="n">urls</span><span class="p">):</span>
<span class="sd">""" configure django engine """</span>
<span class="kn">from</span> <span class="nn">django.conf</span> <span class="kn">import</span> <span class="n">settings</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">settings</span><span class="o">.</span><span class="n">configured</span><span class="p">:</span>
<span class="n">settings</span><span class="o">.</span><span class="n">configure</span><span class="p">(</span><span class="n">DEBUG</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
<span class="n">MIDDLEWARE</span><span class="o">=</span><span class="p">[</span>
<span class="s1">'django.contrib.sessions.middleware.SessionMiddleware'</span><span class="p">,</span>
<span class="s1">'your.middleware.to.be.tested'</span>
<span class="p">],</span>
<span class="n">SESSION_ENGINE</span><span class="o">=</span><span class="s1">'django.contrib.sessions.backends.signed_cookies'</span><span class="p">)</span>
<span class="n">settings</span><span class="o">.</span><span class="n">ROOT_URLCONF</span> <span class="o">=</span> <span class="n">urls</span>
<span class="kn">import</span> <span class="nn">django.apps</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">django</span><span class="o">.</span><span class="n">apps</span><span class="o">.</span><span class="n">apps</span><span class="o">.</span><span class="n">ready</span><span class="p">:</span>
<span class="n">django</span><span class="o">.</span><span class="n">setup</span><span class="p">()</span>
<span class="nd">@pytest</span><span class="o">.</span><span class="n">fixture</span><span class="p">(</span><span class="n">scope</span><span class="o">=</span><span class="s1">'function'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">django_test_client</span><span class="p">(</span><span class="n">configure_django</span><span class="p">):</span>
<span class="sd">""" just django test client """</span>
<span class="k">return</span> <span class="n">Client</span><span class="p">()</span>
</code></pre></div>Corona-bike2020-04-01T00:00:00+02:002020-04-01T00:00:00+02:00Mahotag:maho.pro,2020-04-01:/corona-bike.html<p>Coronavirus or not - I need to do some sports. <br> <img alt="bike" src="http://maho.pro/images/bike/side-l-v.jpg"></p><p>Here is my working station. Coronavirus or not - I need to do some sports. </p>
<p>Positive side effects - I can spend time on meetings, and actually <strong>do something useful </strong> during that time. </p>
<p><img alt="bike" src="http://maho.pro/images/bike/side-l-v.jpg">
<img alt="bike" src="http://maho.pro/images/bike/close-l-front-v.jpg">
<img alt="bike" src="http://maho.pro/images/bike/close-back-nol-v.jpg">
<img alt="bike" src="http://maho.pro/images/bike/side-l-v.jpg">
<img alt="bike" src="http://maho.pro/images/bike/side-nol.jpg"></p>ESP32 for Pycopy (Pfalcon's Micropython)2019-06-29T00:00:00+02:002019-06-29T00:00:00+02:00Mahotag:maho.pro,2019-06-29:/esp32-for-pycopy-pfalcons-micropython.html<p>ESP32 for Pycopy (Pfalcon's Micropython) <a href="http://maho.pro/files/esp32-pycopy-20190328.bin">can be downloaded here</a>. </p>
<p><a href="http://maho.pro/micropython-fork-wars.html">More details</a></p>why I dropped pipenv in favor of homemade bash script?2019-05-18T00:00:00+02:002019-05-18T00:00:00+02:00Mahotag:maho.pro,2019-05-18:/why-i-dropped-pipenv-in-favor-of-homemade-bash-script.html<h1>Long time ago ...</h1>
<p>... i suffered from fact, that I'm not able to track my pip dependencies. <code>pip
freeze</code> was not solution, because it attached lot of dependencies, which was
broken in few months. </p>
<p>I dreamed about software, which will track my <code>pip install</code> commands, and will
store key dependencies in …</p><h1>Long time ago ...</h1>
<p>... i suffered from fact, that I'm not able to track my pip dependencies. <code>pip
freeze</code> was not solution, because it attached lot of dependencies, which was
broken in few months. </p>
<p>I dreamed about software, which will track my <code>pip install</code> commands, and will
store key dependencies in .txt files, which are ready to use requrements when eg. I
want to install something to production. </p>
<h1>Pipenv.</h1>
<p>Then I found <a href="https://github.com/pypa/pipenv/">Pipenv</a>, and I was happy as a
kid. It was what I wanted. </p>
<p>Few weeks later, I realized that it's not exactly what I want. It is error
prone, narcistic and slow. </p>
<h2>why narcistic?</h2>
<p><em>Pipenv</em> ought to be interface to <code>pip</code>. Pip is in the background, but it's quite hidden. </p>
<p>In fact you have no idea what's happening when it tries to install. When failed
- then you usually can't see pip output. </p>
<p>I thought that if I give <code>--verbose</code> option, then I will
see what <code>pip install</code> said. I was wrong. I saw mainly lot of debug things of
pipenv, and <code>pip</code> output hidden beneath them. </p>
<p>Long story short - if <code>pip install</code> fail because:</p>
<ul>
<li>there is no <code>.h</code> file in system,</li>
<li>or wrong download path, </li>
<li>or internet is broken, </li>
<li>your transparent proxy gives you wrong file </li>
</ul>
<p>, then you can't see it without effort. </p>
<h2>why slow?</h2>
<p>Because want's too much. Too much for me. Looks like it tries to determine
optimal combination of libraries, and when I tried to install few libraries by
<code>pipenv</code>: <a href="https://github.com/kivy/kivy">kivy</a>,
<a href="https://github.com/kivy/cymunk">cymunk</a>,
<a href="https://github.com/kivy/kivent">kivent</a>, then calculating dependencies tooks
so long that I wrote simple replacement script in this time. </p>
<h2>what instead of pipenv?</h2>
<ul>
<li>as wrote above, I wrote simple script. It's, clumsy but working. I called it
<a href="https://github.com/maho/yapm">Yapm</a>. </li>
<li><a href="https://github.com/jnoortheen/pipm">pipm</a> - looks nice but doesn't handle
git+https:// packages</li>
<li><a href="https://github.com/jnoortheen/pipm">pipdeptree</a> - also can be useful, but it's not exactly workflow I like. </li>
</ul>My new ESP32 is bricked?2019-04-17T00:00:00+02:002019-04-17T00:00:00+02:00Mahotag:maho.pro,2019-04-17:/my-new-esp32-is-bricked.html<p><strong>TL;DR</strong>: press 'boot' button when flashing ESP32. At least some of them. </p>
<p>I have connected newly bought ESP32 to usb cable and try to flash it with micropython image. </p>
<div class="highlight"><pre><span></span><code><span class="p">(</span><span class="n">micro</span><span class="p">)</span><span class="w"> </span><span class="n">maho</span><span class="err">@</span><span class="n">dlaptop</span><span class="p">:</span><span class="o">~$</span><span class="w"> </span><span class="n">esptool</span><span class="o">.</span><span class="n">py</span><span class="w"> </span><span class="o">--</span><span class="n">port</span><span class="o">=/</span><span class="n">dev</span><span class="o">/</span><span class="n">ttyUSB0</span><span class="w"> </span><span class="n">erase_flash</span><span class="w"></span>
<span class="n">esptool</span><span class="o">.</span><span class="n">py</span><span class="w"> </span><span class="n">v2</span><span class="o">.</span><span class="mi">6</span><span class="w"></span>
<span class="n">Serial</span><span class="w"> </span><span class="n">port</span><span class="w"> </span><span class="o">/</span><span class="n">dev</span><span class="o">/</span><span class="n">ttyUSB0</span><span class="w"></span>
<span class="n">Connecting</span><span class="o">........</span><span class="n">_____</span><span class="o">.....</span><span class="n">_____ …</span></code></pre></div><p><strong>TL;DR</strong>: press 'boot' button when flashing ESP32. At least some of them. </p>
<p>I have connected newly bought ESP32 to usb cable and try to flash it with micropython image. </p>
<div class="highlight"><pre><span></span><code><span class="p">(</span><span class="n">micro</span><span class="p">)</span><span class="w"> </span><span class="n">maho</span><span class="err">@</span><span class="n">dlaptop</span><span class="p">:</span><span class="o">~$</span><span class="w"> </span><span class="n">esptool</span><span class="o">.</span><span class="n">py</span><span class="w"> </span><span class="o">--</span><span class="n">port</span><span class="o">=/</span><span class="n">dev</span><span class="o">/</span><span class="n">ttyUSB0</span><span class="w"> </span><span class="n">erase_flash</span><span class="w"></span>
<span class="n">esptool</span><span class="o">.</span><span class="n">py</span><span class="w"> </span><span class="n">v2</span><span class="o">.</span><span class="mi">6</span><span class="w"></span>
<span class="n">Serial</span><span class="w"> </span><span class="n">port</span><span class="w"> </span><span class="o">/</span><span class="n">dev</span><span class="o">/</span><span class="n">ttyUSB0</span><span class="w"></span>
<span class="n">Connecting</span><span class="o">........</span><span class="n">_____</span><span class="o">.....</span><span class="n">_____</span><span class="o">.....</span><span class="n">_____</span><span class="o">.....</span><span class="n">_____</span><span class="o">.....</span><span class="n">_____</span><span class="o">.....</span><span class="n">_____</span><span class="o">.....</span><span class="n">_____</span><span class="w"></span>
<span class="n">A</span><span class="w"> </span><span class="n">fatal</span><span class="w"> </span><span class="n">error</span><span class="w"> </span><span class="n">occurred</span><span class="p">:</span><span class="w"> </span><span class="n">Failed</span><span class="w"> </span><span class="n">to</span><span class="w"> </span><span class="n">connect</span><span class="w"> </span><span class="n">to</span><span class="w"> </span><span class="n">Espressif</span><span class="w"> </span><span class="n">device</span><span class="p">:</span><span class="w"> </span><span class="n">Invalid</span><span class="w"> </span><span class="n">head</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">packet</span><span class="w"> </span><span class="p">(</span><span class="mh">0x5B</span><span class="p">)</span><span class="w"></span>
<span class="p">(</span><span class="n">micro</span><span class="p">)</span><span class="w"> </span><span class="n">maho</span><span class="err">@</span><span class="n">dlaptop</span><span class="p">:</span><span class="o">~$</span><span class="w"></span>
</code></pre></div>
<p>Does it mean that my ESP32 is broken? I thought so, but it appeared that it flashed properly when I pressed "boot" microbutton while erasing flash and flashing. </p>Micropython fork wars2019-04-05T00:00:00+02:002019-04-05T00:00:00+02:00Mahotag:maho.pro,2019-04-05:/micropython-fork-wars.html<h1>UPDATE</h1>
<p>Pycopy's ESP32 image can be downloaded <a href="http://maho.pro/files/esp32-pycopy-20190328.bin">here</a></p>
<p>If you ecounter following problem:</p>
<div class="highlight"><pre><span></span><code> TypeError: function takes 2 positional arguments but 3 were given
</code></pre></div>
<p>while trying to use <a href="https://github.com/micropython/micropython-lib/tree/master/uasyncio">uasyncio</a> and <a href="https://micropython.org/">micropython</a> - then to save your time, learn a bit about current situation in project:</p>
<p>There is fork war between
<a href="https://github.com/micropython/micropython">Micropython</a> and …</p><h1>UPDATE</h1>
<p>Pycopy's ESP32 image can be downloaded <a href="http://maho.pro/files/esp32-pycopy-20190328.bin">here</a></p>
<p>If you ecounter following problem:</p>
<div class="highlight"><pre><span></span><code> TypeError: function takes 2 positional arguments but 3 were given
</code></pre></div>
<p>while trying to use <a href="https://github.com/micropython/micropython-lib/tree/master/uasyncio">uasyncio</a> and <a href="https://micropython.org/">micropython</a> - then to save your time, learn a bit about current situation in project:</p>
<p>There is fork war between
<a href="https://github.com/micropython/micropython">Micropython</a> and former
Micropython developer - <a href="https://github.com/pfalcon">Pfalcon</a> which
started fork of Micropython - <a href="https://github.com/pfalcon/pycopy">Pycopy</a>. </p>
<p>If you use stock Micropython and install <code>uasyncio</code> from <code>upip</code>, then you have
problem, because <a href="https://pypi.org/project/micropython-uasyncio/">PyPi package</a>
is authored by Pfalcon and is compatible with his fork, and not compatible with
Micropython from http://micropython.org. </p>
<p>There is no visible warning about it and realizing that costed me about day of
googling. </p>
<h2>Why?</h2>
<p>The bone of contention is <a href="https://github.com/micropython/micropython/pull/4217">this PR</a>.
Pfalcon created this PR, others disagree with him. He made fork and claims that micropython is
useless without this change and
<a href="https://forum.micropython.org/viewtopic.php?f=15&t=85&p=32852#p32923">it give him moral right</a>
to make incompatible changes to uasyncio published at PyPi.</p>
<h2>What to do?</h2>
<h3>if you want to use micropython.org</h3>
<p>Then you should use uasyncio from <a href="https://github.com/micropython/micropython-lib">vanilla
micropython-lib</a>. How? By copy
files to and thru. Not very convenient. </p>
<h3>if you want to use upip.install("micropython-lib")</h3>
<p>Then, due to fact that Pfalcon hijacked micropython-uasyncio, you need to use
his Pycopy. Which is not bad, in fact there is soome activity on his repository
while there is no commits on original micropython github. </p>
<p>But if you use ESP32, then you need to compile it's firmware yourself, as there
is no downloadable images available on
<a href="https://github.com/pfalcon/pycopy">Pycopy repo</a>. Which is not so hard and I'll try to
upload my image here. </p>Opowieść o Księciuniu2019-02-20T00:00:00+01:002019-02-20T00:00:00+01:00Mahotag:maho.pro,2019-02-20:/opowiesc-o-ksieciuniu.html<h2>Przed Księciuniem</h2>
<p><span id='przed'/></p>
<p>Pracując w pewniej firmie telekomunikacyjnej, natknąłem się na typa, o którym
chciałem napisać. Dla potomnych, ale też i dla siebie. Żeby wspominając po latach, mieć dowód na to, że pamięć mnie nie zawodzi i to wszystko naprawdę miało miejsce. </p>
<p>Firma, o której mowa, popadła w lekki kryzys i …</p><h2>Przed Księciuniem</h2>
<p><span id='przed'/></p>
<p>Pracując w pewniej firmie telekomunikacyjnej, natknąłem się na typa, o którym
chciałem napisać. Dla potomnych, ale też i dla siebie. Żeby wspominając po latach, mieć dowód na to, że pamięć mnie nie zawodzi i to wszystko naprawdę miało miejsce. </p>
<p>Firma, o której mowa, popadła w lekki kryzys i zarząd postanowił temu zaradzić,
sprowadzając nam <strong>Księciunia</strong>, który dopiero co pracował u Wielkiego Operatora
Telekomunikacyjnego. Wydawałoby się - przepis na sukces! Jednakże ...</p>
<h1>Księciunio przybywa ... Księciunio przemawia.</h1>
<p>Księciunio zaczął niewinnie: to porozmawiał z tym, to porozmawiał z tamtym;
podkreślił wielokrotnie, że bardzo szanuje nasz wkład w to, co zbudowaliśmy,
bardzo ceni sobie wieloletnich pracowników i w ogóle był taki miły i fajny. </p>
<p>Przez dwa miesiące uśpił naszą czujność, a następnie zorganizował imprezę integracyjną.
Normalnie plan każdej integracji jest taki: obiad,
pogadanka, kolacja i wreszcie <strong>impreza</strong>, która jest najważniejszym punktem
programu. Pogadanka jest do rytualnego odbębnienia, nikt tego nie traktuje poważnie --
pół godziny i po krzyku. </p>
<p>Ale nie doceniliśmy zawodnika. Księciunio jak zaczął o 14:00 - to o 18:00 ...
wyczerpała mu się bateria w laptopie. </p>
<p>Normalnego człowieka naszłaby refleksja,
że może za długo gada. On jenak posłał do pokoju po zasilacz i jeszcze gadał dobrą godzinę. Przerwał tylko dlatego,
że głośno domagaliśmy się zakończenia, bo nam kolacja zaraz przepadnie. Bardzo
był niepocieszony, ale jakoś przełknął tę zniewagę. </p>
<p>A co to była za przemowa ... 5 godzin, w czasie których opowiadał <strong>nam</strong> o
<strong>naszych</strong> dziełach, tak że jasne się stało, że nie tylko nie ma pojęcia o
rzeczach, którymi zajmuje się firma, ale wręcz nie ma pojęcia w ogóle o IT. </p>
<h1>Blockchain</h1>
<p>W czasie tego spotkania ujawniła się obsesja na punkcie blockchainu. Nie
wiadomo po co i dlaczego, ale uparł się, że <em>wchodzimy w blockchain</em>.
Jego hasłem było <em>Blockchain - my już tam jesteśmy</em>. Oczywiście -
nie byliśmy, nie mieliśmy zielonego pojęcia o blockchainie, nie mieliśmy
pojęcia jakie moglibyśmy mieć zastosowanie blockchainu w naszej firmie. Nie
przeszkodziło to Księciuniowi zatrudnić zewnętrznego specjalisty od blockchainu i
jeździć z nim po całej Polsce z wykładami na ten temat. Nota bene - koniec
końców temu specowi ponoć nie zapłacił (patrz <a href="#lojalnosc">lojalność</a>).</p>
<h1>Stopka, lista, rejestracja czasu,</h1>
<p>Zaraz po tej integracji były Święta, potem pojechałem na urlop, wracam w sumie
po dwóch tygodniach nieobecności i ... nowa rzeczywistość! Księciunio rozwinął
skrzydła i zaczął odlatywać. </p>
<p>Zgadnijcie co jest najważniejsze w firmie IT w lekkim kryzysie? Stopka,
szyfrowanie dysku i rejestracja czasu. Ze szczególnym naciskiem na stopkę.
Omawianie stopki i szyfrowania dysku dominowało przez miesiąc wszystkie
wtorkowe spotkania (patrz <a href="#bezproduktywnosc">festiwale
bezpdroduktywności</a>).</p>
<p>Wymyślił też, że będziemy rejestrować czas spędzony na zadaniach. Nie ma w tym
nic nadzwyczajnego. Nadzwyczajna jest metoda. Otóż - dziennie trzeba było
wysyłać trzy maile:</p>
<ul>
<li>jeden - na <em>remote@naszafirma.pl</em> o temacie "start", </li>
<li>drugi - na <em>remote@naszafirma.pl</em> o temacie "stop",</li>
<li>trzeci, na <em>remotework@naszafirma.pl</em>, listę zadań <strong>w postaci CSV</strong>, w formacie mniej więcej takim:</li>
</ul>
<div class="highlight"><pre><span></span><code> lp; klient; zagadnienie; liczba godzin;
1; klient a; poprawka listy klientów; 4;
.
.
TOTAL: 8h
</code></pre></div>
<p>Codziennie, <strong>kurwa codziennie</strong>, musieliśmy robić ręczne dzierganie CSV do
treści maila. </p>
<p>Na początku myślieliśmy, że skończy się tak, jak ze wszystkimi
poprzednimi próbami wprowadzenia rejestracji czasu na przestrzeni ostatniej
dekady, czyli: dyrektor zobaczy, że opór materii jest duży i odpuści. Ten nie
odpuścił! No to próbowalismy choćby wynegocjować, żeby to zrobić w jakiś ludzki
sposób, przyjęty na całym w świecie. Przecież są tysiące systemów rejestracji
czasu, od dedykowanych do tego, po zintegrowane z bugtrackerami. Ponownie <a
href="#odpornosc">nie doceniliśmy przeciwnika</a>. Padł argument <em>najpierw
zastosujmy się do tego co jest, a potem pomyślimy czy nie zmienić</em> i to był koniec dyskusji. </p>
<p><span id='odpornosc'/></p>
<h1>Odporność na argumenty,</h1>
<p>Księciunio jak coś wymyślił, to trzymał się tego z podziwu godną konsekwencją.
Nic nie mogło go skłonić do zmiany raz powziętego zamiaru. </p>
<p>Ustawił zasady pracy zespołu i nazwał je szumnie <em>modelem Team1</em>. Jeżeli ktoś
zgłaszał, że te zasady nie trzymają się kupy, to najczęściej to ignorował i po
prostu szedł z tematem dalej. Ewentualnie mówił coś w stylu <em>najpierw
zastosujmy i sprawdźmy jak to działa, a potem możemy to zmieniać</em>. </p>
<p>Sądzę, że nie jest w stanie intelektualnie ogarnąć zmieniających się parametrów
i jeżeli jego idea zderza się z rzeczywistością, wtedy to rzeczywistość musi
dostosować się do pomysłu, bo jej łatwiej.</p>
<p>Co więcej, każda dyskusja z nim spełzała na niczym, ponieważ zawsze przeradzała
się w długą, męczącą, godzinną "dyskusję", głównie tyradę Księciunia,
której nie dawali rady zdzierżyć nawet najwytrwalsi (patrz <a href="#nowomowa">nowomowa</a>). Koniec
końców, nikt się nie decydował na zgłaszanie uwag do czegokolwiek, żeby uniknąć
bezsensownych sporów, które i tak spełzłyby na niczym ...</p>
<p><span id='sposob'/></p>
<h2>sposób ustalania czegokolwiek</h2>
<p>... co kończyło się tym, że <em>tacito consensu</em> wszyscy godzili się na każdą
bzdurę. Raz na jakiś czas, żeby nie było tak jednogłośnie, ten czy ów zdecydował
się na jakiś bezsilny głos protestu, ale nie miało to żadnego praktycznego
znaczenia. </p>
<p>Typowy sposób ustalania nowych ciekawostek:</p>
<ul>
<li>Najpierw na jednym spotkaniu mimochodem napomyka o czymś tam. Nikt tego nie
zauważa, albo zauważa, ale nie dostrzeże potencjału durnoty, który
się kryje w nowym pomyśle. </li>
<li>Na następnym spotkaniu temat jest już poruszany szerzej. Do ogółu dociera
np. to, że nagle w <em>redmine</em> będą musieli wypełniać dodatkowe 5 pól i
przeklikiwać task między chyba 10 statusami, więc podnoszą protest, że to bez
sensu. Wtedy pada argument, że <em>przecież na ostatnim spotkaniu zostało to
ustalone. Jak my mamy funkcjonować, skoro nie honorujemy własnych ustaleń?
Bądźmy poważni</em>. Dyskusja i potem sakramentalne <em>najpierw zastosujmy, sprawdźmy jak
działa, a potem możemy zmieniać</em></li>
<li>większość tego nie stosuje, bo jest niewygodne, nieintuicyjne,
prowadzące do błędów, kontrproduktywne. Myliłby się ten, kto z tego wysnułby
wniosek, że zostało negatywnie zweryfikowane w praktyce. Skoro
ludzie tego nie stosują, to jeszcze nie zostało to sprawdzone, <em>najpierw
zastosujmy, sprawdźmy jak działa, a potem możemy zmieniać</em>. Koło się zamyka.</li>
</ul>
<p><span id='rozpieprz'/></p>
<h1>Rozpieprz zespołu,</h1>
<p>Trochę przed przybyciem Księciunia, w firmie pojawił się zalążek działu <em>Q&A</em>.
Między nami a <em>klientem wewnętrznym</em> (a.k.a. <em>dziewczynami z biznesu</em> a.k.a.
<em>Nimi</em>) zaczął być ktoś, kto z jednej strony zbierze wymagania w jakąś spójną
całość, z drugiej strony zerknie na to, co jest zrobione i przetestuje - od razu
wskazując na słabe punkty. Jednocześnie jednoosobowy dział <em>Q&A</em> pilnował
<em>Onych</em>, żeby testowały to, co wyszło na testy i żeby nie blokowało wdrożeń (lub
nie prowokowało wdrożeń rzeczy nieprzetestowanych). </p>
<p>Takie proxy między nami, a <em>Nimi</em> zadziałały fantastycznie. Przez to, że
przestałem się zmagać z rozkminianiem co poetki mają na myśli, mogłem się
skupić na robocie, która aż furczała. </p>
<p>Księciunio ten układ rozmontował. Nie widział potrzeby. Stwierdził: <em>rozmawiałem z
dziewczynami z biznesu i wyraziły gotowość do testowania we własnym zakresie</em>.
Efekt - taski znowu zaczęły się testować po kilka miesięcy i siłą rzeczy szły
nieprzetestowane na produkcję, co powodowało, że wracały do pilnej poprawki.
Moja produktywność znowu spadła o kilkaset procent. </p>
<p>Tym, którzy zarabiali dużo poniżej średniej rynkowej, odmówił dania choćby
kosmetycznych podwyżek, przez co część zespołu złożyło wypowiedzenie. Braki
zostały uzupełnione zatrudnieniem ludzi za stawki <em>rynkowe
plus</em>, co spowodowało jeszcze większy oburz w zespole i w konsekwencji
wypowiedzenie na dzisiaj złożyli prawie wszyscy (także i Ci świeżo zatrudnieni). </p>
<h1>Delegowanie zadań</h1>
<p>Kuriozalne było delegowanie zadań. Wspomniany wcześniej <strong>Bardzo Ważny
Projekt</strong>, składa się między innymi z forka forka mojego projektu. Logiczne
wydawałoby się, że skoro w zespole jest jedna osoba, która cokolwiek wie o
projekcie, to że ten projekt trafi do niej. Projekt jednak został przydzielony ... kolegom, którzy właśnie złożyli wypowiedzenie. </p>
<p>Ponieważ byli oni już jedną nogą poza firmą, zakończyło się to spektakularną
klapą. <strong>BWP</strong> był na tyle zagrożony, że wszyscy dostali polecenie <em>rzucamy
wszystko i zajmujemy się Bardzo Ważnym Projektem</em>. W tym także i ja. </p>
<p>Pomyślałby ktoś, że dostanę w przydziale tę część, którą robiłem już kiedyś?
Jeden task tak, a potem dostałem przydział w tej części, której nie robiłem
wcześniej. Zaś koledzy męczyli się z rozkminianiem tego, co ktoś wymodził na
bazie mojej niegdysiejszej pracy. </p>
<p>Czyli zasada - każdemu to, co będzie mu łatwiej spieprzyć. Udało się, projekt
jest spieprzony koncertowo!</p>
<p><span id='bezproduktywnosc'></p>
<h1>Festiwale bezproduktywności</h1>
<p>Księciunio postanowił robić cotygodniowe spotkania, we wtorki, o godzinie 12.
Planowo pół godziny, początkowo wychodziło jednak ze dwie godziny albo i
więcej. Kluczowe tematy na początku to stopka i szyfrowanie dysków. Po
miesiącu, kiedy te lightmotivy spotkań się wyczerpały, na tapet wziął
rejestrację czasu, a na sam koniec spotkania przerodziły się w spotkania na
temat Bardzo Ważnego Projektu. </p>
<p>Nie ma nic dziwnego w ideii cyklicznych meetingów, bo jednak zawsze to jest
okazaja do tego, żeby omówić bieżące sprawy, zgłosić jakieś problemy, ustalić
wspólne rozwiązanie itd... Nie tu jednak. </p>
<p>Omawiania bieżących spraw - nie było, zamiast tego było rytualne przepytanie
wszystkich team leaderów na temat ich działu. TL mówili jakiś ogólnik w stylu
<em>realizujemy taski po kolei</em> i to zupełnie wystarczało. Zgłaszania problemów -
także nie! Zgłaszający jakiś problem był zbywany czymś w stylu <em>ja nie chce
takich rzeczy słyszeć, to nie jest konstruktywne i do niczego nie prowadzi</em>.</p>
<p>Kiedy przyszedł czas Bardzo Ważnego Projektu, ustalanie rozwiązań teoretycznie
miało miejsce, problem był jednak taki, że jeżeli osoby A i B chciały ustalić
jakiś szczegół, musiało to być za pośrednictwem i udziałem Księciunia. Nic nie
rozumiał z tego, co uzgadnaliśmy, więc było to nieustanne tłumaczenie dziecku
zasady funkcjonowania podstawowych spraw.</p>
<p>Jeżeli A i B udało się w jakimś rzucie determinacji uzgodnić coś, zwracając się
bezpośrednio do siebie, to i tak Księciunio wkraczał na arenę i czuł się w
obowiązku długo i niezgrabnie podsumować to, co A i B dogadali. Żadne ustalenie
w czasie spotkania nie mogło się odbyć bez udziału Księciunia. </p>
<p>Spotkania były obowiązkowe, nie było mowy o tym, żeby ktoś na spotkanie nie
przybył, albo się spóźnił; nie było tak krytycznej sprawy, która miałaby
usprawiedliwić opuszczenie spotkania. Szczególnie kładł nacisk na to, żeby
pracownicy zdalni, tacy jak ja, uczestniczyli w spotkaniu i byli na czas.
Jednocześnie zupełnie nie dbał o to, co się ze zdalnym dzieje w trakcie. Na
początku, w skutek "przejściowych" problemów technicznych, zdalni wdzwaniali
się na telefon, który leżał na biurku. Nic nie było często przez to słychać, ani
w jedną, ani w drugą stronę. Często kogoś rozłączło i nie mógl się już połączyć
i w skutek tego nie uczestniczył w spotkaniu. Nic nie miało już znaczenia, jeżeli
Księciunio akurat przemawiał.</p>
<p>Koniec końców, wszyscy zdalni po prostu się łączyli, mówili rytualne <em>cześć</em>,
wyłączali kamerę, wyłączali mikrofon i szli do kuchni na obiad, zupełnie
olewając przebieg spotkania. </p>
<p>Kiedy przyszedł czas na akcję <em>wszystkie ręce na pokład do pracy nad
<strong>Bardzo Ważnym Projektem</strong></em>, zgodnie z zasadą, że <em>w miarę budowy
socjalizmu, walka klas zaostrza się</em>, spotkania stały się codzienne oraz dużo dłuższe.
Dwie godziny wyjęte z życiorysu, samo pustosłowie, zero uzgodnień,
bo wszystko trzeba było sobie uzgodnić osobo -- po spotkaniu. W momencie, kiedy
dealine był dwudniowy, Księciunio zorganizował spotkanie po to tylko, żeby
przez godzinę z okładem oznajmić, że bardzo nam się śpieszy, podkreślić wagę
tego terminu, co on oznacza dla firmy i zapytać się jak idzie. </p>
<p><span id='nowomowa'/></p>
<h1>Nowomowa</h1>
<p>Księciunio nie powiedział nigdy <em>robimy</em> ale <em>działamy</em>. A najlepiej
<em>podejmujemy działania</em>, a jego ulubionym dodatkiem do każdego zdania było <em>w
funkcji czasu</em>. A więc, nie robimy taski, tylko <em>działamy w funkcji czasu</em>. </p>
<p>Na początku swojego panowania zapytał się w tych mniej więcej słowach: <em>jakie
są strumienie technologiczne w obszarze dokumentacji</em>?. Co jak się później
okazało, w jego slangu oznacza <em>chłopaki, jak stoimy z dokumentacją?</em>.</p>
<p>Nie powiedział <em>skupiamy się na BWP, robimy taski po kolei</em>, tylko <em>poruszamy
się w zakresie działań operacyjnych BWP, realizujemy zadania w funkcji
czasu</em>.</p>
<h2>Analogie z dupy</h2>
<p><span id='analogie'/></p>
<p>Księciuniu jest mistrzem analogii wziętych z dupy. Przez dobre dwa miesiące, na
spotkaniach, i nie tylko, kształtował obraz naszego zespołu jako rakiety. I
wiadomo, że jak rakieta leci, to każda śrubka musi działać, bo jak jedna śrubka
tylko się obluzuje, to cała rakieta się rozpadnie. </p>
<p>Tak był zadowolony z tego porównania, że potrafił nas katować tym obrazem
rakiety przez pół godziny, opisując nam za jej pomocą jak bardzo ważne jest,
żebyśmy stosowali to, co <em>wspólnie ustaliliśmy</em> (patrz <a href='#sposob'>
sposób ustalania</a>)</p>
<p>Zaś na sam koniec, kiedy w końcy ktoś z góry zobaczył do czego to prowadzi i go
zwolnił, to stojąc na zgliszczach wraku, który powstał pod jego rządami,
rozwinął nową analogię: <em>statek już już wypływał na szerokie wody i pojawił
się wiatr wsteczny i wepchnął go z powrotem</em>.</p>
<p><span id='lojalnosc'></p>
<h1>Lojalność</h1>
<p>Niebezpiecznie było popierać Księciunia.</p>
<p>W firmie były z grubsza dwie osoby, które jako tako popierały Księciunia. Jeden
to był, nazwijmy go Tołdi, który w sumie nie wiem czym się zajmował wcześniej,
zaś po przyjściu Księciunia zajmował się ogólnie realizacją wizji Księciunia,
poza tym od strony <em>biznesowo-analityczno-testowej</em>, (<a href='#rozpieprz'>w zastępstwie nieustniejącego już Q&A
</a>) obsługiwał <strong>Bardzo Ważny Projekt</strong>.</p>
<p>Tołdi był całym sercem z Księciuniem w jego wizjach. Wręcz wyprzedzał jego myśl. Jak Aaron był głosem i ręką Mojżesza, tak Tołdi był głosem i ręką Księciunia.</p>
<p>Księciunio zrekrutował dwóch programistów i z marszu jeden z nich, nazwijmy go Nowy,
został Team Leadem zespołu Pythona i de-facto silnikiem pociągowym <strong>Bardzo
Ważnego Projektu</strong>.</p>
<p>Nowy, z racji funkcji, jako tako musiał Księciunia choćby częściowo popierać w
jego wizjach. A przynajmniej widać było po nim wysiłek próby zrozumienia
pomysłów i zastosowania. Przez jakiś czas też zachowywał dla siebie
nieprzyjazne opinie na temat Księciunia. Starał się w miarę możliwości, acz bez
entuzjazu, pchać do przodu ten wózek, który dostał na barki. Jak na zastaną sytuację
radził sobie bardzo dobrze. </p>
<p>I co się okazało? Były to jedyne dwie osoby które dostały po kieszeni,
proporcjonalnie do swojego entuzjazmu. </p>
<ul>
<li>Nowy - dowiedział się, że za urlop dostanie połowę wynagrodzenia. Dowiedział się w momencie, kiedy na ten urlop szedł. </li>
<li>Tołdi - został zwolniony quasi-dyscyplinarnie i nie dostał kasy za okres wypowiedzenia.</li>
</ul>
<h1>Po co i co dalej?</h1>
<p>Zachodzi pytanie, po co w ogóle Księciunio się tutaj pojawił? Chodzą słuchy, że
miał za zadanie odchudzić zespół, tylko się wymknął spod kontroli i w efekcie odchudził go do zera. </p>
<p>Sądzę, że jednak to krew z krwi i ciało z ciała zarządu, skondensowana emanacja zbiorowej osobowości tych wszystkich nad nami.
Można powiedzieć, rzeczywistość, będąca przed <em>Developem</em> w ukryciu przez tyle lat, nagle nas dogoniła.</p>
<p>Księciunio już nie pracuje w Firmie, pewnie serfuje na falach światowego biznesu, komuś innemu robi rakiety, wypływa okrętem na szerokie wody ... </p>Tworzenie Gier w Kivy i Kivent (jak zacząć)2018-08-20T00:00:00+02:002018-08-20T00:00:00+02:00Mahotag:maho.pro,2018-08-20:/kivykivent-pl.html<h2>Skąd toto?</h2>
<p>To jest artykuł napisany na potrzeby konferencji <a href="https://pl.pycon.org/2018/">PyconPL 2018</a> i jest też do ściągnięcia z <a href="https://github.com/PyConPL/Book/blob/master/2018/presentations/tworzenie-gier-w-kivy-i-kivent/text.md">PyconPLowego githuba</a></p>
<h2>Wstęp</h2>
<p>Zarówno Kivy jak i Kivent mogą służyć do pisania prostych gier w Pythonie. W
tym artykule, na przykładzie <strong>bardzo</strong> prostej aplikacji gropodobnej, porównam
podstawowe aspekty tworzenia gier w jednym i …</p><h2>Skąd toto?</h2>
<p>To jest artykuł napisany na potrzeby konferencji <a href="https://pl.pycon.org/2018/">PyconPL 2018</a> i jest też do ściągnięcia z <a href="https://github.com/PyConPL/Book/blob/master/2018/presentations/tworzenie-gier-w-kivy-i-kivent/text.md">PyconPLowego githuba</a></p>
<h2>Wstęp</h2>
<p>Zarówno Kivy jak i Kivent mogą służyć do pisania prostych gier w Pythonie. W
tym artykule, na przykładzie <strong>bardzo</strong> prostej aplikacji gropodobnej, porównam
podstawowe aspekty tworzenia gier w jednym i drugim.</p>
<h2>Kivy - omówienie</h2>
<p>Kivy (http://kivy.org) jest wieloplatformową (Linux, Windows, macOS, Android, iOS) biblioteką do tworzenia UI. </p>
<p>Napisana jest w Pythonie i Cythonie. Obsługuje zarówno Pythona 2.x jak i 3.x. </p>
<h2>Zalety Kivy</h2>
<ul>
<li>wieloplatformowość. Aplikacja którą stworzymy będzie działała (prawie) tak samo pod linuksem jak i pod androidem, z zastrzeżeniem różnic w rozdzielczości. </li>
<li>przejrzysty i bogaty język KV opisujący nam UI aplikacji (i nie tylko), bogactwo layoutów
szczegóły składni języka można znaleźć pod adresem <a href="http://kivy.org/docs/guide/lang.html">http://kivy.org/docs/guide/lang.html</a></li>
<li>buildozer (o którym niżej)</li>
</ul>
<h2>Wady Kivy</h2>
<ul>
<li>mimo bogactwa layoutów, do szału człowieka może doprowadzić konieczność umieszczenia buttona o zadanych rozmiarach w rogu ekranu,
albo przycisku który dopasuje się do zawartości i jednocześnie będzie miał konkretne proporcje. </li>
<li>inicjalizacja biblioteki, okienek i w ogóle całej warstwy pod spodem jest w momencie <code>import kivy</code> a nie w momencie uruchomienia mainloop. Co czasem wkurza. </li>
<li>twórcy kivy mieli własny pomysł na mechanizmy logowania. Czyli <code>Logger.info</code> zamiast <code>logging.info</code>, defaultowy handler jest na ekran i do plików tekstowych w katalogu domowym.
Co prawda jest to oparte na pythonowy moduł <code>logging</code>, ale próba wpięcia się w to albo wręcz przekierowania tego zupełnie np. do własnego <code>logging.basicConfig(...)</code> jest trudne i nieintuicyjne. </li>
</ul>
<h2>Kilka słów na temat buildozera</h2>
<p>Tworząc aplikację w Kivy, niemal na pewno mamy możliwość uruchomienia jej na androidzie bez większego wysiłku, za pomocą <em>Buildozera</em>.
Buildozer jest to skrypt/nakładka na P4A
(<code>https://github.com/kivy/python-for-android</code>) która robi za użytkownika całą
niewdzięczną robotę, polegająca na ściąganiu odpowiednich SDK, NDK i innych
*DK. </p>
<p>Przy odrobinie szczęścia, można odpalić aplikację napisaną w Kivy bez wnikania
w to jakie paczki do siebie pasują, gdzie je (są gigantyczne przecież)
rozpakować i w ogóle jakie rodzaje *DK w ogóle występują. </p>
<h2>Tworzenie gry w Kivy</h2>
<p>W artykule opiszę najprostszą grę w kivy. (gra to dużo powiedziane ... coś co może być podstawą do gry :). Niech to to będzie przeciskanie się między balonowatymi obiektami do prawej ściany, która będzie celem gry.</p>
<p>Źródło aplikacji jest pod adresem <code>https://gitlab.com/maho/pycon-kivygame</code></p>
<h3>Instalacja kivy, kadłubek aplikacji</h3>
<div class="highlight"><pre><span></span><code>maho@..kivygame$ mkvirtualenv --python<span class="o">=</span>/usr/bin/python3 kivygame
<span class="o">[</span>...<span class="o">]</span>
maho@..kivygame$ pip install cython
Collecting cython
Downloading https://files.pythonhosted.org/packages/93/a3/213a6106aed3d51f5fb6aa0868849b6a3afe240e019f6586c52cac3bbe7b/Cython-0.28.4-cp35-cp35m-manylinux1_x86_64.whl <span class="o">(</span><span class="m">3</span>.3MB<span class="o">)</span>
<span class="m">100</span>% <span class="p">|</span>████████████████████████████████<span class="p">|</span> <span class="m">3</span>.3MB <span class="m">1</span>.4MB/s
Installing collected packages: cython
Successfully installed cython-0.28.4
</code></pre></div>
<p>Niestety nie da się tego załatwić pojedynczym plikiem req.txt, bo pakiety muszą być instalowane w osobnych krokach. </p>
<div class="highlight"><pre><span></span><code>maho@..kivygame$ pip install kivy
Collecting kivy
<span class="o">[</span>...<span class="o">]</span>
Successfully built kivy
Installing collected packages: urllib3, idna, certifi, chardet, requests, Kivy-Garden, docutils, pygments, kivy
Successfully installed Kivy-Garden-0.1.4 certifi-2018.4.16 chardet-3.0.4 docutils-0.14 idna-2.7 kivy-1.10.1 pygments-2.2.0 requests-2.19.1 urllib3-1.23
</code></pre></div>
<p>Aplikację zacznijmy od pliku .py - nazwijmy go <code>main.py</code>. Można go nazwać dowolnie, ale jeżeli zechcemy użyć <em>buildozera</em>
do zbudowania paczki na Androida, to musi się nazywać <code>main.py</code> i koniec. </p>
<p>main.py:</p>
<div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">kivy.app</span> <span class="kn">import</span> <span class="n">App</span>
<span class="kn">from</span> <span class="nn">kivy.uix.widget</span> <span class="kn">import</span> <span class="n">Widget</span>
<span class="k">class</span> <span class="nc">KivyGame</span><span class="p">(</span><span class="n">Widget</span><span class="p">):</span>
<span class="k">pass</span>
<span class="k">class</span> <span class="nc">KivyGameApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="k">pass</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">'__main__'</span><span class="p">:</span>
<span class="n">KivyGameApp</span><span class="p">()</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
</code></pre></div>
<p>Jest zadeklarowana klasa <code>KivyGame</code>, dziedzicząca po <code>Widget</code>, która jest używana w <code>KivyGameApp</code>. </p>
<p><code>KivyGameApp</code> domyślnie ładuje jako <em>root widget</em> to co jest pierwsze w odpowiednim pliku .kv. A odpowiedni plik .kv to to jest <code><nazwa aplikacji bez "App", lowercase>.kv</code></p>
<p>kivygame.kv</p>
<div class="highlight"><pre><span></span><code><span class="n">KivyGame</span><span class="p">:</span>
<span class="n">BoxLayout</span><span class="p">:</span>
<span class="n">pos</span><span class="p">:</span> <span class="n">root</span><span class="o">.</span><span class="n">pos</span>
<span class="n">size</span><span class="p">:</span> <span class="n">root</span><span class="o">.</span><span class="n">size</span>
<span class="n">Label</span><span class="p">:</span>
<span class="n">text</span><span class="p">:</span> <span class="s2">"to ja - tło"</span>
<span class="n">size_hint</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span>
</code></pre></div>
<p>Tutaj mamy <code>Widget</code>, który w sobie zawiera <code>BoxLayout</code>, domyślnie rozciągający się na całe <code>KivyGame</code> i zawierający w sobie <code>Label</code> z tekstem <em>to ja - tło</em>, rozciągający się na całego rodzica, czyli <code>BoxLayout</code> w tym przypadku.</p>
<p><img alt="apka wygląda tak" src="http://maho.pro/images/01-bare-app.png"></p>
<h4>Dodajemy postaci</h4>
<p>Do root widgeta dodaję informację o postaci, nazwijmy ją <em>Fred</em>, w <a href="https://gitlab.com/maho/pycon-kivygame/blob/02-fred-and-baloons/kivygame.kv">kivygame.kv</a>:</p>
<div class="highlight"><pre><span></span><code> <span class="n">Widget</span><span class="p">:</span>
<span class="n">size</span><span class="p">:</span> <span class="mi">145</span><span class="p">,</span> <span class="mi">145</span>
<span class="n">center</span><span class="p">:</span> <span class="mi">300</span><span class="p">,</span> <span class="mi">300</span>
</code></pre></div>
<p>Widget jest poza <code>BoxLayout</code>, bo layout ma za zadanie rozmieścić elementy UI wg jakiegoś algorytmu, a postać ma być dokładnie w określonym miejscu.
Goły widget jednak nie zawiera nic, więc trzeba coś do niego wsadzić. Najprościej narysować coś na jego płótnie:</p>
<div class="highlight"><pre><span></span><code> <span class="n">Widget</span><span class="p">:</span>
<span class="n">canvas</span><span class="p">:</span>
<span class="n">Rectangle</span><span class="p">:</span>
<span class="n">pos</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">pos</span>
<span class="n">size</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">size</span>
<span class="n">source</span><span class="p">:</span> <span class="s1">'img/fred.png'</span>
</code></pre></div>
<p>Żeby móc zidentyfikować Freda wewnątrz aplikacji, trzeba nadać mu id, umieścić referencję do tego id w <code>KivyGame</code>...</p>
<div class="highlight"><pre><span></span><code><span class="n">KivyGame</span><span class="p">:</span>
<span class="n">fred</span><span class="p">:</span> <span class="n">fred</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="n">Widget</span><span class="p">:</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="nb">id</span><span class="p">:</span> <span class="n">fred</span>
</code></pre></div>
<p>i od teraz w KivyGame pojawia się <code>self.fred</code>.</p>
<h4>Dodanie balonów</h4>
<p>Balony dodaję w inny sposób: nie moge ich zadeklarować w pliku .kv, bo nie wiem dokładnie ile ich będzie. Dlatego, w pliku .kv definiuję wygląd klasy <code><Baloon></code> zaś dodanie jej będzie odbywać się w kodzie.</p>
<p>plik <a href="https://gitlab.com/maho/pycon-kivygame/blob/03-randommoves/kivygame.kv">kivygame.kv</a>:</p>
<div class="highlight"><pre><span></span><code><span class="o"><</span><span class="n">Baloon</span><span class="o">></span><span class="p">:</span>
<span class="n">canvas</span><span class="p">:</span>
<span class="n">Rectangle</span><span class="p">:</span>
<span class="n">pos</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">pos</span>
<span class="n">size</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">size</span>
<span class="n">source</span><span class="p">:</span> <span class="s1">'img/baloon.png'</span>
<span class="n">size</span><span class="p">:</span> <span class="mi">100</span><span class="p">,</span> <span class="mi">100</span>
</code></pre></div>
<p>oraz <a href="https://gitlab.com/maho/pycon-kivygame/blob/03-randommoves/main.py">main.py</a>:</p>
<div class="highlight"><pre><span></span><code><span class="k">class</span> <span class="nc">Baloon</span><span class="p">(</span><span class="n">Widget</span><span class="p">):</span>
<span class="k">pass</span>
</code></pre></div>
<p>dodanie 10 balonów w losowych miejscach na arenie o pozycjach</p>
<div class="highlight"><pre><span></span><code><span class="k">class</span> <span class="nc">KivyGame</span><span class="p">(</span><span class="n">Widget</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="o">**</span><span class="n">kwa</span><span class="p">):</span>
<span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="o">**</span><span class="n">kwa</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">baloons</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">defs</span><span class="o">.</span><span class="n">num_baloons</span><span class="p">):</span>
<span class="n">baloon</span> <span class="o">=</span> <span class="n">Baloon</span><span class="p">(</span><span class="n">center</span><span class="o">=</span><span class="p">(</span><span class="n">randint</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="mi">800</span><span class="p">),</span> <span class="n">randint</span><span class="p">(</span><span class="mi">100</span><span class="p">,</span> <span class="mi">500</span><span class="p">)))</span>
<span class="bp">self</span><span class="o">.</span><span class="n">add_widget</span><span class="p">(</span><span class="n">baloon</span><span class="p">)</span>
</code></pre></div>
<p>Efekt jak poniżej:
<img alt="apka z postacią i balonami" src="http://maho.pro/images/02-fred-and-baloons.png"></p>
<h4>Apka z losowymi przemieszczeniami się balonów</h4>
<p>W <code>KivyGame.__init__</code> uruchamiamy wywoływanie metody <code>update</code> 30 razy na sekundę:</p>
<div class="highlight"><pre><span></span><code><span class="k">class</span> <span class="nc">KivyGame</span><span class="p">(</span><span class="n">Widget</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="o">**</span><span class="n">kwa</span><span class="p">):</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="n">Clock</span><span class="o">.</span><span class="n">schedule_interval</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">update</span><span class="p">,</span> <span class="mf">1.0</span> <span class="o">/</span> <span class="mi">30</span><span class="p">)</span>
</code></pre></div>
<p>A tam, modyfikujemy pozycje balonów w sposób losowy:</p>
<div class="highlight"><pre><span></span><code> <span class="k">def</span> <span class="nf">update</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">dt</span><span class="p">):</span>
<span class="k">for</span> <span class="n">b</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">baloons</span><span class="p">:</span>
<span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="n">b</span><span class="o">.</span><span class="n">pos</span>
<span class="n">b</span><span class="o">.</span><span class="n">pos</span> <span class="o">=</span> <span class="p">(</span><span class="n">x</span> <span class="o">+</span> <span class="n">randint</span><span class="p">(</span><span class="o">-</span><span class="mi">5</span><span class="p">,</span> <span class="mi">5</span><span class="p">),</span> <span class="n">y</span> <span class="o">+</span> <span class="n">randint</span><span class="p">(</span><span class="o">-</span><span class="mi">5</span><span class="p">,</span> <span class="mi">5</span><span class="p">))</span>
</code></pre></div>
<p>W repozytorium kod jest oznaczony tagiem <a href="https://gitlab.com/maho/pycon-kivygame/blob/03-randommoves">03-randommoves</a></p>
<h4>Dodany input, można sterować, nadal brak interakcji</h4>
<p>Najprościej dodać obsługe myszki, wystarczy tylko przeciążyć metody <code>on_touch_*</code>. W naszym przypadku - będzie to <code>on_touch_up</code>.
Przyśpieszamy Freda w kierunku punktu kliknięcia. </p>
<div class="highlight"><pre><span></span><code><span class="k">class</span> <span class="nc">KivyGame</span><span class="p">(</span><span class="n">Widget</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="o">**</span><span class="n">kwa</span><span class="p">):</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">fred_speed</span> <span class="o">=</span> <span class="n">Vector</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">update</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">dt</span><span class="p">):</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">fred</span><span class="o">.</span><span class="n">pos</span> <span class="o">=</span> <span class="n">Vector</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">fred</span><span class="o">.</span><span class="n">pos</span><span class="p">)</span> <span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">fred_speed</span>
</code></pre></div>
<p>i samo przyśpieszenie:</p>
<div class="highlight"><pre><span></span><code> <span class="k">def</span> <span class="nf">on_touch_up</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">touch</span><span class="p">):</span>
<span class="n">vdir</span> <span class="o">=</span> <span class="n">Vector</span><span class="p">(</span><span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">)</span> <span class="o">-</span> <span class="bp">self</span><span class="o">.</span><span class="n">fred</span><span class="o">.</span><span class="n">center</span> <span class="c1"># wynik to Vector</span>
<span class="bp">self</span><span class="o">.</span><span class="n">fred_speed</span> <span class="o">+=</span> <span class="n">vdir</span> <span class="o">/</span> <span class="mi">100</span>
</code></pre></div>
<p>Klawiatura jest ciutkę mniej oczywista: </p>
<div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">kivy.base</span> <span class="kn">import</span> <span class="n">EventLoop</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="kn">from</span> <span class="nn">kivy.core.window</span> <span class="kn">import</span> <span class="n">Keyboard</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="k">class</span> <span class="nc">KivyGame</span><span class="p">(</span><span class="n">Widget</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="o">**</span><span class="n">kwa</span><span class="p">):</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="n">EventLoop</span><span class="o">.</span><span class="n">window</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="n">on_key_up</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">on_key_up</span><span class="p">)</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">on_key_up</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">__window</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="o">*</span><span class="n">__</span><span class="p">,</span> <span class="o">**</span><span class="n">___</span><span class="p">):</span>
<span class="c1"># code = Keyboard.keycode_to_string(None, key) # można tak, ale to brzydki hack</span>
<span class="n">dx</span><span class="p">,</span> <span class="n">dy</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span>
<span class="k">if</span> <span class="n">key</span> <span class="o">==</span> <span class="n">Keyboard</span><span class="o">.</span><span class="n">keycodes</span><span class="p">[</span><span class="s1">'up'</span><span class="p">]:</span>
<span class="n">dy</span> <span class="o">=</span> <span class="mi">5</span>
<span class="k">elif</span> <span class="n">key</span> <span class="o">==</span> <span class="n">Keyboard</span><span class="o">.</span><span class="n">keycodes</span><span class="p">[</span><span class="s1">'down'</span><span class="p">]:</span>
<span class="n">dy</span> <span class="o">=</span> <span class="o">-</span><span class="mi">5</span>
<span class="k">elif</span> <span class="n">key</span> <span class="o">==</span> <span class="n">Keyboard</span><span class="o">.</span><span class="n">keycodes</span><span class="p">[</span><span class="s1">'left'</span><span class="p">]:</span>
<span class="n">dx</span> <span class="o">=</span> <span class="o">-</span><span class="mi">5</span>
<span class="k">elif</span> <span class="n">key</span> <span class="o">==</span> <span class="n">Keyboard</span><span class="o">.</span><span class="n">keycodes</span><span class="p">[</span><span class="s1">'right'</span><span class="p">]:</span>
<span class="n">dx</span> <span class="o">=</span> <span class="o">+</span><span class="mi">5</span>
<span class="bp">self</span><span class="o">.</span><span class="n">fred_speed</span> <span class="o">+=</span> <span class="n">Vector</span><span class="p">(</span><span class="n">dx</span><span class="p">,</span> <span class="n">dy</span><span class="p">)</span>
</code></pre></div>
<p>Czyli, możemy sterować już Fredem, ale nadal jest zero interakcji między Fredem a balonami. </p>
<h4>Fizyka: jest interakcja, ale wszystko nam ucieka</h4>
<p>Do fizyki mamy 2 świetne biblioteki: <strong>pymunk</strong> (http://pymunk.org ) i
<strong>cymunk</strong> (http://github.com/kivy/cymunk ) - obie oparte na <strong>Chipmunk</strong>
(http://chipmunk-physics.net ). </p>
<p>Do niedawna tylko <em>Cymunk</em> wchodził w grę, jeżeli trzeba było zbudować grę na
androida. Od ponad roku <em>Pymunk</em> ma także swoją receptę w <em>Python4android</em> więc należy sądzić że też jest używalny. </p>
<p>Cymunk jest nieco do tyłu w stosunku do Pymunka jeżeli chodzi o funckcjonalność
i obsługiwaną wersję <em>Chipmunka</em>, użyję jednak Cymunka w tym przykładzie, jako
że <em>Kivent</em>, o którym jest w dalszej części artykułu, używa właśnie <em>Cymunka</em>. </p>
<p>Instalacja cymunk:</p>
<div class="highlight"><pre><span></span><code> maho@dlaptop:~/workspace/kiventgames/kivygame$ pip install git+https://github.com/kivy/cymunk
</code></pre></div>
<p>I wkładam <em>cymunka</em> do aplikacji:</p>
<div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">cymunk</span> <span class="kn">import</span> <span class="n">Space</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="k">class</span> <span class="nc">KivyGame</span><span class="p">(</span><span class="n">Widget</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="o">**</span><span class="n">kwa</span><span class="p">):</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">init_physics</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">init_physics</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">space</span> <span class="o">=</span> <span class="n">Space</span><span class="p">()</span> <span class="c1"># cała fizyka dzieje się w ramach obiektu Space()</span>
<span class="k">def</span> <span class="nf">update</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">dt</span><span class="p">):</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">space</span><span class="o">.</span><span class="n">step</span><span class="p">(</span><span class="mf">1.0</span> <span class="o">/</span> <span class="mi">30</span><span class="p">)</span>
</code></pre></div>
<p>W update robimy <code>tick</code> fizyki - mówimy jej że minęło <code>1/30</code> sekundy. Można podać zmienną <code>dt</code> zamiast sztywnego <code>1/30</code>,
ale problem jest taki że jeżeli coś się przytka w systemie i np. następne
update zrobi się nie za <code>1/30</code> sekundy a za jedną sekundę, to może się np. okazać
że jakiś obiekt przeleciał przez ścianę. Lepiej więc poświęcić zgodność czasu
fizyki <em>chipmunka</em> z czasem rzeczywistym, niż narazić się na fantastycznie
wielkie przyśpieszenia osiągane przez obiekty. </p>
<p>Trzeba powiązać obiekty z <code>self.space</code>:</p>
<div class="highlight"><pre><span></span><code> <span class="k">def</span> <span class="nf">init_physics</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">space</span> <span class="o">=</span> <span class="n">Space</span><span class="p">()</span> <span class="c1"># cała fizyka dzieje się w ramach obiektu Space()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">init_body</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">fred</span><span class="p">,</span> <span class="mi">72</span><span class="p">)</span>
<span class="k">for</span> <span class="n">b</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">baloons</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">init_body</span><span class="p">(</span><span class="n">b</span><span class="p">,</span> <span class="mi">50</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">init_body</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">widget</span><span class="p">,</span> <span class="n">r</span><span class="p">):</span>
<span class="sd">""" initialize cymunk body for given widget as circle</span>
<span class="sd"> of radius=r</span>
<span class="sd"> """</span>
<span class="n">widget</span><span class="o">.</span><span class="n">body</span> <span class="o">=</span> <span class="n">Body</span><span class="p">(</span><span class="n">defs</span><span class="o">.</span><span class="n">mass</span><span class="p">,</span> <span class="n">defs</span><span class="o">.</span><span class="n">moment</span><span class="p">)</span>
<span class="n">widget</span><span class="o">.</span><span class="n">body</span><span class="o">.</span><span class="n">position</span> <span class="o">=</span> <span class="n">widget</span><span class="o">.</span><span class="n">center</span>
<span class="bp">self</span><span class="o">.</span><span class="n">widgets_with_bodies</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">widget</span><span class="p">)</span>
<span class="n">shape</span> <span class="o">=</span> <span class="n">Circle</span><span class="p">(</span><span class="n">widget</span><span class="o">.</span><span class="n">body</span><span class="p">,</span> <span class="n">r</span><span class="p">)</span>
<span class="n">shape</span><span class="o">.</span><span class="n">elasticity</span> <span class="o">=</span> <span class="n">defs</span><span class="o">.</span><span class="n">elasticity</span>
<span class="n">shape</span><span class="o">.</span><span class="n">friction</span> <span class="o">=</span> <span class="n">defs</span><span class="o">.</span><span class="n">friction</span>
<span class="n">shape</span><span class="o">.</span><span class="n">collision_type</span> <span class="o">=</span> <span class="n">defs</span><span class="o">.</span><span class="n">default_collision_type</span>
<span class="bp">self</span><span class="o">.</span><span class="n">space</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">widget</span><span class="o">.</span><span class="n">body</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">space</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">shape</span><span class="p">)</span>
</code></pre></div>
<p>następnie sprawić, żeby aplikacja śliedziła pozycje obiektów</p>
<div class="highlight"><pre><span></span><code> <span class="k">def</span> <span class="nf">update</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">dt</span><span class="p">):</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="k">for</span> <span class="n">w</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">widgets_with_bodies</span><span class="p">:</span>
<span class="n">w</span><span class="o">.</span><span class="n">center</span> <span class="o">=</span> <span class="nb">tuple</span><span class="p">(</span><span class="n">w</span><span class="o">.</span><span class="n">body</span><span class="o">.</span><span class="n">position</span><span class="p">)</span>
</code></pre></div>
<p>Jest jeden drobny szkopuł: <code>self.fred</code> nie ma w <code>KivyGame.__init__</code>, całe procesowanie rzeczy zdefiniowanych w <code>kivygame.kv</code> dzieje się "tuż po" <code>__init__</code>. Dlatego trzeba odłożyć operacje na tych obiektach na "tuż po". W tym celu:</p>
<div class="highlight"><pre><span></span><code><span class="k">class</span> <span class="nc">KivyGame</span><span class="p">(</span><span class="n">Widget</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="o">**</span><span class="n">kwa</span><span class="p">):</span>
<span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="o">**</span><span class="n">kwa</span><span class="p">)</span>
<span class="n">Clock</span><span class="o">.</span><span class="n">schedule_once</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">init_widgets</span><span class="p">)</span> <span class="c1"># tutaj przekazujemy do "tuż po"</span>
<span class="k">def</span> <span class="nf">init_widgets</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">dt</span><span class="p">):</span> <span class="c1"># a to się wykona "tuż po"</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">init_physics</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">init_physics</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">init_body</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">fred</span><span class="p">,</span> <span class="mi">72</span><span class="p">)</span>
<span class="k">for</span> <span class="n">b</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">baloons</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">init_body</span><span class="p">(</span><span class="n">b</span><span class="p">,</span> <span class="mi">50</span><span class="p">)</span>
</code></pre></div>
<p>Po uruchomieniu wygląda tak:</p>
<p><img alt="po uruchomieniu wygląda tak" src="http://maho.pro/images/05-physics-01.png"></p>
<p>Balony się pięknie rozsunęły. Ale, widać że nie da się już sterować Fredem, zaś balony przestały się ruszać. To oczywiste, wszelkie zmiany do <code>self.fred.pos</code> są kasowane poprzez ustawianie tej pozycji do fizycznego ciała. Dlatego, trzeba od tej pory operować tylko na ciałach fizycznych. </p>
<p>Dlatego to:</p>
<div class="highlight"><pre><span></span><code> <span class="n">b</span><span class="o">.</span><span class="n">pos</span> <span class="o">=</span> <span class="p">(</span><span class="n">x</span> <span class="o">+</span> <span class="n">randint</span><span class="p">(</span><span class="o">-</span><span class="mi">5</span><span class="p">,</span> <span class="mi">5</span><span class="p">),</span> <span class="n">y</span> <span class="o">+</span> <span class="n">randint</span><span class="p">(</span><span class="o">-</span><span class="mi">5</span><span class="p">,</span> <span class="mi">5</span><span class="p">))</span>
</code></pre></div>
<p>zamieniam na </p>
<div class="highlight"><pre><span></span><code> <span class="n">b</span><span class="o">.</span><span class="n">body</span><span class="o">.</span><span class="n">apply_impulse</span><span class="p">((</span><span class="n">randint</span><span class="p">(</span><span class="o">-</span><span class="mi">10</span><span class="p">,</span> <span class="mi">10</span><span class="p">),</span> <span class="n">randint</span><span class="p">(</span><span class="o">-</span><span class="mi">10</span><span class="p">,</span> <span class="mi">10</span><span class="p">)))</span>
</code></pre></div>
<p>Zaś sterowanie Fredem zamieniam na:</p>
<p>zamieniam tym:</p>
<div class="highlight"><pre><span></span><code> <span class="k">def</span> <span class="nf">on_key_up</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">__window</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="o">*</span><span class="n">__</span><span class="p">,</span> <span class="o">**</span><span class="n">___</span><span class="p">):</span>
<span class="c1"># code = Keyboard.keycode_to_string(None, key) # można tak, ale to brzydki hack</span>
<span class="n">dx</span><span class="p">,</span> <span class="n">dy</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span>
<span class="k">if</span> <span class="n">key</span> <span class="o">==</span> <span class="n">Keyboard</span><span class="o">.</span><span class="n">keycodes</span><span class="p">[</span><span class="s1">'up'</span><span class="p">]:</span>
<span class="n">dy</span> <span class="o">=</span> <span class="mi">500</span>
<span class="k">elif</span> <span class="n">key</span> <span class="o">==</span> <span class="n">Keyboard</span><span class="o">.</span><span class="n">keycodes</span><span class="p">[</span><span class="s1">'down'</span><span class="p">]:</span>
<span class="n">dy</span> <span class="o">=</span> <span class="o">-</span><span class="mi">500</span>
<span class="k">elif</span> <span class="n">key</span> <span class="o">==</span> <span class="n">Keyboard</span><span class="o">.</span><span class="n">keycodes</span><span class="p">[</span><span class="s1">'left'</span><span class="p">]:</span>
<span class="n">dx</span> <span class="o">=</span> <span class="o">-</span><span class="mi">500</span>
<span class="k">elif</span> <span class="n">key</span> <span class="o">==</span> <span class="n">Keyboard</span><span class="o">.</span><span class="n">keycodes</span><span class="p">[</span><span class="s1">'right'</span><span class="p">]:</span>
<span class="n">dx</span> <span class="o">=</span> <span class="o">+</span><span class="mi">500</span>
<span class="bp">self</span><span class="o">.</span><span class="n">fred</span><span class="o">.</span><span class="n">body</span><span class="o">.</span><span class="n">apply_impulse</span><span class="p">(</span><span class="n">Vector</span><span class="p">(</span><span class="n">dx</span><span class="p">,</span> <span class="n">dy</span><span class="p">))</span>
<span class="k">def</span> <span class="nf">on_touch_up</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">touch</span><span class="p">):</span>
<span class="n">vdir</span> <span class="o">=</span> <span class="n">Vector</span><span class="p">(</span><span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">)</span> <span class="o">-</span> <span class="bp">self</span><span class="o">.</span><span class="n">fred</span><span class="o">.</span><span class="n">center</span> <span class="c1"># wynik to Vector</span>
<span class="bp">self</span><span class="o">.</span><span class="n">fred</span><span class="o">.</span><span class="n">body</span><span class="o">.</span><span class="n">apply_impulse</span><span class="p">(</span><span class="n">vdir</span> <span class="o">*</span> <span class="mi">5</span><span class="p">)</span>
</code></pre></div>
<p>Kod pod tagiem <a href="https://gitlab.com/maho/pycon-kivygame/tree/05-physics-02">05-physics-02</a></p>
<h4>Dodajemy ściany</h4>
<p>Balony i główny bohater nam uciekają, trzeba zrobić ściany. Ściany będą na granicy okienka, a więc i tak niewidoczne, a więc tworzymy te obiekty tylko w <code>Space</code> cymunka. </p>
<p>Żeby się nie okazało że zmniejszając okienko, "przelecimy" ścianą przez obiekt, ściany zrobimy naprawdę bardzo grube, jak na obrazku:</p>
<p><img alt="ściany" src="http://maho.pro/images/walls.png"></p>
<p>Korzystamy ze kształtu <code>Segment</code>, któremu nadamy bardzo dużą grubość (400px). </p>
<div class="highlight"><pre><span></span><code><span class="k">class</span> <span class="nc">KivyGame</span><span class="p">(</span><span class="n">Widget</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="o">**</span><span class="n">kwa</span><span class="p">):</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">walls</span> <span class="o">=</span> <span class="p">[]</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">init_physics</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">create_walls</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">create_walls</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">segments</span><span class="p">,</span> <span class="n">R</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">wall_segments</span><span class="p">()</span>
<span class="k">for</span> <span class="n">v1</span><span class="p">,</span> <span class="n">v2</span> <span class="ow">in</span> <span class="n">segments</span><span class="p">:</span>
<span class="n">wall</span> <span class="o">=</span> <span class="n">Segment</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">space</span><span class="o">.</span><span class="n">static_body</span><span class="p">,</span> <span class="n">v1</span><span class="p">,</span> <span class="n">v2</span><span class="p">,</span> <span class="n">R</span><span class="p">)</span>
<span class="n">wall</span><span class="o">.</span><span class="n">elasticity</span> <span class="o">=</span> <span class="n">defs</span><span class="o">.</span><span class="n">elasticity</span>
<span class="n">wall</span><span class="o">.</span><span class="n">friction</span> <span class="o">=</span> <span class="n">defs</span><span class="o">.</span><span class="n">wall_friction</span>
<span class="n">wall</span><span class="o">.</span><span class="n">collision_type</span> <span class="o">=</span> <span class="n">defs</span><span class="o">.</span><span class="n">default_collision_type</span>
<span class="bp">self</span><span class="o">.</span><span class="n">space</span><span class="o">.</span><span class="n">add_static</span><span class="p">(</span><span class="n">wall</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">walls</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">wall</span><span class="p">)</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">wall_segments</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">w</span><span class="p">,</span> <span class="n">h</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">size</span>
<span class="n">R</span> <span class="o">=</span> <span class="mi">200</span>
<span class="k">return</span> <span class="p">[(</span><span class="n">Vec2d</span><span class="p">(</span><span class="o">-</span><span class="mi">2</span> <span class="o">*</span> <span class="n">R</span><span class="p">,</span> <span class="o">-</span><span class="n">R</span><span class="p">),</span> <span class="n">Vec2d</span><span class="p">(</span><span class="n">w</span> <span class="o">+</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">R</span><span class="p">,</span> <span class="o">-</span><span class="n">R</span><span class="p">)),</span>
<span class="p">(</span><span class="n">Vec2d</span><span class="p">(</span><span class="o">-</span><span class="n">R</span><span class="p">,</span> <span class="o">-</span><span class="mi">2</span> <span class="o">*</span> <span class="n">R</span><span class="p">),</span> <span class="n">Vec2d</span><span class="p">(</span><span class="o">-</span><span class="n">R</span><span class="p">,</span> <span class="n">h</span> <span class="o">+</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">R</span><span class="p">)),</span>
<span class="p">(</span><span class="n">Vec2d</span><span class="p">(</span><span class="o">-</span><span class="mi">2</span> <span class="o">*</span> <span class="n">R</span><span class="p">,</span> <span class="n">h</span> <span class="o">+</span> <span class="n">R</span><span class="p">),</span> <span class="n">Vec2d</span><span class="p">(</span><span class="n">w</span> <span class="o">+</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">R</span><span class="p">,</span> <span class="n">h</span> <span class="o">+</span> <span class="n">R</span><span class="p">)),</span>
<span class="p">(</span><span class="n">Vec2d</span><span class="p">(</span><span class="n">w</span> <span class="o">+</span> <span class="n">R</span><span class="p">,</span> <span class="n">h</span> <span class="o">+</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">R</span><span class="p">),</span> <span class="n">Vec2d</span><span class="p">(</span><span class="n">w</span> <span class="o">+</span> <span class="n">R</span><span class="p">,</span> <span class="o">-</span><span class="mi">2</span> <span class="o">*</span> <span class="n">R</span><span class="p">))],</span> <span class="n">R</span>
</code></pre></div>
<p><code>Segment</code> jest dodany jako ciało statyczne, nie domyślne -- dynamiczne. Ciało statyczne jest to ciało które uczestniczy w odbiciach z innymi ciałami, ale nie podlega siłom/nie zmienia położenia. </p>
<p>Dodatkowo, reagujemy na zmianę rozmiaru okna (po zmianie parametrów kształtów ciała dynamicznego, reindeksujemy przestrzeń)</p>
<div class="highlight"><pre><span></span><code><span class="k">class</span> <span class="nc">KivyGame</span><span class="p">(</span><span class="n">Widget</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="o">**</span><span class="n">kwa</span><span class="p">):</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="n">Window</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="n">on_resize</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">on_resize</span><span class="p">)</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">on_resize</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">_win</span><span class="p">,</span> <span class="n">w</span><span class="p">,</span> <span class="n">h</span><span class="p">):</span>
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">walls</span><span class="p">:</span>
<span class="k">return</span>
<span class="n">Logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s2">"move walls with width w=</span><span class="si">%s</span><span class="s2"> h=</span><span class="si">%s</span><span class="s2">"</span><span class="p">,</span> <span class="n">w</span><span class="p">,</span> <span class="n">h</span><span class="p">)</span>
<span class="n">segments</span><span class="p">,</span> <span class="n">__R</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">wall_segments</span><span class="p">()</span>
<span class="k">for</span> <span class="p">(</span><span class="n">v1</span><span class="p">,</span> <span class="n">v2</span><span class="p">),</span> <span class="n">wall</span> <span class="ow">in</span> <span class="nb">zip</span><span class="p">(</span><span class="n">segments</span><span class="p">,</span>
<span class="bp">self</span><span class="o">.</span><span class="n">walls</span><span class="p">):</span>
<span class="n">wall</span><span class="o">.</span><span class="n">a</span> <span class="o">=</span> <span class="n">v1</span>
<span class="n">wall</span><span class="o">.</span><span class="n">b</span> <span class="o">=</span> <span class="n">v2</span>
<span class="bp">self</span><span class="o">.</span><span class="n">space</span><span class="o">.</span><span class="n">reindex_static</span><span class="p">()</span>
</code></pre></div>
<p>Pełny kod pod tagiem <a href="https://gitlab.com/maho/pycon-kivygame/tree/06-walls">06-walls</a></p>
<h4>Cel gry, Screen Manager, Plansza success</h4>
<p>Żeby aplikacja chociaż trochę przypominała grę, musi mieć jakiś cel. Niech to będzie dotarcie do ściany po prawej. </p>
<p>W tym celu robimy dwie rzeczy. Identyfikujemy która ściana jest celem i nadajemy jej inny <code>collision_type</code></p>
<div class="highlight"><pre><span></span><code> <span class="k">def</span> <span class="nf">create_walls</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="k">for</span> <span class="n">v1</span><span class="p">,</span> <span class="n">v2</span><span class="p">,</span> <span class="n">goal</span> <span class="ow">in</span> <span class="p">[(</span><span class="n">Vec2d</span><span class="p">(</span><span class="o">-</span><span class="mi">2</span><span class="o">*</span><span class="n">R</span><span class="p">,</span> <span class="o">-</span><span class="n">R</span><span class="p">),</span> <span class="n">Vec2d</span><span class="p">(</span><span class="n">w</span> <span class="o">+</span> <span class="mi">2</span><span class="o">*</span><span class="n">R</span><span class="p">,</span> <span class="o">-</span><span class="n">R</span><span class="p">),</span> <span class="kc">False</span><span class="p">),</span>
<span class="p">(</span><span class="n">Vec2d</span><span class="p">(</span><span class="o">-</span><span class="n">R</span><span class="p">,</span> <span class="o">-</span><span class="mi">2</span><span class="o">*</span><span class="n">R</span><span class="p">),</span> <span class="n">Vec2d</span><span class="p">(</span><span class="o">-</span><span class="n">R</span><span class="p">,</span> <span class="n">h</span> <span class="o">+</span> <span class="mi">2</span><span class="o">*</span><span class="n">R</span><span class="p">),</span> <span class="kc">False</span><span class="p">),</span>
<span class="p">(</span><span class="n">Vec2d</span><span class="p">(</span><span class="o">-</span><span class="mi">2</span><span class="o">*</span><span class="n">R</span><span class="p">,</span> <span class="n">h</span> <span class="o">+</span> <span class="n">R</span><span class="p">),</span> <span class="n">Vec2d</span><span class="p">(</span><span class="n">w</span> <span class="o">+</span> <span class="mi">2</span><span class="o">*</span><span class="n">R</span><span class="p">,</span> <span class="n">h</span> <span class="o">+</span> <span class="n">R</span><span class="p">),</span> <span class="kc">False</span><span class="p">),</span>
<span class="p">(</span><span class="n">Vec2d</span><span class="p">(</span><span class="n">w</span> <span class="o">+</span> <span class="n">R</span><span class="p">,</span> <span class="n">h</span> <span class="o">+</span> <span class="mi">2</span><span class="o">*</span><span class="n">R</span><span class="p">),</span> <span class="n">Vec2d</span><span class="p">(</span><span class="n">w</span> <span class="o">+</span> <span class="n">R</span><span class="p">,</span> <span class="o">-</span><span class="mi">2</span><span class="o">*</span><span class="n">R</span><span class="p">),</span> <span class="kc">True</span><span class="p">)]:</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="n">wall</span><span class="o">.</span><span class="n">collision_type</span> <span class="o">=</span> <span class="n">defs</span><span class="o">.</span><span class="n">goal_collision_type</span> <span class="k">if</span> <span class="n">goal</span> <span class="k">else</span> <span class="n">defs</span><span class="o">.</span><span class="n">default_collision_type</span>
</code></pre></div>
<p>oraz Fredowi:</p>
<div class="highlight"><pre><span></span><code> <span class="k">def</span> <span class="nf">init_physics</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">init_body</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">fred</span><span class="p">,</span> <span class="mi">72</span><span class="p">,</span> <span class="n">defs</span><span class="o">.</span><span class="n">fred_collision_type</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">init_body</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">widget</span><span class="p">,</span> <span class="n">r</span><span class="p">,</span> <span class="n">collision_type</span><span class="o">=</span><span class="n">defs</span><span class="o">.</span><span class="n">default_collision_type</span><span class="p">):</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="n">shape</span><span class="o">.</span><span class="n">collision_type</span> <span class="o">=</span> <span class="n">collision_type</span>
</code></pre></div>
<p>handler kolizji między <code>defs.fred_collision_type</code> a <code>defs.goal_collision_type</code></p>
<div class="highlight"><pre><span></span><code> <span class="k">def</span> <span class="nf">init_physics</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">space</span><span class="o">.</span><span class="n">add_collision_handler</span><span class="p">(</span><span class="n">defs</span><span class="o">.</span><span class="n">goal_collision_type</span><span class="p">,</span>
<span class="n">defs</span><span class="o">.</span><span class="n">fred_collision_type</span><span class="p">,</span>
<span class="bp">self</span><span class="o">.</span><span class="n">goal_reached</span><span class="p">)</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">goal_reached</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">_space</span><span class="p">,</span> <span class="n">_arbiter</span><span class="p">):</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"</span><span class="se">\n\n</span><span class="s2">SUCCESS!!!</span><span class="se">\n\n</span><span class="s2">"</span><span class="p">)</span>
</code></pre></div>
<p>Kod pod tagiem <a href="https://gitlab.com/maho/pycon-kivygame/tree/07-goal-01">07-goal-01</a></p>
<p>No tak, ale print na konsolę to kiepski pomysł w grze. Trzeba zrobić jakąś planszę. Najlepiej użyć <code>ScreenManager</code> który trzyma grę jako jeden z ekranów, planszę z wynikiem jako inny, można tam jeszcze wstawić planszę powitalną, planszę z <em>Game Over</em> itd...</p>
<p>A więc w kivygame.kv jest zawartość klasy GameScreenManager (w nawiasach ostrych - a więc nie mamy tworzenia instancji, ta będzie stworzona w <em>main.py</em>)</p>
<div class="highlight"><pre><span></span><code><span class="o"><</span><span class="n">GameScreenManager</span><span class="o">></span><span class="p">:</span>
<span class="n">Screen</span><span class="p">:</span> <span class="c1"># pierwszy screen jest wyświetlany domyślnie</span>
<span class="n">name</span><span class="p">:</span> <span class="s1">'game'</span>
<span class="n">KivyGame</span><span class="p">:</span>
<span class="n">Screen</span><span class="p">:</span>
<span class="n">name</span><span class="p">:</span> <span class="s1">'success'</span>
<span class="n">BoxLayout</span><span class="p">:</span>
<span class="n">pos</span><span class="p">:</span> <span class="n">root</span><span class="o">.</span><span class="n">pos</span>
<span class="n">size</span><span class="p">:</span> <span class="n">root</span><span class="o">.</span><span class="n">size</span>
<span class="n">Label</span><span class="p">:</span>
<span class="n">font_size</span><span class="p">:</span> <span class="s1">'100sp'</span>
<span class="n">text</span><span class="p">:</span> <span class="s2">"Success!"</span>
</code></pre></div>
<p>oraz w main.py:</p>
<div class="highlight"><pre><span></span><code><span class="k">class</span> <span class="nc">GameScreenManager</span><span class="p">(</span><span class="n">ScreenManager</span><span class="p">):</span>
<span class="k">pass</span>
<span class="k">class</span> <span class="nc">KivyGame</span><span class="p">(</span><span class="n">Widget</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="o">**</span><span class="n">kwa</span><span class="p">):</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">app</span> <span class="o">=</span> <span class="n">App</span><span class="o">.</span><span class="n">get_running_app</span><span class="p">()</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">goal_reached</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">_space</span><span class="p">,</span> <span class="n">_arbiter</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">sm</span><span class="o">.</span><span class="n">current</span> <span class="o">=</span> <span class="s1">'success'</span> <span class="c1"># <-- tak, to wystarczy zeby zmienić screen</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="k">class</span> <span class="nc">KivyGameApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span>
<span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">sm</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">sm</span> <span class="o">=</span> <span class="n">GameScreenManager</span><span class="p">()</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">sm</span>
</code></pre></div>
<p>Tag <a href="https://gitlab.com/maho/pycon-kivygame/tree/07-goal-2">07-goal-2</a></p>
<p>Wynik: </p>
<p><img alt="goal reached" src="http://maho.pro/images/07-goal02.png"></p>
<h3>Stress test - zwiększamy liczbę balonów ...</h3>
<p>No to zróbmy mały stress test - zwiększmy liczbę balonów do 20, 40, 80, 160 .... i zobaczmy kiedy zacznie "ciąć". </p>
<ul>
<li>Przy 80 balonach, musiałem zmniejszyć rozmiar balona o połowę, ponieważ nie mieściły się na planszy. (tag <code>08-stress-80</code>)</li>
<li>Przy 320 balonach, ponownie musiałem to zrobić (tag <code>08-stress-320</code>) i wtedy odnotowałem pierwsze objawy zacinania, zwłaszcza w momencie kiedy robiło się ciasno. </li>
<li>Przy 1280 balonach, zacinanie stało się bardzo widoczne, mimo zmniejszenia rozmiaru balodu do <code>15,15</code>. Ale jeszcze momentami ruch był płynny. </li>
<li>3000 balonów to już było zdecydowanie za dużo dla mojego sprzetu (i3 z 1.6, bez wspomagania grafiki)</li>
</ul>
<h1>KivEnt</h1>
<h2>Kivent - omówienie</h2>
<p><a href="http://kivent.org">Kivent</a> jest biblioteką do tworzenia gier, opartą na <strong>Kivy</strong> i <strong>Cymunk</strong>.
Składa się z kilku modułów, z których interesują nas tutaj dwa: <code>kivent_core</code> i
<code>kivent_cymunk</code>. </p>
<p>Głownym autorem <em>KivEnt</em> jest <a href="https://github.com/Kovak">Jacob Kovacs</a>.</p>
<h2>Zalety i wady Kivent w stosunku do Kivy</h2>
<p>Aplikacja w <em>Kivent</em> jest jednocześnie aplikacją w <em>Kivy</em>, więc automatycznie mają zastosowaie wady i zalety <em>Kivy</em>. </p>
<p>Zalety:</p>
<ul>
<li>jest biblioteką "wszystkomającą". Jest tam i fizyka i kamera, i śledzenie tą kamerą obiektu, wsparcie dla przesuwania dwoma palcami itd. Dużo bajerów które mogą się przydać. </li>
<li>jest wydajniejsza (napisana w Cythonie)</li>
</ul>
<p>Wady:
* ma znacznie mniejsze grono developerów, więc jak znajdziesz błąd - nastaw się na to że będziesz go sam naprawiał.
* jest skomplikowana, jeżeli znajdziesz błąd i będziesz go sam naprawiał, nastaw się że nie będzie lekko (no chyba że dobrze znasz Cythona i OpenGL).
* więcej się trzeba "opisać" żeby stworzyć minimalną aplikację. Więcej wysiłku żeby rozpocząć - gorsza do prototypowania.
* Kivent oparte jest na wzorcu <strong>ECS</strong> (<em>Entity-Component-System</em>) i o ile jest
to ponoć lepszy sposób na tworzenie gier niż oparcie ich o <strong>OOP</strong>, to nie
ułatwia to stworzenia szybkiego prototypu gry, zwłaszcza jeżeli ktoś nie jest
"otrzaskany" z <strong>ECS</strong>.</p>
<h2>Tworzenie gry w Kivent</h2>
<p>To będzie taka sama gra jak w Kivy, z dodatkowym udziałem kilku bajerów pochodzących z <strong>Kivent</strong></p>
<h4>instalacja kivent, kadłubek</h4>
<p>Najpierw, do już stworzonego virtualenva, należy doinstalować <code>kivent_core</code> i <code>kivent_cymunk</code></p>
<div class="highlight"><pre><span></span><code>maho@dlaptop:~/workspace/kiventgames/kiventgame$ pip install <span class="s2">"git+https://github.com/kivy/kivent#egg=kivent_core&subdirectory=modules/core"</span>
Collecting kivent_core from git+https://github.com/kivy/kivent#egg<span class="o">=</span>kivent_core<span class="p">&</span><span class="nv">subdirectory</span><span class="o">=</span>modules/core
<span class="o">[</span>...<span class="o">]</span>
Installing collected packages: kivent-core
Successfully installed kivent-core-2.2.0.dev0
maho@dlaptop:~/workspace/kiventgames/kiventgame$ pip install <span class="s2">"git+https://github.com/kivy/kivent#egg=kivent_cymunk&subdirectory=modules/cymunk"</span>
Collecting kivent_cymunk from git+https://github.com/kivy/kivent#egg<span class="o">=</span>kivent_cymunk<span class="p">&</span><span class="nv">subdirectory</span><span class="o">=</span>modules/cymunk
Cloning https://github.com/kivy/kivent to /tmp/pip-install-ndhjjtuk/kivent-cymunk
<span class="o">[</span>...<span class="o">]</span>
Installing collected packages: kivent-cymunk
Successfully installed kivent-cymunk-1.0.0
</code></pre></div>
<p>Teraz prościutka, pusta aplikacja. Skopiowane z drobnymi modyfikacjami <a href="https://github.com/kivy/kivent/tree/master/examples/4_adding_physics_objects">z przykładów KivEnt</a>. </p>
<p><a href="https://gitlab.com/maho/pycon-kiventgame/blob/01-emptyapp/kiventgame.kv">kiventgame.kv</a>:</p>
<div class="highlight"><pre><span></span><code><span class="n">KiventGame</span><span class="p">:</span>
<span class="n">gameworld</span><span class="p">:</span> <span class="n">gameworld</span>
<span class="n">GameWorld</span><span class="p">:</span>
<span class="nb">id</span><span class="p">:</span> <span class="n">gameworld</span>
<span class="n">gamescreenmanager</span><span class="p">:</span> <span class="n">gamescreenmanager</span>
<span class="n">size_of_gameworld</span><span class="p">:</span> <span class="mi">100</span><span class="o">*</span><span class="mi">1024</span>
<span class="n">size_of_entity_block</span><span class="p">:</span> <span class="mi">128</span>
<span class="n">system_count</span><span class="p">:</span> <span class="mi">2</span>
<span class="n">zones</span><span class="p">:</span> <span class="p">{</span><span class="s1">'general'</span><span class="p">:</span> <span class="mi">10000</span><span class="p">}</span>
<span class="n">GameScreenManager</span><span class="p">:</span>
<span class="nb">id</span><span class="p">:</span> <span class="n">gamescreenmanager</span>
<span class="n">size</span><span class="p">:</span> <span class="n">root</span><span class="o">.</span><span class="n">size</span>
<span class="n">pos</span><span class="p">:</span> <span class="n">root</span><span class="o">.</span><span class="n">pos</span>
<span class="n">gameworld</span><span class="p">:</span> <span class="n">gameworld</span>
<span class="n">GameScreen</span><span class="p">:</span>
<span class="n">name</span><span class="p">:</span> <span class="s1">'main'</span>
</code></pre></div>
<p><a href="https://gitlab.com/maho/pycon-kiventgame/blob/01-emptyapp/main.py">main.py</a>:</p>
<div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">kivy</span>
<span class="kn">from</span> <span class="nn">kivy.app</span> <span class="kn">import</span> <span class="n">App</span>
<span class="kn">from</span> <span class="nn">kivy.uix.widget</span> <span class="kn">import</span> <span class="n">Widget</span>
<span class="kn">import</span> <span class="nn">kivent_core</span>
<span class="k">class</span> <span class="nc">KiventGame</span><span class="p">(</span><span class="n">Widget</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">gameworld</span><span class="o">.</span><span class="n">init_gameworld</span><span class="p">([],</span> <span class="n">callback</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">init_game</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">init_game</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">setup_states</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">set_state</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">setup_states</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">gameworld</span><span class="o">.</span><span class="n">add_state</span><span class="p">(</span><span class="n">state_name</span><span class="o">=</span><span class="s1">'main'</span><span class="p">,</span>
<span class="n">systems_added</span><span class="o">=</span><span class="p">[],</span>
<span class="n">systems_removed</span><span class="o">=</span><span class="p">[],</span> <span class="n">systems_paused</span><span class="o">=</span><span class="p">[],</span>
<span class="n">systems_unpaused</span><span class="o">=</span><span class="p">[],</span>
<span class="n">screenmanager_screen</span><span class="o">=</span><span class="s1">'main'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">set_state</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">gameworld</span><span class="o">.</span><span class="n">state</span> <span class="o">=</span> <span class="s1">'main'</span>
<span class="k">class</span> <span class="nc">KiventGameApp</span><span class="p">(</span><span class="n">App</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">pass</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">'__main__'</span><span class="p">:</span>
<span class="n">KiventGameApp</span><span class="p">()</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
</code></pre></div>
<h4>Dodajemy postaci</h4>
<p>I o ile dotąd wyglądało to mniej więcej prosto, to teraz człowiek się opisać jak głupi.</p>
<p>Najpierw, trzeba pobrać plik shadera, skopiowałem go z przykładów <em>Kivent</em>:</p>
<div class="highlight"><pre><span></span><code>maho@dlaptop:~/workspace/kiventgames/kiventgame$ mkdir assets
<span class="o">(</span>kivygame<span class="o">)</span>
maho@dlaptop:~/workspace/kiventgames/kiventgame$ wget -nv -O assets/positionrotateshader.glsl https://raw.githubusercontent.com/kivy/kivent/master/examples/assets/glsl/positionrotateshader.glsl
<span class="m">2018</span>-07-23 <span class="m">00</span>:13:03 URL:https://raw.githubusercontent.com/kivy/kivent/master/examples/assets/glsl/positionrotateshader.glsl <span class="o">[</span><span class="m">1360</span>/1360<span class="o">]</span> -> <span class="s2">"assets/positionrotateshader.glsl"</span> <span class="o">[</span><span class="m">1</span><span class="o">]</span>
</code></pre></div>
<p>W kiventgame.kv trzeba dopisać kilka systemów:</p>
<div class="highlight"><pre><span></span><code><span class="o"><</span><span class="n">KiventGame</span><span class="o">></span><span class="p">:</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="n">PositionSystem2D</span><span class="p">:</span>
<span class="n">system_id</span><span class="p">:</span> <span class="s1">'position'</span>
<span class="n">gameworld</span><span class="p">:</span> <span class="n">gameworld</span>
<span class="n">zones</span><span class="p">:</span> <span class="p">[</span><span class="s1">'general'</span><span class="p">]</span>
<span class="n">RotateSystem2D</span><span class="p">:</span>
<span class="n">system_id</span><span class="p">:</span> <span class="s1">'rotate'</span>
<span class="n">gameworld</span><span class="p">:</span> <span class="n">gameworld</span>
<span class="n">zones</span><span class="p">:</span> <span class="p">[</span><span class="s1">'general'</span><span class="p">]</span>
<span class="n">RotateRenderer</span><span class="p">:</span>
<span class="n">gameworld</span><span class="p">:</span> <span class="n">gameworld</span>
<span class="n">zones</span><span class="p">:</span> <span class="p">[</span><span class="s1">'general'</span><span class="p">]</span>
<span class="n">shader_source</span><span class="p">:</span> <span class="s1">'assets/positionrotateshader.glsl'</span>
<span class="n">CymunkPhysics</span><span class="p">:</span>
<span class="n">gameworld</span><span class="p">:</span> <span class="n">root</span><span class="o">.</span><span class="n">gameworld</span>
<span class="n">zones</span><span class="p">:</span> <span class="p">[</span><span class="s1">'general'</span><span class="p">]</span>
</code></pre></div>
<p>Zaś w main.py, trzeba te system dodać do <code>init_gameworld</code>:</p>
<div class="highlight"><pre><span></span><code><span class="k">class</span> <span class="nc">KiventGame</span><span class="p">(</span><span class="n">Widget</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">gameworld</span><span class="o">.</span><span class="n">init_gameworld</span><span class="p">([</span><span class="s1">'cymunk_physics'</span><span class="p">,</span> <span class="s1">'rotate'</span><span class="p">,</span> <span class="s1">'rotate_renderer'</span><span class="p">,</span> <span class="s1">'position'</span><span class="p">],</span>
<span class="n">callback</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">init_game</span><span class="p">)</span>
</code></pre></div>
<p>No i dodanie obiektu: dodanie obiektu Freda, wymaga wypełnienia kolosalnego wprost słownika, gdzie nie ma żadnych wartości domyślnych. Literalnie każdy jeden parametr musi być wypełniony. </p>
<div class="highlight"><pre><span></span><code> <span class="k">def</span> <span class="nf">draw_objects</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">create_dict</span> <span class="o">=</span> <span class="p">{</span>
<span class="s1">'cymunk_physics'</span><span class="p">:</span> <span class="p">{</span>
<span class="s1">'main_shape'</span><span class="p">:</span> <span class="s1">'circle'</span><span class="p">,</span>
<span class="s1">'velocity'</span><span class="p">:</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span>
<span class="s1">'position'</span><span class="p">:</span> <span class="p">(</span><span class="mi">300</span><span class="p">,</span> <span class="mi">300</span><span class="p">),</span>
<span class="s1">'vel_limit'</span><span class="p">:</span> <span class="mi">250</span><span class="p">,</span>
<span class="s1">'mass'</span><span class="p">:</span> <span class="n">defs</span><span class="o">.</span><span class="n">mass</span><span class="p">,</span>
<span class="s1">'col_shapes'</span><span class="p">:</span> <span class="p">[{</span>
<span class="s1">'shape_type'</span><span class="p">:</span> <span class="s1">'circle'</span><span class="p">,</span>
<span class="s1">'elasticity'</span><span class="p">:</span> <span class="n">defs</span><span class="o">.</span><span class="n">elasticity</span><span class="p">,</span>
<span class="s1">'collision_type'</span><span class="p">:</span> <span class="n">defs</span><span class="o">.</span><span class="n">fred_collision_type</span><span class="p">,</span>
<span class="s1">'friction'</span><span class="p">:</span> <span class="n">defs</span><span class="o">.</span><span class="n">friction</span><span class="p">,</span>
<span class="s1">'shape_info'</span><span class="p">:</span> <span class="p">{</span>
<span class="s1">'inner_radius'</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="s1">'outer_radius'</span><span class="p">:</span> <span class="mi">80</span><span class="p">,</span>
<span class="s1">'mass'</span><span class="p">:</span> <span class="n">defs</span><span class="o">.</span><span class="n">mass</span><span class="p">,</span> <span class="s1">'offset'</span><span class="p">:</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}],</span>
<span class="s1">'angular_velocity'</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="c1"># literalnie wszystkie klucze muszą być wypełnione</span>
<span class="s1">'ang_vel_limit'</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
<span class="s1">'angle'</span><span class="p">:</span> <span class="mi">0</span>
<span class="p">},</span>
<span class="s1">'rotate_renderer'</span><span class="p">:</span> <span class="p">{</span>
<span class="s1">'texture'</span><span class="p">:</span> <span class="s1">'fred'</span><span class="p">,</span>
<span class="s1">'size'</span><span class="p">:</span> <span class="p">(</span><span class="mi">145</span><span class="p">,</span> <span class="mi">145</span><span class="p">),</span>
<span class="s1">'render'</span><span class="p">:</span> <span class="kc">True</span>
<span class="p">},</span>
<span class="s1">'position'</span><span class="p">:</span> <span class="p">(</span><span class="mi">300</span><span class="p">,</span> <span class="mi">300</span><span class="p">),</span>
<span class="s1">'rotate'</span><span class="p">:</span> <span class="mi">0</span>
<span class="p">}</span>
<span class="c1"># id fred, to jest int - identyfikator Freda w systemach (Position, Renderer, Cymunk)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">id_fred</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">gameworld</span><span class="o">.</span><span class="n">init_entity</span><span class="p">(</span><span class="n">create_dict</span><span class="p">,</span>
<span class="p">[</span><span class="s1">'position'</span><span class="p">,</span> <span class="s1">'rotate'</span><span class="p">,</span>
<span class="s1">'rotate_renderer'</span><span class="p">,</span>
<span class="s1">'cymunk_physics'</span><span class="p">])</span>
</code></pre></div>
<p>a uprzednio, tekstura musi być załadowana:</p>
<div class="highlight"><pre><span></span><code><span class="n">texture_manager</span><span class="o">.</span><span class="n">load_image</span><span class="p">(</span><span class="s1">'img/fred.png'</span><span class="p">)</span>
<span class="n">texture_manager</span><span class="o">.</span><span class="n">load_image</span><span class="p">(</span><span class="s1">'img/baloon.png'</span><span class="p">)</span>
</code></pre></div>
<p><a href="https://gitlab.com/maho/pycon-kiventgame/blob/02-objects-01/">Tag: 02-objects-01</a></p>
<p>Żeby dodać balony, w/w funkcję zmieniam na bardziej uniwersalną:</p>
<div class="highlight"><pre><span></span><code><span class="k">class</span> <span class="nc">KiventGame</span><span class="p">(</span><span class="n">Widget</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">ids_baloons</span> <span class="o">=</span> <span class="p">[]</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">draw_object</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">texture</span><span class="p">,</span> <span class="n">pos</span><span class="p">,</span> <span class="n">size</span><span class="p">,</span> <span class="n">collision_type</span><span class="o">=</span><span class="n">defs</span><span class="o">.</span><span class="n">default_collision_type</span><span class="p">):</span>
<span class="n">w</span><span class="p">,</span> <span class="n">h</span> <span class="o">=</span> <span class="n">size</span>
<span class="n">R</span> <span class="o">=</span> <span class="p">(</span><span class="n">w</span> <span class="o">+</span> <span class="n">h</span><span class="p">)</span> <span class="o">/</span> <span class="mi">4</span>
<span class="n">create_dict</span> <span class="o">=</span> <span class="p">{</span>
<span class="s1">'cymunk_physics'</span><span class="p">:</span> <span class="p">{</span>
<span class="s1">'main_shape'</span><span class="p">:</span> <span class="s1">'circle'</span><span class="p">,</span>
<span class="s1">'velocity'</span><span class="p">:</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span>
<span class="s1">'position'</span><span class="p">:</span> <span class="n">pos</span><span class="p">,</span>
<span class="s1">'vel_limit'</span><span class="p">:</span> <span class="mi">250</span><span class="p">,</span>
<span class="s1">'mass'</span><span class="p">:</span> <span class="n">defs</span><span class="o">.</span><span class="n">mass</span><span class="p">,</span>
<span class="s1">'col_shapes'</span><span class="p">:</span> <span class="p">[{</span>
<span class="s1">'shape_type'</span><span class="p">:</span> <span class="s1">'circle'</span><span class="p">,</span>
<span class="s1">'elasticity'</span><span class="p">:</span> <span class="n">defs</span><span class="o">.</span><span class="n">elasticity</span><span class="p">,</span>
<span class="s1">'collision_type'</span><span class="p">:</span> <span class="n">collision_type</span><span class="p">,</span>
<span class="s1">'friction'</span><span class="p">:</span> <span class="n">defs</span><span class="o">.</span><span class="n">friction</span><span class="p">,</span>
<span class="s1">'shape_info'</span><span class="p">:</span> <span class="p">{</span>
<span class="s1">'inner_radius'</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="s1">'outer_radius'</span><span class="p">:</span> <span class="n">R</span><span class="p">,</span>
<span class="s1">'mass'</span><span class="p">:</span> <span class="n">defs</span><span class="o">.</span><span class="n">mass</span><span class="p">,</span> <span class="s1">'offset'</span><span class="p">:</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}],</span>
<span class="s1">'angular_velocity'</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="c1"># literalnie wszystkie klucze muszą być wypełnione</span>
<span class="s1">'ang_vel_limit'</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
<span class="s1">'angle'</span><span class="p">:</span> <span class="n">radians</span><span class="p">(</span><span class="mi">180</span><span class="p">)</span>
<span class="p">},</span>
<span class="s1">'rotate_renderer'</span><span class="p">:</span> <span class="p">{</span>
<span class="s1">'texture'</span><span class="p">:</span> <span class="n">texture</span><span class="p">,</span>
<span class="s1">'size'</span><span class="p">:</span> <span class="n">size</span><span class="p">,</span>
<span class="s1">'render'</span><span class="p">:</span> <span class="kc">True</span>
<span class="p">},</span>
<span class="s1">'position'</span><span class="p">:</span> <span class="n">pos</span><span class="p">,</span>
<span class="s1">'rotate'</span><span class="p">:</span> <span class="mi">0</span>
<span class="p">}</span>
<span class="c1"># id fred, to jest int - identyfikator Freda w systemach (Position, Renderer, Cymunk)</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">gameworld</span><span class="o">.</span><span class="n">init_entity</span><span class="p">(</span><span class="n">create_dict</span><span class="p">,</span>
<span class="p">[</span><span class="s1">'position'</span><span class="p">,</span> <span class="s1">'rotate'</span><span class="p">,</span>
<span class="s1">'rotate_renderer'</span><span class="p">,</span>
<span class="s1">'cymunk_physics'</span><span class="p">])</span>
<span class="k">def</span> <span class="nf">draw_objects</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">id_fred</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">draw_object</span><span class="p">(</span><span class="s1">'fred'</span><span class="p">,</span> <span class="p">(</span><span class="mi">300</span><span class="p">,</span> <span class="mi">300</span><span class="p">),</span> <span class="p">(</span><span class="mi">145</span><span class="p">,</span> <span class="mi">145</span><span class="p">),</span> <span class="n">defs</span><span class="o">.</span><span class="n">fred_collision_type</span><span class="p">)</span>
<span class="k">for</span> <span class="n">__</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">defs</span><span class="o">.</span><span class="n">num_baloons</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">ids_baloons</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">draw_object</span><span class="p">(</span><span class="s1">'baloon'</span><span class="p">,</span>
<span class="p">(</span><span class="n">randint</span><span class="p">(</span><span class="mi">300</span><span class="p">,</span> <span class="mi">400</span><span class="p">),</span> <span class="n">randint</span><span class="p">(</span><span class="mi">300</span><span class="p">,</span> <span class="mi">400</span><span class="p">)),</span>
<span class="p">(</span><span class="mi">50</span><span class="p">,</span> <span class="mi">50</span><span class="p">)))</span>
</code></pre></div>
<h4>Input</h4>
<p>To nadal <em>Kivy</em>, więc input będzie niemal identyczny jak w <em>KivyGame</em>:</p>
<div class="highlight"><pre><span></span><code><span class="k">class</span> <span class="nc">KiventGame</span><span class="p">(</span><span class="n">Widget</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="n">EventLoop</span><span class="o">.</span><span class="n">window</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="n">on_key_up</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">on_key_up</span><span class="p">)</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">on_key_up</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">__window</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="o">*</span><span class="n">__</span><span class="p">,</span> <span class="o">**</span><span class="n">___</span><span class="p">):</span>
<span class="n">dx</span><span class="p">,</span> <span class="n">dy</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span>
<span class="k">if</span> <span class="n">key</span> <span class="o">==</span> <span class="n">Keyboard</span><span class="o">.</span><span class="n">keycodes</span><span class="p">[</span><span class="s1">'up'</span><span class="p">]:</span>
<span class="n">dy</span> <span class="o">=</span> <span class="mi">1500</span>
<span class="k">elif</span> <span class="n">key</span> <span class="o">==</span> <span class="n">Keyboard</span><span class="o">.</span><span class="n">keycodes</span><span class="p">[</span><span class="s1">'down'</span><span class="p">]:</span>
<span class="n">dy</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1500</span>
<span class="k">elif</span> <span class="n">key</span> <span class="o">==</span> <span class="n">Keyboard</span><span class="o">.</span><span class="n">keycodes</span><span class="p">[</span><span class="s1">'left'</span><span class="p">]:</span>
<span class="n">dx</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1500</span>
<span class="k">elif</span> <span class="n">key</span> <span class="o">==</span> <span class="n">Keyboard</span><span class="o">.</span><span class="n">keycodes</span><span class="p">[</span><span class="s1">'right'</span><span class="p">]:</span>
<span class="n">dx</span> <span class="o">=</span> <span class="o">+</span><span class="mi">1500</span>
<span class="n">fred</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">gameworld</span><span class="o">.</span><span class="n">entities</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">id_fred</span><span class="p">]</span>
<span class="n">fred</span><span class="o">.</span><span class="n">cymunk_physics</span><span class="o">.</span><span class="n">body</span><span class="o">.</span><span class="n">apply_impulse</span><span class="p">(</span><span class="n">Vec2d</span><span class="p">(</span><span class="n">dx</span><span class="p">,</span> <span class="n">dy</span><span class="p">))</span>
<span class="k">def</span> <span class="nf">on_touch_up</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">touch</span><span class="p">):</span>
<span class="n">fred</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">gameworld</span><span class="o">.</span><span class="n">entities</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">id_fred</span><span class="p">]</span>
<span class="n">vdir</span> <span class="o">=</span> <span class="n">Vector</span><span class="p">(</span><span class="n">touch</span><span class="o">.</span><span class="n">pos</span><span class="p">)</span> <span class="o">-</span> <span class="n">fred</span><span class="o">.</span><span class="n">position</span><span class="o">.</span><span class="n">pos</span>
<span class="n">fred</span><span class="o">.</span><span class="n">cymunk_physics</span><span class="o">.</span><span class="n">body</span><span class="o">.</span><span class="n">apply_impulse</span><span class="p">(</span><span class="n">vdir</span> <span class="o">*</span> <span class="mi">5</span><span class="p">)</span>
</code></pre></div>
<p><a href="https://gitlab.com/maho/pycon-kiventgame/blob/03-input/">Tag: 03-input</a></p>
<h4>Ściany</h4>
<p>Podobnie jak w Kivy:</p>
<div class="highlight"><pre><span></span><code><span class="k">class</span> <span class="nc">KiventGame</span><span class="p">(</span><span class="n">Widget</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="n">Window</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="n">on_resize</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">on_resize</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">init_game</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">create_walls</span><span class="p">()</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">create_walls</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">segments</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">wall_segments</span><span class="p">()</span>
<span class="n">shapes</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="p">(</span><span class="n">v1</span><span class="p">,</span> <span class="n">v2</span><span class="p">),</span> <span class="n">goal</span> <span class="ow">in</span> <span class="nb">zip</span><span class="p">(</span><span class="n">segments</span><span class="p">,</span> <span class="p">[</span><span class="kc">False</span><span class="p">,</span> <span class="kc">False</span><span class="p">,</span> <span class="kc">False</span><span class="p">,</span> <span class="kc">True</span><span class="p">]):</span>
<span class="n">coltype</span> <span class="o">=</span> <span class="n">defs</span><span class="o">.</span><span class="n">goal_collision_type</span> <span class="k">if</span> <span class="n">goal</span> <span class="k">else</span> <span class="n">defs</span><span class="o">.</span><span class="n">default_collision_type</span>
<span class="n">shapes</span><span class="o">.</span><span class="n">append</span><span class="p">({</span><span class="s1">'shape_type'</span><span class="p">:</span> <span class="s1">'segment'</span><span class="p">,</span>
<span class="s1">'elasticity'</span><span class="p">:</span> <span class="n">defs</span><span class="o">.</span><span class="n">elasticity</span><span class="p">,</span>
<span class="s1">'collision_type'</span><span class="p">:</span> <span class="n">coltype</span><span class="p">,</span>
<span class="s1">'friction'</span><span class="p">:</span> <span class="n">defs</span><span class="o">.</span><span class="n">friction</span><span class="p">,</span>
<span class="s1">'shape_info'</span><span class="p">:</span> <span class="p">{</span><span class="s1">'a'</span><span class="p">:</span> <span class="n">v1</span><span class="p">,</span> <span class="s1">'b'</span><span class="p">:</span> <span class="n">v2</span><span class="p">,</span> <span class="s1">'radius'</span><span class="p">:</span> <span class="n">defs</span><span class="o">.</span><span class="n">wall_size</span><span class="p">,</span>
<span class="s1">'mass'</span><span class="p">:</span> <span class="mi">0</span><span class="p">}</span> <span class="c1"># mass: 0 => obiekt statyczny</span>
<span class="p">})</span>
<span class="bp">self</span><span class="o">.</span><span class="n">id_walls</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">gameworld</span><span class="o">.</span><span class="n">init_entity</span><span class="p">({</span>
<span class="s1">'cymunk_physics'</span><span class="p">:</span> <span class="p">{</span>
<span class="s1">'main_shape'</span><span class="p">:</span> <span class="s1">'shape'</span><span class="p">,</span>
<span class="s1">'velocity'</span><span class="p">:</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span>
<span class="s1">'position'</span><span class="p">:</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span>
<span class="s1">'vel_limit'</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
<span class="s1">'mass'</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
<span class="s1">'col_shapes'</span><span class="p">:</span> <span class="n">shapes</span><span class="p">,</span>
<span class="s1">'angular_velocity'</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
<span class="s1">'ang_vel_limit'</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
<span class="s1">'angle'</span><span class="p">:</span> <span class="mi">0</span>
<span class="p">},</span>
<span class="s1">'position'</span><span class="p">:</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span>
<span class="s1">'rotate'</span><span class="p">:</span> <span class="mi">0</span>
<span class="p">},</span> <span class="p">[</span><span class="s1">'position'</span><span class="p">,</span> <span class="s1">'rotate'</span><span class="p">,</span> <span class="s1">'cymunk_physics'</span><span class="p">])</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">on_resize</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">_win</span><span class="p">,</span> <span class="n">_w</span><span class="p">,</span> <span class="n">_h</span><span class="p">):</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">id_walls</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">return</span>
<span class="n">walls</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">gameworld</span><span class="o">.</span><span class="n">entities</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">id_walls</span><span class="p">]</span>
<span class="k">for</span> <span class="p">(</span><span class="n">v1</span><span class="p">,</span> <span class="n">v2</span><span class="p">),</span> <span class="n">shape</span> <span class="ow">in</span> <span class="nb">zip</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">wall_segments</span><span class="p">(),</span> <span class="n">walls</span><span class="o">.</span><span class="n">cymunk_physics</span><span class="o">.</span><span class="n">shapes</span><span class="p">):</span>
<span class="n">shape</span><span class="o">.</span><span class="n">a</span> <span class="o">=</span> <span class="n">v1</span>
<span class="n">shape</span><span class="o">.</span><span class="n">b</span> <span class="o">=</span> <span class="n">v2</span>
<span class="bp">self</span><span class="o">.</span><span class="n">physics</span><span class="o">.</span><span class="n">space</span><span class="o">.</span><span class="n">reindex_static</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">wall_segments</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">w</span><span class="p">,</span> <span class="n">h</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">size</span>
<span class="n">R</span> <span class="o">=</span> <span class="n">defs</span><span class="o">.</span><span class="n">wall_size</span>
<span class="k">return</span> <span class="p">[(</span><span class="n">Vec2d</span><span class="p">(</span><span class="o">-</span><span class="mi">2</span> <span class="o">*</span> <span class="n">R</span><span class="p">,</span> <span class="o">-</span><span class="n">R</span><span class="p">),</span> <span class="n">Vec2d</span><span class="p">(</span><span class="n">w</span> <span class="o">+</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">R</span><span class="p">,</span> <span class="o">-</span><span class="n">R</span><span class="p">)),</span>
<span class="p">(</span><span class="n">Vec2d</span><span class="p">(</span><span class="o">-</span><span class="n">R</span><span class="p">,</span> <span class="o">-</span><span class="mi">2</span> <span class="o">*</span> <span class="n">R</span><span class="p">),</span> <span class="n">Vec2d</span><span class="p">(</span><span class="o">-</span><span class="n">R</span><span class="p">,</span> <span class="n">h</span> <span class="o">+</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">R</span><span class="p">)),</span>
<span class="p">(</span><span class="n">Vec2d</span><span class="p">(</span><span class="o">-</span><span class="mi">2</span> <span class="o">*</span> <span class="n">R</span><span class="p">,</span> <span class="n">h</span> <span class="o">+</span> <span class="n">R</span><span class="p">),</span> <span class="n">Vec2d</span><span class="p">(</span><span class="n">w</span> <span class="o">+</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">R</span><span class="p">,</span> <span class="n">h</span> <span class="o">+</span> <span class="n">R</span><span class="p">)),</span>
<span class="p">(</span><span class="n">Vec2d</span><span class="p">(</span><span class="n">w</span> <span class="o">+</span> <span class="n">R</span><span class="p">,</span> <span class="n">h</span> <span class="o">+</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">R</span><span class="p">),</span> <span class="n">Vec2d</span><span class="p">(</span><span class="n">w</span> <span class="o">+</span> <span class="n">R</span><span class="p">,</span> <span class="o">-</span><span class="mi">2</span> <span class="o">*</span> <span class="n">R</span><span class="p">))]</span>
</code></pre></div>
<p>Oraz <code>CymunkPhysics</code> musi uzyskać uchwyt w pliku .kv i mieć dostęp do self.physics:</p>
<div class="highlight"><pre><span></span><code><span class="o"><</span><span class="n">KiventGame</span><span class="o">></span><span class="p">:</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="n">physics</span><span class="p">:</span> <span class="n">physics</span>
<span class="n">GameWorld</span><span class="p">:</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="n">CymunkPhysics</span><span class="p">:</span>
<span class="nb">id</span><span class="p">:</span> <span class="n">physics</span>
</code></pre></div>
<p>Wygląda zupełnie jak w wersji <em>Kivy</em>:</p>
<p><img alt="like in Kivy" src="http://maho.pro/images/kivent-walls.png"></p>
<p>Tag: <code>04-walls</code></p>
<h4>Dodajemy cel - dotarcie do ściany, użycie Screen Managera, Plansza success</h4>
<p>Ponownie: analogicznie jak w <em>Kivy</em>:</p>
<div class="highlight"><pre><span></span><code><span class="o"><</span><span class="n">KiventGame</span><span class="o">></span><span class="p">:</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="n">GameScreenManager</span><span class="p">:</span>
<span class="n">GameScreen</span><span class="p">:</span>
<span class="n">name</span><span class="p">:</span> <span class="s1">'main'</span>
<span class="n">GameScreen</span><span class="p">:</span>
<span class="n">name</span><span class="p">:</span> <span class="s1">'success'</span>
<span class="n">BoxLayout</span><span class="p">:</span>
<span class="n">pos</span><span class="p">:</span> <span class="n">root</span><span class="o">.</span><span class="n">pos</span>
<span class="n">size</span><span class="p">:</span> <span class="n">root</span><span class="o">.</span><span class="n">size</span>
<span class="n">Label</span><span class="p">:</span>
<span class="n">font_size</span><span class="p">:</span> <span class="s1">'100sp'</span>
<span class="n">text</span><span class="p">:</span> <span class="s2">"Success!"</span>
</code></pre></div>
<p>i w main.py:</p>
<div class="highlight"><pre><span></span><code><span class="k">class</span> <span class="nc">KiventGame</span><span class="p">(</span><span class="n">Widget</span><span class="p">):</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">init_game</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">physics</span><span class="o">.</span><span class="n">space</span><span class="o">.</span><span class="n">add_collision_handler</span><span class="p">(</span><span class="n">defs</span><span class="o">.</span><span class="n">goal_collision_type</span><span class="p">,</span>
<span class="n">defs</span><span class="o">.</span><span class="n">fred_collision_type</span><span class="p">,</span>
<span class="bp">self</span><span class="o">.</span><span class="n">goal_reached</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">setup_states</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">gameworld</span><span class="o">.</span><span class="n">add_state</span><span class="p">(</span><span class="n">state_name</span><span class="o">=</span><span class="s1">'main'</span><span class="p">,</span>
<span class="n">systems_added</span><span class="o">=</span><span class="p">[],</span>
<span class="n">systems_removed</span><span class="o">=</span><span class="p">[],</span> <span class="n">systems_paused</span><span class="o">=</span><span class="p">[],</span>
<span class="n">systems_unpaused</span><span class="o">=</span><span class="p">[],</span>
<span class="n">screenmanager_screen</span><span class="o">=</span><span class="s1">'main'</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">gameworld</span><span class="o">.</span><span class="n">add_state</span><span class="p">(</span><span class="n">state_name</span><span class="o">=</span><span class="s1">'success'</span><span class="p">,</span>
<span class="n">systems_added</span><span class="o">=</span><span class="p">[],</span>
<span class="n">systems_removed</span><span class="o">=</span><span class="p">[],</span> <span class="n">systems_paused</span><span class="o">=</span><span class="p">[</span><span class="s1">'cymunk_physics'</span><span class="p">],</span>
<span class="n">systems_unpaused</span><span class="o">=</span><span class="p">[],</span>
<span class="n">screenmanager_screen</span><span class="o">=</span><span class="s1">'success'</span><span class="p">)</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">goal_reached</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">_space</span><span class="p">,</span> <span class="n">_arbiter</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">gameworld</span><span class="o">.</span><span class="n">state</span> <span class="o">=</span> <span class="s1">'success'</span>
</code></pre></div>
<p>Zauważmy, że tutaj nie ma zmiany nazwy ekranu w <code>ScreenManagerze</code>, ale jest dodanie stanu <code>GameWorld</code>, gdzie przy okazji można zapauzować fizykę.</p>
<p>Interesujące jest także to, że w przypadku <em>Kivy</em> - ekran z planszą "Sukces" zastępował grę, tutaj domyślnie plansza jest nad grą:</p>
<p><img alt="tak to wygląda" src="http://maho.pro/images/kivent-success.png"></p>
<h4>Stress test</h4>
<p>Podobnie jak w przypadku <em>Kivy</em>, stress test dla zwiększanej liczby balonów (20, 40, 80....)</p>
<p>Widać ogromną różnicę. O ile w przypadku aplikacji w <em>Kivy</em> proces brał cały czas od 90% CPU wzwyż, tutaj mamy ok. 20% i dopiero przy 320 balonach mamy 30% CPU i wiatraki się włączyły. Mimo wszystko nie ma mowy o jakimkolwiek zacinaniu. </p>
<p><img alt="320 balonów" src="http://maho.pro/images/kivent-stress-320.png"></p>
<p>Przy 1280 balonach musiałem zmniejszyć rozmiar balonu do 15x15, bo nie mieściły się na planszy.</p>
<p>Przy 5000 balonach dopiero gra straciła używalność, przez kilka długich sekund, kiedy "balony" były "na kupie", wiatraki szalały a na ekranie nic się nie działo, potem jednak
CPU spadło do 60% i gra odzyskała płynność.</p>
<p><img alt="5000 balonów" src="http://maho.pro/images/kivent-stress-5000.png"></p>
<h2>Kivy vs Kivent - podsumowanie</h2>
<p>Mimo że w Kivy wszystko trzeba sobie samemu powiązać, jest o wiele prościej i o wiele szybciej jest napisać prototyp gry. </p>
<p>W Kivent znowu, żeby rozpocząć, trzeba się rozpisać że aż się odechciewa. Dodatkowo w Kivent występują tego typu błedy:</p>
<div class="highlight"><pre><span></span><code><span class="o">[</span>INFO <span class="o">]</span> <span class="o">[</span>Base <span class="o">]</span> Leaving application <span class="k">in</span> progress...
Traceback <span class="o">(</span>most recent call last<span class="o">)</span>:
<span class="o">[</span>...<span class="o">]</span>
File <span class="s2">"main.py"</span>, line <span class="m">23</span>, <span class="k">in</span> init_game
self.draw_objects<span class="o">()</span>
File <span class="s2">"main.py"</span>, line <span class="m">66</span>, <span class="k">in</span> draw_objects
<span class="o">[</span><span class="s1">'position'</span>, <span class="s1">'renderer'</span>, <span class="s1">'cymunk_physics'</span><span class="o">])</span>
File <span class="s2">"kivent_core/gameworld.pyx"</span>, line <span class="m">436</span>, <span class="k">in</span> kivent_core.gameworld.GameWorld.init_entity
File <span class="s2">"kivent_cymunk/physics.pyx"</span>, line <span class="m">466</span>, <span class="k">in</span> kivent_cymunk.physics.CymunkPhysics.create_component
File <span class="s2">"kivent_core/systems/gamesystem.pyx"</span>, line <span class="m">260</span>, <span class="k">in</span> kivent_core.systems.gamesystem.GameSystem.create_component
File <span class="s2">"kivent_cymunk/physics.pyx"</span>, line <span class="m">456</span>, <span class="k">in</span> kivent_cymunk.physics.CymunkPhysics.init_component
File <span class="s2">"kivent_cymunk/physics.pyx"</span>, line <span class="m">289</span>, <span class="k">in</span> kivent_cymunk.physics.CymunkPhysics._init_component
File <span class="s2">"kivent_core/systems/staticmemgamesystem.pyx"</span>, line <span class="m">441</span>, <span class="k">in</span> kivent_core.systems.staticmemgamesystem.ZonedAggregator.add_entity
IndexError: list index out of range
</code></pre></div>
<p>i wiem, że bład polega na tym że mam źle wypełniony create_dict, ale komunikat o błędzie jest <strong>absolutnie bezużyteczny</strong> w tropieniu, co jest przyczyną i trzeba macać metodą prób i błedów. </p>
<p>Z drugiej strony, w Kivent, jeżeli już się przemożemy, jest o wiele, wiele wydajniej. Dodatkowo, jest cała masa gadżetów które nie zostały tutaj opisane, jak skalowanie mapy (ręczne i automatyczne), przesywanie dwoma palcami, śledzenie obiektu (kamerą), wiele kamer w widoku, <em>particles</em> i wiele innych. </p>
<p>Reasumując: jeżeli chcesz na szybko machnąć prototyp, wybierz <em>Kivy</em>. Jeżeli jednak coś więcej, przepisz to potem na <em>Kivent</em>. </p>Effective hg blame in VIM2018-07-15T00:00:00+02:002018-07-15T00:00:00+02:00Mahotag:maho.pro,2018-07-15:/effective-hg-blame-in-vim.html<p>If you want to open tab in vim, which shows you file history, file blame (who changed what) and patch history, here is easy command:</p>
<div class="highlight"><pre><span></span><code> :let fname=expand("%") | tabnew | execute 'read!hg blame '.fname | vnew | execute 'read!hg glog '.fname | new | execute 'read!hg glog -p '.fname
</code></pre></div>(Non) humming servo2018-05-28T00:00:00+02:002018-05-28T00:00:00+02:00Mahotag:maho.pro,2018-05-28:/non-humming-servo.html<p>While I'm IoT, my servo is humming in almost every position. I hear annoying noise every time it's not moving.</p>
<p>After asking Dr. Google, I know that: servo is bad and I need to use another
one, or is set to position which it unable to achieve and need to …</p><p>While I'm IoT, my servo is humming in almost every position. I hear annoying noise every time it's not moving.</p>
<p>After asking Dr. Google, I know that: servo is bad and I need to use another
one, or is set to position which it unable to achieve and need to find another
position [<a class="reference external" href="https://www.rcgroups.com/forums/showpost.php?s=aa4e5ad3d9dee0619af010499f9f99e5&p=38462029&postcount=14">2</a>], or sigal transmitter makes inaccurate signal and servo set
different possition each time and I need to add capacitor to average it [<a class="reference external" href="https://electronics.stackexchange.com/questions/77502/is-there-a-way-to-stop-servos-from-shaking">3</a>],
or it's connected to two different sources of DC [<a class="reference external" href="https://www.elektroda.pl/rtvforum/topic3228896.html#15870929">4</a>] or grounds are not
connected.</p>
<p>None of suggestions above worked. All my servos jitters, I tried various combinations about DC sources, ground is common and I tried all available servo position. It makes noise all the time. Louder or more silent, but almost all the time. `</p>
<p>So, I decided to just ... turn off servo, once it reach the target.</p>
<p>I asumed that 1 second is enough for servo to turn from min to max position (may vary in different servos), the <a class="reference external" href="https://github.com/maho/python-and-dog/blob/2ndtry/dispenser/servo.py">code</a> is here:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">random</span> <span class="kn">import</span> <span class="n">random</span>
<span class="kn">import</span> <span class="nn">machine</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="k">class</span> <span class="nc">Servo</span><span class="p">:</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">pin_num</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">pin</span> <span class="o">=</span> <span class="n">machine</span><span class="o">.</span><span class="n">Pin</span><span class="p">(</span><span class="n">pin_num</span><span class="p">,</span> <span class="n">machine</span><span class="o">.</span><span class="n">Pin</span><span class="o">.</span><span class="n">OUT</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">pwm</span> <span class="o">=</span> <span class="n">machine</span><span class="o">.</span><span class="n">PWM</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">pin</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">pwm</span><span class="o">.</span><span class="n">freq</span><span class="p">(</span><span class="mi">50</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">pwm</span><span class="o">.</span><span class="n">duty</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">pos</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">def</span> <span class="nf">set</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">pos</span><span class="p">):</span>
<span class="n">ftime</span> <span class="o">=</span> <span class="mf">1.0</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">pos</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">sleeptime</span> <span class="o">=</span> <span class="n">ftime</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">sleeptime</span> <span class="o">=</span> <span class="n">ftime</span> <span class="o">*</span> <span class="nb">abs</span><span class="p">(</span><span class="n">pos</span> <span class="o">-</span> <span class="bp">self</span><span class="o">.</span><span class="n">pos</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">pos</span> <span class="o">=</span> <span class="n">pos</span>
<span class="n">duty</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="mi">40</span> <span class="o">+</span> <span class="p">(</span><span class="mi">115</span><span class="o">-</span><span class="mi">40</span><span class="p">)</span><span class="o">*</span><span class="n">pos</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">pwm</span><span class="o">.</span><span class="n">duty</span><span class="p">(</span><span class="n">duty</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"sleeptime"</span><span class="p">,</span> <span class="n">sleeptime</span><span class="p">)</span>
<span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="n">sleeptime</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">pwm</span><span class="o">.</span><span class="n">duty</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</pre></div>
(nie) buczące servo2018-05-28T00:00:00+02:002018-05-28T00:00:00+02:00Mahotag:maho.pro,2018-05-28:/non-humming-servo-pl.html<p>Bawię się IoT i szumi mi serwo. W prawie każdej pozycji, kiedy się nie przesuwa - buczy i hałasuje. I denerwuje.</p>
<p>Po konsultacji z Doktorem Góglem wynika że albo serwo jest kiepskie i trzeba
kupić inne, albo jest ustawiane w pozycji w której nie potrafi się ustawić
i trzeba znaleźć inną …</p><p>Bawię się IoT i szumi mi serwo. W prawie każdej pozycji, kiedy się nie przesuwa - buczy i hałasuje. I denerwuje.</p>
<p>Po konsultacji z Doktorem Góglem wynika że albo serwo jest kiepskie i trzeba
kupić inne, albo jest ustawiane w pozycji w której nie potrafi się ustawić
i trzeba znaleźć inną [<a class="reference external" href="https://www.rcgroups.com/forums/showpost.php?s=aa4e5ad3d9dee0619af010499f9f99e5&p=38462029&postcount=14">2</a>], albo nadajnik sygnału daje sygnał na tyle
niedokładny że każe serwu ustawić się raz tak a raz inaczej i trzebaby wstawić
kondensator żeby to uśrednić [<a class="reference external" href="https://electronics.stackexchange.com/questions/77502/is-there-a-way-to-stop-servos-from-shaking">3</a>], albo podłączenie do różnych źródeł zasilania
[<a class="reference external" href="https://www.elektroda.pl/rtvforum/topic3228896.html#15870929">4</a>] i/lub niepołączenie ich mas.</p>
<p>Żadna z tych sugestii nie okazała się przydatna dla mnie. Wszystkie serwa które mam mają tę przypadłość, próbowałem podłączać i do takich samych, i do różnych źródeł zasilania, masa jest wspólna, próbowałem różnych pozycji ustawienia serwa i cały czas buczy. Raz bardziej, raz mniej, ale cały czas.</p>
<p>Dlatego postanowiłem serwo po prostu ... wyłaczyć jak dojdzie do celu.</p>
<p>Założyłem że 1s wystarczy żeby serwo przesunęło się z pozycji minimalnej na maksymalną (to może być różne dla różnych serw), <a class="reference external" href="https://github.com/maho/python-and-dog/blob/2ndtry/dispenser/servo.py">kod</a> wygląda tak:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">random</span> <span class="kn">import</span> <span class="n">random</span>
<span class="kn">import</span> <span class="nn">machine</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="k">class</span> <span class="nc">Servo</span><span class="p">:</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">pin_num</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">pin</span> <span class="o">=</span> <span class="n">machine</span><span class="o">.</span><span class="n">Pin</span><span class="p">(</span><span class="n">pin_num</span><span class="p">,</span> <span class="n">machine</span><span class="o">.</span><span class="n">Pin</span><span class="o">.</span><span class="n">OUT</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">pwm</span> <span class="o">=</span> <span class="n">machine</span><span class="o">.</span><span class="n">PWM</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">pin</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">pwm</span><span class="o">.</span><span class="n">freq</span><span class="p">(</span><span class="mi">50</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">pwm</span><span class="o">.</span><span class="n">duty</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">pos</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">def</span> <span class="nf">set</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">pos</span><span class="p">):</span>
<span class="n">ftime</span> <span class="o">=</span> <span class="mf">1.0</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">pos</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">sleeptime</span> <span class="o">=</span> <span class="n">ftime</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">sleeptime</span> <span class="o">=</span> <span class="n">ftime</span> <span class="o">*</span> <span class="nb">abs</span><span class="p">(</span><span class="n">pos</span> <span class="o">-</span> <span class="bp">self</span><span class="o">.</span><span class="n">pos</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">pos</span> <span class="o">=</span> <span class="n">pos</span>
<span class="n">duty</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="mi">40</span> <span class="o">+</span> <span class="p">(</span><span class="mi">115</span><span class="o">-</span><span class="mi">40</span><span class="p">)</span><span class="o">*</span><span class="n">pos</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">pwm</span><span class="o">.</span><span class="n">duty</span><span class="p">(</span><span class="n">duty</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"sleeptime"</span><span class="p">,</span> <span class="n">sleeptime</span><span class="p">)</span>
<span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="n">sleeptime</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">pwm</span><span class="o">.</span><span class="n">duty</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</pre></div>
pmount hangs on autocompletion2018-05-09T00:00:00+02:002018-05-09T00:00:00+02:00Mahotag:maho.pro,2018-05-09:/pmount-hangs-on-autocompletion.html<h1>Problem</h1>
<p>Bash autocompletion is very good thing if works. If doesn't, becames pain in the ass. Like in Debian's pmount - when you type '/dev/sdc' hit tab and wait forever. </p>
<p>Problem is described in many places (eg <a href="https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=776240">here</a> and
<a href="https://codereview.stackexchange.com/questions/191141/bash-completion-functions-for-pmount-and-pumount">here</a>), so why shouldn't I describe it once more?</p>
<h1>Reason</h1>
<p>Script …</p><h1>Problem</h1>
<p>Bash autocompletion is very good thing if works. If doesn't, becames pain in the ass. Like in Debian's pmount - when you type '/dev/sdc' hit tab and wait forever. </p>
<p>Problem is described in many places (eg <a href="https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=776240">here</a> and
<a href="https://codereview.stackexchange.com/questions/191141/bash-completion-functions-for-pmount-and-pumount">here</a>), so why shouldn't I describe it once more?</p>
<h1>Reason</h1>
<p>Script is in <code>/etc/bash_completion.d/pmount</code>. How to trace what exactly is doing there? Like that:</p>
<div class="highlight"><pre><span></span><code><span class="n">maho</span><span class="nv">@dlaptop</span><span class="err">:</span><span class="o">~</span><span class="err">$</span><span class="w"> </span><span class="k">set</span><span class="w"> </span><span class="o">-</span><span class="n">x</span><span class="w"></span>
<span class="n">maho</span><span class="nv">@dlaptop</span><span class="err">:</span><span class="o">~</span><span class="err">$</span><span class="w"> </span><span class="n">pmount</span><span class="w"> </span><span class="o">/</span><span class="n">dev</span><span class="o">/<</span><span class="n">HIT</span><span class="w"> </span><span class="n">TAB</span><span class="o">>+</span><span class="w"> </span><span class="k">local</span><span class="w"> </span><span class="n">cur</span><span class="w"> </span><span class="n">prev</span><span class="w"> </span><span class="n">options</span><span class="w"> </span><span class="n">devices</span><span class="w"> </span><span class="n">fslist</span><span class="w"></span>
<span class="o">+</span><span class="w"> </span><span class="n">options</span><span class="o">=</span><span class="s1">' -r --read-only -w --read-write -s --sync -A --noatime -e --exec \</span>
<span class="s1"> -t filesystem --type filesystem -c charset --charset charset -u umask \</span>
<span class="s1"> --umask umask --dmask dmask --fmask fmask -p file --passphrase file \</span>
<span class="s1"> -h --help -d --debug -V --version'</span><span class="w"></span>
<span class="o">+</span><span class="w"> </span><span class="n">fslist</span><span class="o">=</span><span class="s1">' ascii cp1250 cp1251 cp1255 cp437 cp737 cp775 cp850 cp852 cp855 cp857 cp860 cp861 cp862 cp863 cp864 cp865 cp866 cp869 cp874 cp932 cp936 cp949 cp950 euc-jp iso8859-1 iso8859-13 iso8859-14 iso8859-15 iso8859-2 iso8859-3 iso8859-4 iso8859-5 iso8859-6 iso8859-7 iso8859-9 koi8-r koi8-ru koi8-u utf8'</span><span class="w"></span>
<span class="o">+</span><span class="w"> </span><span class="n">COMPREPLY</span><span class="o">=</span><span class="p">()</span><span class="w"></span>
<span class="o">+</span><span class="w"> </span><span class="n">cur</span><span class="o">=/</span><span class="n">dev</span><span class="o">/</span><span class="w"></span>
<span class="o">+</span><span class="w"> </span><span class="n">prev</span><span class="o">=</span><span class="n">pmount</span><span class="w"></span>
<span class="o">+</span><span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="ss">"$prev"</span><span class="w"> </span><span class="ow">in</span><span class="w"></span>
<span class="o">+</span><span class="w"> </span><span class="o">[</span><span class="n">[ /dev/ == -* </span><span class="o">]</span><span class="err">]</span><span class="w"></span>
<span class="o">+++</span><span class="w"> </span><span class="n">grep</span><span class="w"> </span><span class="o">-</span><span class="n">v</span><span class="w"> </span><span class="s1">'^[[:space:]]*#'</span><span class="w"> </span><span class="o">/</span><span class="n">etc</span><span class="o">/</span><span class="n">pmount</span><span class="p">.</span><span class="n">allow</span><span class="w"></span>
<span class="o">++</span><span class="w"> </span><span class="n">sed</span><span class="w"> </span><span class="o">-</span><span class="n">e</span><span class="w"> </span><span class="s1">'s,\(^/dev/\)\(.*\),\1\2 \2,'</span><span class="w"></span>
<span class="o">++</span><span class="w"> </span><span class="n">sort</span><span class="w"> </span><span class="o">-</span><span class="n">u</span><span class="w"></span>
<span class="o">+++</span><span class="w"> </span><span class="n">grep</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">/</span><span class="n">sys</span><span class="o">/</span><span class="n">block</span><span class="o">/</span><span class="n">dm</span><span class="o">-</span><span class="mi">0</span><span class="o">/</span><span class="n">removable</span><span class="w"> </span><span class="o">/</span><span class="n">sys</span><span class="o">/</span><span class="n">block</span><span class="o">/</span><span class="n">dm</span><span class="o">-</span><span class="mi">1</span><span class="o">/</span><span class="n">removable</span><span class="w"> </span><span class="o">/</span><span class="n">sys</span><span class="o">/</span><span class="n">block</span><span class="o">/</span><span class="n">dm</span><span class="o">-</span><span class="mi">2</span><span class="o">/</span><span class="n">removable</span><span class="w"> </span><span class="o">/</span><span class="n">sys</span><span class="o">/</span><span class="n">block</span><span class="o">/</span><span class="n">dm</span><span class="o">-</span><span class="mi">3</span><span class="o">/</span><span class="n">removable</span><span class="w"> </span><span class="o">/</span><span class="n">sys</span><span class="o">/</span><span class="n">block</span><span class="o">/</span><span class="n">sda</span><span class="o">/</span><span class="n">removable</span><span class="w"> </span><span class="o">/</span><span class="n">sys</span><span class="o">/</span><span class="n">block</span><span class="o">/</span><span class="n">sdb</span><span class="o">/</span><span class="n">removable</span><span class="w"></span>
<span class="o">+++</span><span class="w"> </span><span class="n">sed</span><span class="w"> </span><span class="o">-</span><span class="n">e</span><span class="w"> </span><span class="s1">'s,/sys/block/,/dev/,;s,/removable:1,*,'</span><span class="w"></span>
<span class="o">++</span><span class="w"> </span><span class="n">command</span><span class="w"> </span><span class="n">ls</span><span class="w"> </span><span class="o">/</span><span class="n">dev</span><span class="o">/</span><span class="n">sdb</span><span class="w"> </span><span class="o">/</span><span class="n">dev</span><span class="o">/</span><span class="n">sdb1</span><span class="w"></span>
<span class="o">++</span><span class="w"> </span><span class="n">sed</span><span class="w"> </span><span class="o">-</span><span class="n">e</span><span class="w"> </span><span class="s1">'s,.*\($mdir/[^ ]*\).*,\1,'</span><span class="w"></span>
<span class="o">++</span><span class="w"> </span><span class="n">grep</span><span class="w"> </span><span class="o">/</span><span class="k">proc</span><span class="o">/</span><span class="n">mounts</span><span class="w"></span>
</code></pre></div>
<p>Ok, it's grep of stdin about /proc/mounts. Probably it meant to be grep for <em>something</em> in /proc/mounts</p>
<p>Indeed. In code:</p>
<div class="highlight"><pre><span></span><code><span class="w"> </span><span class="nt">devices</span><span class="o">=</span><span class="s2">"$( command ls $(grep -v '^</span><span class="cp">[</span><span class="err">[</span><span class="p">:</span><span class="nx">space</span><span class="p">:</span><span class="cp">]</span><span class="s2">]*#' /etc/pmount.allow ) $(grep 1 /sys/block/*/removable | sed -e 's,/sys/block/,/dev/,;s,/removable:1,*,') 2>/dev/null | sort -u | sed -e 's,\(^/dev/\)\(.*\),\1\2 \2,' ; grep $mdir /proc/mounts | sed -e 's,.*\($mdir/</span><span class="cp">[</span><span class="p">^</span><span class="w"> </span><span class="cp">]</span><span class="s2">*\).*,\1,' )"</span><span class="w"></span>
</code></pre></div>
<p>But <code>$mdir</code> is empty. </p>
<h1>Solution:</h1>
<div class="highlight"><pre><span></span><code><span class="n">maho</span><span class="nv">@dlaptop</span><span class="err">:</span><span class="o">~</span><span class="err">$</span><span class="w"> </span><span class="n">diff</span><span class="w"> </span><span class="o">-</span><span class="n">u</span><span class="w"> </span><span class="o">/</span><span class="n">tmp</span><span class="o">/</span><span class="n">ee</span><span class="p">.</span><span class="n">pmount</span><span class="w"> </span><span class="o">/</span><span class="n">etc</span><span class="o">/</span><span class="n">bash_completion</span><span class="p">.</span><span class="n">d</span><span class="o">/</span><span class="n">pmount</span><span class="w"></span>
<span class="o">---</span><span class="w"> </span><span class="o">/</span><span class="n">tmp</span><span class="o">/</span><span class="n">ee</span><span class="p">.</span><span class="n">pmount</span><span class="w"> </span><span class="mi">2018</span><span class="o">-</span><span class="mi">05</span><span class="o">-</span><span class="mi">09</span><span class="w"> </span><span class="mi">14</span><span class="err">:</span><span class="mi">54</span><span class="err">:</span><span class="mf">34.260864752</span><span class="w"> </span><span class="o">+</span><span class="mi">0200</span><span class="w"></span>
<span class="o">+++</span><span class="w"> </span><span class="o">/</span><span class="n">etc</span><span class="o">/</span><span class="n">bash_completion</span><span class="p">.</span><span class="n">d</span><span class="o">/</span><span class="n">pmount</span><span class="w"> </span><span class="mi">2018</span><span class="o">-</span><span class="mi">05</span><span class="o">-</span><span class="mi">09</span><span class="w"> </span><span class="mi">14</span><span class="err">:</span><span class="mi">53</span><span class="err">:</span><span class="mf">31.347835234</span><span class="w"> </span><span class="o">+</span><span class="mi">0200</span><span class="w"></span>
<span class="err">@@</span><span class="w"> </span><span class="o">-</span><span class="mi">22</span><span class="p">,</span><span class="mi">7</span><span class="w"> </span><span class="o">+</span><span class="mi">22</span><span class="p">,</span><span class="mi">7</span><span class="w"> </span><span class="err">@@</span><span class="w"></span>
<span class="w"> </span><span class="n">have</span><span class="w"> </span><span class="n">pmount</span><span class="w"> </span><span class="o">&&</span><span class="w"></span>
<span class="w"> </span><span class="n">_pmount</span><span class="p">()</span><span class="w"> </span><span class="err">{</span><span class="w"></span>
<span class="o">-</span><span class="w"> </span><span class="k">local</span><span class="w"> </span><span class="n">cur</span><span class="w"> </span><span class="n">prev</span><span class="w"> </span><span class="n">options</span><span class="w"> </span><span class="n">devices</span><span class="w"> </span><span class="n">fslist</span><span class="w"></span>
<span class="o">+</span><span class="w"> </span><span class="k">local</span><span class="w"> </span><span class="n">cur</span><span class="w"> </span><span class="n">prev</span><span class="w"> </span><span class="n">options</span><span class="w"> </span><span class="n">devices</span><span class="w"> </span><span class="n">fslist</span><span class="w"> </span><span class="n">mdir</span><span class="w"></span>
<span class="w"> </span><span class="n">options</span><span class="o">=</span><span class="err">'</span><span class="w"> </span><span class="o">-</span><span class="n">r</span><span class="w"> </span><span class="o">--</span><span class="k">read</span><span class="o">-</span><span class="k">only</span><span class="w"> </span><span class="o">-</span><span class="n">w</span><span class="w"> </span><span class="o">--</span><span class="k">read</span><span class="o">-</span><span class="k">write</span><span class="w"> </span><span class="o">-</span><span class="n">s</span><span class="w"> </span><span class="o">--</span><span class="n">sync</span><span class="w"> </span><span class="o">-</span><span class="n">A</span><span class="w"> </span><span class="o">--</span><span class="n">noatime</span><span class="w"> </span><span class="o">-</span><span class="n">e</span><span class="w"> </span><span class="o">--</span><span class="k">exec</span><span class="w"> </span><span class="err">\</span><span class="w"></span>
<span class="w"> </span><span class="o">-</span><span class="n">t</span><span class="w"> </span><span class="n">filesystem</span><span class="w"> </span><span class="o">--</span><span class="n">type</span><span class="w"> </span><span class="n">filesystem</span><span class="w"> </span><span class="o">-</span><span class="n">c</span><span class="w"> </span><span class="n">charset</span><span class="w"> </span><span class="o">--</span><span class="n">charset</span><span class="w"> </span><span class="n">charset</span><span class="w"> </span><span class="o">-</span><span class="n">u</span><span class="w"> </span><span class="n">umask</span><span class="w"> </span><span class="err">\</span><span class="w"></span>
<span class="err">@@</span><span class="w"> </span><span class="o">-</span><span class="mi">34</span><span class="p">,</span><span class="mi">6</span><span class="w"> </span><span class="o">+</span><span class="mi">34</span><span class="p">,</span><span class="mi">8</span><span class="w"> </span><span class="err">@@</span><span class="w"></span>
<span class="w"> </span><span class="n">cur</span><span class="o">=</span><span class="err">${</span><span class="n">COMP_WORDS</span><span class="o">[</span><span class="n">COMP_CWORD</span><span class="o">]</span><span class="err">}</span><span class="w"></span>
<span class="w"> </span><span class="n">prev</span><span class="o">=</span><span class="err">${</span><span class="n">COMP_WORDS</span><span class="o">[</span><span class="n">COMP_CWORD-1</span><span class="o">]</span><span class="err">}</span><span class="w"></span>
<span class="o">+</span><span class="w"> </span><span class="n">mdir</span><span class="o">=</span><span class="ss">"$(readlink -f /media)"</span><span class="w"></span>
<span class="o">+</span><span class="w"></span>
<span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="ss">"$prev"</span><span class="w"> </span><span class="ow">in</span><span class="w"></span>
<span class="w"> </span><span class="o">-</span><span class="err">@</span><span class="p">(</span><span class="n">t</span><span class="o">|-</span><span class="n">type</span><span class="p">))</span><span class="w"></span>
<span class="w"> </span><span class="n">COMPREPLY</span><span class="o">=</span><span class="p">(</span><span class="w"> </span><span class="err">$</span><span class="p">(</span><span class="w"> </span><span class="n">grep</span><span class="w"> </span><span class="ss">"^[[:space:]]$cur"</span><span class="w"> </span><span class="o">/</span><span class="k">proc</span><span class="o">/</span><span class="n">filesystems</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="p">)</span><span class="w"></span>
</code></pre></div>
<p>Voila!</p>PG dump with update2018-03-09T00:00:00+01:002018-03-09T00:00:00+01:00Mahotag:maho.pro,2018-03-09:/pg-dump-with-update.html<p>Many times, I need to edit some value in particular row in table. The easiest way is to use <a href="https://www.pgadmin.org/">pgAdminIII</a> - however I don't use it, I'm addicted to <strong>psql</strong>. </p>
<p>Consider such table:</p>
<div class="highlight"><pre><span></span><code><span class="n">maho</span><span class="o">=#</span><span class="w"> </span><span class="k">CREATE</span><span class="w"> </span><span class="k">TABLE</span><span class="w"> </span><span class="n">example</span><span class="p">(</span><span class="n">id</span><span class="w"> </span><span class="nb">SERIAL</span><span class="p">,</span><span class="w"> </span><span class="n">vals</span><span class="w"> </span><span class="nb">VARCHAR</span><span class="p">);</span><span class="w"></span>
<span class="k">CREATE</span><span class="w"> </span><span class="k">TABLE</span><span class="w"></span>
<span class="n">maho</span><span class="o">=#</span><span class="w"> </span><span class="k">INSERT</span><span class="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="n">example</span><span class="p">(</span><span class="n">vals</span><span class="p">)</span><span class="w"> </span><span class="k">VALUES</span><span class="p">(</span><span class="s1">'some string …</span></code></pre></div><p>Many times, I need to edit some value in particular row in table. The easiest way is to use <a href="https://www.pgadmin.org/">pgAdminIII</a> - however I don't use it, I'm addicted to <strong>psql</strong>. </p>
<p>Consider such table:</p>
<div class="highlight"><pre><span></span><code><span class="n">maho</span><span class="o">=#</span><span class="w"> </span><span class="k">CREATE</span><span class="w"> </span><span class="k">TABLE</span><span class="w"> </span><span class="n">example</span><span class="p">(</span><span class="n">id</span><span class="w"> </span><span class="nb">SERIAL</span><span class="p">,</span><span class="w"> </span><span class="n">vals</span><span class="w"> </span><span class="nb">VARCHAR</span><span class="p">);</span><span class="w"></span>
<span class="k">CREATE</span><span class="w"> </span><span class="k">TABLE</span><span class="w"></span>
<span class="n">maho</span><span class="o">=#</span><span class="w"> </span><span class="k">INSERT</span><span class="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="n">example</span><span class="p">(</span><span class="n">vals</span><span class="p">)</span><span class="w"> </span><span class="k">VALUES</span><span class="p">(</span><span class="s1">'some string value1'</span><span class="p">);</span><span class="w"></span>
<span class="k">INSERT</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="mi">1</span><span class="w"></span>
<span class="n">maho</span><span class="o">=#</span><span class="w"> </span><span class="k">INSERT</span><span class="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="n">example</span><span class="p">(</span><span class="n">vals</span><span class="p">)</span><span class="w"> </span><span class="k">VALUES</span><span class="p">(</span><span class="s1">'some</span>
<span class="s1">maho'</span><span class="o">#</span><span class="w"> </span><span class="n">string</span><span class="w"></span>
<span class="n">maho</span><span class="s1">'# value</span>
<span class="s1">maho'</span><span class="o">#</span><span class="w"> </span><span class="k">with</span><span class="w"></span>
<span class="n">maho</span><span class="s1">'# newlines'</span><span class="p">);</span><span class="w"></span>
<span class="k">INSERT</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="mi">1</span><span class="w"></span>
</code></pre></div>
<p>Now when I need to edit second value, I need to:
* select value from table</p>
<div class="highlight"><pre><span></span><code><span class="n">maho</span><span class="o">=#</span><span class="w"> </span><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">example</span><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">id</span><span class="o">=</span><span class="mi">2</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">vals</span><span class="w"></span>
<span class="c1">----+----------</span>
<span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">some</span><span class="w"> </span><span class="o">+</span><span class="w"></span>
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">string</span><span class="w"> </span><span class="o">+</span><span class="w"></span>
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">value</span><span class="w"> </span><span class="o">+</span><span class="w"></span>
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">with</span><span class="w"> </span><span class="o">+</span><span class="w"></span>
<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">newlines</span><span class="w"></span>
<span class="p">(</span><span class="mi">1</span><span class="w"> </span><span class="n">wiersz</span><span class="p">)</span><span class="w"></span>
</code></pre></div>
<ul>
<li>copy it, somehow strip <em>+</em> chars, prepare appropriate update. </li>
</ul>
<p>Complicated, annoying. </p>
<p>So I made <a href="https://github.com/maho/utils/blob/master/pg_updump.py">simple script</a>, which works like pg_dump, but generates UPDATEs not INSERTs. </p>
<p>In this case it would be used like that:</p>
<div class="highlight"><pre><span></span><code>maho@dlaptop:~/workspace/abo3$ ~/utils/public/pg_updump.py -d postgres:///maho -t example <span class="nv">id</span><span class="o">=</span><span class="m">2</span> >/tmp/ee.sql
</code></pre></div>
<p>edit /tmp/ee.sql</p>
<div class="highlight"><pre><span></span><code><span class="k">UPDATE</span><span class="w"> </span><span class="n">example</span><span class="w"> </span><span class="k">SET</span><span class="w"> </span><span class="n">vals</span><span class="o">=</span><span class="s1">'some</span>
<span class="s1">string</span>
<span class="s1">value</span>
<span class="s1">with</span>
<span class="s1">newlinesss'</span><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">id</span><span class="o">=</span><span class="mi">2</span><span class="w"></span>
</code></pre></div>
<p>and include it to psql. Voila'</p>
<p>It be of course very convenient if it's pluggable into psql, but I found no way (so far) to pass connection strings from psql to this script.</p>Vuejs, Node i różne takie JSy2018-02-23T00:00:00+01:002018-02-23T00:00:00+01:00Mahotag:maho.pro,2018-02-23:/vuejs-node-i-rozne-takie-jsy.html<p>Fajne są te frameworki, React, Vuejs, Angular itd... Ale momentami mam wrażenie
że ogon macha psem :). </p>
<p>Będąc zmęczony Angularem, postanowiłem następny prosty interfejsik wykonać w
czymś innym. Padło na Vuejs, ponoć najprostsze. </p>
<p>Coś tam zrobiłem działającego, postanowiłem ubrać to w jakieś ikonki. W
poprzednim <a href="[http://www.getbootstrap.org]">bootstrapie</a> były
<a href="[http://getbootstrap.com/2.3.2/base-css.html#icons]">glyphicons</a> - w najnowszym
to …</p><p>Fajne są te frameworki, React, Vuejs, Angular itd... Ale momentami mam wrażenie
że ogon macha psem :). </p>
<p>Będąc zmęczony Angularem, postanowiłem następny prosty interfejsik wykonać w
czymś innym. Padło na Vuejs, ponoć najprostsze. </p>
<p>Coś tam zrobiłem działającego, postanowiłem ubrać to w jakieś ikonki. W
poprzednim <a href="[http://www.getbootstrap.org]">bootstrapie</a> były
<a href="[http://getbootstrap.com/2.3.2/base-css.html#icons]">glyphicons</a> - w najnowszym
to wywalili i radź sobie człowieku. </p>
<p>Do głowy mi nie przyszło, że taka prosta sprawa jak zastosowanie ikonek, zajmie
mi <strong>TYLE</strong> czasu. Na dobrą sprawę powinienem pobrać po prostu kilka plików .png i
je wrzucić do projektu, no ale się zawziąłem ....</p>
<p>Na pierwszy ruch idzie rzeczone <a href="[http://glyphicons.com]">Glyphicons</a> (teraz
osobny projekt), ale jest komercyjne do użytku komercyjnego, więc nie
przejdzie. No to jedziemy dalej i myślę sobie że zastosuję
<a href="[https://octicons.github.com/]">Octicons</a>. Skoro Vuejs, no to trzeba ściągnąć
coś dostosowane do Vue. Jest kilka pakietów, wybrałem <code>vue-octicon</code> a w
międzyczasie minęło już prawie 2 godziny. </p>
<p>Godzina później, no cos nie działa. Próbuję i tak, i inaczej, cholera mnie
bierze. Błędów nie ma, ikonka się nie pojawia. Coś działa, bo ewidentnie w DOM
strony widzę <code><svg></code> w odpowiednim miejscu, no ale ikonka się nie pojawia. </p>
<p>Biorę robię na boku ... no cholera działa. Ikonka pojawia się taka jak chce.
Upewniam się że w aplikacji jest tak samo jak w usecase na boku - no tam się
ikonka pojawia, w aplikacji nie. Minęło już 3 godziny. </p>
<p>Może przyczyną jest to że apka jest na browserify a usecase jest na webpacku.
Przerabiam aplikację na webpack. Więc z powrotem, <code>rm -rf .nev && nodeenv .nev
&& . .nev/bin/activate && npm install -g vue-cli</code> i inicjalizuję apkę opartą
na webpacku, kopiuję ze starego wszystkie pliki .vue, .js, upewniam się że
wszystko działa jak trzeba. Dodaję <code>npm install --save vue-octicon</code> - KUPA.
Jakiś błąd (którego nie zanotowałem) świadczący ewidentnie że instalacja apki
VUE jest skopana. Ponowienie procedury, to samo. Minęły kolejne 2 godziny.
Razem 5. </p>
<p>Walczę jeszcze dłuższą chwilę. Potem wywalam <code>vue-octicon</code>, biorę
<a href="[https://github.com/vuejs/awesome-vue]">vue-awesome</a>, wrzucam do projektu,
używam <code><icon name=xxxxx/></code> i działa. </p>
<p>Dzień roboczy spędzony na dodaniu ikonki do aplikacji. Przynajmniej sobię
trochę poprawiłem strukturę katalogów. </p>Queryset repr trick2018-02-15T00:00:00+01:002018-02-15T00:00:00+01:00Mahotag:maho.pro,2018-02-15:/queryset-repr-trick.html<p>Sometimes, when I'm debugging something using pudb (probably it affects any
interactive debugger), pudb is reflecting value of variables of QuerySet type,
which makes unneccesary query to database. It makes debugging slow, and
sometimes it breaks debugging context because of making changes in db. </p>
<p>To avoid that, add following code …</p><p>Sometimes, when I'm debugging something using pudb (probably it affects any
interactive debugger), pudb is reflecting value of variables of QuerySet type,
which makes unneccesary query to database. It makes debugging slow, and
sometimes it breaks debugging context because of making changes in db. </p>
<p>To avoid that, add following code in bottom of your settings.py in django app:</p>
<div class="highlight"><pre><span></span><code><span class="k">if</span> <span class="n">DEBUG</span><span class="p">:</span>
<span class="kn">from</span> <span class="nn">django.db.models.query</span> <span class="kn">import</span> <span class="n">QuerySet</span>
<span class="n">QuerySet</span><span class="o">.</span><span class="fm">__repr__</span> <span class="o">=</span> <span class="k">lambda</span> <span class="bp">self</span><span class="p">:</span> <span class="s2">"<QS '</span><span class="si">%s</span><span class="s2">'>"</span><span class="o">%</span><span class="nb">str</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">query</span><span class="p">)</span>
</code></pre></div>blog is live again2018-02-04T00:00:00+01:002018-02-04T00:00:00+01:00Mahotag:maho.pro,2018-02-04:/blog-is-live-again.html<p>And my blog is live again, this time using <a href="http://getpelican.com">Pelican</a> - static blog content generator. </p>
<p><em>UPDATE</em>: I thougt about using <a href="http://www.disqus.com">Disqus</a> for comments, but looks that they started adding ads. So, maybe static generated blog is NOT good idea?</p>