aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdrian Herrmann <adrian.herrmann@qt.io>2022-09-23 21:23:01 +0200
committerAdrian Herrmann <adrian.herrmann@qt.io>2022-10-06 08:34:00 +0200
commit493afb7bef94a8c1115a200b64f1df2d3f502591 (patch)
treee2991a7b0443cd451366823b3d1bb55d4a13b984
parent23374ffc4215c2492d52a2d3b34c1dd7af61afb2 (diff)
examples: Add async examples
Add two examples that demonstrate how to use Qt together with an async package (Trio). Task-number: PYSIDE-769 Pick-to: 6.3 Change-Id: I1514eecc0a2eb65c6bb493857d901cf8817b7b52 Reviewed-by: Cristian Maureira-Fredes <cristian.maureira-fredes@qt.io>
-rw-r--r--examples/async/eratosthenes/doc/eratosthenes.rst42
-rw-r--r--examples/async/eratosthenes/doc/eratosthenes.svg1
-rw-r--r--examples/async/eratosthenes/eratosthenes.py202
-rw-r--r--examples/async/eratosthenes/eratosthenes.pyproject3
-rw-r--r--examples/async/eratosthenes/requirements.txt2
-rw-r--r--examples/async/minimal/doc/minimal.pngbin0 -> 8135 bytes
-rw-r--r--examples/async/minimal/doc/minimal.rst41
-rw-r--r--examples/async/minimal/minimal.py115
-rw-r--r--examples/async/minimal/minimal.pyproject3
-rw-r--r--examples/async/minimal/requirements.txt1
10 files changed, 410 insertions, 0 deletions
diff --git a/examples/async/eratosthenes/doc/eratosthenes.rst b/examples/async/eratosthenes/doc/eratosthenes.rst
new file mode 100644
index 000000000..37758e99f
--- /dev/null
+++ b/examples/async/eratosthenes/doc/eratosthenes.rst
@@ -0,0 +1,42 @@
+Async examples
+==============
+
+The Python language provides keywords for asynchronous operations, i.e.,
+"async" to define coroutines or "await" to schedule asynchronous calls in the
+event loop (see `PEP 492 <https://peps.python.org/pep-0492/>`_). It is up to
+packages to implement an event loop, support for these keywords, and more.
+
+One such package is `trio`. Since both an async package and Qt itself work with
+event loops, special care must be taken to ensure that both event loops work
+with each other. trio offers a dedicated `low-level API
+<https://trio.readthedocs.io/en/stable/reference-lowlevel.html>`_ for more
+complicated use cases such as this. Specifically, there exists a function
+`start_guest_run` that enables running the Trio event loop as a "guest" inside
+another event loop - Qt's in our case.
+
+Based on this functionality, two examples for async usage with Qt have been
+implemented: `eratosthenes` and `minimal`:
+
+.. image:: eratosthenes.svg
+ :alt: Async example: Eratosthenes
+ :width: 400
+
+* `eratosthenes` is a more extensive example that visualizes the Sieve of
+Eratosthenes algorithm. This algorithm per se is not one that is particularly
+suitable for asynchronous operations as it's not I/O-heavy, but synchronizing
+coroutines to a configurable tick allows for a good visualization.
+* `minimal` is a minimal example featuring a button that triggers an
+asynchronous coroutine with a sleep. It is designed to highlight which
+boilerplate code is essential for an async program with Qt and offers a
+starting point for more complex programs.
+
+Both examples feature:
+
+1. A window class.
+2. An `AsyncHelper` class containing `start_guest_run` plus helpers and
+callbacks necessary for its invocation. The entry point for the Trio guest run
+is provided as an argument from outside, which can be any async function.
+
+While `eratosthenes` offloads the asynchronous logic that will run in trio's
+event loop into a separate class, `minimal` demonstrates that async functions
+can be integrated into any class, including subclasses of Qt classes.
diff --git a/examples/async/eratosthenes/doc/eratosthenes.svg b/examples/async/eratosthenes/doc/eratosthenes.svg
new file mode 100644
index 000000000..eaf53da50
--- /dev/null
+++ b/examples/async/eratosthenes/doc/eratosthenes.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="1210.666" height="886.666" viewBox="0 0 908 665" baseProfile="tiny" xmlns:v="https://vecta.io/nano"><style><![CDATA[.B{stroke:none}.C{font-family:.AppleSystemUIFont}.D{font-size:13px}.E{font-weight:700}.F{fill:#000}.G{fill:#548f7c}.H{fill:#bab040}.I{fill:#51a660}.J{fill:#a193b6}.K{fill:#8a4a8f}.L{fill:#45b2a7}.M{fill:#916bbd}.N{fill:#7dad6b}.O{fill:#628f47}.P{fill:#6474c0}.Q{fill:#b182b0}.R{fill:#a94257}.S{fill:#a1794d}.T{fill:#bbac54}.U{fill:#6e4344}.V{fill:#a38b9d}.W{fill:#6c4268}.X{fill:#974c66}.Y{fill:#8163bb}.Z{fill:#bda991}.a{fill:#599c9d}.b{fill:#7ec05e}.c{fill:#b26b51}.d{fill:#434da9}.e{fill:#a46a69}.f{fill:#6a6467}.g{fill:#954ca5}.h{fill:#6f4965}.i{fill:#7a99b1}.j{fill:#786344}.k{fill:#898683}.l{fill:#ac989d}.m{fill:#4f5a7b}.n{fill:#b28c5f}.o{fill:#639747}.p{fill:#aba64a}.q{fill:#916c5e}.r{fill:#b0929e}.s{fill:#909864}.t{fill:#a14faf}]]></style><g fill="none" stroke="#000" fill-rule="evenodd" class="C B"><path d="M0 0h908v665H0z" fill="#ececec" class="D"/><g font-size="14" class="F"><text xml:space="preserve" y="25.781" x="192">🥳</text><text xml:space="preserve" x="214.016" y="25.781"> Congratulations! You found all the prime numbers and solved mathematics. </text><text xml:space="preserve" x="693.031" y="25.781">🥳</text></g><text xml:space="preserve" y="61.906" x="34" class="D F">1</text><text xml:space="preserve" y="61.906" x="67" class="D F">2</text><text xml:space="preserve" y="61.906" x="101" class="D F">3</text><text xml:space="preserve" y="61.906" x="136" class="D E l">4</text><text xml:space="preserve" y="61.906" x="171" class="D F">5</text><text xml:space="preserve" y="61.906" x="206" class="D E U">6</text><text xml:space="preserve" y="61.906" x="241" class="D F">7</text><text xml:space="preserve" y="61.906" x="275" class="D E l">8</text><text xml:space="preserve" y="61.906" x="310" class="D E U">9</text><text xml:space="preserve" y="61.906" x="341" class="D E M">10</text><text xml:space="preserve" y="61.906" x="378" class="D F">11</text><text xml:space="preserve" y="61.906" x="411" class="D E U">12</text><text xml:space="preserve" y="61.906" x="446" class="D F">13</text><text xml:space="preserve" y="61.906" x="480" class="D E G">14</text><text xml:space="preserve" y="61.906" x="516" class="D E M">15</text><text xml:space="preserve" y="61.906" x="550" class="D E l">16</text><text xml:space="preserve" y="61.906" x="586" class="D F">17</text><text xml:space="preserve" y="61.906" x="619" class="D E U">18</text><text xml:space="preserve" y="61.906" x="655" class="D F">19</text><text xml:space="preserve" y="61.906" x="689" class="D E M">20</text><text xml:space="preserve" y="61.906" x="725" class="D E G">21</text><text xml:space="preserve" y="61.906" x="758" class="D E H">22</text><text xml:space="preserve" y="61.906" x="793" class="D F">23</text><text xml:space="preserve" y="61.906" x="827" class="D E U">24</text><text xml:space="preserve" y="61.906" x="862" class="D E M">25</text><text xml:space="preserve" y="85.906" x="28" class="D E I">26</text><text xml:space="preserve" y="85.906" x="63" class="D E U">27</text><text xml:space="preserve" y="85.906" x="96" class="D E G">28</text><text xml:space="preserve" y="85.906" x="132" class="D F">29</text><text xml:space="preserve" y="85.906" x="166" class="D E M">30</text><text xml:space="preserve" y="85.906" x="203" class="D F">31</text><text xml:space="preserve" y="85.906" x="236" class="D E K">32</text><text xml:space="preserve" y="85.906" x="270" class="D E H">33</text><text xml:space="preserve" y="85.906" x="305" class="D E J">34</text><text xml:space="preserve" y="85.906" x="341" class="D E G">35</text><text xml:space="preserve" y="85.906" x="375" class="D E U">36</text><text xml:space="preserve" y="85.906" x="411" class="D F">37</text><text xml:space="preserve" y="85.906" x="444" class="D E L">38</text><text xml:space="preserve" y="85.906" x="479" class="D E I">39</text><text xml:space="preserve" y="85.906" x="514" class="D E M">40</text><text xml:space="preserve" y="85.906" x="551" class="D F">41</text><text xml:space="preserve" y="85.906" x="584" class="D E G">42</text><text xml:space="preserve" y="85.906" x="619" class="D F">43</text><text xml:space="preserve" y="85.906" x="653" class="D E H">44</text><text xml:space="preserve" y="85.906" x="688" class="D E M">45</text><text xml:space="preserve" y="85.906" x="723" class="D E N">46</text><text xml:space="preserve" y="85.906" x="759" class="D F">47</text><text xml:space="preserve" y="85.906" x="792" class="D E K">48</text><text xml:space="preserve" y="85.906" x="827" class="D E G">49</text><text xml:space="preserve" y="85.906" x="861" class="D E M">50</text><text xml:space="preserve" y="109.906" x="29" class="D E J">51</text><text xml:space="preserve" y="109.906" x="62" class="D E I">52</text><text xml:space="preserve" y="109.906" x="97" class="D F">53</text><text xml:space="preserve" y="109.906" x="131" class="D E U">54</text><text xml:space="preserve" y="109.906" x="167" class="D E H">55</text><text xml:space="preserve" y="109.906" x="201" class="D E G">56</text><text xml:space="preserve" y="109.906" x="236" class="D E L">57</text><text xml:space="preserve" y="109.906" x="270" class="D E O">58</text><text xml:space="preserve" y="109.906" x="306" class="D F">59</text><text xml:space="preserve" y="109.906" x="340" class="D E M">60</text><text xml:space="preserve" y="109.906" x="377" class="D F">61</text><text xml:space="preserve" y="109.906" x="410" class="D E P">62</text><text xml:space="preserve" y="109.906" x="444" class="D E G">63</text><text xml:space="preserve" y="109.906" x="479" class="D E K">64</text><text xml:space="preserve" y="109.906" x="514" class="D E I">65</text><text xml:space="preserve" y="109.906" x="549" class="D E H">66</text><text xml:space="preserve" y="109.906" x="585" class="D F">67</text><text xml:space="preserve" y="109.906" x="618" class="D E J">68</text><text xml:space="preserve" y="109.906" x="653" class="D E N">69</text><text xml:space="preserve" y="109.906" x="689" class="D E G">70</text><text xml:space="preserve" y="109.906" x="725" class="D F">71</text><text xml:space="preserve" y="109.906" x="759" class="D E U">72</text><text xml:space="preserve" y="109.906" x="793" class="D F">73</text><text xml:space="preserve" y="109.906" x="828" class="D E Q">74</text><text xml:space="preserve" y="109.906" x="862" class="D E M">75</text><text xml:space="preserve" y="133.906" x="28" class="D E L">76</text><text xml:space="preserve" y="133.906" x="63" class="D E H">77</text><text xml:space="preserve" y="133.906" x="97" class="D E I">78</text><text xml:space="preserve" y="133.906" x="132" class="D F">79</text><text xml:space="preserve" y="133.906" x="166" class="D E K">80</text><text xml:space="preserve" y="133.906" x="202" class="D E U">81</text><text xml:space="preserve" y="133.906" x="236" class="D E R">82</text><text xml:space="preserve" y="133.906" x="271" class="D F">83</text><text xml:space="preserve" y="133.906" x="305" class="D E G">84</text><text xml:space="preserve" y="133.906" x="340" class="D E J">85</text><text xml:space="preserve" y="133.906" x="375" class="D E S">86</text><text xml:space="preserve" y="133.906" x="410" class="D E O">87</text><text xml:space="preserve" y="133.906" x="444" class="D E H">88</text><text xml:space="preserve" y="133.906" x="480" class="D F">89</text><text xml:space="preserve" y="133.906" x="514" class="D E M">90</text><text xml:space="preserve" y="133.906" x="550" class="D E I">91</text><text xml:space="preserve" y="133.906" x="584" class="D E N">92</text><text xml:space="preserve" y="133.906" x="618" class="D E P">93</text><text xml:space="preserve" y="133.906" x="653" class="D E T">94</text><text xml:space="preserve" y="133.906" x="688" class="D E L">95</text><text xml:space="preserve" y="133.906" x="723" class="D E K">96</text><text xml:space="preserve" y="133.906" x="759" class="D F">97</text><text xml:space="preserve" y="133.906" x="792" class="D E G">98</text><text xml:space="preserve" y="133.906" x="827" class="D E H">99</text><text xml:space="preserve" y="133.906" x="858" class="D E M">100</text><text xml:space="preserve" y="157.906" x="27" class="D F">101</text><text xml:space="preserve" y="157.906" x="59" class="D E J">102</text><text xml:space="preserve" y="157.906" x="94" class="D F">103</text><text xml:space="preserve" y="157.906" x="128" class="D E I">104</text><text xml:space="preserve" y="157.906" x="163" class="D E G">105</text><text xml:space="preserve" y="157.906" x="198" class="D E V">106</text><text xml:space="preserve" y="157.906" x="234" class="D F">107</text><text xml:space="preserve" y="157.906" x="267" class="D E W">108</text><text xml:space="preserve" y="157.906" x="303" class="D F">109</text><text xml:space="preserve" y="157.906" x="338" class="D E H">110</text><text xml:space="preserve" y="157.906" x="374" class="D E Q">111</text><text xml:space="preserve" y="157.906" x="408" class="D E K">112</text><text xml:space="preserve" y="157.906" x="443" class="D F">113</text><text xml:space="preserve" y="157.906" x="477" class="D E L">114</text><text xml:space="preserve" y="157.906" x="512" class="D E N">115</text><text xml:space="preserve" y="157.906" x="547" class="D E O">116</text><text xml:space="preserve" y="157.906" x="582" class="D E I">117</text><text xml:space="preserve" y="157.906" x="616" class="D E X">118</text><text xml:space="preserve" y="157.906" x="651" class="D E J">119</text><text xml:space="preserve" y="157.906" x="685" class="D E M">120</text><text xml:space="preserve" y="157.906" x="721" class="D E H">121</text><text xml:space="preserve" y="157.906" x="755" class="D E Y">122</text><text xml:space="preserve" y="157.906" x="789" class="D E R">123</text><text xml:space="preserve" y="157.906" x="824" class="D E P">124</text><text xml:space="preserve" y="157.906" x="858" class="D E M">125</text><text xml:space="preserve" y="181.906" x="25" class="D E G">126</text><text xml:space="preserve" y="181.906" x="60" class="D F">127</text><text xml:space="preserve" y="181.906" x="93" class="D E K">128</text><text xml:space="preserve" y="181.906" x="128" class="D E S">129</text><text xml:space="preserve" y="181.906" x="163" class="D E I">130</text><text xml:space="preserve" y="181.906" x="200" class="D F">131</text><text xml:space="preserve" y="181.906" x="233" class="D E H">132</text><text xml:space="preserve" y="181.906" x="267" class="D E L">133</text><text xml:space="preserve" y="181.906" x="302" class="D E Z">134</text><text xml:space="preserve" y="181.906" x="337" class="D E M">135</text><text xml:space="preserve" y="181.906" x="372" class="D E J">136</text><text xml:space="preserve" y="181.906" x="408" class="D F">137</text><text xml:space="preserve" y="181.906" x="441" class="D E N">138</text><text xml:space="preserve" y="181.906" x="477" class="D F">139</text><text xml:space="preserve" y="181.906" x="511" class="D E G">140</text><text xml:space="preserve" y="181.906" x="547" class="D E T">141</text><text xml:space="preserve" y="181.906" x="581" class="D E a">142</text><text xml:space="preserve" y="181.906" x="615" class="D E I">143</text><text xml:space="preserve" y="181.906" x="650" class="D E K">144</text><text xml:space="preserve" y="181.906" x="685" class="D E O">145</text><text xml:space="preserve" y="181.906" x="720" class="D E b">146</text><text xml:space="preserve" y="181.906" x="755" class="D E G">147</text><text xml:space="preserve" y="181.906" x="789" class="D E Q">148</text><text xml:space="preserve" y="181.906" x="824" class="D F">149</text><text xml:space="preserve" y="181.906" x="858" class="D E M">150</text><text xml:space="preserve" y="205.906" x="27" class="D F">151</text><text xml:space="preserve" y="205.906" x="59" class="D E L">152</text><text xml:space="preserve" y="205.906" x="93" class="D E J">153</text><text xml:space="preserve" y="205.906" x="128" class="D E H">154</text><text xml:space="preserve" y="205.906" x="163" class="D E P">155</text><text xml:space="preserve" y="205.906" x="198" class="D E I">156</text><text xml:space="preserve" y="205.906" x="234" class="D F">157</text><text xml:space="preserve" y="205.906" x="267" class="D E c">158</text><text xml:space="preserve" y="205.906" x="302" class="D E V">159</text><text xml:space="preserve" y="205.906" x="337" class="D E K">160</text><text xml:space="preserve" y="205.906" x="373" class="D E N">161</text><text xml:space="preserve" y="205.906" x="407" class="D E W">162</text><text xml:space="preserve" y="205.906" x="442" class="D F">163</text><text xml:space="preserve" y="205.906" x="476" class="D E R">164</text><text xml:space="preserve" y="205.906" x="511" class="D E H">165</text><text xml:space="preserve" y="205.906" x="546" class="D E d">166</text><text xml:space="preserve" y="205.906" x="582" class="D F">167</text><text xml:space="preserve" y="205.906" x="615" class="D E G">168</text><text xml:space="preserve" y="205.906" x="650" class="D E I">169</text><text xml:space="preserve" y="205.906" x="686" class="D E J">170</text><text xml:space="preserve" y="205.906" x="722" class="D E L">171</text><text xml:space="preserve" y="205.906" x="755" class="D E S">172</text><text xml:space="preserve" y="205.906" x="790" class="D F">173</text><text xml:space="preserve" y="205.906" x="824" class="D E O">174</text><text xml:space="preserve" y="205.906" x="859" class="D E G">175</text><text xml:space="preserve" y="229.906" x="25" class="D E K">176</text><text xml:space="preserve" y="229.906" x="60" class="D E X">177</text><text xml:space="preserve" y="229.906" x="93" class="D E e">178</text><text xml:space="preserve" y="229.906" x="129" class="D F">179</text><text xml:space="preserve" y="229.906" x="163" class="D E M">180</text><text xml:space="preserve" y="229.906" x="200" class="D F">181</text><text xml:space="preserve" y="229.906" x="233" class="D E I">182</text><text xml:space="preserve" y="229.906" x="267" class="D E Y">183</text><text xml:space="preserve" y="229.906" x="302" class="D E N">184</text><text xml:space="preserve" y="229.906" x="337" class="D E Q">185</text><text xml:space="preserve" y="229.906" x="372" class="D E P">186</text><text xml:space="preserve" y="229.906" x="407" class="D E J">187</text><text xml:space="preserve" y="229.906" x="441" class="D E T">188</text><text xml:space="preserve" y="229.906" x="476" class="D E G">189</text><text xml:space="preserve" y="229.906" x="511" class="D E L">190</text><text xml:space="preserve" y="229.906" x="548" class="D F">191</text><text xml:space="preserve" y="229.906" x="581" class="D E K">192</text><text xml:space="preserve" y="229.906" x="616" class="D F">193</text><text xml:space="preserve" y="229.906" x="650" class="D E f">194</text><text xml:space="preserve" y="229.906" x="685" class="D E I">195</text><text xml:space="preserve" y="229.906" x="720" class="D E G">196</text><text xml:space="preserve" y="229.906" x="756" class="D F">197</text><text xml:space="preserve" y="229.906" x="789" class="D E H">198</text><text xml:space="preserve" y="229.906" x="824" class="D F">199</text><text xml:space="preserve" y="229.906" x="857" class="D E M">200</text><text xml:space="preserve" y="253.906" x="25" class="D E Z">201</text><text xml:space="preserve" y="253.906" x="58" class="D E g">202</text><text xml:space="preserve" y="253.906" x="92" class="D E O">203</text><text xml:space="preserve" y="253.906" x="127" class="D E J">204</text><text xml:space="preserve" y="253.906" x="162" class="D E R">205</text><text xml:space="preserve" y="253.906" x="197" class="D E h">206</text><text xml:space="preserve" y="253.906" x="232" class="D E N">207</text><text xml:space="preserve" y="253.906" x="266" class="D E K">208</text><text xml:space="preserve" y="253.906" x="301" class="D E L">209</text><text xml:space="preserve" y="253.906" x="337" class="D E G">210</text><text xml:space="preserve" y="253.906" x="374" class="D F">211</text><text xml:space="preserve" y="253.906" x="407" class="D E V">212</text><text xml:space="preserve" y="253.906" x="441" class="D E a">213</text><text xml:space="preserve" y="253.906" x="476" class="D E i">214</text><text xml:space="preserve" y="253.906" x="511" class="D E S">215</text><text xml:space="preserve" y="253.906" x="546" class="D E W">216</text><text xml:space="preserve" y="253.906" x="581" class="D E P">217</text><text xml:space="preserve" y="253.906" x="615" class="D E j">218</text><text xml:space="preserve" y="253.906" x="650" class="D E b">219</text><text xml:space="preserve" y="253.906" x="684" class="D E H">220</text><text xml:space="preserve" y="253.906" x="721" class="D E J">221</text><text xml:space="preserve" y="253.906" x="754" class="D E Q">222</text><text xml:space="preserve" y="253.906" x="789" class="D F">223</text><text xml:space="preserve" y="253.906" x="823" class="D E K">224</text><text xml:space="preserve" y="253.906" x="858" class="D E M">225</text><text xml:space="preserve" y="277.906" x="24" class="D E k">226</text><text xml:space="preserve" y="277.906" x="59" class="D F">227</text><text xml:space="preserve" y="277.906" x="92" class="D E L">228</text><text xml:space="preserve" y="277.906" x="128" class="D F">229</text><text xml:space="preserve" y="277.906" x="162" class="D E N">230</text><text xml:space="preserve" y="277.906" x="198" class="D E H">231</text><text xml:space="preserve" y="277.906" x="232" class="D E O">232</text><text xml:space="preserve" y="277.906" x="267" class="D F">233</text><text xml:space="preserve" y="277.906" x="301" class="D E I">234</text><text xml:space="preserve" y="277.906" x="336" class="D E T">235</text><text xml:space="preserve" y="277.906" x="371" class="D E X">236</text><text xml:space="preserve" y="277.906" x="406" class="D E c">237</text><text xml:space="preserve" y="277.906" x="440" class="D E J">238</text><text xml:space="preserve" y="277.906" x="476" class="D F">239</text><text xml:space="preserve" y="277.906" x="510" class="D E K">240</text><text xml:space="preserve" y="277.906" x="547" class="D F">241</text><text xml:space="preserve" y="277.906" x="580" class="D E H">242</text><text xml:space="preserve" y="277.906" x="614" class="D E m">243</text><text xml:space="preserve" y="277.906" x="649" class="D E Y">244</text><text xml:space="preserve" y="277.906" x="684" class="D E G">245</text><text xml:space="preserve" y="277.906" x="719" class="D E R">246</text><text xml:space="preserve" y="277.906" x="754" class="D E L">247</text><text xml:space="preserve" y="277.906" x="788" class="D E P">248</text><text xml:space="preserve" y="277.906" x="823" class="D E d">249</text><text xml:space="preserve" y="277.906" x="857" class="D E M">250</text><text xml:space="preserve" y="301.906" x="26" class="D F">251</text><text xml:space="preserve" y="301.906" x="58" class="D E G">252</text><text xml:space="preserve" y="301.906" x="92" class="D E N">253</text><text xml:space="preserve" y="301.906" x="127" class="D E n">254</text><text xml:space="preserve" y="301.906" x="162" class="D E J">255</text><text xml:space="preserve" y="301.906" x="197" class="D E K">256</text><text xml:space="preserve" y="301.906" x="233" class="D F">257</text><text xml:space="preserve" y="301.906" x="266" class="D E S">258</text><text xml:space="preserve" y="301.906" x="301" class="D E Q">259</text><text xml:space="preserve" y="301.906" x="336" class="D E I">260</text><text xml:space="preserve" y="301.906" x="372" class="D E O">261</text><text xml:space="preserve" y="301.906" x="406" class="D E o">262</text><text xml:space="preserve" y="301.906" x="441" class="D F">263</text><text xml:space="preserve" y="301.906" x="475" class="D E H">264</text><text xml:space="preserve" y="301.906" x="510" class="D E V">265</text><text xml:space="preserve" y="301.906" x="545" class="D E L">266</text><text xml:space="preserve" y="301.906" x="580" class="D E e">267</text><text xml:space="preserve" y="301.906" x="614" class="D E Z">268</text><text xml:space="preserve" y="301.906" x="650" class="D F">269</text><text xml:space="preserve" y="301.906" x="685" class="D E W">270</text><text xml:space="preserve" y="301.906" x="722" class="D F">271</text><text xml:space="preserve" y="301.906" x="754" class="D E K">272</text><text xml:space="preserve" y="301.906" x="789" class="D E I">273</text><text xml:space="preserve" y="301.906" x="824" class="D E p">274</text><text xml:space="preserve" y="301.906" x="858" class="D E H">275</text><text xml:space="preserve" y="325.906" x="24" class="D E N">276</text><text xml:space="preserve" y="325.906" x="59" class="D F">277</text><text xml:space="preserve" y="325.906" x="93" class="D E q">278</text><text xml:space="preserve" y="325.906" x="128" class="D E P">279</text><text xml:space="preserve" y="325.906" x="162" class="D E G">280</text><text xml:space="preserve" y="325.906" x="199" class="D F">281</text><text xml:space="preserve" y="325.906" x="232" class="D E T">282</text><text xml:space="preserve" y="325.906" x="267" class="D F">283</text><text xml:space="preserve" y="325.906" x="301" class="D E a">284</text><text xml:space="preserve" y="325.906" x="336" class="D E L">285</text><text xml:space="preserve" y="325.906" x="371" class="D E I">286</text><text xml:space="preserve" y="325.906" x="406" class="D E R">287</text><text xml:space="preserve" y="325.906" x="440" class="D E K">288</text><text xml:space="preserve" y="325.906" x="475" class="D E J">289</text><text xml:space="preserve" y="325.906" x="510" class="D E O">290</text><text xml:space="preserve" y="325.906" x="546" class="D E f">291</text><text xml:space="preserve" y="325.906" x="580" class="D E b">292</text><text xml:space="preserve" y="325.906" x="615" class="D F">293</text><text xml:space="preserve" y="325.906" x="649" class="D E G">294</text><text xml:space="preserve" y="325.906" x="684" class="D E X">295</text><text xml:space="preserve" y="325.906" x="719" class="D E Q">296</text><text xml:space="preserve" y="325.906" x="754" class="D E H">297</text><text xml:space="preserve" y="325.906" x="788" class="D E r">298</text><text xml:space="preserve" y="325.906" x="823" class="D E N">299</text><text xml:space="preserve" y="325.906" x="857" class="D E M">300</text><text xml:space="preserve" y="349.906" x="25" class="D E S">301</text><text xml:space="preserve" y="349.906" x="58" class="D E s">302</text><text xml:space="preserve" y="349.906" x="92" class="D E g">303</text><text xml:space="preserve" y="349.906" x="127" class="D E L">304</text><text xml:space="preserve" y="349.906" x="162" class="D E Y">305</text><text xml:space="preserve" y="349.906" x="197" class="D E J">306</text><text xml:space="preserve" y="349.906" x="233" class="D F">307</text><text xml:space="preserve" y="349.906" x="266" class="D E H">308</text><text xml:space="preserve" y="349.906" x="301" class="D E h">309</text><text xml:space="preserve" y="349.906" x="337" class="D E P">310</text><text xml:space="preserve" y="349.906" x="374" class="D F">311</text><text xml:space="preserve" y="349.906" x="407" class="D E I">312</text><text xml:space="preserve" y="349.906" x="442" class="D F">313</text><text fill="#72a694" xml:space="preserve" y="349.906" x="476" class="D E">314</text><text xml:space="preserve" y="349.906" x="511" class="D E G">315</text><text xml:space="preserve" y="349.906" x="546" class="D E c">316</text><text xml:space="preserve" y="349.906" x="582" class="D F">317</text><text xml:space="preserve" y="349.906" x="615" class="D E V">318</text><text xml:space="preserve" y="349.906" x="650" class="D E O">319</text><text xml:space="preserve" y="349.906" x="684" class="D E K">320</text><text xml:space="preserve" y="349.906" x="720" class="D E i">321</text><text xml:space="preserve" y="349.906" x="754" class="D E N">322</text><text xml:space="preserve" y="349.906" x="788" class="D E L">323</text><text xml:space="preserve" y="349.906" x="823" class="D E W">324</text><text xml:space="preserve" y="349.906" x="857" class="D E I">325</text><text fill="#aa85a2" xml:space="preserve" y="373.906" x="24" class="D E">326</text><text xml:space="preserve" y="373.906" x="58" class="D E j">327</text><text xml:space="preserve" y="373.906" x="92" class="D E R">328</text><text xml:space="preserve" y="373.906" x="127" class="D E T">329</text><text xml:space="preserve" y="373.906" x="162" class="D E H">330</text><text xml:space="preserve" y="373.906" x="199" class="D F">331</text><text xml:space="preserve" y="373.906" x="232" class="D E d">332</text><text xml:space="preserve" y="373.906" x="266" class="D E Q">333</text><text fill="#bca451" xml:space="preserve" y="373.906" x="301" class="D E">334</text><text xml:space="preserve" y="373.906" x="336" class="D E Z">335</text><text xml:space="preserve" y="373.906" x="371" class="D E K">336</text><text xml:space="preserve" y="373.906" x="407" class="D F">337</text><text xml:space="preserve" y="373.906" x="440" class="D E I">338</text><text xml:space="preserve" y="373.906" x="475" class="D E k">339</text><text xml:space="preserve" y="373.906" x="510" class="D E J">340</text><text xml:space="preserve" y="373.906" x="546" class="D E P">341</text><text xml:space="preserve" y="373.906" x="580" class="D E L">342</text><text xml:space="preserve" y="373.906" x="614" class="D E G">343</text><text xml:space="preserve" y="373.906" x="649" class="D E S">344</text><text xml:space="preserve" y="373.906" x="684" class="D E N">345</text><text fill="#8e8dba" xml:space="preserve" y="373.906" x="719" class="D E">346</text><text xml:space="preserve" y="373.906" x="755" class="D F">347</text><text xml:space="preserve" y="373.906" x="788" class="D E O">348</text><text xml:space="preserve" y="373.906" x="823" class="D F">349</text><text xml:space="preserve" y="373.906" x="857" class="D E G">350</text><text xml:space="preserve" y="397.906" x="25" class="D E I">351</text><text xml:space="preserve" y="397.906" x="58" class="D E K">352</text><text xml:space="preserve" y="397.906" x="93" class="D F">353</text><text xml:space="preserve" y="397.906" x="127" class="D E X">354</text><text xml:space="preserve" y="397.906" x="162" class="D E a">355</text><text xml:space="preserve" y="397.906" x="197" class="D E e">356</text><text xml:space="preserve" y="397.906" x="232" class="D E J">357</text><text fill="#79a79e" xml:space="preserve" y="397.906" x="266" class="D E">358</text><text xml:space="preserve" y="397.906" x="302" class="D F">359</text><text xml:space="preserve" y="397.906" x="336" class="D E t">360</text><text xml:space="preserve" y="397.906" x="372" class="D E L">361</text><text fill="#656186" xml:space="preserve" y="397.906" x="406" class="D E">362</text><text xml:space="preserve" y="397.906" x="440" class="D E H">363</text><text xml:space="preserve" y="397.906" x="475" class="D E I">364</text><text xml:space="preserve" y="397.906" x="510" class="D E b">365</text><text xml:space="preserve" y="397.906" x="545" class="D E Y">366</text><text xml:space="preserve" y="397.906" x="581" class="D F">367</text><text xml:space="preserve" y="397.906" x="614" class="D E N">368</text><text xml:space="preserve" y="397.906" x="649" class="D E R">369</text><text xml:space="preserve" y="397.906" x="685" class="D E Q">370</text><text xml:space="preserve" y="397.906" x="721" class="D E V">371</text><text xml:space="preserve" y="397.906" x="754" class="D E P">372</text><text xml:space="preserve" y="397.906" x="789" class="D F">373</text><text xml:space="preserve" y="397.906" x="823" class="D E J">374</text><text xml:space="preserve" y="397.906" x="858" class="D E M">375</text><text xml:space="preserve" y="421.906" x="24" class="D E T">376</text><text xml:space="preserve" y="421.906" x="59" class="D E O">377</text><text xml:space="preserve" y="421.906" x="92" class="D E W">378</text><text xml:space="preserve" y="421.906" x="128" class="D F">379</text><text xml:space="preserve" y="421.906" x="162" class="D E L">380</text><text xml:space="preserve" y="421.906" x="198" class="D E n">381</text><text fill="#94894b" xml:space="preserve" y="421.906" x="232" class="D E">382</text><text xml:space="preserve" y="421.906" x="267" class="D F">383</text><text xml:space="preserve" y="421.906" x="301" class="D E K">384</text><text xml:space="preserve" y="421.906" x="336" class="D E H">385</text><text fill="#87759e" xml:space="preserve" y="421.906" x="371" class="D E">386</text><text xml:space="preserve" y="421.906" x="406" class="D E S">387</text><text xml:space="preserve" y="421.906" x="440" class="D E f">388</text><text xml:space="preserve" y="421.906" x="476" class="D F">389</text><text xml:space="preserve" y="421.906" x="510" class="D E I">390</text><text xml:space="preserve" y="421.906" x="546" class="D E N">391</text><text xml:space="preserve" y="421.906" x="580" class="D E G">392</text><text xml:space="preserve" y="421.906" x="614" class="D E o">393</text><text fill="#9f6db4" xml:space="preserve" y="421.906" x="649" class="D E">394</text><text xml:space="preserve" y="421.906" x="684" class="D E c">395</text><text xml:space="preserve" y="421.906" x="719" class="D E H">396</text><text xml:space="preserve" y="421.906" x="755" class="D F">397</text><text fill="#aebba6" xml:space="preserve" y="421.906" x="788" class="D E">398</text><text xml:space="preserve" y="421.906" x="823" class="D E L">399</text><text xml:space="preserve" y="421.906" x="857" class="D E K">400</text><text xml:space="preserve" y="445.906" x="25" class="D F">401</text><text xml:space="preserve" y="445.906" x="58" class="D E Z">402</text><text xml:space="preserve" y="445.906" x="92" class="D E P">403</text><text xml:space="preserve" y="445.906" x="127" class="D E g">404</text><text xml:space="preserve" y="445.906" x="162" class="D E m">405</text><text xml:space="preserve" y="445.906" x="197" class="D E O">406</text><text xml:space="preserve" y="445.906" x="232" class="D E Q">407</text><text xml:space="preserve" y="445.906" x="266" class="D E J">408</text><text xml:space="preserve" y="445.906" x="302" class="D F">409</text><text xml:space="preserve" y="445.906" x="337" class="D E R">410</text><text xml:space="preserve" y="445.906" x="373" class="D E p">411</text><text xml:space="preserve" y="445.906" x="407" class="D E h">412</text><text xml:space="preserve" y="445.906" x="441" class="D E X">413</text><text xml:space="preserve" y="445.906" x="476" class="D E N">414</text><text xml:space="preserve" y="445.906" x="511" class="D E d">415</text><text xml:space="preserve" y="445.906" x="546" class="D E K">416</text><text xml:space="preserve" y="445.906" x="581" class="D E q">417</text><text xml:space="preserve" y="445.906" x="615" class="D E L">418</text><text xml:space="preserve" y="445.906" x="651" class="D F">419</text><text xml:space="preserve" y="445.906" x="684" class="D E G">420</text><text xml:space="preserve" y="445.906" x="721" class="D F">421</text><text fill="#61a569" xml:space="preserve" y="445.906" x="754" class="D E">422</text><text xml:space="preserve" y="445.906" x="788" class="D E T">423</text><text xml:space="preserve" y="445.906" x="823" class="D E V">424</text><text xml:space="preserve" y="445.906" x="857" class="D E J">425</text><text xml:space="preserve" y="469.906" x="24" class="D E a">426</text><text xml:space="preserve" y="469.906" x="58" class="D E Y">427</text><text xml:space="preserve" y="469.906" x="92" class="D E i">428</text><text xml:space="preserve" y="469.906" x="127" class="D E I">429</text><text xml:space="preserve" y="469.906" x="162" class="D E S">430</text><text xml:space="preserve" y="469.906" x="199" class="D F">431</text><text xml:space="preserve" y="469.906" x="232" class="D E W">432</text><text xml:space="preserve" y="469.906" x="267" class="D F">433</text><text xml:space="preserve" y="469.906" x="301" class="D E P">434</text><text xml:space="preserve" y="469.906" x="336" class="D E O">435</text><text xml:space="preserve" y="469.906" x="371" class="D E j">436</text><text xml:space="preserve" y="469.906" x="406" class="D E N">437</text><text xml:space="preserve" y="469.906" x="440" class="D E b">438</text><text xml:space="preserve" y="469.906" x="476" class="D F">439</text><text xml:space="preserve" y="469.906" x="510" class="D E H">440</text><text xml:space="preserve" y="469.906" x="546" class="D E G">441</text><text xml:space="preserve" y="469.906" x="580" class="D E J">442</text><text xml:space="preserve" y="469.906" x="615" class="D F">443</text><text xml:space="preserve" y="469.906" x="649" class="D E Q">444</text><text xml:space="preserve" y="469.906" x="684" class="D E e">445</text><text fill="#7e6bb4" xml:space="preserve" y="469.906" x="719" class="D E">446</text><text xml:space="preserve" y="469.906" x="754" class="D E r">447</text><text xml:space="preserve" y="469.906" x="788" class="D E K">448</text><text xml:space="preserve" y="469.906" x="823" class="D F">449</text><text xml:space="preserve" y="469.906" x="857" class="D E M">450</text><text xml:space="preserve" y="493.906" x="25" class="D E R">451</text><text xml:space="preserve" y="493.906" x="58" class="D E k">452</text><text xml:space="preserve" y="493.906" x="92" class="D E s">453</text><text fill="#a2569d" xml:space="preserve" y="493.906" x="127" class="D E">454</text><text xml:space="preserve" y="493.906" x="162" class="D E I">455</text><text xml:space="preserve" y="493.906" x="197" class="D E L">456</text><text xml:space="preserve" y="493.906" x="233" class="D F">457</text><text fill="#67ab80" xml:space="preserve" y="493.906" x="266" class="D E">458</text><text xml:space="preserve" y="493.906" x="301" class="D E J">459</text><text xml:space="preserve" y="493.906" x="336" class="D E N">460</text><text xml:space="preserve" y="493.906" x="373" class="D F">461</text><text xml:space="preserve" y="493.906" x="406" class="D E H">462</text><text xml:space="preserve" y="493.906" x="441" class="D F">463</text><text xml:space="preserve" y="493.906" x="475" class="D E O">464</text><text xml:space="preserve" y="493.906" x="510" class="D E P">465</text><text fill="#5db273" xml:space="preserve" y="493.906" x="545" class="D E">466</text><text xml:space="preserve" y="493.906" x="581" class="D F">467</text><text xml:space="preserve" y="493.906" x="614" class="D E I">468</text><text xml:space="preserve" y="493.906" x="649" class="D E Z">469</text><text xml:space="preserve" y="493.906" x="685" class="D E T">470</text><text fill="#72a694" xml:space="preserve" y="493.906" x="721" class="D E">471</text><text xml:space="preserve" y="493.906" x="754" class="D E X">472</text><text xml:space="preserve" y="493.906" x="789" class="D E S">473</text><text xml:space="preserve" y="493.906" x="823" class="D E c">474</text><text xml:space="preserve" y="493.906" x="858" class="D E L">475</text><text xml:space="preserve" y="517.906" x="24" class="D E J">476</text><text xml:space="preserve" y="517.906" x="58" class="D E V">477</text><text fill="#5c989e" xml:space="preserve" y="517.906" x="92" class="D E">478</text><text xml:space="preserve" y="517.906" x="128" class="D F">479</text><text xml:space="preserve" y="517.906" x="162" class="D E K">480</text><text xml:space="preserve" y="517.906" x="198" class="D E Q">481</text><text fill="#bb54ab" xml:space="preserve" y="517.906" x="232" class="D E">482</text><text xml:space="preserve" y="517.906" x="266" class="D E N">483</text><text xml:space="preserve" y="517.906" x="301" class="D E H">484</text><text xml:space="preserve" y="517.906" x="336" class="D E f">485</text><text xml:space="preserve" y="517.906" x="371" class="D E W">486</text><text xml:space="preserve" y="517.906" x="407" class="D F">487</text><text xml:space="preserve" y="517.906" x="440" class="D E Y">488</text><text fill="#aa85a2" xml:space="preserve" y="517.906" x="475" class="D E">489</text><text xml:space="preserve" y="517.906" x="510" class="D E G">490</text><text xml:space="preserve" y="517.906" x="547" class="D F">491</text><text xml:space="preserve" y="517.906" x="580" class="D E R">492</text><text xml:space="preserve" y="517.906" x="614" class="D E O">493</text><text xml:space="preserve" y="517.906" x="649" class="D E L">494</text><text xml:space="preserve" y="517.906" x="684" class="D E H">495</text><text xml:space="preserve" y="517.906" x="719" class="D E P">496</text><text xml:space="preserve" y="517.906" x="754" class="D E a">497</text><text xml:space="preserve" y="517.906" x="788" class="D E d">498</text><text xml:space="preserve" y="517.906" x="823" class="D F">499</text><text xml:space="preserve" y="517.906" x="857" class="D E M">500</text><text fill="#bca451" xml:space="preserve" y="541.906" x="25" class="D E">501</text><text fill="#40448a" xml:space="preserve" y="541.906" x="58" class="D E">502</text><text xml:space="preserve" y="541.906" x="93" class="D F">503</text><text xml:space="preserve" y="541.906" x="127" class="D E t">504</text><text xml:space="preserve" y="541.906" x="162" class="D E g">505</text><text xml:space="preserve" y="541.906" x="197" class="D E N">506</text><text xml:space="preserve" y="541.906" x="232" class="D E I">507</text><text xml:space="preserve" y="541.906" x="266" class="D E n">508</text><text xml:space="preserve" y="541.906" x="302" class="D F">509</text><text xml:space="preserve" y="541.906" x="337" class="D E J">510</text><text xml:space="preserve" y="541.906" x="373" class="D E b">511</text><text xml:space="preserve" y="541.906" x="407" class="D E K">512</text><text xml:space="preserve" y="541.906" x="441" class="D E L">513</text><text fill="#b9a185" xml:space="preserve" y="541.906" x="476" class="D E">514</text><text xml:space="preserve" y="541.906" x="511" class="D E h">515</text><text xml:space="preserve" y="541.906" x="546" class="D E S">516</text><text xml:space="preserve" y="541.906" x="581" class="D E T">517</text><text xml:space="preserve" y="541.906" x="615" class="D E Q">518</text><text fill="#8e8dba" xml:space="preserve" y="541.906" x="650" class="D E">519</text><text xml:space="preserve" y="541.906" x="684" class="D E I">520</text><text xml:space="preserve" y="541.906" x="721" class="D F">521</text><text xml:space="preserve" y="541.906" x="754" class="D E O">522</text><text xml:space="preserve" y="541.906" x="789" class="D F">523</text><text xml:space="preserve" y="541.906" x="823" class="D E o">524</text><text xml:space="preserve" y="541.906" x="857" class="D E G">525</text><text fill="#5f5fb5" xml:space="preserve" y="565.906" x="24" class="D E">526</text><text xml:space="preserve" y="565.906" x="58" class="D E P">527</text><text xml:space="preserve" y="565.906" x="92" class="D E K">528</text><text xml:space="preserve" y="565.906" x="127" class="D E N">529</text><text xml:space="preserve" y="565.906" x="162" class="D E V">530</text><text xml:space="preserve" y="565.906" x="198" class="D E X">531</text><text xml:space="preserve" y="565.906" x="232" class="D E L">532</text><text xml:space="preserve" y="565.906" x="266" class="D E R">533</text><text xml:space="preserve" y="565.906" x="301" class="D E e">534</text><text xml:space="preserve" y="565.906" x="336" class="D E i">535</text><text xml:space="preserve" y="565.906" x="371" class="D E Z">536</text><text fill="#79a79e" xml:space="preserve" y="565.906" x="406" class="D E">537</text><text fill="#a8b675" xml:space="preserve" y="565.906" x="440" class="D E">538</text><text xml:space="preserve" y="565.906" x="475" class="D E H">539</text><text xml:space="preserve" y="565.906" x="510" class="D E W">540</text><text xml:space="preserve" y="565.906" x="547" class="D F">541</text><text fill="#4c7c9f" xml:space="preserve" y="565.906" x="580" class="D E">542</text><text fill="#656186" xml:space="preserve" y="565.906" x="614" class="D E">543</text><text xml:space="preserve" y="565.906" x="649" class="D E J">544</text><text xml:space="preserve" y="565.906" x="684" class="D E j">545</text><text xml:space="preserve" y="565.906" x="719" class="D E I">546</text><text xml:space="preserve" y="565.906" x="755" class="D F">547</text><text xml:space="preserve" y="565.906" x="788" class="D E p">548</text><text xml:space="preserve" y="565.906" x="823" class="D E Y">549</text><text xml:space="preserve" y="565.906" x="857" class="D E H">550</text><text xml:space="preserve" y="589.906" x="25" class="D E O">551</text><text xml:space="preserve" y="589.906" x="58" class="D E N">552</text><text xml:space="preserve" y="589.906" x="92" class="D E c">553</text><text fill="#5d90b0" xml:space="preserve" y="589.906" x="127" class="D E">554</text><text xml:space="preserve" y="589.906" x="162" class="D E Q">555</text><text xml:space="preserve" y="589.906" x="197" class="D E q">556</text><text xml:space="preserve" y="589.906" x="233" class="D F">557</text><text xml:space="preserve" y="589.906" x="266" class="D E P">558</text><text xml:space="preserve" y="589.906" x="301" class="D E S">559</text><text xml:space="preserve" y="589.906" x="336" class="D E K">560</text><text xml:space="preserve" y="589.906" x="372" class="D E J">561</text><text fill="#578f8b" xml:space="preserve" y="589.906" x="406" class="D E">562</text><text xml:space="preserve" y="589.906" x="441" class="D F">563</text><text xml:space="preserve" y="589.906" x="475" class="D E T">564</text><text xml:space="preserve" y="589.906" x="510" class="D E k">565</text><text fill="#8985af" xml:space="preserve" y="589.906" x="545" class="D E">566</text><text xml:space="preserve" y="589.906" x="580" class="D E m">567</text><text xml:space="preserve" y="589.906" x="614" class="D E a">568</text><text xml:space="preserve" y="589.906" x="650" class="D F">569</text><text xml:space="preserve" y="589.906" x="685" class="D E L">570</text><text xml:space="preserve" y="589.906" x="721" class="D F">571</text><text xml:space="preserve" y="589.906" x="754" class="D E I">572</text><text fill="#94894b" xml:space="preserve" y="589.906" x="789" class="D E">573</text><text xml:space="preserve" y="589.906" x="823" class="D E R">574</text><text xml:space="preserve" y="589.906" x="858" class="D E N">575</text><text xml:space="preserve" y="613.906" x="24" class="D E t">576</text><text xml:space="preserve" y="613.906" x="59" class="D F">577</text><text xml:space="preserve" y="613.906" x="92" class="D E J">578</text><text fill="#87759e" xml:space="preserve" y="613.906" x="128" class="D E">579</text><text xml:space="preserve" y="613.906" x="162" class="D E O">580</text><text xml:space="preserve" y="613.906" x="198" class="D E d">581</text><text xml:space="preserve" y="613.906" x="232" class="D E f">582</text><text xml:space="preserve" y="613.906" x="266" class="D E V">583</text><text xml:space="preserve" y="613.906" x="301" class="D E b">584</text><text xml:space="preserve" y="613.906" x="336" class="D E I">585</text><text fill="#ab6984" xml:space="preserve" y="613.906" x="371" class="D E">586</text><text xml:space="preserve" y="613.906" x="407" class="D F">587</text><text xml:space="preserve" y="613.906" x="440" class="D E G">588</text><text xml:space="preserve" y="613.906" x="475" class="D E P">589</text><text xml:space="preserve" y="613.906" x="510" class="D E X">590</text><text fill="#9f6db4" xml:space="preserve" y="613.906" x="546" class="D E">591</text><text xml:space="preserve" y="613.906" x="580" class="D E Q">592</text><text xml:space="preserve" y="613.906" x="615" class="D F">593</text><text xml:space="preserve" y="613.906" x="649" class="D E W">594</text><text xml:space="preserve" y="613.906" x="684" class="D E J">595</text><text xml:space="preserve" y="613.906" x="719" class="D E r">596</text><text fill="#aebba6" xml:space="preserve" y="613.906" x="754" class="D E">597</text><text xml:space="preserve" y="613.906" x="788" class="D E N">598</text><text xml:space="preserve" y="613.906" x="823" class="D F">599</text><text xml:space="preserve" y="613.906" x="857" class="D E M">600</text><text xml:space="preserve" y="637.906" x="26" class="D F">601</text><text xml:space="preserve" y="637.906" x="58" class="D E S">602</text><text xml:space="preserve" y="637.906" x="92" class="D E Z">603</text><text xml:space="preserve" y="637.906" x="127" class="D E s">604</text><text xml:space="preserve" y="637.906" x="162" class="D E H">605</text><text xml:space="preserve" y="637.906" x="197" class="D E g">606</text><text xml:space="preserve" y="637.906" x="233" class="D F">607</text><text xml:space="preserve" y="637.906" x="266" class="D E L">608</text><text xml:space="preserve" y="637.906" x="301" class="D E O">609</text><text xml:space="preserve" y="637.906" x="337" class="D E Y">610</text><text xml:space="preserve" y="637.906" x="373" class="D E T">611</text><text xml:space="preserve" y="637.906" x="407" class="D E J">612</text><text xml:space="preserve" y="637.906" x="442" class="D F">613</text><text fill="#9b6f5d" xml:space="preserve" y="637.906" x="476" class="D E">614</text><text xml:space="preserve" y="637.906" x="511" class="D E R">615</text><text xml:space="preserve" y="637.906" x="546" class="D E H">616</text><text xml:space="preserve" y="637.906" x="582" class="D F">617</text><text xml:space="preserve" y="637.906" x="615" class="D E h">618</text><text xml:space="preserve" y="637.906" x="651" class="D F">619</text><text xml:space="preserve" y="637.906" x="684" class="D E P">620</text><text xml:space="preserve" y="637.906" x="720" class="D E N">621</text><text fill="#be7384" xml:space="preserve" y="637.906" x="754" class="D E">622</text><text xml:space="preserve" y="637.906" x="788" class="D E e">623</text><text xml:space="preserve" y="637.906" x="823" class="D E K">624</text><text xml:space="preserve" y="637.906" x="857" class="D E M">625</text></g></svg>
diff --git a/examples/async/eratosthenes/eratosthenes.py b/examples/async/eratosthenes/eratosthenes.py
new file mode 100644
index 000000000..7123de739
--- /dev/null
+++ b/examples/async/eratosthenes/eratosthenes.py
@@ -0,0 +1,202 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtCore import (Qt, QEvent, QObject, QTimer, Signal, Slot)
+from PySide6.QtGui import (QColor, QFont, QPalette)
+from PySide6.QtWidgets import (QApplication, QGridLayout, QLabel, QMainWindow, QVBoxLayout, QWidget)
+
+import outcome
+import signal
+import sys
+import traceback
+import trio
+from random import randint
+
+
+class MainWindow(QMainWindow):
+
+ set_num = Signal(int, QColor)
+
+ def __init__(self, rows, cols):
+ super().__init__()
+
+ self.rows = rows
+ self.cols = cols
+
+ widget_central = QWidget()
+ self.setCentralWidget(widget_central)
+
+ layout_outer = QVBoxLayout(widget_central)
+
+ self.widget_outer_text = QLabel()
+ font = QFont()
+ font.setPointSize(14)
+ self.widget_outer_text.setFont(font)
+ layout_outer.addWidget(self.widget_outer_text, alignment=Qt.AlignmentFlag.AlignCenter)
+
+ widget_inner_grid = QWidget()
+ layout_outer.addWidget(widget_inner_grid, alignment=Qt.AlignmentFlag.AlignCenter)
+
+ self.layout_inner_grid = QGridLayout(widget_inner_grid)
+ k = 1
+ for i in range(self.rows):
+ for j in range(self.cols):
+ box = QLabel(f"{k}")
+ self.layout_inner_grid.addWidget(box, i, j, Qt.AlignmentFlag.AlignCenter)
+ k += 1
+
+ self.set_num.connect(self.set_num_handler)
+
+ @Slot(int, QColor)
+ def set_num_handler(self, i, color):
+ row = int((i - 1) / self.cols)
+ col = (i - 1) - (row * self.cols)
+ widget = self.layout_inner_grid.itemAtPosition(row, col).widget()
+
+ font = QFont()
+ font.setWeight(QFont.Bold)
+ palette = QPalette()
+ palette.setColor(QPalette.WindowText, color)
+ widget.setFont(font)
+ widget.setPalette(palette)
+
+
+class Eratosthenes():
+
+ """ This Sieve of Eratosthenes runs on a configurable tick (default
+ 0.1 seconds). At each tick, a new subroutine will be created
+ that will check multiples of the next prime number. Each of
+ these subroutines also operates on the same second tick. The
+ tick is coordinated through the trio event loop's internal clock. """
+
+ def __init__(self, num, window, tick=0.1):
+ self.num = num
+ self.sieve = [True] * self.num
+ self.base = 0
+ self.window = window
+ self.tick = tick
+ self.coroutines = []
+ self.done = False
+ self.nursery = None
+
+ def get_tick(self):
+ return trio.lowlevel.current_clock().current_time() + self.tick
+
+ async def start(self):
+ async with trio.open_nursery() as self.nursery:
+ self.nursery.start_soon(self.update_text)
+ while self.base <= self.num / 2:
+ await trio.sleep_until(self.get_tick())
+ for i in range(self.base + 1, self.num):
+ if self.sieve[i]:
+ self.base = i
+ break
+ self.nursery.start_soon(self.mark_number, self.base + 1)
+ while sum(self.coroutines) > 0:
+ await trio.sleep_until(self.get_tick())
+ self.done = True
+
+ async def mark_number(self, base):
+ id = len(self.coroutines)
+ self.coroutines.append(1)
+ color = QColor(randint(64, 192), randint(64, 192), randint(64, 192))
+ for i in range(2 * base, self.num + 1, base):
+ if self.sieve[i - 1]:
+ self.sieve[i - 1] = False
+ self.window.set_num.emit(i, color)
+ await trio.sleep_until(self.get_tick())
+ self.coroutines[id] = 0
+
+ async def update_text(self):
+ while not self.done:
+ tick = self.get_tick()
+ await trio.sleep_until(tick)
+ if int(tick) % 2:
+ text = "⚙️ ...Calculating prime numbers... ⚙️"
+ else:
+ text = "👩‍💻 ...Hacking the universe... 👩‍💻"
+ self.window.widget_outer_text.setText(text)
+
+ self.window.widget_outer_text.setText(
+ "🥳 Congratulations! You found all the prime numbers and solved mathematics. 🥳"
+ )
+
+
+class AsyncHelper(QObject):
+
+ trigger_signal = Signal()
+
+ class ReenterQtObject(QObject):
+ """ This is a QObject to which an event will be posted, allowing
+ Trio to resume when the event is handled. event.fn() is the
+ next entry point of the Trio event loop. """
+ def event(self, event):
+ if event.type() == QEvent.User + 1:
+ event.fn()
+ return True
+ return False
+
+ class ReenterQtEvent(QEvent):
+ """ This is the QEvent that will be handled by the ReenterQtObject.
+ self.fn is the next entry point of the Trio event loop. """
+ def __init__(self, fn):
+ super().__init__(QEvent.Type(QEvent.User + 1))
+ self.fn = fn
+
+ def __init__(self, entry=None):
+ super().__init__()
+ self.reenter_qt = self.ReenterQtObject()
+ self.entry = entry
+
+ def set_entry(self, entry):
+ self.entry = entry
+
+ @Slot()
+ def launch_guest_run(self):
+ """ To use Trio and Qt together, one must run the Trio event
+ loop as a "guest" inside the Qt "host" event loop. """
+ if not self.entry:
+ raise Exception("No entry point for the Trio guest run was set.")
+ trio.lowlevel.start_guest_run(
+ self.entry,
+ run_sync_soon_threadsafe=self.next_guest_run_schedule,
+ done_callback=self.trio_done_callback,
+ )
+
+ def next_guest_run_schedule(self, fn):
+ """ This function serves to re-schedule the guest (Trio) event
+ loop inside the host (Qt) event loop. It is called by Trio
+ at the end of an event loop run in order to relinquish back
+ to Qt's event loop. By posting an event on the Qt event loop
+ that contains Trio's next entry point, it ensures that Trio's
+ event loop will be scheduled again by Qt. """
+ QApplication.postEvent(self.reenter_qt, self.ReenterQtEvent(fn))
+
+ def trio_done_callback(self, outcome_):
+ """ This function is called by Trio when its event loop has
+ finished. """
+ if isinstance(outcome_, outcome.Error):
+ error = outcome_.error
+ traceback.print_exception(type(error), error, error.__traceback__)
+
+
+if __name__ == "__main__":
+ rows = 40
+ cols = 40
+ num = rows * cols
+
+ app = QApplication(sys.argv)
+ main_window = MainWindow(rows, cols)
+ eratosthenes = Eratosthenes(num, main_window)
+ async_helper = AsyncHelper(entry=eratosthenes.start)
+
+ # This establishes the entry point for the Trio guest run. It varies
+ # depending on how and when its event loop is to be triggered, e.g.,
+ # from the beginning (as here) or rather at a specific moment like
+ # a button press.
+ QTimer.singleShot(0, async_helper.launch_guest_run)
+
+ main_window.show()
+
+ signal.signal(signal.SIGINT, signal.SIG_DFL)
+ app.exec()
diff --git a/examples/async/eratosthenes/eratosthenes.pyproject b/examples/async/eratosthenes/eratosthenes.pyproject
new file mode 100644
index 000000000..8ea189b3c
--- /dev/null
+++ b/examples/async/eratosthenes/eratosthenes.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["eratosthenes.py"]
+}
diff --git a/examples/async/eratosthenes/requirements.txt b/examples/async/eratosthenes/requirements.txt
new file mode 100644
index 000000000..e2cc10204
--- /dev/null
+++ b/examples/async/eratosthenes/requirements.txt
@@ -0,0 +1,2 @@
+trio
+outcome
diff --git a/examples/async/minimal/doc/minimal.png b/examples/async/minimal/doc/minimal.png
new file mode 100644
index 000000000..b8a18963f
--- /dev/null
+++ b/examples/async/minimal/doc/minimal.png
Binary files differ
diff --git a/examples/async/minimal/doc/minimal.rst b/examples/async/minimal/doc/minimal.rst
new file mode 100644
index 000000000..a72e8a73c
--- /dev/null
+++ b/examples/async/minimal/doc/minimal.rst
@@ -0,0 +1,41 @@
+Async examples
+==============
+
+The Python language provides keywords for asynchronous operations, i.e.,
+"async" to define coroutines or "await" to schedule asynchronous calls in the
+event loop (see `PEP 492 <https://peps.python.org/pep-0492/>`_). It is up to
+packages to implement an event loop, support for these keywords, and more.
+
+One such package is `trio`. Since both an async package and Qt itself work with
+event loops, special care must be taken to ensure that both event loops work
+with each other. trio offers a dedicated `low-level API
+<https://trio.readthedocs.io/en/stable/reference-lowlevel.html>`_ for more
+complicated use cases such as this. Specifically, there exists a function
+`start_guest_run` that enables running the Trio event loop as a "guest" inside
+another event loop - Qt's in our case.
+
+Based on this functionality, two examples for async usage with Qt have been
+implemented: `eratosthenes` and `minimal`:
+
+.. image:: minimal.png
+ :alt: Async example: Minimal
+
+* `eratosthenes` is a more extensive example that visualizes the Sieve of
+Eratosthenes algorithm. This algorithm per se is not one that is particularly
+suitable for asynchronous operations as it's not I/O-heavy, but synchronizing
+coroutines to a configurable tick allows for a good visualization.
+* `minimal` is a minimal example featuring a button that triggers an
+asynchronous coroutine with a sleep. It is designed to highlight which
+boilerplate code is essential for an async program with Qt and offers a
+starting point for more complex programs.
+
+Both examples feature:
+
+1. A window class.
+2. An `AsyncHelper` class containing `start_guest_run` plus helpers and
+callbacks necessary for its invocation. The entry point for the Trio guest run
+is provided as an argument from outside, which can be any async function.
+
+While `eratosthenes` offloads the asynchronous logic that will run in trio's
+event loop into a separate class, `minimal` demonstrates that async functions
+can be integrated into any class, including subclasses of Qt classes.
diff --git a/examples/async/minimal/minimal.py b/examples/async/minimal/minimal.py
new file mode 100644
index 000000000..1769a2dfc
--- /dev/null
+++ b/examples/async/minimal/minimal.py
@@ -0,0 +1,115 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtCore import (Qt, QEvent, QObject, Signal, Slot)
+from PySide6.QtWidgets import (QApplication, QLabel, QMainWindow, QPushButton, QVBoxLayout, QWidget)
+
+import outcome
+import signal
+import sys
+import traceback
+import trio
+
+
+class MainWindow(QMainWindow):
+
+ def __init__(self, async_signal):
+ super().__init__()
+
+ self.async_signal = async_signal
+
+ widget = QWidget()
+ self.setCentralWidget(widget)
+
+ layout = QVBoxLayout(widget)
+
+ self.text = QLabel("The answer is 42.")
+ layout.addWidget(self.text, alignment=Qt.AlignmentFlag.AlignCenter)
+
+ async_trigger = QPushButton(text="What is the question?")
+ async_trigger.clicked.connect(self.async_start)
+ layout.addWidget(async_trigger, alignment=Qt.AlignmentFlag.AlignCenter)
+
+ @Slot()
+ def async_start(self):
+ self.async_signal.emit()
+
+ async def set_text(self):
+ await trio.sleep(1)
+ self.text.setText("What do you get if you multiply six by nine?")
+
+
+class AsyncHelper(QObject):
+
+ trigger_signal = Signal()
+
+ class ReenterQtObject(QObject):
+ """ This is a QObject to which an event will be posted, allowing
+ Trio to resume when the event is handled. event.fn() is the
+ next entry point of the Trio event loop. """
+ def event(self, event):
+ if event.type() == QEvent.User + 1:
+ event.fn()
+ return True
+ return False
+
+ class ReenterQtEvent(QEvent):
+ """ This is the QEvent that will be handled by the ReenterQtObject.
+ self.fn is the next entry point of the Trio event loop. """
+ def __init__(self, fn):
+ super().__init__(QEvent.Type(QEvent.User + 1))
+ self.fn = fn
+
+ def __init__(self, entry=None):
+ super().__init__()
+ self.reenter_qt = self.ReenterQtObject()
+ self.entry = entry
+
+ def set_entry(self, entry):
+ self.entry = entry
+
+ @Slot()
+ def launch_guest_run(self):
+ """ To use Trio and Qt together, one must run the Trio event
+ loop as a "guest" inside the Qt "host" event loop. """
+ if not self.entry:
+ raise Exception("No entry point for the Trio guest run was set.")
+ trio.lowlevel.start_guest_run(
+ self.entry,
+ run_sync_soon_threadsafe=self.next_guest_run_schedule,
+ done_callback=self.trio_done_callback,
+ )
+
+ def next_guest_run_schedule(self, fn):
+ """ This function serves to re-schedule the guest (Trio) event
+ loop inside the host (Qt) event loop. It is called by Trio
+ at the end of an event loop run in order to relinquish back
+ to Qt's event loop. By posting an event on the Qt event loop
+ that contains Trio's next entry point, it ensures that Trio's
+ event loop will be scheduled again by Qt. """
+ QApplication.postEvent(self.reenter_qt, self.ReenterQtEvent(fn))
+
+ def trio_done_callback(self, outcome_):
+ """ This function is called by Trio when its event loop has
+ finished. """
+ if isinstance(outcome_, outcome.Error):
+ error = outcome_.error
+ traceback.print_exception(type(error), error, error.__traceback__)
+
+
+if __name__ == "__main__":
+ app = QApplication(sys.argv)
+ async_helper = AsyncHelper()
+ main_window = MainWindow(async_helper.trigger_signal)
+ async_helper.set_entry(main_window.set_text)
+
+ # This establishes the entry point for the Trio guest run. It varies
+ # depending on how and when its event loop is to be triggered, e.g.,
+ # at a specific moment like a button press (as here) or rather from
+ # the beginning.
+ async_helper.trigger_signal.connect(async_helper.launch_guest_run)
+
+ main_window.show()
+
+ signal.signal(signal.SIGINT, signal.SIG_DFL)
+ app.exec()
diff --git a/examples/async/minimal/minimal.pyproject b/examples/async/minimal/minimal.pyproject
new file mode 100644
index 000000000..97ff6dbc5
--- /dev/null
+++ b/examples/async/minimal/minimal.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["minimal.py"]
+}
diff --git a/examples/async/minimal/requirements.txt b/examples/async/minimal/requirements.txt
new file mode 100644
index 000000000..ae0d704f0
--- /dev/null
+++ b/examples/async/minimal/requirements.txt
@@ -0,0 +1 @@
+trio