/docs/source/tutorial/index.rst

https://bitbucket.org/prologic/circuits/ · ReStructuredText · 346 lines · 230 code · 116 blank · 0 comment · 0 complexity · 57d7e087cde904093f018273860b87b7 MD5 · raw file

  1. .. _Python Programming Language: http://www.python.org/
  2. Tutorial
  3. ========
  4. Overview
  5. --------
  6. Welcome to the circuits tutorial. This 5-minute tutorial is going to walk you
  7. through the basic concepts of circuits. The goal will be to introduce
  8. new concepts incrementally with walk-through examples that you can try out!
  9. By the time you've finished, you should have a good basic understanding
  10. of circuits, how it feels and where to go from there.
  11. The Component
  12. -------------
  13. First up, let's show how you can use the ``Component`` and run it in a very
  14. simple application.
  15. .. literalinclude:: 001.py
  16. :language: python
  17. :linenos:
  18. :download:`Download 001.py <001.py>`
  19. Okay so that's pretty boring as it doesn't do very much! But that's okay...
  20. Read on!
  21. Let's try to create our own custom Component called ``MyComponent``.
  22. .. literalinclude:: 002.py
  23. :language: python
  24. :linenos:
  25. :download:`Download 002.py <002.py>`
  26. Okay, so this still isn't very sueful! But at least we can create
  27. components with the behavior we want.
  28. Let's move on to something more interesting...
  29. Event Handlers
  30. --------------
  31. Let's now extend our little example to say "Hello World!" when its started.
  32. .. literalinclude:: 003.py
  33. :language: python
  34. :linenos:
  35. :download:`Download 003.py <003.py>`
  36. Here we've created a simple **Event Handler** that listens for events on
  37. the "started" channel. Methods defined in a Component are converted into
  38. Event Handlers.
  39. Running this we get::
  40. Hello World!
  41. Alright! We have something slightly more useful! Whoohoo it says hello!
  42. .. note:: Press ^C (*Ctrl + C*) to exit.
  43. Registering Components
  44. ----------------------
  45. So now that we've learned how to use a Component, create a custom Component
  46. and create simple Event Handlers, let's try something a bit more complex
  47. and create two components that each do something fairly simple.
  48. Let's create two components:
  49. - ``Bob``
  50. - ``Fred``
  51. .. literalinclude:: 004.py
  52. :language: python
  53. :linenos:
  54. :download:`Download 004.py <004.py>`
  55. Notice the way we register the two components ``Bob`` and ``Fred`` together
  56. ? Don't worry if this doesn't make sense right now. Think of it as putting
  57. two components together and plugging them into a circuits board.
  58. Running this example produces the following result::
  59. Hello I'm Bob!
  60. Hello I'm Fred!
  61. Cool! We have two components that each do something and print a simple
  62. message on the screen!
  63. Complex Components
  64. ------------------
  65. Now, what if we wanted to create a Complex Component ? Let's say we wanted
  66. to create a new Component made up of two other smaller components ?
  67. We can do this by simply registering components to a Complex Component
  68. during initialization.
  69. .. literalinclude:: 005.py
  70. :language: python
  71. :linenos:
  72. :download:`Download 005.py <005.py>`
  73. So now ``Pound`` is a Component that consists of two other components
  74. registered to it: ``Bob`` and ``Fred``
  75. The output of this is identical to the previous::
  76. * <Pound/* 3391:MainThread (queued=0, channels=1, handlers=3) [R]>
  77. * <Bob/* 3391:MainThread (queued=0, channels=1, handlers=1) [S]>
  78. * <Fred/* 3391:MainThread (queued=0, channels=1, handlers=1) [S]>
  79. Hello I'm Bob!
  80. Hello I'm Fred!
  81. The only difference is that ``Bob`` and ``Fred`` are now part of a more
  82. Complex Component called ``Pound``. This can be illustrated by the
  83. following diagram:
  84. .. graphviz::
  85. digraph G {
  86. "Pound-1344" -> "Bob-9b0c";
  87. "Pound-1344" -> "Fred-e98a";
  88. }
  89. .. note::
  90. The extra lines in the above output are an ASCII representation of the
  91. above graph (*produced by pydot + graphviz*).
  92. Cool :-)
  93. Component Inheritence
  94. ---------------------
  95. Since circuits is a framework written for the `Python Programming
  96. Language`_ it naturally inherits properties of Object Orientated
  97. Programming (OOP) -- such as inheritence.
  98. So let's take our ``Bob`` and ``Fred`` components and create a Base
  99. Component called ``Dog`` and modify our two dogs (``Bob`` and ``Fred``) to
  100. subclass this.
  101. .. literalinclude:: 006.py
  102. :language: python
  103. :linenos:
  104. :download:`Download 006.py <006.py>`
  105. Now let's try to run this and see what happens::
  106. Woof! I'm Bob!
  107. Woof! I'm Fred!
  108. So both dogs barked~ Hmmm
  109. Component Channels
  110. ------------------
  111. What if we only want one of our dogs to bark ? How do we do this without
  112. causing the other one to bark as well ?
  113. Easy! Use a separate ``channel`` like so:
  114. .. literalinclude:: 007.py
  115. :language: python
  116. :linenos:
  117. :download:`Download 007.py <007.py>`
  118. .. note::
  119. Events can be fired with either the ``.fire(...)`` or ``.fireEvent(...)``
  120. method.
  121. If you run this, you'll get::
  122. Woof! I'm Bob!
  123. Event Objects
  124. -------------
  125. So far in our tutorial we have been defining an Event Handler for a builtin
  126. Event called ``Started`` (*which incidently gets fired on a channel called
  127. "started"*). What if we wanted to define our own Event Handlers and our own
  128. Events ? You've already seen how easy it is to create a new Event Handler
  129. by simply defining a normal Python method on a Component.
  130. Defining your own Events helps with documentation and testing and makes
  131. things a little easier.
  132. Example::
  133. class MyEvent(Event):
  134. """MyEvent"""
  135. So here's our example where we'll define a new Event claled ``Bark``
  136. and make our ``Dog`` fire a ``Bark`` event when our application starts up.
  137. .. literalinclude:: 008.py
  138. :language: python
  139. :linenos:
  140. :download:`Download 008.py <008.py>`
  141. If you run this, you'll get::
  142. Woof! I'm Bob!
  143. Woof! I'm Fred!
  144. The Debugger
  145. ------------
  146. Lastly...
  147. Asynchronous programming has many advntages but can be a little harder to
  148. write and follow. A silently caught exception in an Event Handler, or an Event
  149. that never gets fired, or any number of other weird things can cause your
  150. application to fail and leave you scratching your head.
  151. Fortunately circuits comes with a ``Debugger`` Component to help you keep
  152. track of what's going on in your application, and allows you to tell what
  153. your application is doing.
  154. Let's say that we defined out ``bark`` Event Handler in our ``Dog``
  155. Component as follows::
  156. def bark(self):
  157. print("Woof! I'm %s!" % name)
  158. Now clearly there is no such variable as ``name`` in the local scope.
  159. For reference here's the entire example...
  160. .. literalinclude:: 009.py
  161. :language: python
  162. :linenos:
  163. :download:`Download 009.py <009.py>`
  164. If you run this, you'll get:
  165. That's right! You get nothing! Why ? Well in circuits any error or
  166. exception that occurs in a running application is automatically caught and
  167. dealt with in a way that lets your application "keep on going". Crashing is
  168. unwanted behavior in a system so we expect to be able to recover from
  169. horrible situations.
  170. SO what do we do ? Welll that's easy. circuits come with a ``Debugger``
  171. that lets you log all events as well as all errors so you can quickly and
  172. easily discover which Event is causing a problem and which Event Handler to
  173. look at.
  174. If you change Line 34 of our example...
  175. From:
  176. .. literalinclude:: 009.py
  177. :language: python
  178. :lines: 34
  179. .. code-block:: python
  180. from circuits import Debugger
  181. (Pound() + Debugger()).run()
  182. Then run this, you'll get the following::
  183. <Registered[bob:registered] [<Bob/bob 3191:MainThread (queued=0, channels=2, handlers=2) [S]>, <Pound/* 3191:MainThread (queued=0, channels=5, handlers=5) [R]>] {}>
  184. <Registered[fred:registered] [<Fred/fred 3191:MainThread (queued=0, channels=2, handlers=2) [S]>, <Pound/* 3191:MainThread (queued=0, channels=5, handlers=5) [R]>] {}>
  185. <Registered[*:registered] [<Debugger/* 3191:MainThread (queued=0, channels=1, handlers=1) [S]>, <Pound/* 3191:MainThread (queued=0, channels=5, handlers=5) [R]>] {}>
  186. <Started[*:started] [<Pound/* 3191:MainThread (queued=0, channels=5, handlers=5) [R]>, None] {}>
  187. <Bark[bob:bark] [] {}>
  188. <Bark[fred:bark] [] {}>
  189. <Error[*:exception] [<type 'exceptions.NameError'>, NameError("global name 'name' is not defined",), [' File "/home/prologic/work/circuits/circuits/core/manager.py", line 459, in __handleEvent\n retval = handler(*eargs, **ekwargs)\n', ' File "source/tutorial/009.py", line 22, in bark\n print("Woof! I\'m %s!" % name)\n'], <bound method ?.bark of <Bob/bob 3191:MainThread (queued=0, channels=2, handlers=2) [S]>>] {}>
  190. ERROR <listener on ('bark',) {target='bob', priority=0.0}> (<type 'exceptions.NameError'>): global name 'name' is not defined
  191. File "/home/prologic/work/circuits/circuits/core/manager.py", line 459, in __handleEvent
  192. retval = handler(*eargs, **ekwargs)
  193. File "source/tutorial/009.py", line 22, in bark
  194. print("Woof! I'm %s!" % name)
  195. <Error[*:exception] [<type 'exceptions.NameError'>, NameError("global name 'name' is not defined",), [' File "/home/prologic/work/circuits/circuits/core/manager.py", line 459, in __handleEvent\n retval = handler(*eargs, **ekwargs)\n', ' File "source/tutorial/009.py", line 22, in bark\n print("Woof! I\'m %s!" % name)\n'], <bound method ?.bark of <Fred/fred 3191:MainThread (queued=0, channels=2, handlers=2) [S]>>] {}>
  196. ERROR <listener on ('bark',) {target='fred', priority=0.0}> (<type 'exceptions.NameError'>): global name 'name' is not defined
  197. File "/home/prologic/work/circuits/circuits/core/manager.py", line 459, in __handleEvent
  198. retval = handler(*eargs, **ekwargs)
  199. File "source/tutorial/009.py", line 22, in bark
  200. print("Woof! I'm %s!" % name)
  201. ^C<Signal[*:signal] [2, <frame object at 0x808e8ec>] {}>
  202. <Stopped[*:stopped] [<Pound/* 3191:MainThread (queued=0, channels=5, handlers=5) [S]>] {}>
  203. <Stopped[*:stopped] [<Pound/* 3191:MainThread (queued=0, channels=5, handlers=5) [S]>] {}>
  204. You'll notice whereas there was no output before there is now a pretty
  205. detailed output with the ``Debugger`` added to the application. Looking
  206. through the output, we find that the application does indeed start
  207. correctly, but when we fire our ``Bark`` Event it coughs up two exceptions,
  208. one for each of our dogs (``Bob`` and ``Fred``).
  209. From the error we can tell where the error is and roughly where to look in
  210. the code.
  211. .. note::
  212. You'll notice many other events that are displayed in the above output.
  213. These are all default events that circuits has builtin which your
  214. application can respond to. Each builtin Event has a special meaning
  215. with relation to the state of the application at that point.
  216. See: :py:mod:`circuits.core.events` for detailed documentation regarding
  217. these events.
  218. The correct code for the ``bark`` Event Handler should be::
  219. def bark(self):
  220. print("Woof! I'm %s!" % self.name)
  221. Running again with our coorection results in the expected output::
  222. Woof! I'm Bob!
  223. Woof! I'm Fred!
  224. That's it folks!
  225. Hopefully this gives you a feel of what circuits is all about and a easy
  226. tutorial on some of the basic concepts. As you're no doubt itching to get
  227. started on your next circuits project, here's some recommended reading:
  228. - :doc:`../faq`
  229. - :doc:`../guides/index`
  230. - :doc:`../api/index`